From f92a7557d8226a0b78e6ff8eb95cc11fd26ace25 Mon Sep 17 00:00:00 2001 From: Simon Corsin Date: Fri, 17 Apr 2026 12:13:16 -0700 Subject: [PATCH 1/9] Update yoga lib (cherry picked from commit 34df6ba73d0837ad10e153c6c3839cdbc908264f) --- third-party/yoga/src/yoga/Bitfield.h | 145 - third-party/yoga/src/yoga/CMakeLists.txt | 41 + third-party/yoga/src/yoga/CompactValue.h | 184 - third-party/yoga/src/yoga/Utils.cpp | 67 - third-party/yoga/src/yoga/Utils.h | 143 - third-party/yoga/src/yoga/YGConfig.cpp | 108 +- third-party/yoga/src/yoga/YGConfig.h | 220 +- third-party/yoga/src/yoga/YGEnums.cpp | 102 +- third-party/yoga/src/yoga/YGEnums.h | 154 +- third-party/yoga/src/yoga/YGFloatOptional.h | 70 - third-party/yoga/src/yoga/YGLayout.cpp | 42 - third-party/yoga/src/yoga/YGLayout.hpp | 75 - third-party/yoga/src/yoga/YGMacros.h | 86 +- third-party/yoga/src/yoga/YGNode.cpp | 745 +-- third-party/yoga/src/yoga/YGNode.h | 611 ++- third-party/yoga/src/yoga/YGNodeLayout.cpp | 100 + third-party/yoga/src/yoga/YGNodeLayout.h | 45 + third-party/yoga/src/yoga/YGNodePrint.cpp | 225 - third-party/yoga/src/yoga/YGNodePrint.h | 25 - third-party/yoga/src/yoga/YGNodeStyle.cpp | 774 +++ third-party/yoga/src/yoga/YGNodeStyle.h | 243 + third-party/yoga/src/yoga/YGPixelGrid.cpp | 22 + third-party/yoga/src/yoga/YGPixelGrid.h | 29 + third-party/yoga/src/yoga/YGStyle.cpp | 56 - third-party/yoga/src/yoga/YGStyle.h | 203 - third-party/yoga/src/yoga/YGValue.cpp | 12 +- third-party/yoga/src/yoga/YGValue.h | 85 +- third-party/yoga/src/yoga/Yoga-internal.h | 151 - third-party/yoga/src/yoga/Yoga.cpp | 4406 ----------------- third-party/yoga/src/yoga/Yoga.h | 372 +- .../src/yoga/algorithm/AbsoluteLayout.cpp | 593 +++ .../yoga/src/yoga/algorithm/AbsoluteLayout.h | 41 + third-party/yoga/src/yoga/algorithm/Align.h | 83 + .../yoga/src/yoga/algorithm/Baseline.cpp | 78 + .../yoga/src/yoga/algorithm/Baseline.h | 21 + .../yoga/src/yoga/algorithm/BoundAxis.h | 79 + third-party/yoga/src/yoga/algorithm/Cache.cpp | 121 + third-party/yoga/src/yoga/algorithm/Cache.h | 30 + .../src/yoga/algorithm/CalculateLayout.cpp | 2528 ++++++++++ .../yoga/src/yoga/algorithm/CalculateLayout.h | 60 + .../yoga/src/yoga/algorithm/FlexDirection.h | 120 + .../yoga/src/yoga/algorithm/FlexLine.cpp | 124 + .../yoga/src/yoga/algorithm/FlexLine.h | 75 + .../yoga/src/yoga/algorithm/PixelGrid.cpp | 135 + .../yoga/src/yoga/algorithm/PixelGrid.h | 29 + .../yoga/src/yoga/algorithm/SizingMode.h | 73 + .../src/yoga/algorithm/TrailingPosition.h | 44 + third-party/yoga/src/yoga/config/Config.cpp | 135 + third-party/yoga/src/yoga/config/Config.h | 92 + .../yoga/src/yoga/debug/AssertFatal.cpp | 54 + third-party/yoga/src/yoga/debug/AssertFatal.h | 29 + third-party/yoga/src/yoga/debug/Log.cpp | 107 + third-party/yoga/src/yoga/debug/Log.h | 34 + third-party/yoga/src/yoga/enums/Align.h | 49 + third-party/yoga/src/yoga/enums/BoxSizing.h | 40 + third-party/yoga/src/yoga/enums/Dimension.h | 40 + third-party/yoga/src/yoga/enums/Direction.h | 41 + third-party/yoga/src/yoga/enums/Display.h | 42 + third-party/yoga/src/yoga/enums/Edge.h | 47 + third-party/yoga/src/yoga/enums/Errata.h | 41 + .../yoga/src/yoga/enums/ExperimentalFeature.h | 40 + .../yoga/src/yoga/enums/FlexDirection.h | 42 + .../yoga/src/yoga/enums/GridTrackType.h | 43 + third-party/yoga/src/yoga/enums/Gutter.h | 41 + third-party/yoga/src/yoga/enums/Justify.h | 48 + third-party/yoga/src/yoga/enums/LogLevel.h | 44 + third-party/yoga/src/yoga/enums/MeasureMode.h | 41 + third-party/yoga/src/yoga/enums/NodeType.h | 40 + third-party/yoga/src/yoga/enums/Overflow.h | 41 + .../yoga/src/yoga/enums/PhysicalEdge.h | 21 + .../yoga/src/yoga/enums/PositionType.h | 41 + third-party/yoga/src/yoga/enums/Unit.h | 45 + third-party/yoga/src/yoga/enums/Wrap.h | 41 + third-party/yoga/src/yoga/enums/YogaEnums.h | 84 + third-party/yoga/src/yoga/event/event.cpp | 20 +- third-party/yoga/src/yoga/event/event.h | 70 +- .../yoga/src/yoga/internal/experiments-inl.h | 32 - .../yoga/src/yoga/internal/experiments.cpp | 38 - .../yoga/src/yoga/internal/experiments.h | 26 - third-party/yoga/src/yoga/log.cpp | 68 - third-party/yoga/src/yoga/log.h | 38 - third-party/yoga/src/yoga/module.modulemap | 21 + .../yoga/src/yoga/node/CachedMeasurement.h | 53 + .../yoga/src/yoga/node/LayoutResults.cpp | 48 + .../yoga/src/yoga/node/LayoutResults.h | 128 + .../yoga/src/yoga/node/LayoutableChildren.h | 148 + third-party/yoga/src/yoga/node/Node.cpp | 472 ++ third-party/yoga/src/yoga/node/Node.h | 339 ++ .../yoga/src/yoga/numeric/Comparison.h | 81 + .../yoga/src/yoga/numeric/FloatOptional.h | 84 + third-party/yoga/src/yoga/style/GridLine.h | 53 + third-party/yoga/src/yoga/style/GridTrack.h | 63 + .../yoga/src/yoga/style/SmallValueBuffer.h | 133 + third-party/yoga/src/yoga/style/Style.h | 945 ++++ third-party/yoga/src/yoga/style/StyleLength.h | 121 + .../yoga/src/yoga/style/StyleSizeLength.h | 152 + .../yoga/src/yoga/style/StyleValueHandle.h | 114 + .../yoga/src/yoga/style/StyleValuePool.h | 194 + 98 files changed, 11135 insertions(+), 7444 deletions(-) delete mode 100755 third-party/yoga/src/yoga/Bitfield.h create mode 100644 third-party/yoga/src/yoga/CMakeLists.txt delete mode 100755 third-party/yoga/src/yoga/CompactValue.h delete mode 100755 third-party/yoga/src/yoga/Utils.cpp delete mode 100755 third-party/yoga/src/yoga/Utils.h mode change 100755 => 100644 third-party/yoga/src/yoga/YGConfig.cpp mode change 100755 => 100644 third-party/yoga/src/yoga/YGConfig.h mode change 100755 => 100644 third-party/yoga/src/yoga/YGEnums.cpp mode change 100755 => 100644 third-party/yoga/src/yoga/YGEnums.h delete mode 100755 third-party/yoga/src/yoga/YGFloatOptional.h delete mode 100755 third-party/yoga/src/yoga/YGLayout.cpp delete mode 100755 third-party/yoga/src/yoga/YGLayout.hpp mode change 100755 => 100644 third-party/yoga/src/yoga/YGMacros.h mode change 100755 => 100644 third-party/yoga/src/yoga/YGNode.cpp mode change 100755 => 100644 third-party/yoga/src/yoga/YGNode.h create mode 100644 third-party/yoga/src/yoga/YGNodeLayout.cpp create mode 100644 third-party/yoga/src/yoga/YGNodeLayout.h delete mode 100755 third-party/yoga/src/yoga/YGNodePrint.cpp delete mode 100755 third-party/yoga/src/yoga/YGNodePrint.h create mode 100644 third-party/yoga/src/yoga/YGNodeStyle.cpp create mode 100644 third-party/yoga/src/yoga/YGNodeStyle.h create mode 100644 third-party/yoga/src/yoga/YGPixelGrid.cpp create mode 100644 third-party/yoga/src/yoga/YGPixelGrid.h delete mode 100755 third-party/yoga/src/yoga/YGStyle.cpp delete mode 100755 third-party/yoga/src/yoga/YGStyle.h mode change 100755 => 100644 third-party/yoga/src/yoga/YGValue.cpp mode change 100755 => 100644 third-party/yoga/src/yoga/YGValue.h delete mode 100755 third-party/yoga/src/yoga/Yoga-internal.h delete mode 100755 third-party/yoga/src/yoga/Yoga.cpp mode change 100755 => 100644 third-party/yoga/src/yoga/Yoga.h create mode 100644 third-party/yoga/src/yoga/algorithm/AbsoluteLayout.cpp create mode 100644 third-party/yoga/src/yoga/algorithm/AbsoluteLayout.h create mode 100644 third-party/yoga/src/yoga/algorithm/Align.h create mode 100644 third-party/yoga/src/yoga/algorithm/Baseline.cpp create mode 100644 third-party/yoga/src/yoga/algorithm/Baseline.h create mode 100644 third-party/yoga/src/yoga/algorithm/BoundAxis.h create mode 100644 third-party/yoga/src/yoga/algorithm/Cache.cpp create mode 100644 third-party/yoga/src/yoga/algorithm/Cache.h create mode 100644 third-party/yoga/src/yoga/algorithm/CalculateLayout.cpp create mode 100644 third-party/yoga/src/yoga/algorithm/CalculateLayout.h create mode 100644 third-party/yoga/src/yoga/algorithm/FlexDirection.h create mode 100644 third-party/yoga/src/yoga/algorithm/FlexLine.cpp create mode 100644 third-party/yoga/src/yoga/algorithm/FlexLine.h create mode 100644 third-party/yoga/src/yoga/algorithm/PixelGrid.cpp create mode 100644 third-party/yoga/src/yoga/algorithm/PixelGrid.h create mode 100644 third-party/yoga/src/yoga/algorithm/SizingMode.h create mode 100644 third-party/yoga/src/yoga/algorithm/TrailingPosition.h create mode 100644 third-party/yoga/src/yoga/config/Config.cpp create mode 100644 third-party/yoga/src/yoga/config/Config.h create mode 100644 third-party/yoga/src/yoga/debug/AssertFatal.cpp create mode 100644 third-party/yoga/src/yoga/debug/AssertFatal.h create mode 100644 third-party/yoga/src/yoga/debug/Log.cpp create mode 100644 third-party/yoga/src/yoga/debug/Log.h create mode 100644 third-party/yoga/src/yoga/enums/Align.h create mode 100644 third-party/yoga/src/yoga/enums/BoxSizing.h create mode 100644 third-party/yoga/src/yoga/enums/Dimension.h create mode 100644 third-party/yoga/src/yoga/enums/Direction.h create mode 100644 third-party/yoga/src/yoga/enums/Display.h create mode 100644 third-party/yoga/src/yoga/enums/Edge.h create mode 100644 third-party/yoga/src/yoga/enums/Errata.h create mode 100644 third-party/yoga/src/yoga/enums/ExperimentalFeature.h create mode 100644 third-party/yoga/src/yoga/enums/FlexDirection.h create mode 100644 third-party/yoga/src/yoga/enums/GridTrackType.h create mode 100644 third-party/yoga/src/yoga/enums/Gutter.h create mode 100644 third-party/yoga/src/yoga/enums/Justify.h create mode 100644 third-party/yoga/src/yoga/enums/LogLevel.h create mode 100644 third-party/yoga/src/yoga/enums/MeasureMode.h create mode 100644 third-party/yoga/src/yoga/enums/NodeType.h create mode 100644 third-party/yoga/src/yoga/enums/Overflow.h create mode 100644 third-party/yoga/src/yoga/enums/PhysicalEdge.h create mode 100644 third-party/yoga/src/yoga/enums/PositionType.h create mode 100644 third-party/yoga/src/yoga/enums/Unit.h create mode 100644 third-party/yoga/src/yoga/enums/Wrap.h create mode 100644 third-party/yoga/src/yoga/enums/YogaEnums.h mode change 100755 => 100644 third-party/yoga/src/yoga/event/event.cpp mode change 100755 => 100644 third-party/yoga/src/yoga/event/event.h delete mode 100755 third-party/yoga/src/yoga/internal/experiments-inl.h delete mode 100755 third-party/yoga/src/yoga/internal/experiments.cpp delete mode 100755 third-party/yoga/src/yoga/internal/experiments.h delete mode 100755 third-party/yoga/src/yoga/log.cpp delete mode 100755 third-party/yoga/src/yoga/log.h create mode 100644 third-party/yoga/src/yoga/module.modulemap create mode 100644 third-party/yoga/src/yoga/node/CachedMeasurement.h create mode 100644 third-party/yoga/src/yoga/node/LayoutResults.cpp create mode 100644 third-party/yoga/src/yoga/node/LayoutResults.h create mode 100644 third-party/yoga/src/yoga/node/LayoutableChildren.h create mode 100644 third-party/yoga/src/yoga/node/Node.cpp create mode 100644 third-party/yoga/src/yoga/node/Node.h create mode 100644 third-party/yoga/src/yoga/numeric/Comparison.h create mode 100644 third-party/yoga/src/yoga/numeric/FloatOptional.h create mode 100644 third-party/yoga/src/yoga/style/GridLine.h create mode 100644 third-party/yoga/src/yoga/style/GridTrack.h create mode 100644 third-party/yoga/src/yoga/style/SmallValueBuffer.h create mode 100644 third-party/yoga/src/yoga/style/Style.h create mode 100644 third-party/yoga/src/yoga/style/StyleLength.h create mode 100644 third-party/yoga/src/yoga/style/StyleSizeLength.h create mode 100644 third-party/yoga/src/yoga/style/StyleValueHandle.h create mode 100644 third-party/yoga/src/yoga/style/StyleValuePool.h diff --git a/third-party/yoga/src/yoga/Bitfield.h b/third-party/yoga/src/yoga/Bitfield.h deleted file mode 100755 index 02645961..00000000 --- a/third-party/yoga/src/yoga/Bitfield.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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/third-party/yoga/src/yoga/CMakeLists.txt b/third-party/yoga/src/yoga/CMakeLists.txt new file mode 100644 index 00000000..b6eca1ac --- /dev/null +++ b/third-party/yoga/src/yoga/CMakeLists.txt @@ -0,0 +1,41 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + + +cmake_minimum_required(VERSION 3.13...3.26) +project(yogacore) +set(CMAKE_VERBOSE_MAKEFILE on) + +if(TARGET yogacore) + return() +endif() + +include(CheckIPOSupported) + +set(YOGA_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/..) +include(${YOGA_ROOT}/cmake/project-defaults.cmake) + + +file(GLOB SOURCES CONFIGURE_DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/**/*.cpp) + +add_library(yogacore STATIC ${SOURCES}) + +# Yoga conditionally uses when building for Android +if (ANDROID) + target_link_libraries(yogacore log) +endif() + +check_ipo_supported(RESULT result) +if(result) + set_target_properties(yogacore PROPERTIES + CMAKE_INTERPROCEDURAL_OPTIMIZATION true) +endif() + +target_include_directories(yogacore + PUBLIC + $ + $) diff --git a/third-party/yoga/src/yoga/CompactValue.h b/third-party/yoga/src/yoga/CompactValue.h deleted file mode 100755 index be933a16..00000000 --- a/third-party/yoga/src/yoga/CompactValue.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - * 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/third-party/yoga/src/yoga/Utils.cpp b/third-party/yoga/src/yoga/Utils.cpp deleted file mode 100755 index 761f3515..00000000 --- a/third-party/yoga/src/yoga/Utils.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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/third-party/yoga/src/yoga/Utils.h b/third-party/yoga/src/yoga/Utils.h deleted file mode 100755 index bce8dfca..00000000 --- a/third-party/yoga/src/yoga/Utils.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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/third-party/yoga/src/yoga/YGConfig.cpp b/third-party/yoga/src/yoga/YGConfig.cpp old mode 100755 new mode 100644 index fb72e80c..b3a82e1a --- a/third-party/yoga/src/yoga/YGConfig.cpp +++ b/third-party/yoga/src/yoga/YGConfig.cpp @@ -1,44 +1,92 @@ /* - * Copyright (c) Facebook, Inc. and its affiliates. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -#include "YGConfig.h" +#include +#include +#include -YGConfig::YGConfig(YGLogger logger) : cloneNodeCallback_{nullptr} { - logger_.noContext = logger; - loggerUsesContext_ = false; +using namespace facebook; +using namespace facebook::yoga; + +YGConfigRef YGConfigNew(void) { + return new yoga::Config(getDefaultLogger()); +} + +void YGConfigFree(const YGConfigRef config) { + delete resolveRef(config); +} + +YGConfigConstRef YGConfigGetDefault() { + return &yoga::Config::getDefault(); +} + +void YGConfigSetUseWebDefaults(const YGConfigRef config, const bool enabled) { + resolveRef(config)->setUseWebDefaults(enabled); +} + +bool YGConfigGetUseWebDefaults(const YGConfigConstRef config) { + return resolveRef(config)->useWebDefaults(); +} + +void YGConfigSetPointScaleFactor( + const YGConfigRef config, + const float pixelsInPoint) { + yoga::assertFatalWithConfig( + resolveRef(config), + pixelsInPoint >= 0.0f, + "Scale factor should not be less than zero"); + + resolveRef(config)->setPointScaleFactor(pixelsInPoint); +} + +float YGConfigGetPointScaleFactor(const YGConfigConstRef config) { + return resolveRef(config)->getPointScaleFactor(); } -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); +void YGConfigSetErrata(YGConfigRef config, YGErrata errata) { + resolveRef(config)->setErrata(scopedEnum(errata)); +} + +YGErrata YGConfigGetErrata(YGConfigConstRef config) { + return unscopedEnum(resolveRef(config)->getErrata()); +} + +void YGConfigSetLogger(const YGConfigRef config, YGLogger logger) { + if (logger != nullptr) { + resolveRef(config)->setLogger(logger); } else { - logger_.noContext(config, node, logLevel, format, args); + resolveRef(config)->setLogger(getDefaultLogger()); } } -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; +void YGConfigSetContext(const YGConfigRef config, void* context) { + resolveRef(config)->setContext(context); +} + +void* YGConfigGetContext(const YGConfigConstRef config) { + return resolveRef(config)->getContext(); +} + +void YGConfigSetExperimentalFeatureEnabled( + const YGConfigRef config, + const YGExperimentalFeature feature, + const bool enabled) { + resolveRef(config)->setExperimentalFeatureEnabled( + scopedEnum(feature), enabled); +} + +bool YGConfigIsExperimentalFeatureEnabled( + const YGConfigConstRef config, + const YGExperimentalFeature feature) { + return resolveRef(config)->isExperimentalFeatureEnabled(scopedEnum(feature)); +} + +void YGConfigSetCloneNodeFunc( + const YGConfigRef config, + const YGCloneNodeFunc callback) { + resolveRef(config)->setCloneNodeCallback(callback); } diff --git a/third-party/yoga/src/yoga/YGConfig.h b/third-party/yoga/src/yoga/YGConfig.h old mode 100755 new mode 100644 index e87d6758..12f6e204 --- a/third-party/yoga/src/yoga/YGConfig.h +++ b/third-party/yoga/src/yoga/YGConfig.h @@ -1,76 +1,158 @@ /* - * Copyright (c) Facebook, Inc. and its affiliates. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #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}); - } -}; + +#include +#include +#include + +#include +#include + +YG_EXTERN_C_BEGIN + +typedef struct YGNode* YGNodeRef; +typedef const struct YGNode* YGNodeConstRef; + +/** + * Handle to a mutable Yoga configuration. + */ +typedef struct YGConfig* YGConfigRef; + +/** + * Handle to an immutable Yoga configuration. + */ +typedef const struct YGConfig* YGConfigConstRef; + +/** + * Allocates a set of configuration options. The configuration may be applied to + * multiple nodes (i.e. a single global config), or can be applied more + * granularly per-node. + */ +YG_EXPORT YGConfigRef YGConfigNew(void); + +/** + * Frees the associated Yoga configuration. + */ +YG_EXPORT void YGConfigFree(YGConfigRef config); + +/** + * Returns the default config values set by Yoga. + */ +YG_EXPORT YGConfigConstRef YGConfigGetDefault(void); + +/** + * Yoga by default creates new nodes with style defaults different from flexbox + * on web (e.g. `YGFlexDirectionColumn` and `YGPositionRelative`). + * `UseWebDefaults` instructs Yoga to instead use a default style consistent + * with the web. + */ +YG_EXPORT void YGConfigSetUseWebDefaults(YGConfigRef config, bool enabled); + +/** + * Whether the configuration is set to use web defaults. + */ +YG_EXPORT bool YGConfigGetUseWebDefaults(YGConfigConstRef config); + +/** + * Yoga will by default round final layout positions and dimensions to the + * nearst point. `pointScaleFactor` controls the density of the grid used for + * layout rounding (e.g. to round to the closest display pixel). + * + * May be set to 0.0f to avoid rounding the layout results. + */ +YG_EXPORT void YGConfigSetPointScaleFactor( + YGConfigRef config, + float pixelsInPoint); + +/** + * Get the currently set point scale factor. + */ +YG_EXPORT float YGConfigGetPointScaleFactor(YGConfigConstRef config); + +/** + * Configures how Yoga balances W3C conformance vs compatibility with layouts + * created against earlier versions of Yoga. + * + * By default Yoga will prioritize W3C conformance. `Errata` may be set to ask + * Yoga to produce specific incorrect behaviors. E.g. `YGConfigSetErrata(config, + * YGErrataStretchFlexBasis)`. + * + * YGErrata is a bitmask, and multiple errata may be set at once. Predefined + * constants exist for convenience: + * 1. YGErrataNone: No errata + * 2. YGErrataClassic: Match layout behaviors of Yoga 1.x + * 3. YGErrataAll: Match layout behaviors of Yoga 1.x, including + * `UseLegacyStretchBehaviour` + */ +YG_EXPORT void YGConfigSetErrata(YGConfigRef config, YGErrata errata); + +/** + * Get the currently set errata. + */ +YG_EXPORT YGErrata YGConfigGetErrata(YGConfigConstRef config); + +/** + * Function pointer type for YGConfigSetLogger. + */ +typedef int (*YGLogger)( + YGConfigConstRef config, + YGNodeConstRef node, + YGLogLevel level, + const char* format, + va_list args); + +/** + * Set a custom log function for to use when logging diagnostics or fatal. + * errors. + */ +YG_EXPORT void YGConfigSetLogger(YGConfigRef config, YGLogger logger); + +/** + * Sets an arbitrary context pointer on the config which may be read from during + * callbacks. + */ +YG_EXPORT void YGConfigSetContext(YGConfigRef config, void* context); + +/** + * Gets the currently set context. + */ +YG_EXPORT void* YGConfigGetContext(YGConfigConstRef config); + +/** + * Function pointer type for YGConfigSetCloneNodeFunc. + */ +typedef YGNodeRef (*YGCloneNodeFunc)( + YGNodeConstRef oldNode, + YGNodeConstRef owner, + size_t childIndex); + +/** + * Enable an experimental/unsupported feature in Yoga. + */ +YG_EXPORT void YGConfigSetExperimentalFeatureEnabled( + YGConfigRef config, + YGExperimentalFeature feature, + bool enabled); + +/** + * Whether an experimental feature is set. + */ +YG_EXPORT bool YGConfigIsExperimentalFeatureEnabled( + YGConfigConstRef config, + YGExperimentalFeature feature); + +/** + * Sets a callback, called during layout, to create a new mutable Yoga node if + * Yoga must write to it and its owner is not its parent observed during layout. + */ +YG_EXPORT void YGConfigSetCloneNodeFunc( + YGConfigRef config, + YGCloneNodeFunc callback); + +YG_EXTERN_C_END diff --git a/third-party/yoga/src/yoga/YGEnums.cpp b/third-party/yoga/src/yoga/YGEnums.cpp old mode 100755 new mode 100644 index 3c130129..538b0b08 --- a/third-party/yoga/src/yoga/YGEnums.cpp +++ b/third-party/yoga/src/yoga/YGEnums.cpp @@ -1,11 +1,13 @@ /* - * Copyright (c) Facebook, Inc. and its affiliates. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -#include "YGEnums.h" +// @generated by enums.py +// clang-format off +#include const char* YGAlignToString(const YGAlign value) { switch (value) { @@ -25,6 +27,22 @@ const char* YGAlignToString(const YGAlign value) { return "space-between"; case YGAlignSpaceAround: return "space-around"; + case YGAlignSpaceEvenly: + return "space-evenly"; + case YGAlignStart: + return "start"; + case YGAlignEnd: + return "end"; + } + return "unknown"; +} + +const char* YGBoxSizingToString(const YGBoxSizing value) { + switch (value) { + case YGBoxSizingBorderBox: + return "border-box"; + case YGBoxSizingContentBox: + return "content-box"; } return "unknown"; } @@ -57,6 +75,10 @@ const char* YGDisplayToString(const YGDisplay value) { return "flex"; case YGDisplayNone: return "none"; + case YGDisplayContents: + return "contents"; + case YGDisplayGrid: + return "grid"; } return "unknown"; } @@ -85,10 +107,30 @@ const char* YGEdgeToString(const YGEdge value) { return "unknown"; } +const char* YGErrataToString(const YGErrata value) { + switch (value) { + case YGErrataNone: + return "none"; + case YGErrataStretchFlexBasis: + return "stretch-flex-basis"; + case YGErrataAbsolutePositionWithoutInsetsExcludesPadding: + return "absolute-position-without-insets-excludes-padding"; + case YGErrataAbsolutePercentAgainstInnerSize: + return "absolute-percent-against-inner-size"; + case YGErrataAll: + return "all"; + case YGErrataClassic: + return "classic"; + } + return "unknown"; +} + const char* YGExperimentalFeatureToString(const YGExperimentalFeature value) { switch (value) { case YGExperimentalFeatureWebFlexBasis: return "web-flex-basis"; + case YGExperimentalFeatureFixFlexBasisFitContent: + return "fix-flex-basis-fit-content"; } return "unknown"; } @@ -107,8 +149,38 @@ const char* YGFlexDirectionToString(const YGFlexDirection value) { return "unknown"; } +const char* YGGridTrackTypeToString(const YGGridTrackType value) { + switch (value) { + case YGGridTrackTypeAuto: + return "auto"; + case YGGridTrackTypePoints: + return "points"; + case YGGridTrackTypePercent: + return "percent"; + case YGGridTrackTypeFr: + return "fr"; + case YGGridTrackTypeMinmax: + return "minmax"; + } + return "unknown"; +} + +const char* YGGutterToString(const YGGutter value) { + switch (value) { + case YGGutterColumn: + return "column"; + case YGGutterRow: + return "row"; + case YGGutterAll: + return "all"; + } + return "unknown"; +} + const char* YGJustifyToString(const YGJustify value) { switch (value) { + case YGJustifyAuto: + return "auto"; case YGJustifyFlexStart: return "flex-start"; case YGJustifyCenter: @@ -121,6 +193,12 @@ const char* YGJustifyToString(const YGJustify value) { return "space-around"; case YGJustifySpaceEvenly: return "space-evenly"; + case YGJustifyStretch: + return "stretch"; + case YGJustifyStart: + return "start"; + case YGJustifyEnd: + return "end"; } return "unknown"; } @@ -179,6 +257,8 @@ const char* YGOverflowToString(const YGOverflow value) { const char* YGPositionTypeToString(const YGPositionType value) { switch (value) { + case YGPositionTypeStatic: + return "static"; case YGPositionTypeRelative: return "relative"; case YGPositionTypeAbsolute: @@ -187,18 +267,6 @@ const char* YGPositionTypeToString(const YGPositionType value) { 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: @@ -209,6 +277,12 @@ const char* YGUnitToString(const YGUnit value) { return "percent"; case YGUnitAuto: return "auto"; + case YGUnitMaxContent: + return "max-content"; + case YGUnitFitContent: + return "fit-content"; + case YGUnitStretch: + return "stretch"; } return "unknown"; } diff --git a/third-party/yoga/src/yoga/YGEnums.h b/third-party/yoga/src/yoga/YGEnums.h old mode 100755 new mode 100644 index 4241281d..aa0b1d43 --- a/third-party/yoga/src/yoga/YGEnums.h +++ b/third-party/yoga/src/yoga/YGEnums.h @@ -1,60 +1,18 @@ /* - * Copyright (c) Facebook, Inc. and its affiliates. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ +// @generated by enums.py +// clang-format off #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 +#include YG_EXTERN_C_BEGIN -YG_ENUM_SEQ_DECL( +YG_ENUM_DECL( YGAlign, YGAlignAuto, YGAlignFlexStart, @@ -63,19 +21,35 @@ YG_ENUM_SEQ_DECL( YGAlignStretch, YGAlignBaseline, YGAlignSpaceBetween, - YGAlignSpaceAround); + YGAlignSpaceAround, + YGAlignSpaceEvenly, + YGAlignStart, + YGAlignEnd) -YG_ENUM_SEQ_DECL(YGDimension, YGDimensionWidth, YGDimensionHeight) +YG_ENUM_DECL( + YGBoxSizing, + YGBoxSizingBorderBox, + YGBoxSizingContentBox) -YG_ENUM_SEQ_DECL( +YG_ENUM_DECL( + YGDimension, + YGDimensionWidth, + YGDimensionHeight) + +YG_ENUM_DECL( YGDirection, YGDirectionInherit, YGDirectionLTR, YGDirectionRTL) -YG_ENUM_SEQ_DECL(YGDisplay, YGDisplayFlex, YGDisplayNone) +YG_ENUM_DECL( + YGDisplay, + YGDisplayFlex, + YGDisplayNone, + YGDisplayContents, + YGDisplayGrid) -YG_ENUM_SEQ_DECL( +YG_ENUM_DECL( YGEdge, YGEdgeLeft, YGEdgeTop, @@ -87,25 +61,56 @@ YG_ENUM_SEQ_DECL( YGEdgeVertical, YGEdgeAll) -YG_ENUM_SEQ_DECL(YGExperimentalFeature, YGExperimentalFeatureWebFlexBasis) +YG_ENUM_DECL( + YGErrata, + YGErrataNone = 0, + YGErrataStretchFlexBasis = 1, + YGErrataAbsolutePositionWithoutInsetsExcludesPadding = 2, + YGErrataAbsolutePercentAgainstInnerSize = 4, + YGErrataAll = 2147483647, + YGErrataClassic = 2147483646) +YG_DEFINE_ENUM_FLAG_OPERATORS(YGErrata) + +YG_ENUM_DECL( + YGExperimentalFeature, + YGExperimentalFeatureWebFlexBasis, + YGExperimentalFeatureFixFlexBasisFitContent) -YG_ENUM_SEQ_DECL( +YG_ENUM_DECL( YGFlexDirection, YGFlexDirectionColumn, YGFlexDirectionColumnReverse, YGFlexDirectionRow, YGFlexDirectionRowReverse) -YG_ENUM_SEQ_DECL( +YG_ENUM_DECL( + YGGridTrackType, + YGGridTrackTypeAuto, + YGGridTrackTypePoints, + YGGridTrackTypePercent, + YGGridTrackTypeFr, + YGGridTrackTypeMinmax) + +YG_ENUM_DECL( + YGGutter, + YGGutterColumn, + YGGutterRow, + YGGutterAll) + +YG_ENUM_DECL( YGJustify, + YGJustifyAuto, YGJustifyFlexStart, YGJustifyCenter, YGJustifyFlexEnd, YGJustifySpaceBetween, YGJustifySpaceAround, - YGJustifySpaceEvenly) + YGJustifySpaceEvenly, + YGJustifyStretch, + YGJustifyStart, + YGJustifyEnd) -YG_ENUM_SEQ_DECL( +YG_ENUM_DECL( YGLogLevel, YGLogLevelError, YGLogLevelWarn, @@ -114,38 +119,43 @@ YG_ENUM_SEQ_DECL( YGLogLevelVerbose, YGLogLevelFatal) -YG_ENUM_SEQ_DECL( +YG_ENUM_DECL( YGMeasureMode, YGMeasureModeUndefined, YGMeasureModeExactly, YGMeasureModeAtMost) -YG_ENUM_SEQ_DECL(YGNodeType, YGNodeTypeDefault, YGNodeTypeText) +YG_ENUM_DECL( + YGNodeType, + YGNodeTypeDefault, + YGNodeTypeText) -YG_ENUM_SEQ_DECL( +YG_ENUM_DECL( YGOverflow, YGOverflowVisible, YGOverflowHidden, YGOverflowScroll) -YG_ENUM_SEQ_DECL(YGPositionType, YGPositionTypeRelative, YGPositionTypeAbsolute) - YG_ENUM_DECL( - YGPrintOptions, - YGPrintOptionsLayout = 1, - YGPrintOptionsStyle = 2, - YGPrintOptionsChildren = 4) + YGPositionType, + YGPositionTypeStatic, + YGPositionTypeRelative, + YGPositionTypeAbsolute) -YG_ENUM_SEQ_DECL( +YG_ENUM_DECL( YGUnit, YGUnitUndefined, YGUnitPoint, YGUnitPercent, - YGUnitAuto) + YGUnitAuto, + YGUnitMaxContent, + YGUnitFitContent, + YGUnitStretch) -YG_ENUM_SEQ_DECL(YGWrap, YGWrapNoWrap, YGWrapWrap, YGWrapWrapReverse) +YG_ENUM_DECL( + YGWrap, + YGWrapNoWrap, + YGWrapWrap, + YGWrapWrapReverse) YG_EXTERN_C_END - -#undef YG_ENUM_DECL -#undef YG_ENUM_SEQ_DECL diff --git a/third-party/yoga/src/yoga/YGFloatOptional.h b/third-party/yoga/src/yoga/YGFloatOptional.h deleted file mode 100755 index e4cf0284..00000000 --- a/third-party/yoga/src/yoga/YGFloatOptional.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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/third-party/yoga/src/yoga/YGLayout.cpp b/third-party/yoga/src/yoga/YGLayout.cpp deleted file mode 100755 index 725a0047..00000000 --- a/third-party/yoga/src/yoga/YGLayout.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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.hpp" -#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/third-party/yoga/src/yoga/YGLayout.hpp b/third-party/yoga/src/yoga/YGLayout.hpp deleted file mode 100755 index bdec4ad3..00000000 --- a/third-party/yoga/src/yoga/YGLayout.hpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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; - static constexpr size_t didUseCustomMeasureIdx = 4; - facebook::yoga::Bitfield flags_ = - {YGDirectionInherit, false, 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 didUseCustomMeasure() const { return flags_.at(); } - decltype(flags_)::Ref didUseCustomMeasure() { - return flags_.at(); - } - - bool operator==(YGLayout layout) const; - bool operator!=(YGLayout layout) const { return !(*this == layout); } -}; diff --git a/third-party/yoga/src/yoga/YGMacros.h b/third-party/yoga/src/yoga/YGMacros.h old mode 100755 new mode 100644 index c6917f1b..ab9b2627 --- a/third-party/yoga/src/yoga/YGMacros.h +++ b/third-party/yoga/src/yoga/YGMacros.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Facebook, Inc. and its affiliates. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. @@ -7,6 +7,10 @@ #pragma once +#ifdef __cplusplus +#include +#endif + #ifdef __cplusplus #define YG_EXTERN_C_BEGIN extern "C" { #define YG_EXTERN_C_END } @@ -15,24 +19,34 @@ #define YG_EXTERN_C_END #endif -#ifdef _WINDLL -#define WIN_EXPORT __declspec(dllexport) +#if defined(__cplusplus) +#define YG_DEPRECATED(message) [[deprecated(message)]] +#elif defined(_MSC_VER) +#define YG_DEPRECATED(message) __declspec(deprecated(message)) #else -#define WIN_EXPORT +#define YG_DEPRECATED(message) __attribute__((deprecated(message))) #endif -#ifndef YOGA_EXPORT -#ifdef _MSC_VER -#define YOGA_EXPORT +#ifdef _WINDLL +#define YG_EXPORT __declspec(dllexport) +#elif defined(__EMSCRIPTEN__) +#define YG_EXPORT __attribute__((visibility("default"), used)) +#elif !defined(_MSC_VER) +#define YG_EXPORT __attribute__((visibility("default"))) #else -#define YOGA_EXPORT __attribute__((visibility("default"))) +#define YG_EXPORT +#endif + +#ifdef __OBJC__ +#if __has_include() +#import #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. +// the header is a mismatch for the Yoga ABI. #define YG_ENUM_BEGIN(name) NS_ENUM(int, name) #define YG_ENUM_END(name) #else @@ -40,14 +54,48 @@ #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 +#ifdef __cplusplus +#define YG_DEFINE_ENUM_FLAG_OPERATORS(name) \ + extern "C++" { \ + constexpr name operator~(name a) { \ + return static_cast( \ + ~static_cast::type>(a)); \ + } \ + constexpr name operator|(name a, name b) { \ + return static_cast( \ + static_cast::type>(a) | \ + static_cast::type>(b)); \ + } \ + constexpr name operator&(name a, name b) { \ + return static_cast( \ + static_cast::type>(a) & \ + static_cast::type>(b)); \ + } \ + constexpr name operator^(name a, name b) { \ + return static_cast( \ + static_cast::type>(a) ^ \ + static_cast::type>(b)); \ + } \ + inline name& operator|=(name& a, name b) { \ + return reinterpret_cast( \ + reinterpret_cast::type&>(a) |= \ + static_cast::type>(b)); \ + } \ + inline name& operator&=(name& a, name b) { \ + return reinterpret_cast( \ + reinterpret_cast::type&>(a) &= \ + static_cast::type>(b)); \ + } \ + inline name& operator^=(name& a, name b) { \ + return reinterpret_cast( \ + reinterpret_cast::type&>(a) ^= \ + static_cast::type>(b)); \ + } \ + } +#else +#define YG_DEFINE_ENUM_FLAG_OPERATORS(name) #endif + +#define YG_ENUM_DECL(NAME, ...) \ + typedef YG_ENUM_BEGIN(NAME){__VA_ARGS__} YG_ENUM_END(NAME); \ + YG_EXPORT const char* NAME##ToString(NAME); diff --git a/third-party/yoga/src/yoga/YGNode.cpp b/third-party/yoga/src/yoga/YGNode.cpp old mode 100755 new mode 100644 index 1d49b007..f55cab6b --- a/third-party/yoga/src/yoga/YGNode.cpp +++ b/third-party/yoga/src/yoga/YGNode.cpp @@ -1,585 +1,380 @@ /* - * Copyright (c) Facebook, Inc. and its affiliates. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -#include "YGNode.h" -#include -#include -#include "CompactValue.h" -#include "Utils.h" +#include -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); - } -} +#include +#include +#include +#include +#include +#include -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()); +using namespace facebook; +using namespace facebook::yoga; - return leadingPosition.isUndefined() - ? YGFloatOptional{0} - : YGResolveValue(leadingPosition, axisSize); +YGNodeRef YGNodeNew(void) { + return YGNodeNewWithConfig(YGConfigGetDefault()); } -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()); +YGNodeRef YGNodeNewWithConfig(const YGConfigConstRef config) { + auto* node = new yoga::Node{resolveRef(config)}; + yoga::assertFatal( + config != nullptr, "Tried to construct YGNode with null config"); + Event::publish(node, {config}); - return trailingPosition.isUndefined() - ? YGFloatOptional{0} - : YGResolveValue(trailingPosition, axisSize); + return node; } -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(); +YGNodeRef YGNodeClone(YGNodeConstRef oldNodeRef) { + auto oldNode = resolveRef(oldNodeRef); + const auto node = new yoga::Node(*oldNode); + Event::publish(node, {node->getConfig()}); + node->setOwner(nullptr); + return node; } -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(); -} +void YGNodeFree(const YGNodeRef nodeRef) { + const auto node = resolveRef(nodeRef); -YGFloatOptional YGNode::getLeadingMargin( - const YGFlexDirection axis, - const float widthSize) const { - if (YGFlexDirectionIsRow(axis) && - !style_.margin()[YGEdgeStart].isUndefined()) { - return YGResolveValueMargin(style_.margin()[YGEdgeStart], widthSize); + if (auto owner = node->getOwner()) { + owner->removeChild(node); + node->setOwner(nullptr); } - 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); + const size_t childCount = node->getChildCount(); + for (size_t i = 0; i < childCount; i++) { + auto child = node->getChild(i); + child->setOwner(nullptr); } - 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); -} + node->clearChildren(); -float YGNode::baseline(float width, float height, void* layoutContext) { - return flags_.at() - ? baseline_.withContext(this, width, height, layoutContext) - : baseline_.noContext(this, width, height); + Event::publish(node, {YGNodeGetConfig(node)}); + delete resolveRef(node); } -// Setters +void YGNodeFreeRecursive(YGNodeRef rootRef) { + const auto root = resolveRef(rootRef); -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); + size_t skipped = 0; + while (root->getChildCount() > skipped) { + const auto child = root->getChild(skipped); + if (child->getOwner() != root) { + // Don't free shared nodes that we don't own. + skipped += 1; + } else { + YGNodeRemoveChild(root, child); + YGNodeFreeRecursive(child); + } } - - measure_ = measureFunc; + YGNodeFree(root); } -void YGNode::setMeasureFunc(YGMeasureFunc measureFunc) { - flags_.at() = false; - decltype(YGNode::measure_) m; - m.noContext = measureFunc; - setMeasureFunc(m); +void YGNodeFinalize(const YGNodeRef node) { + Event::publish(node, {YGNodeGetConfig(node)}); + delete resolveRef(node); } -YOGA_EXPORT void YGNode::setMeasureFunc(MeasureWithContextFn measureFunc) { - flags_.at() = true; - decltype(YGNode::measure_) m; - m.withContext = measureFunc; - setMeasureFunc(m); +void YGNodeReset(YGNodeRef node) { + resolveRef(node)->reset(); } -void YGNode::replaceChild(YGNodeRef child, uint32_t index) { - children_[index] = child; +void YGNodeCalculateLayout( + const YGNodeRef node, + const float ownerWidth, + const float ownerHeight, + const YGDirection ownerDirection) { + yoga::calculateLayout( + resolveRef(node), ownerWidth, ownerHeight, scopedEnum(ownerDirection)); } -void YGNode::replaceChild(YGNodeRef oldChild, YGNodeRef newChild) { - std::replace(children_.begin(), children_.end(), oldChild, newChild); +bool YGNodeGetHasNewLayout(YGNodeConstRef node) { + return resolveRef(node)->getHasNewLayout(); } -void YGNode::insertChild(YGNodeRef child, uint32_t index) { - children_.insert(children_.begin() + index, child); +void YGNodeSetHasNewLayout(YGNodeRef node, bool hasNewLayout) { + resolveRef(node)->setHasNewLayout(hasNewLayout); } -void YGNode::setDirty(bool isDirty) { - if (isDirty == flags_.at()) { - return; - } - flags_.at() = isDirty; - if (isDirty && dirtied_) { - dirtied_(this); - } +bool YGNodeIsDirty(YGNodeConstRef node) { + return resolveRef(node)->isDirty(); } -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 YGNodeMarkDirty(const YGNodeRef nodeRef) { + const auto node = resolveRef(nodeRef); -void YGNode::removeChild(uint32_t index) { - children_.erase(children_.begin() + index); -} - -void YGNode::setLayoutDirection(YGDirection direction) { - layout_.direction() = direction; -} + yoga::assertFatalWithNode( + node, + node->hasMeasureFunc(), + "Only leaf nodes with custom measure functions " + "should manually mark themselves as dirty"); -void YGNode::setLayoutMargin(float margin, int index) { - layout_.margin[index] = margin; + node->markDirtyAndPropagate(); } -void YGNode::setLayoutBorder(float border, int index) { - layout_.border[index] = border; +void YGNodeSetDirtiedFunc(YGNodeRef node, YGDirtiedFunc dirtiedFunc) { + resolveRef(node)->setDirtiedFunc(dirtiedFunc); } -void YGNode::setLayoutPadding(float padding, int index) { - layout_.padding[index] = padding; +YGDirtiedFunc YGNodeGetDirtiedFunc(YGNodeConstRef node) { + return resolveRef(node)->getDirtiedFunc(); } -void YGNode::setLayoutLastOwnerDirection(YGDirection direction) { - layout_.lastOwnerDirection = direction; -} +void YGNodeInsertChild( + const YGNodeRef ownerRef, + const YGNodeRef childRef, + const size_t index) { + auto owner = resolveRef(ownerRef); + auto child = resolveRef(childRef); -void YGNode::setLayoutComputedFlexBasis( - const YGFloatOptional computedFlexBasis) { - layout_.computedFlexBasis = computedFlexBasis; -} + yoga::assertFatalWithNode( + owner, + child->getOwner() == nullptr, + "Child already has a owner, it must be removed first."); -void YGNode::setLayoutPosition(float position, int index) { - layout_.position[index] = position; -} + yoga::assertFatalWithNode( + owner, + !owner->hasMeasureFunc(), + "Cannot add child: Nodes with measure functions cannot have children."); -void YGNode::setLayoutComputedFlexBasisGeneration( - uint32_t computedFlexBasisGeneration) { - layout_.computedFlexBasisGeneration = computedFlexBasisGeneration; + owner->insertChild(child, index); + child->setOwner(owner); + owner->markDirtyAndPropagate(); } -void YGNode::setLayoutMeasuredDimension(float measuredDimension, int index) { - layout_.measuredDimensions[index] = measuredDimension; -} +void YGNodeSwapChild( + const YGNodeRef ownerRef, + const YGNodeRef childRef, + const size_t index) { + auto owner = resolveRef(ownerRef); + auto child = resolveRef(childRef); -void YGNode::setLayoutHadOverflow(bool hadOverflow) { - layout_.hadOverflow() = hadOverflow; + owner->replaceChild(child, index); + child->setOwner(owner); } -void YGNode::setLayoutDimension(float dimension, int index) { - layout_.dimensions[index] = dimension; -} +void YGNodeRemoveChild( + const YGNodeRef ownerRef, + const YGNodeRef excludedChildRef) { + auto owner = resolveRef(ownerRef); + auto excludedChild = resolveRef(excludedChildRef); -// 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); + if (owner->getChildCount() == 0) { + // This is an empty set. Nothing to remove. + return; } - 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]]; + // 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); + // Mark dirty to invalidate cache, but suppress the dirtied callback + // since the node is being detached from the tree and should not + // propagate dirty signals through external callback mechanisms. + auto dirtiedFunc = excludedChild->getDirtiedFunc(); + excludedChild->setDirtiedFunc(nullptr); + excludedChild->setDirty(true); + excludedChild->setDirtiedFunc(dirtiedFunc); + } + owner->markDirtyAndPropagate(); } } -YGValue YGNode::marginTrailingValue(const YGFlexDirection axis) const { - if (YGFlexDirectionIsRow(axis) && !style_.margin()[YGEdgeEnd].isUndefined()) { - return style_.margin()[YGEdgeEnd]; - } else { - return style_.margin()[trailing[axis]]; - } -} +void YGNodeRemoveAllChildren(const YGNodeRef ownerRef) { + auto owner = resolveRef(ownerRef); -YGValue YGNode::resolveFlexBasisPtr() const { - YGValue flexBasis = style_.flexBasis(); - if (flexBasis.unit != YGUnitAuto && flexBasis.unit != YGUnitUndefined) { - return flexBasis; + const size_t childCount = owner->getChildCount(); + if (childCount == 0) { + // This is an empty set already. Nothing to do. + return; } - if (!style_.flex().isUndefined() && style_.flex().unwrap() > 0.0f) { - return flags_.at() ? YGValueAuto : YGValueZero; + auto* firstChild = owner->getChild(0); + if (firstChild->getOwner() == owner) { + // If the first child has this node as its owner, we assume that this child + // set is unique. + for (size_t i = 0; i < childCount; i++) { + yoga::Node* oldChild = owner->getChild(i); + oldChild->setLayout({}); // layout is no longer valid + oldChild->setOwner(nullptr); + // Mark dirty to invalidate cache, but suppress the dirtied callback + // since the node is being detached from the tree and should not + // propagate dirty signals through external callback mechanisms. + auto dirtiedFunc = oldChild->getDirtiedFunc(); + oldChild->setDirtiedFunc(nullptr); + oldChild->setDirty(true); + oldChild->setDirtiedFunc(dirtiedFunc); + } + owner->clearChildren(); + owner->markDirtyAndPropagate(); + return; } - return YGValueAuto; + // Otherwise, we are not the owner of the child set. We don't have to do + // anything to clear it. + owner->setChildren({}); + owner->markDirtyAndPropagate(); } -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]; - } +void YGNodeSetChildren( + const YGNodeRef ownerRef, + const YGNodeRef* childrenRefs, + const size_t count) { + auto owner = resolveRef(ownerRef); + auto children = reinterpret_cast(childrenRefs); + + if (owner == nullptr) { + return; } -} -YGDirection YGNode::resolveDirection(const YGDirection ownerDirection) { - if (style_.direction() == YGDirectionInherit) { - return ownerDirection > YGDirectionInherit ? ownerDirection - : YGDirectionLTR; + const std::vector childrenVector = {children, children + count}; + if (childrenVector.empty()) { + if (owner->getChildCount() > 0) { + for (auto* child : owner->getChildren()) { + child->setLayout({}); + child->setOwner(nullptr); + } + owner->setChildren({}); + owner->markDirtyAndPropagate(); + } } else { - return style_.direction(); + if (owner->getChildCount() > 0) { + for (auto* 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(childrenVector.begin(), childrenVector.end(), oldChild) == + childrenVector.end()) { + oldChild->setLayout({}); + oldChild->setOwner(nullptr); + } + } + } + owner->setChildren(childrenVector); + for (yoga::Node* child : childrenVector) { + child->setOwner(owner); + } + owner->markDirtyAndPropagate(); } } -YOGA_EXPORT void YGNode::clearChildren() { - children_.clear(); - children_.shrink_to_fit(); -} - -// Other Methods +YGNodeRef YGNodeGetChild(const YGNodeRef nodeRef, const size_t index) { + const auto node = resolveRef(nodeRef); -void YGNode::cloneChildrenIfNeeded(void* cloneContext) { - iterChildrenAfterCloningIfNeeded([](YGNodeRef, void*) {}, cloneContext); -} - -void YGNode::markDirtyAndPropogate() { - if (!flags_.at()) { - setDirty(true); - setLayoutComputedFlexBasis(YGFloatOptional()); - if (owner_) { - owner_->markDirtyAndPropogate(); - } + if (index < node->getChildren().size()) { + return node->getChild(index); } + return nullptr; } -void YGNode::markDirtyAndPropogateDownwards() { - flags_.at() = true; - for_each(children_.begin(), children_.end(), [](YGNodeRef childNode) { - childNode->markDirtyAndPropogateDownwards(); - }); +size_t YGNodeGetChildCount(const YGNodeConstRef node) { + return resolveRef(node)->getChildren().size(); } -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; +YGNodeRef YGNodeGetOwner(const YGNodeRef node) { + return resolveRef(node)->getOwner(); } -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; +YGNodeRef YGNodeGetParent(const YGNodeRef node) { + return resolveRef(node)->getOwner(); } -bool YGNode::isNodeFlexible() { - return ( - (style_.positionType() == YGPositionTypeRelative) && - (resolveFlexGrow() != 0 || resolveFlexShrink() != 0)); +void YGNodeSetConfig(YGNodeRef node, YGConfigRef config) { + resolveRef(node)->setConfig(resolveRef(config)); } -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); +YGConfigConstRef YGNodeGetConfig(YGNodeRef node) { + return resolveRef(node)->getConfig(); } -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); +void YGNodeSetContext(YGNodeRef node, void* context) { + return resolveRef(node)->setContext(context); } -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)); +void* YGNodeGetContext(YGNodeConstRef node) { + return resolveRef(node)->getContext(); } -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); +void YGNodeSetMeasureFunc(YGNodeRef node, YGMeasureFunc measureFunc) { + resolveRef(node)->setMeasureFunc(measureFunc); +} - return YGFloatOptionalMax(resolvedValue, YGFloatOptional(0.0f)); +bool YGNodeHasMeasureFunc(YGNodeConstRef node) { + return resolveRef(node)->hasMeasureFunc(); } -YGFloatOptional YGNode::getLeadingPaddingAndBorder( - const YGFlexDirection axis, - const float widthSize) const { - return getLeadingPadding(axis, widthSize) + - YGFloatOptional(getLeadingBorder(axis)); +void YGNodeSetBaselineFunc(YGNodeRef node, YGBaselineFunc baselineFunc) { + resolveRef(node)->setBaselineFunc(baselineFunc); } -YGFloatOptional YGNode::getTrailingPaddingAndBorder( - const YGFlexDirection axis, - const float widthSize) const { - return getTrailingPadding(axis, widthSize) + - YGFloatOptional(getTrailingBorder(axis)); +bool YGNodeHasBaselineFunc(YGNodeConstRef node) { + return resolveRef(node)->hasBaselineFunc(); } -bool YGNode::didUseLegacyFlag() { - bool didUseLegacyFlag = layout_.didUseLegacyFlag(); - if (didUseLegacyFlag) { - return true; - } - for (const auto& child : children_) { - if (child->layout_.didUseLegacyFlag()) { - didUseLegacyFlag = true; - break; - } +void YGNodeSetIsReferenceBaseline(YGNodeRef nodeRef, bool isReferenceBaseline) { + const auto node = resolveRef(nodeRef); + if (node->isReferenceBaseline() != isReferenceBaseline) { + node->setIsReferenceBaseline(isReferenceBaseline); + node->markDirtyAndPropagate(); } - return didUseLegacyFlag; } -void YGNode::setLayoutDoesLegacyFlagAffectsLayout( - bool doesLegacyFlagAffectsLayout) { - layout_.doesLegacyStretchFlagAffectsLayout() = doesLegacyFlagAffectsLayout; +bool YGNodeIsReferenceBaseline(YGNodeConstRef node) { + return resolveRef(node)->isReferenceBaseline(); } -void YGNode::setLayoutDidUseLegacyFlag(bool didUseLegacyFlag) { - layout_.didUseLegacyFlag() = didUseLegacyFlag; +void YGNodeSetNodeType(YGNodeRef node, YGNodeType nodeType) { + return resolveRef(node)->setNodeType(scopedEnum(nodeType)); } -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; +YGNodeType YGNodeGetNodeType(YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->getNodeType()); } -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"); +void YGNodeSetAlwaysFormsContainingBlock( + YGNodeRef node, + bool alwaysFormsContainingBlock) { + resolveRef(node)->setAlwaysFormsContainingBlock(alwaysFormsContainingBlock); +} - clearChildren(); +bool YGNodeGetAlwaysFormsContainingBlock(YGNodeConstRef node) { + return resolveRef(node)->alwaysFormsContainingBlock(); +} - auto webDefaults = flags_.at(); - *this = YGNode{getConfig()}; - if (webDefaults) { - useWebDefaults(); - } +// TODO: This leaks internal details to the public API. Remove after removing +// ComponentKit usage of it. +bool YGNodeCanUseCachedMeasurement( + YGMeasureMode widthMode, + float availableWidth, + YGMeasureMode heightMode, + float availableHeight, + YGMeasureMode lastWidthMode, + float lastAvailableWidth, + YGMeasureMode lastHeightMode, + float lastAvailableHeight, + float lastComputedWidth, + float lastComputedHeight, + float marginRow, + float marginColumn, + YGConfigRef config) { + return yoga::canUseCachedMeasurement( + sizingMode(scopedEnum(widthMode)), + availableWidth, + sizingMode(scopedEnum(heightMode)), + availableHeight, + sizingMode(scopedEnum(lastWidthMode)), + lastAvailableWidth, + sizingMode(scopedEnum(lastHeightMode)), + lastAvailableHeight, + lastComputedWidth, + lastComputedHeight, + marginRow, + marginColumn, + resolveRef(config)); } diff --git a/third-party/yoga/src/yoga/YGNode.h b/third-party/yoga/src/yoga/YGNode.h old mode 100755 new mode 100644 index 061a02f2..8ff3130c --- a/third-party/yoga/src/yoga/YGNode.h +++ b/third-party/yoga/src/yoga/YGNode.h @@ -1,323 +1,304 @@ /* - * Copyright (c) Facebook, Inc. and its affiliates. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #pragma once -#include -#include -#include "Bitfield.h" -#include "CompactValue.h" -#include "YGConfig.h" -#include "YGLayout.hpp" -#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(); -}; +#include +#include + +#include +#include +#include + +YG_EXTERN_C_BEGIN + +/** + * Handle to a mutable Yoga Node. + */ +typedef struct YGNode* YGNodeRef; + +/** + * Handle to an immutable Yoga Node. + */ +typedef const struct YGNode* YGNodeConstRef; + +/** + * Heap allocates and returns a new Yoga node using Yoga settings. + */ +YG_EXPORT YGNodeRef YGNodeNew(void); + +/** + * Heap allocates and returns a new Yoga node, with customized settings. + */ +YG_EXPORT YGNodeRef YGNodeNewWithConfig(YGConfigConstRef config); + +/** + * Returns a mutable copy of an existing node, with the same context and + * children, but no owner set. Does not call the function set by + * YGConfigSetCloneNodeFunc(). + */ +YG_EXPORT YGNodeRef YGNodeClone(YGNodeConstRef node); + +/** + * Frees the Yoga node, disconnecting it from its owner and children. + */ +YG_EXPORT void YGNodeFree(YGNodeRef node); + +/** + * Frees the subtree of Yoga nodes rooted at the given node. + */ +YG_EXPORT void YGNodeFreeRecursive(YGNodeRef node); + +/** + * Frees the Yoga node without disconnecting it from its owner or children. + * Allows garbage collecting Yoga nodes in parallel when the entire tree is + * unreachable. + */ +YG_EXPORT void YGNodeFinalize(YGNodeRef node); + +/** + * Resets the node to its default state. + */ +YG_EXPORT void YGNodeReset(YGNodeRef node); + +/** + * Calculates the layout of the tree rooted at the given node. + * + * Layout results may be read after calling YGNodeCalculateLayout() using + * functions like YGNodeLayoutGetLeft(), YGNodeLayoutGetTop(), etc. + * + * YGNodeGetHasNewLayout() may be read to know if the layout of the node or its + * subtrees may have changed since the last time YGNodeCalculate() was called. + */ +YG_EXPORT void YGNodeCalculateLayout( + YGNodeRef node, + float availableWidth, + float availableHeight, + YGDirection ownerDirection); + +/** + * Whether the given node may have new layout results. Must be reset by calling + * YGNodeSetHasNewLayout(). + */ +YG_EXPORT bool YGNodeGetHasNewLayout(YGNodeConstRef node); + +/** + * Sets whether a nodes layout is considered new. + */ +YG_EXPORT void YGNodeSetHasNewLayout(YGNodeRef node, bool hasNewLayout); + +/** + * Whether the node's layout results are dirty due to it or its children + * changing. + */ +YG_EXPORT bool YGNodeIsDirty(YGNodeConstRef node); + +/** + * Marks a node with custom measure function as dirty. + */ +YG_EXPORT void YGNodeMarkDirty(YGNodeRef node); + +typedef void (*YGDirtiedFunc)(YGNodeConstRef node); + +/** + * Called when a change is made to the Yoga tree which dirties this node. + */ +YG_EXPORT void YGNodeSetDirtiedFunc(YGNodeRef node, YGDirtiedFunc dirtiedFunc); + +/** + * Returns a dirtied func if set. + */ +YG_EXPORT YGDirtiedFunc YGNodeGetDirtiedFunc(YGNodeConstRef node); + +/** + * Inserts a child node at the given index. + */ +YG_EXPORT void YGNodeInsertChild(YGNodeRef node, YGNodeRef child, size_t index); + +/** + * Replaces the child node at a given index with a new one. + */ +YG_EXPORT void YGNodeSwapChild(YGNodeRef node, YGNodeRef child, size_t index); + +/** + * Removes the given child node. + */ +YG_EXPORT void YGNodeRemoveChild(YGNodeRef node, YGNodeRef child); + +/** + * Removes all children nodes. + */ +YG_EXPORT void YGNodeRemoveAllChildren(YGNodeRef node); + +/** + * Sets children according to the given list of nodes. + */ +YG_EXPORT void +YGNodeSetChildren(YGNodeRef owner, const YGNodeRef* children, size_t count); + +/** + * Get the child node at a given index. + */ +YG_EXPORT YGNodeRef YGNodeGetChild(YGNodeRef node, size_t index); + +/** + * The number of child nodes. + */ +YG_EXPORT size_t YGNodeGetChildCount(YGNodeConstRef node); + +/** + * Get the parent/owner currently set for a node. + */ +YG_EXPORT YGNodeRef YGNodeGetOwner(YGNodeRef node); + +/** + * Get the parent/owner currently set for a node. + */ +YG_EXPORT YGNodeRef YGNodeGetParent(YGNodeRef node); + +/** + * Set a new config for the node after creation. + */ +YG_EXPORT void YGNodeSetConfig(YGNodeRef node, YGConfigRef config); + +/** + * Get the config currently set on the node. + */ +YG_EXPORT YGConfigConstRef YGNodeGetConfig(YGNodeRef node); + +/** + * Sets extra data on the Yoga node which may be read from during callbacks. + */ +YG_EXPORT void YGNodeSetContext(YGNodeRef node, void* context); + +/** + * Returns the context or NULL if no context has been set. + */ +YG_EXPORT void* YGNodeGetContext(YGNodeConstRef node); + +typedef struct YGSize { + float width; + float height; +} YGSize; + +/** + * Returns the computed dimensions of the node, following the constraints of + * `widthMode` and `heightMode`: + * + * YGMeasureModeUndefined: The parent has not imposed any constraint on the + * child. It can be whatever size it wants. + * + * YGMeasureModeAtMost: The child can be as large as it wants up to the + * specified size. + * + * YGMeasureModeExactly: The parent has determined an exact size for the + * child. The child is going to be given those bounds regardless of how big it + * wants to be. + * + * @returns the size of the leaf node, measured under the given constraints. + */ +typedef YGSize (*YGMeasureFunc)( + YGNodeConstRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode); + +/** + * Allows providing custom measurements for a Yoga leaf node (usually for + * measuring text). YGNodeMarkDirty() must be set if content effecting the + * measurements of the node changes. + */ +YG_EXPORT void YGNodeSetMeasureFunc(YGNodeRef node, YGMeasureFunc measureFunc); + +/** + * Whether a measure function is set. + */ +YG_EXPORT bool YGNodeHasMeasureFunc(YGNodeConstRef node); + +/** + * @returns a defined offset to baseline (ascent). + */ +typedef float (*YGBaselineFunc)(YGNodeConstRef node, float width, float height); + +/** + * Set a custom function for determining the text baseline for use in baseline + * alignment. + */ +YG_EXPORT void YGNodeSetBaselineFunc( + YGNodeRef node, + YGBaselineFunc baselineFunc); + +/** + * Whether a baseline function is set. + */ +YG_EXPORT bool YGNodeHasBaselineFunc(YGNodeConstRef node); + +/** + * Sets this node should be considered the reference baseline among siblings. + */ +YG_EXPORT void YGNodeSetIsReferenceBaseline( + YGNodeRef node, + bool isReferenceBaseline); + +/** + * Whether this node is set as the reference baseline. + */ +YG_EXPORT bool YGNodeIsReferenceBaseline(YGNodeConstRef node); + +/** + * Sets whether a leaf node's layout results may be truncated during layout + * rounding. + */ +YG_EXPORT void YGNodeSetNodeType(YGNodeRef node, YGNodeType nodeType); + +/** + * Whether a leaf node's layout results may be truncated during layout + * rounding. + */ +YG_EXPORT YGNodeType YGNodeGetNodeType(YGNodeConstRef node); + +/** + * Make it so that this node will always form a containing block for any + * descendant nodes. This is useful for when a node has a property outside of + * of Yoga that will form a containing block. For example, transforms or some of + * the others listed in + * https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block + */ +YG_EXPORT void YGNodeSetAlwaysFormsContainingBlock( + YGNodeRef node, + bool alwaysFormsContainingBlock); + +/** + * Whether the node will always form a containing block for any descendant. This + * can happen in situation where the client implements something like a + * transform that can affect containing blocks but is not handled by Yoga + * directly. + */ +YG_EXPORT bool YGNodeGetAlwaysFormsContainingBlock(YGNodeConstRef node); + +/** + * @deprecated + */ +YG_DEPRECATED( + "YGNodeCanUseCachedMeasurement may be removed in a future version of Yoga") +YG_EXPORT bool YGNodeCanUseCachedMeasurement( + YGMeasureMode widthMode, + float availableWidth, + YGMeasureMode heightMode, + float availableHeight, + YGMeasureMode lastWidthMode, + float lastAvailableWidth, + YGMeasureMode lastHeightMode, + float lastAvailableHeight, + float lastComputedWidth, + float lastComputedHeight, + float marginRow, + float marginColumn, + YGConfigRef config); + +YG_EXTERN_C_END diff --git a/third-party/yoga/src/yoga/YGNodeLayout.cpp b/third-party/yoga/src/yoga/YGNodeLayout.cpp new file mode 100644 index 00000000..50c8766e --- /dev/null +++ b/third-party/yoga/src/yoga/YGNodeLayout.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include +#include + +using namespace facebook; +using namespace facebook::yoga; + +namespace { + +template +float getResolvedLayoutProperty(const YGNodeConstRef nodeRef, const Edge edge) { + const auto node = resolveRef(nodeRef); + yoga::assertFatalWithNode( + node, + edge <= Edge::End, + "Cannot get layout properties of multi-edge shorthands"); + + if (edge == Edge::Start) { + if (node->getLayout().direction() == Direction::RTL) { + return (node->getLayout().*LayoutMember)(PhysicalEdge::Right); + } else { + return (node->getLayout().*LayoutMember)(PhysicalEdge::Left); + } + } + + if (edge == Edge::End) { + if (node->getLayout().direction() == Direction::RTL) { + return (node->getLayout().*LayoutMember)(PhysicalEdge::Left); + } else { + return (node->getLayout().*LayoutMember)(PhysicalEdge::Right); + } + } + + return (node->getLayout().*LayoutMember)(static_cast(edge)); +} + +} // namespace + +float YGNodeLayoutGetLeft(const YGNodeConstRef node) { + return resolveRef(node)->getLayout().position(PhysicalEdge::Left); +} + +float YGNodeLayoutGetTop(const YGNodeConstRef node) { + return resolveRef(node)->getLayout().position(PhysicalEdge::Top); +} + +float YGNodeLayoutGetRight(const YGNodeConstRef node) { + return resolveRef(node)->getLayout().position(PhysicalEdge::Right); +} + +float YGNodeLayoutGetBottom(const YGNodeConstRef node) { + return resolveRef(node)->getLayout().position(PhysicalEdge::Bottom); +} + +float YGNodeLayoutGetWidth(const YGNodeConstRef node) { + return resolveRef(node)->getLayout().dimension(Dimension::Width); +} + +float YGNodeLayoutGetHeight(const YGNodeConstRef node) { + return resolveRef(node)->getLayout().dimension(Dimension::Height); +} + +YGDirection YGNodeLayoutGetDirection(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->getLayout().direction()); +} + +bool YGNodeLayoutGetHadOverflow(const YGNodeConstRef node) { + return resolveRef(node)->getLayout().hadOverflow(); +} + +float YGNodeLayoutGetMargin(YGNodeConstRef node, YGEdge edge) { + return getResolvedLayoutProperty<&LayoutResults::margin>( + node, scopedEnum(edge)); +} + +float YGNodeLayoutGetBorder(YGNodeConstRef node, YGEdge edge) { + return getResolvedLayoutProperty<&LayoutResults::border>( + node, scopedEnum(edge)); +} + +float YGNodeLayoutGetPadding(YGNodeConstRef node, YGEdge edge) { + return getResolvedLayoutProperty<&LayoutResults::padding>( + node, scopedEnum(edge)); +} + +float YGNodeLayoutGetRawHeight(YGNodeConstRef node) { + return resolveRef(node)->getLayout().rawDimension(Dimension::Height); +} + +float YGNodeLayoutGetRawWidth(YGNodeConstRef node) { + return resolveRef(node)->getLayout().rawDimension(Dimension::Width); +} diff --git a/third-party/yoga/src/yoga/YGNodeLayout.h b/third-party/yoga/src/yoga/YGNodeLayout.h new file mode 100644 index 00000000..4f25af21 --- /dev/null +++ b/third-party/yoga/src/yoga/YGNodeLayout.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include +#include +#include + +YG_EXTERN_C_BEGIN + +YG_EXPORT float YGNodeLayoutGetLeft(YGNodeConstRef node); +YG_EXPORT float YGNodeLayoutGetTop(YGNodeConstRef node); +YG_EXPORT float YGNodeLayoutGetRight(YGNodeConstRef node); +YG_EXPORT float YGNodeLayoutGetBottom(YGNodeConstRef node); +YG_EXPORT float YGNodeLayoutGetWidth(YGNodeConstRef node); +YG_EXPORT float YGNodeLayoutGetHeight(YGNodeConstRef node); +YG_EXPORT YGDirection YGNodeLayoutGetDirection(YGNodeConstRef node); +YG_EXPORT bool YGNodeLayoutGetHadOverflow(YGNodeConstRef 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. +YG_EXPORT float YGNodeLayoutGetMargin(YGNodeConstRef node, YGEdge edge); +YG_EXPORT float YGNodeLayoutGetBorder(YGNodeConstRef node, YGEdge edge); +YG_EXPORT float YGNodeLayoutGetPadding(YGNodeConstRef node, YGEdge edge); + +/** + * Return the measured height of the node, before layout rounding + */ +YG_EXPORT float YGNodeLayoutGetRawHeight(YGNodeConstRef node); + +/** + * Return the measured width of the node, before layout rounding + */ +YG_EXPORT float YGNodeLayoutGetRawWidth(YGNodeConstRef node); + +YG_EXTERN_C_END diff --git a/third-party/yoga/src/yoga/YGNodePrint.cpp b/third-party/yoga/src/yoga/YGNodePrint.cpp deleted file mode 100755 index 26efa485..00000000 --- a/third-party/yoga/src/yoga/YGNodePrint.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* - * 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/third-party/yoga/src/yoga/YGNodePrint.h b/third-party/yoga/src/yoga/YGNodePrint.h deleted file mode 100755 index 3db504b4..00000000 --- a/third-party/yoga/src/yoga/YGNodePrint.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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/third-party/yoga/src/yoga/YGNodeStyle.cpp b/third-party/yoga/src/yoga/YGNodeStyle.cpp new file mode 100644 index 00000000..e43a38e6 --- /dev/null +++ b/third-party/yoga/src/yoga/YGNodeStyle.cpp @@ -0,0 +1,774 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include +#include + +using namespace facebook; +using namespace facebook::yoga; + +namespace { + +template +void updateStyle(YGNodeRef node, ValueT value) { + auto& style = resolveRef(node)->style(); + if ((style.*GetterT)() != value) { + (style.*SetterT)(value); + resolveRef(node)->markDirtyAndPropagate(); + } +} + +template +void updateStyle(YGNodeRef node, IdxT idx, ValueT value) { + auto& style = resolveRef(node)->style(); + if ((style.*GetterT)(idx) != value) { + (style.*SetterT)(idx, value); + resolveRef(node)->markDirtyAndPropagate(); + } +} + +} // namespace + +void YGNodeCopyStyle(YGNodeRef dstNode, YGNodeConstRef srcNode) { + auto dst = resolveRef(dstNode); + auto src = resolveRef(srcNode); + + if (dst->style() != src->style()) { + dst->setStyle(src->style()); + dst->markDirtyAndPropagate(); + } +} + +void YGNodeStyleSetDirection(const YGNodeRef node, const YGDirection value) { + updateStyle<&Style::direction, &Style::setDirection>(node, scopedEnum(value)); +} + +YGDirection YGNodeStyleGetDirection(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().direction()); +} + +void YGNodeStyleSetFlexDirection( + const YGNodeRef node, + const YGFlexDirection flexDirection) { + updateStyle<&Style::flexDirection, &Style::setFlexDirection>( + node, scopedEnum(flexDirection)); +} + +YGFlexDirection YGNodeStyleGetFlexDirection(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().flexDirection()); +} + +void YGNodeStyleSetJustifyContent( + const YGNodeRef node, + const YGJustify justifyContent) { + updateStyle<&Style::justifyContent, &Style::setJustifyContent>( + node, scopedEnum(justifyContent)); +} + +YGJustify YGNodeStyleGetJustifyContent(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().justifyContent()); +} + +void YGNodeStyleSetJustifyItems(YGNodeRef node, const YGJustify justifyItems) { + updateStyle<&Style::justifyItems, &Style::setJustifyItems>( + node, scopedEnum(justifyItems)); +} + +YGJustify YGNodeStyleGetJustifyItems(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().justifyItems()); +} + +void YGNodeStyleSetJustifySelf(YGNodeRef node, const YGJustify justifySelf) { + updateStyle<&Style::justifySelf, &Style::setJustifySelf>( + node, scopedEnum(justifySelf)); +} + +YGJustify YGNodeStyleGetJustifySelf(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().justifySelf()); +} + +void YGNodeStyleSetAlignContent( + const YGNodeRef node, + const YGAlign alignContent) { + updateStyle<&Style::alignContent, &Style::setAlignContent>( + node, scopedEnum(alignContent)); +} + +YGAlign YGNodeStyleGetAlignContent(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().alignContent()); +} + +void YGNodeStyleSetAlignItems(const YGNodeRef node, const YGAlign alignItems) { + updateStyle<&Style::alignItems, &Style::setAlignItems>( + node, scopedEnum(alignItems)); +} + +YGAlign YGNodeStyleGetAlignItems(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().alignItems()); +} + +void YGNodeStyleSetAlignSelf(const YGNodeRef node, const YGAlign alignSelf) { + updateStyle<&Style::alignSelf, &Style::setAlignSelf>( + node, scopedEnum(alignSelf)); +} + +YGAlign YGNodeStyleGetAlignSelf(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().alignSelf()); +} + +void YGNodeStyleSetPositionType( + const YGNodeRef node, + const YGPositionType positionType) { + updateStyle<&Style::positionType, &Style::setPositionType>( + node, scopedEnum(positionType)); +} + +YGPositionType YGNodeStyleGetPositionType(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().positionType()); +} + +void YGNodeStyleSetFlexWrap(const YGNodeRef node, const YGWrap flexWrap) { + updateStyle<&Style::flexWrap, &Style::setFlexWrap>( + node, scopedEnum(flexWrap)); +} + +YGWrap YGNodeStyleGetFlexWrap(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().flexWrap()); +} + +void YGNodeStyleSetOverflow(const YGNodeRef node, const YGOverflow overflow) { + updateStyle<&Style::overflow, &Style::setOverflow>( + node, scopedEnum(overflow)); +} + +YGOverflow YGNodeStyleGetOverflow(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().overflow()); +} + +void YGNodeStyleSetDisplay(const YGNodeRef node, const YGDisplay display) { + updateStyle<&Style::display, &Style::setDisplay>(node, scopedEnum(display)); +} + +YGDisplay YGNodeStyleGetDisplay(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().display()); +} + +void YGNodeStyleSetFlex(const YGNodeRef node, const float flex) { + updateStyle<&Style::flex, &Style::setFlex>(node, FloatOptional{flex}); +} + +float YGNodeStyleGetFlex(const YGNodeConstRef nodeRef) { + const auto node = resolveRef(nodeRef); + return node->style().flex().isUndefined() ? YGUndefined + : node->style().flex().unwrap(); +} + +void YGNodeStyleSetFlexGrow(const YGNodeRef node, const float flexGrow) { + updateStyle<&Style::flexGrow, &Style::setFlexGrow>( + node, FloatOptional{flexGrow}); +} + +float YGNodeStyleGetFlexGrow(const YGNodeConstRef nodeRef) { + const auto node = resolveRef(nodeRef); + return node->style().flexGrow().isUndefined() + ? Style::DefaultFlexGrow + : node->style().flexGrow().unwrap(); +} + +void YGNodeStyleSetFlexShrink(const YGNodeRef node, const float flexShrink) { + updateStyle<&Style::flexShrink, &Style::setFlexShrink>( + node, FloatOptional{flexShrink}); +} + +float YGNodeStyleGetFlexShrink(const YGNodeConstRef nodeRef) { + const auto node = resolveRef(nodeRef); + return node->style().flexShrink().isUndefined() + ? (node->getConfig()->useWebDefaults() ? Style::WebDefaultFlexShrink + : Style::DefaultFlexShrink) + : node->style().flexShrink().unwrap(); +} + +void YGNodeStyleSetFlexBasis(const YGNodeRef node, const float flexBasis) { + updateStyle<&Style::flexBasis, &Style::setFlexBasis>( + node, StyleSizeLength::points(flexBasis)); +} + +void YGNodeStyleSetFlexBasisPercent( + const YGNodeRef node, + const float flexBasisPercent) { + updateStyle<&Style::flexBasis, &Style::setFlexBasis>( + node, StyleSizeLength::percent(flexBasisPercent)); +} + +void YGNodeStyleSetFlexBasisAuto(const YGNodeRef node) { + updateStyle<&Style::flexBasis, &Style::setFlexBasis>( + node, StyleSizeLength::ofAuto()); +} + +void YGNodeStyleSetFlexBasisMaxContent(const YGNodeRef node) { + updateStyle<&Style::flexBasis, &Style::setFlexBasis>( + node, StyleSizeLength::ofMaxContent()); +} + +void YGNodeStyleSetFlexBasisFitContent(const YGNodeRef node) { + updateStyle<&Style::flexBasis, &Style::setFlexBasis>( + node, StyleSizeLength::ofFitContent()); +} + +void YGNodeStyleSetFlexBasisStretch(const YGNodeRef node) { + updateStyle<&Style::flexBasis, &Style::setFlexBasis>( + node, StyleSizeLength::ofStretch()); +} + +YGValue YGNodeStyleGetFlexBasis(const YGNodeConstRef node) { + return (YGValue)resolveRef(node)->style().flexBasis(); +} + +void YGNodeStyleSetPosition(YGNodeRef node, YGEdge edge, float points) { + updateStyle<&Style::position, &Style::setPosition>( + node, scopedEnum(edge), StyleLength::points(points)); +} + +void YGNodeStyleSetPositionPercent(YGNodeRef node, YGEdge edge, float percent) { + updateStyle<&Style::position, &Style::setPosition>( + node, scopedEnum(edge), StyleLength::percent(percent)); +} + +void YGNodeStyleSetPositionAuto(YGNodeRef node, YGEdge edge) { + updateStyle<&Style::position, &Style::setPosition>( + node, scopedEnum(edge), StyleLength::ofAuto()); +} + +YGValue YGNodeStyleGetPosition(YGNodeConstRef node, YGEdge edge) { + return (YGValue)resolveRef(node)->style().position(scopedEnum(edge)); +} + +void YGNodeStyleSetMargin(YGNodeRef node, YGEdge edge, float points) { + updateStyle<&Style::margin, &Style::setMargin>( + node, scopedEnum(edge), StyleLength::points(points)); +} + +void YGNodeStyleSetMarginPercent(YGNodeRef node, YGEdge edge, float percent) { + updateStyle<&Style::margin, &Style::setMargin>( + node, scopedEnum(edge), StyleLength::percent(percent)); +} + +void YGNodeStyleSetMarginAuto(YGNodeRef node, YGEdge edge) { + updateStyle<&Style::margin, &Style::setMargin>( + node, scopedEnum(edge), StyleLength::ofAuto()); +} + +YGValue YGNodeStyleGetMargin(YGNodeConstRef node, YGEdge edge) { + return (YGValue)resolveRef(node)->style().margin(scopedEnum(edge)); +} + +void YGNodeStyleSetPadding(YGNodeRef node, YGEdge edge, float points) { + updateStyle<&Style::padding, &Style::setPadding>( + node, scopedEnum(edge), StyleLength::points(points)); +} + +void YGNodeStyleSetPaddingPercent(YGNodeRef node, YGEdge edge, float percent) { + updateStyle<&Style::padding, &Style::setPadding>( + node, scopedEnum(edge), StyleLength::percent(percent)); +} + +YGValue YGNodeStyleGetPadding(YGNodeConstRef node, YGEdge edge) { + return (YGValue)resolveRef(node)->style().padding(scopedEnum(edge)); +} + +void YGNodeStyleSetBorder( + const YGNodeRef node, + const YGEdge edge, + const float border) { + updateStyle<&Style::border, &Style::setBorder>( + node, scopedEnum(edge), StyleLength::points(border)); +} + +float YGNodeStyleGetBorder(const YGNodeConstRef node, const YGEdge edge) { + auto border = resolveRef(node)->style().border(scopedEnum(edge)); + if (border.isUndefined() || border.isAuto()) { + return YGUndefined; + } + + return static_cast(border).value; +} + +void YGNodeStyleSetGap( + const YGNodeRef node, + const YGGutter gutter, + const float gapLength) { + updateStyle<&Style::gap, &Style::setGap>( + node, scopedEnum(gutter), StyleLength::points(gapLength)); +} + +void YGNodeStyleSetGapPercent(YGNodeRef node, YGGutter gutter, float percent) { + updateStyle<&Style::gap, &Style::setGap>( + node, scopedEnum(gutter), StyleLength::percent(percent)); +} + +YGValue YGNodeStyleGetGap(const YGNodeConstRef node, const YGGutter gutter) { + return (YGValue)resolveRef(node)->style().gap(scopedEnum(gutter)); +} + +void YGNodeStyleSetAspectRatio(const YGNodeRef node, const float aspectRatio) { + updateStyle<&Style::aspectRatio, &Style::setAspectRatio>( + node, FloatOptional{aspectRatio}); +} + +float YGNodeStyleGetAspectRatio(const YGNodeConstRef node) { + const FloatOptional op = resolveRef(node)->style().aspectRatio(); + return op.isUndefined() ? YGUndefined : op.unwrap(); +} + +void YGNodeStyleSetBoxSizing(YGNodeRef node, YGBoxSizing boxSizing) { + updateStyle<&Style::boxSizing, &Style::setBoxSizing>( + node, scopedEnum(boxSizing)); +} + +YGBoxSizing YGNodeStyleGetBoxSizing(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().boxSizing()); +} + +void YGNodeStyleSetWidth(YGNodeRef node, float points) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Width, StyleSizeLength::points(points)); +} + +void YGNodeStyleSetWidthPercent(YGNodeRef node, float percent) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Width, StyleSizeLength::percent(percent)); +} + +void YGNodeStyleSetWidthAuto(YGNodeRef node) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Width, StyleSizeLength::ofAuto()); +} + +void YGNodeStyleSetWidthMaxContent(YGNodeRef node) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Width, StyleSizeLength::ofMaxContent()); +} + +void YGNodeStyleSetWidthFitContent(YGNodeRef node) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Width, StyleSizeLength::ofFitContent()); +} + +void YGNodeStyleSetWidthStretch(YGNodeRef node) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Width, StyleSizeLength::ofStretch()); +} + +YGValue YGNodeStyleGetWidth(YGNodeConstRef node) { + return (YGValue)resolveRef(node)->style().dimension(Dimension::Width); +} + +void YGNodeStyleSetHeight(YGNodeRef node, float points) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Height, StyleSizeLength::points(points)); +} + +void YGNodeStyleSetHeightPercent(YGNodeRef node, float percent) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Height, StyleSizeLength::percent(percent)); +} + +void YGNodeStyleSetHeightAuto(YGNodeRef node) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Height, StyleSizeLength::ofAuto()); +} + +void YGNodeStyleSetHeightMaxContent(YGNodeRef node) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Height, StyleSizeLength::ofMaxContent()); +} + +void YGNodeStyleSetHeightFitContent(YGNodeRef node) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Height, StyleSizeLength::ofFitContent()); +} + +void YGNodeStyleSetHeightStretch(YGNodeRef node) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Height, StyleSizeLength::ofStretch()); +} + +YGValue YGNodeStyleGetHeight(YGNodeConstRef node) { + return (YGValue)resolveRef(node)->style().dimension(Dimension::Height); +} + +void YGNodeStyleSetMinWidth(const YGNodeRef node, const float minWidth) { + updateStyle<&Style::minDimension, &Style::setMinDimension>( + node, Dimension::Width, StyleSizeLength::points(minWidth)); +} + +void YGNodeStyleSetMinWidthPercent(const YGNodeRef node, const float minWidth) { + updateStyle<&Style::minDimension, &Style::setMinDimension>( + node, Dimension::Width, StyleSizeLength::percent(minWidth)); +} + +void YGNodeStyleSetMinWidthMaxContent(const YGNodeRef node) { + updateStyle<&Style::minDimension, &Style::setMinDimension>( + node, Dimension::Width, StyleSizeLength::ofMaxContent()); +} + +void YGNodeStyleSetMinWidthFitContent(const YGNodeRef node) { + updateStyle<&Style::minDimension, &Style::setMinDimension>( + node, Dimension::Width, StyleSizeLength::ofFitContent()); +} + +void YGNodeStyleSetMinWidthStretch(const YGNodeRef node) { + updateStyle<&Style::minDimension, &Style::setMinDimension>( + node, Dimension::Width, StyleSizeLength::ofStretch()); +} + +YGValue YGNodeStyleGetMinWidth(const YGNodeConstRef node) { + return (YGValue)resolveRef(node)->style().minDimension(Dimension::Width); +} + +void YGNodeStyleSetMinHeight(const YGNodeRef node, const float minHeight) { + updateStyle<&Style::minDimension, &Style::setMinDimension>( + node, Dimension::Height, StyleSizeLength::points(minHeight)); +} + +void YGNodeStyleSetMinHeightPercent( + const YGNodeRef node, + const float minHeight) { + updateStyle<&Style::minDimension, &Style::setMinDimension>( + node, Dimension::Height, StyleSizeLength::percent(minHeight)); +} + +void YGNodeStyleSetMinHeightMaxContent(const YGNodeRef node) { + updateStyle<&Style::minDimension, &Style::setMinDimension>( + node, Dimension::Height, StyleSizeLength::ofMaxContent()); +} + +void YGNodeStyleSetMinHeightFitContent(const YGNodeRef node) { + updateStyle<&Style::minDimension, &Style::setMinDimension>( + node, Dimension::Height, StyleSizeLength::ofFitContent()); +} + +void YGNodeStyleSetMinHeightStretch(const YGNodeRef node) { + updateStyle<&Style::minDimension, &Style::setMinDimension>( + node, Dimension::Height, StyleSizeLength::ofStretch()); +} + +YGValue YGNodeStyleGetMinHeight(const YGNodeConstRef node) { + return (YGValue)resolveRef(node)->style().minDimension(Dimension::Height); +} + +void YGNodeStyleSetMaxWidth(const YGNodeRef node, const float maxWidth) { + updateStyle<&Style::maxDimension, &Style::setMaxDimension>( + node, Dimension::Width, StyleSizeLength::points(maxWidth)); +} + +void YGNodeStyleSetMaxWidthPercent(const YGNodeRef node, const float maxWidth) { + updateStyle<&Style::maxDimension, &Style::setMaxDimension>( + node, Dimension::Width, StyleSizeLength::percent(maxWidth)); +} + +void YGNodeStyleSetMaxWidthMaxContent(const YGNodeRef node) { + updateStyle<&Style::maxDimension, &Style::setMaxDimension>( + node, Dimension::Width, StyleSizeLength::ofMaxContent()); +} + +void YGNodeStyleSetMaxWidthFitContent(const YGNodeRef node) { + updateStyle<&Style::maxDimension, &Style::setMaxDimension>( + node, Dimension::Width, StyleSizeLength::ofFitContent()); +} + +void YGNodeStyleSetMaxWidthStretch(const YGNodeRef node) { + updateStyle<&Style::maxDimension, &Style::setMaxDimension>( + node, Dimension::Width, StyleSizeLength::ofStretch()); +} + +YGValue YGNodeStyleGetMaxWidth(const YGNodeConstRef node) { + return (YGValue)resolveRef(node)->style().maxDimension(Dimension::Width); +} + +void YGNodeStyleSetMaxHeight(const YGNodeRef node, const float maxHeight) { + updateStyle<&Style::maxDimension, &Style::setMaxDimension>( + node, Dimension::Height, StyleSizeLength::points(maxHeight)); +} + +void YGNodeStyleSetMaxHeightPercent( + const YGNodeRef node, + const float maxHeight) { + updateStyle<&Style::maxDimension, &Style::setMaxDimension>( + node, Dimension::Height, StyleSizeLength::percent(maxHeight)); +} + +void YGNodeStyleSetMaxHeightMaxContent(const YGNodeRef node) { + updateStyle<&Style::maxDimension, &Style::setMaxDimension>( + node, Dimension::Height, StyleSizeLength::ofMaxContent()); +} + +void YGNodeStyleSetMaxHeightFitContent(const YGNodeRef node) { + updateStyle<&Style::maxDimension, &Style::setMaxDimension>( + node, Dimension::Height, StyleSizeLength::ofFitContent()); +} + +void YGNodeStyleSetMaxHeightStretch(const YGNodeRef node) { + updateStyle<&Style::maxDimension, &Style::setMaxDimension>( + node, Dimension::Height, StyleSizeLength::ofStretch()); +} + +YGValue YGNodeStyleGetMaxHeight(const YGNodeConstRef node) { + return (YGValue)resolveRef(node)->style().maxDimension(Dimension::Height); +} + +// Grid Item Placement Properties + +void YGNodeStyleSetGridColumnStart(YGNodeRef node, int32_t gridColumnStart) { + updateStyle<&Style::gridColumnStart, &Style::setGridColumnStart>( + node, GridLine::fromInteger(gridColumnStart)); +} + +void YGNodeStyleSetGridColumnStartAuto(YGNodeRef node) { + updateStyle<&Style::gridColumnStart, &Style::setGridColumnStart>( + node, GridLine::auto_()); +} + +void YGNodeStyleSetGridColumnStartSpan(YGNodeRef node, int32_t span) { + updateStyle<&Style::gridColumnStart, &Style::setGridColumnStart>( + node, GridLine::span(span)); +} + +int32_t YGNodeStyleGetGridColumnStart(YGNodeConstRef node) { + const auto& gridLine = resolveRef(node)->style().gridColumnStart(); + return gridLine.isInteger() ? gridLine.integer : 0; +} + +void YGNodeStyleSetGridColumnEnd(YGNodeRef node, int32_t gridColumnEnd) { + updateStyle<&Style::gridColumnEnd, &Style::setGridColumnEnd>( + node, GridLine::fromInteger(gridColumnEnd)); +} + +void YGNodeStyleSetGridColumnEndAuto(YGNodeRef node) { + updateStyle<&Style::gridColumnEnd, &Style::setGridColumnEnd>( + node, GridLine::auto_()); +} + +void YGNodeStyleSetGridColumnEndSpan(YGNodeRef node, int32_t span) { + updateStyle<&Style::gridColumnEnd, &Style::setGridColumnEnd>( + node, GridLine::span(span)); +} + +int32_t YGNodeStyleGetGridColumnEnd(YGNodeConstRef node) { + const auto& gridLine = resolveRef(node)->style().gridColumnEnd(); + return gridLine.isInteger() ? gridLine.integer : 0; +} + +void YGNodeStyleSetGridRowStart(YGNodeRef node, int32_t gridRowStart) { + updateStyle<&Style::gridRowStart, &Style::setGridRowStart>( + node, GridLine::fromInteger(gridRowStart)); +} + +void YGNodeStyleSetGridRowStartAuto(YGNodeRef node) { + updateStyle<&Style::gridRowStart, &Style::setGridRowStart>( + node, GridLine::auto_()); +} + +void YGNodeStyleSetGridRowStartSpan(YGNodeRef node, int32_t span) { + updateStyle<&Style::gridRowStart, &Style::setGridRowStart>( + node, GridLine::span(span)); +} + +int32_t YGNodeStyleGetGridRowStart(YGNodeConstRef node) { + const auto& gridLine = resolveRef(node)->style().gridRowStart(); + return gridLine.isInteger() ? gridLine.integer : 0; +} + +void YGNodeStyleSetGridRowEnd(YGNodeRef node, int32_t gridRowEnd) { + updateStyle<&Style::gridRowEnd, &Style::setGridRowEnd>( + node, GridLine::fromInteger(gridRowEnd)); +} + +void YGNodeStyleSetGridRowEndAuto(YGNodeRef node) { + updateStyle<&Style::gridRowEnd, &Style::setGridRowEnd>( + node, GridLine::auto_()); +} + +void YGNodeStyleSetGridRowEndSpan(YGNodeRef node, int32_t span) { + updateStyle<&Style::gridRowEnd, &Style::setGridRowEnd>( + node, GridLine::span(span)); +} + +int32_t YGNodeStyleGetGridRowEnd(YGNodeConstRef node) { + const auto& gridLine = resolveRef(node)->style().gridRowEnd(); + return gridLine.isInteger() ? gridLine.integer : 0; +} + +// Grid Container Properties + +namespace { + +GridTrackSize gridTrackSizeFromTypeAndValue(YGGridTrackType type, float value) { + switch (type) { + case YGGridTrackTypePoints: + return GridTrackSize::length(value); + case YGGridTrackTypePercent: + return GridTrackSize::percent(value); + case YGGridTrackTypeFr: + return GridTrackSize::fr(value); + case YGGridTrackTypeAuto: + return GridTrackSize::auto_(); + case YGGridTrackTypeMinmax: + return GridTrackSize::auto_(); + } + fatalWithMessage("Unknown YGGridTrackType"); +} + +StyleSizeLength styleSizeLengthFromTypeAndValue( + YGGridTrackType type, + float value) { + switch (type) { + case YGGridTrackTypePoints: + return StyleSizeLength::points(value); + case YGGridTrackTypePercent: + return StyleSizeLength::percent(value); + case YGGridTrackTypeFr: + return StyleSizeLength::stretch(value); + case YGGridTrackTypeAuto: + return StyleSizeLength::ofAuto(); + case YGGridTrackTypeMinmax: + return StyleSizeLength::ofAuto(); + } + fatalWithMessage("Unknown YGGridTrackType"); +} + +} // namespace + +// GridTemplateColumns + +void YGNodeStyleSetGridTemplateColumnsCount(YGNodeRef node, size_t count) { + resolveRef(node)->style().resizeGridTemplateColumns(count); + resolveRef(node)->markDirtyAndPropagate(); +} + +void YGNodeStyleSetGridTemplateColumn( + YGNodeRef node, + size_t index, + YGGridTrackType type, + float value) { + resolveRef(node)->style().setGridTemplateColumnAt( + index, gridTrackSizeFromTypeAndValue(type, value)); + resolveRef(node)->markDirtyAndPropagate(); +} + +void YGNodeStyleSetGridTemplateColumnMinMax( + YGNodeRef node, + size_t index, + YGGridTrackType minType, + float minValue, + YGGridTrackType maxType, + float maxValue) { + resolveRef(node)->style().setGridTemplateColumnAt( + index, + GridTrackSize::minmax( + styleSizeLengthFromTypeAndValue(minType, minValue), + styleSizeLengthFromTypeAndValue(maxType, maxValue))); + resolveRef(node)->markDirtyAndPropagate(); +} + +// GridTemplateRows + +void YGNodeStyleSetGridTemplateRowsCount(YGNodeRef node, size_t count) { + resolveRef(node)->style().resizeGridTemplateRows(count); + resolveRef(node)->markDirtyAndPropagate(); +} + +void YGNodeStyleSetGridTemplateRow( + YGNodeRef node, + size_t index, + YGGridTrackType type, + float value) { + resolveRef(node)->style().setGridTemplateRowAt( + index, gridTrackSizeFromTypeAndValue(type, value)); + resolveRef(node)->markDirtyAndPropagate(); +} + +void YGNodeStyleSetGridTemplateRowMinMax( + YGNodeRef node, + size_t index, + YGGridTrackType minType, + float minValue, + YGGridTrackType maxType, + float maxValue) { + resolveRef(node)->style().setGridTemplateRowAt( + index, + GridTrackSize::minmax( + styleSizeLengthFromTypeAndValue(minType, minValue), + styleSizeLengthFromTypeAndValue(maxType, maxValue))); + resolveRef(node)->markDirtyAndPropagate(); +} + +// GridAutoColumns + +void YGNodeStyleSetGridAutoColumnsCount(YGNodeRef node, size_t count) { + resolveRef(node)->style().resizeGridAutoColumns(count); + resolveRef(node)->markDirtyAndPropagate(); +} + +void YGNodeStyleSetGridAutoColumn( + YGNodeRef node, + size_t index, + YGGridTrackType type, + float value) { + resolveRef(node)->style().setGridAutoColumnAt( + index, gridTrackSizeFromTypeAndValue(type, value)); + resolveRef(node)->markDirtyAndPropagate(); +} + +void YGNodeStyleSetGridAutoColumnMinMax( + YGNodeRef node, + size_t index, + YGGridTrackType minType, + float minValue, + YGGridTrackType maxType, + float maxValue) { + resolveRef(node)->style().setGridAutoColumnAt( + index, + GridTrackSize::minmax( + styleSizeLengthFromTypeAndValue(minType, minValue), + styleSizeLengthFromTypeAndValue(maxType, maxValue))); + resolveRef(node)->markDirtyAndPropagate(); +} + +// GridAutoRows + +void YGNodeStyleSetGridAutoRowsCount(YGNodeRef node, size_t count) { + resolveRef(node)->style().resizeGridAutoRows(count); + resolveRef(node)->markDirtyAndPropagate(); +} + +void YGNodeStyleSetGridAutoRow( + YGNodeRef node, + size_t index, + YGGridTrackType type, + float value) { + resolveRef(node)->style().setGridAutoRowAt( + index, gridTrackSizeFromTypeAndValue(type, value)); + resolveRef(node)->markDirtyAndPropagate(); +} + +void YGNodeStyleSetGridAutoRowMinMax( + YGNodeRef node, + size_t index, + YGGridTrackType minType, + float minValue, + YGGridTrackType maxType, + float maxValue) { + resolveRef(node)->style().setGridAutoRowAt( + index, + GridTrackSize::minmax( + styleSizeLengthFromTypeAndValue(minType, minValue), + styleSizeLengthFromTypeAndValue(maxType, maxValue))); + resolveRef(node)->markDirtyAndPropagate(); +} diff --git a/third-party/yoga/src/yoga/YGNodeStyle.h b/third-party/yoga/src/yoga/YGNodeStyle.h new file mode 100644 index 00000000..a1c7e095 --- /dev/null +++ b/third-party/yoga/src/yoga/YGNodeStyle.h @@ -0,0 +1,243 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include + +YG_EXTERN_C_BEGIN + +YG_EXPORT void YGNodeCopyStyle(YGNodeRef dstNode, YGNodeConstRef srcNode); + +YG_EXPORT void YGNodeStyleSetDirection(YGNodeRef node, YGDirection direction); +YG_EXPORT YGDirection YGNodeStyleGetDirection(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetFlexDirection( + YGNodeRef node, + YGFlexDirection flexDirection); +YG_EXPORT YGFlexDirection YGNodeStyleGetFlexDirection(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetJustifyContent( + YGNodeRef node, + YGJustify justifyContent); +YG_EXPORT YGJustify YGNodeStyleGetJustifyContent(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetJustifyItems( + YGNodeRef node, + YGJustify justifyItems); +YG_EXPORT YGJustify YGNodeStyleGetJustifyItems(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetJustifySelf(YGNodeRef node, YGJustify justifySelf); +YG_EXPORT YGJustify YGNodeStyleGetJustifySelf(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetAlignContent(YGNodeRef node, YGAlign alignContent); +YG_EXPORT YGAlign YGNodeStyleGetAlignContent(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetAlignItems(YGNodeRef node, YGAlign alignItems); +YG_EXPORT YGAlign YGNodeStyleGetAlignItems(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetAlignSelf(YGNodeRef node, YGAlign alignSelf); +YG_EXPORT YGAlign YGNodeStyleGetAlignSelf(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetPositionType( + YGNodeRef node, + YGPositionType positionType); +YG_EXPORT YGPositionType YGNodeStyleGetPositionType(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetFlexWrap(YGNodeRef node, YGWrap flexWrap); +YG_EXPORT YGWrap YGNodeStyleGetFlexWrap(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetOverflow(YGNodeRef node, YGOverflow overflow); +YG_EXPORT YGOverflow YGNodeStyleGetOverflow(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetDisplay(YGNodeRef node, YGDisplay display); +YG_EXPORT YGDisplay YGNodeStyleGetDisplay(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetFlex(YGNodeRef node, float flex); +YG_EXPORT float YGNodeStyleGetFlex(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetFlexGrow(YGNodeRef node, float flexGrow); +YG_EXPORT float YGNodeStyleGetFlexGrow(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetFlexShrink(YGNodeRef node, float flexShrink); +YG_EXPORT float YGNodeStyleGetFlexShrink(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetFlexBasis(YGNodeRef node, float flexBasis); +YG_EXPORT void YGNodeStyleSetFlexBasisPercent(YGNodeRef node, float flexBasis); +YG_EXPORT void YGNodeStyleSetFlexBasisAuto(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetFlexBasisMaxContent(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetFlexBasisFitContent(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetFlexBasisStretch(YGNodeRef node); +YG_EXPORT YGValue YGNodeStyleGetFlexBasis(YGNodeConstRef node); + +YG_EXPORT void +YGNodeStyleSetPosition(YGNodeRef node, YGEdge edge, float position); +YG_EXPORT void +YGNodeStyleSetPositionPercent(YGNodeRef node, YGEdge edge, float position); +YG_EXPORT YGValue YGNodeStyleGetPosition(YGNodeConstRef node, YGEdge edge); +YG_EXPORT void YGNodeStyleSetPositionAuto(YGNodeRef node, YGEdge edge); + +YG_EXPORT +void YGNodeStyleSetMargin(YGNodeRef node, YGEdge edge, float margin); +YG_EXPORT void +YGNodeStyleSetMarginPercent(YGNodeRef node, YGEdge edge, float margin); +YG_EXPORT void YGNodeStyleSetMarginAuto(YGNodeRef node, YGEdge edge); +YG_EXPORT YGValue YGNodeStyleGetMargin(YGNodeConstRef node, YGEdge edge); + +YG_EXPORT void +YGNodeStyleSetPadding(YGNodeRef node, YGEdge edge, float padding); +YG_EXPORT void +YGNodeStyleSetPaddingPercent(YGNodeRef node, YGEdge edge, float padding); +YG_EXPORT YGValue YGNodeStyleGetPadding(YGNodeConstRef node, YGEdge edge); + +YG_EXPORT void YGNodeStyleSetBorder(YGNodeRef node, YGEdge edge, float border); +YG_EXPORT float YGNodeStyleGetBorder(YGNodeConstRef node, YGEdge edge); + +YG_EXPORT void +YGNodeStyleSetGap(YGNodeRef node, YGGutter gutter, float gapLength); +YG_EXPORT void +YGNodeStyleSetGapPercent(YGNodeRef node, YGGutter gutter, float gapLength); +YG_EXPORT YGValue YGNodeStyleGetGap(YGNodeConstRef node, YGGutter gutter); + +YG_EXPORT void YGNodeStyleSetBoxSizing(YGNodeRef node, YGBoxSizing boxSizing); +YG_EXPORT YGBoxSizing YGNodeStyleGetBoxSizing(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetWidth(YGNodeRef node, float width); +YG_EXPORT void YGNodeStyleSetWidthPercent(YGNodeRef node, float width); +YG_EXPORT void YGNodeStyleSetWidthAuto(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetWidthMaxContent(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetWidthFitContent(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetWidthStretch(YGNodeRef node); +YG_EXPORT YGValue YGNodeStyleGetWidth(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetHeight(YGNodeRef node, float height); +YG_EXPORT void YGNodeStyleSetHeightPercent(YGNodeRef node, float height); +YG_EXPORT void YGNodeStyleSetHeightAuto(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetHeightMaxContent(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetHeightFitContent(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetHeightStretch(YGNodeRef node); +YG_EXPORT YGValue YGNodeStyleGetHeight(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetMinWidth(YGNodeRef node, float minWidth); +YG_EXPORT void YGNodeStyleSetMinWidthPercent(YGNodeRef node, float minWidth); +YG_EXPORT void YGNodeStyleSetMinWidthMaxContent(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetMinWidthFitContent(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetMinWidthStretch(YGNodeRef node); +YG_EXPORT YGValue YGNodeStyleGetMinWidth(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetMinHeight(YGNodeRef node, float minHeight); +YG_EXPORT void YGNodeStyleSetMinHeightPercent(YGNodeRef node, float minHeight); +YG_EXPORT void YGNodeStyleSetMinHeightMaxContent(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetMinHeightFitContent(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetMinHeightStretch(YGNodeRef node); +YG_EXPORT YGValue YGNodeStyleGetMinHeight(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetMaxWidth(YGNodeRef node, float maxWidth); +YG_EXPORT void YGNodeStyleSetMaxWidthPercent(YGNodeRef node, float maxWidth); +YG_EXPORT void YGNodeStyleSetMaxWidthMaxContent(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetMaxWidthFitContent(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetMaxWidthStretch(YGNodeRef node); +YG_EXPORT YGValue YGNodeStyleGetMaxWidth(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetMaxHeight(YGNodeRef node, float maxHeight); +YG_EXPORT void YGNodeStyleSetMaxHeightPercent(YGNodeRef node, float maxHeight); +YG_EXPORT void YGNodeStyleSetMaxHeightMaxContent(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetMaxHeightFitContent(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetMaxHeightStretch(YGNodeRef node); +YG_EXPORT YGValue YGNodeStyleGetMaxHeight(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetAspectRatio(YGNodeRef node, float aspectRatio); +YG_EXPORT float YGNodeStyleGetAspectRatio(YGNodeConstRef node); + +// Grid Item Properties +YG_EXPORT void YGNodeStyleSetGridColumnStart( + YGNodeRef node, + int gridColumnStart); +YG_EXPORT void YGNodeStyleSetGridColumnStartAuto(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetGridColumnStartSpan(YGNodeRef node, int span); +YG_EXPORT int YGNodeStyleGetGridColumnStart(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetGridColumnEnd(YGNodeRef node, int gridColumnEnd); +YG_EXPORT void YGNodeStyleSetGridColumnEndAuto(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetGridColumnEndSpan(YGNodeRef node, int span); +YG_EXPORT int YGNodeStyleGetGridColumnEnd(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetGridRowStart(YGNodeRef node, int gridRowStart); +YG_EXPORT void YGNodeStyleSetGridRowStartAuto(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetGridRowStartSpan(YGNodeRef node, int span); +YG_EXPORT int YGNodeStyleGetGridRowStart(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetGridRowEnd(YGNodeRef node, int gridRowEnd); +YG_EXPORT void YGNodeStyleSetGridRowEndAuto(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetGridRowEndSpan(YGNodeRef node, int span); +YG_EXPORT int YGNodeStyleGetGridRowEnd(YGNodeConstRef node); + +// Grid Container Properties +YG_EXPORT void YGNodeStyleSetGridTemplateColumnsCount( + YGNodeRef node, + size_t count); +YG_EXPORT void YGNodeStyleSetGridTemplateColumn( + YGNodeRef node, + size_t index, + YGGridTrackType type, + float value); +YG_EXPORT void YGNodeStyleSetGridTemplateColumnMinMax( + YGNodeRef node, + size_t index, + YGGridTrackType minType, + float minValue, + YGGridTrackType maxType, + float maxValue); + +YG_EXPORT void YGNodeStyleSetGridTemplateRowsCount( + YGNodeRef node, + size_t count); +YG_EXPORT void YGNodeStyleSetGridTemplateRow( + YGNodeRef node, + size_t index, + YGGridTrackType type, + float value); +YG_EXPORT void YGNodeStyleSetGridTemplateRowMinMax( + YGNodeRef node, + size_t index, + YGGridTrackType minType, + float minValue, + YGGridTrackType maxType, + float maxValue); + +YG_EXPORT void YGNodeStyleSetGridAutoColumnsCount(YGNodeRef node, size_t count); +YG_EXPORT void YGNodeStyleSetGridAutoColumn( + YGNodeRef node, + size_t index, + YGGridTrackType type, + float value); +YG_EXPORT void YGNodeStyleSetGridAutoColumnMinMax( + YGNodeRef node, + size_t index, + YGGridTrackType minType, + float minValue, + YGGridTrackType maxType, + float maxValue); + +YG_EXPORT void YGNodeStyleSetGridAutoRowsCount(YGNodeRef node, size_t count); +YG_EXPORT void YGNodeStyleSetGridAutoRow( + YGNodeRef node, + size_t index, + YGGridTrackType type, + float value); +YG_EXPORT void YGNodeStyleSetGridAutoRowMinMax( + YGNodeRef node, + size_t index, + YGGridTrackType minType, + float minValue, + YGGridTrackType maxType, + float maxValue); + +YG_EXTERN_C_END diff --git a/third-party/yoga/src/yoga/YGPixelGrid.cpp b/third-party/yoga/src/yoga/YGPixelGrid.cpp new file mode 100644 index 00000000..a855a292 --- /dev/null +++ b/third-party/yoga/src/yoga/YGPixelGrid.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include + +using namespace facebook; +using namespace facebook::yoga; + +float YGRoundValueToPixelGrid( + const double value, + const double pointScaleFactor, + const bool forceCeil, + const bool forceFloor) { + return yoga::roundValueToPixelGrid( + value, pointScaleFactor, forceCeil, forceFloor); +} diff --git a/third-party/yoga/src/yoga/YGPixelGrid.h b/third-party/yoga/src/yoga/YGPixelGrid.h new file mode 100644 index 00000000..e2a6aeb6 --- /dev/null +++ b/third-party/yoga/src/yoga/YGPixelGrid.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include +#include +#include + +YG_EXTERN_C_BEGIN + +/** + * Rounds a point value to the nearest whole pixel, given a pointScaleFactor + * describing pixel density. + * @returns the rounded value in points + */ +YG_EXPORT float YGRoundValueToPixelGrid( + double value, + double pointScaleFactor, + bool forceCeil, + bool forceFloor); + +YG_EXTERN_C_END diff --git a/third-party/yoga/src/yoga/YGStyle.cpp b/third-party/yoga/src/yoga/YGStyle.cpp deleted file mode 100755 index e8033bdf..00000000 --- a/third-party/yoga/src/yoga/YGStyle.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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/third-party/yoga/src/yoga/YGStyle.h b/third-party/yoga/src/yoga/YGStyle.h deleted file mode 100755 index b066b346..00000000 --- a/third-party/yoga/src/yoga/YGStyle.h +++ /dev/null @@ -1,203 +0,0 @@ -/* - * 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/third-party/yoga/src/yoga/YGValue.cpp b/third-party/yoga/src/yoga/YGValue.cpp old mode 100755 new mode 100644 index 37383a55..7a469b9c --- a/third-party/yoga/src/yoga/YGValue.cpp +++ b/third-party/yoga/src/yoga/YGValue.cpp @@ -1,12 +1,20 @@ /* - * Copyright (c) Facebook, Inc. and its affiliates. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -#include "YGValue.h" +#include +#include + +using namespace facebook; +using namespace facebook::yoga; const YGValue YGValueZero = {0, YGUnitPoint}; const YGValue YGValueUndefined = {YGUndefined, YGUnitUndefined}; const YGValue YGValueAuto = {YGUndefined, YGUnitAuto}; + +bool YGFloatIsUndefined(const float value) { + return yoga::isUndefined(value); +} diff --git a/third-party/yoga/src/yoga/YGValue.h b/third-party/yoga/src/yoga/YGValue.h old mode 100755 new mode 100644 index aaa10c3f..13813522 --- a/third-party/yoga/src/yoga/YGValue.h +++ b/third-party/yoga/src/yoga/YGValue.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Facebook, Inc. and its affiliates. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. @@ -7,33 +7,56 @@ #pragma once -#include -#include "YGEnums.h" -#include "YGMacros.h" +#include -YG_EXTERN_C_BEGIN +#include +#include -// Not defined in MSVC++ -#ifndef NAN -static const uint32_t __nan = 0x7fc00000; -#define NAN (*(const float*) __nan) +/** + * Float value to represent "undefined" in style values. + */ +#ifdef __cplusplus +#include +constexpr float YGUndefined = std::numeric_limits::quiet_NaN(); +#else +#include +#define YGUndefined NAN #endif -#define YGUndefined NAN +YG_EXTERN_C_BEGIN +/** + * Structure used to represent a dimension in a style. + */ 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; +/** + * Constant for a dimension of "auto". + */ +YG_EXPORT extern const YGValue YGValueAuto; + +/** + * Constant for a dimension which is not defined. + */ +YG_EXPORT extern const YGValue YGValueUndefined; + +/** + * Constant for a dimension that is zero-length. + */ +YG_EXPORT extern const YGValue YGValueZero; + +/** + * Whether a dimension represented as a float is defined. + */ +YG_EXPORT bool YGFloatIsUndefined(float value); YG_EXTERN_C_END +// Equality operators for comparison of YGValue in C++ #ifdef __cplusplus - inline bool operator==(const YGValue& lhs, const YGValue& rhs) { if (lhs.unit != rhs.unit) { return false; @@ -42,43 +65,19 @@ inline bool operator==(const YGValue& lhs, const YGValue& rhs) { switch (lhs.unit) { case YGUnitUndefined: case YGUnitAuto: + case YGUnitFitContent: + case YGUnitMaxContent: + case YGUnitStretch: return true; case YGUnitPoint: case YGUnitPercent: return lhs.value == rhs.value; + default: + return false; } - - 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/third-party/yoga/src/yoga/Yoga-internal.h b/third-party/yoga/src/yoga/Yoga-internal.h deleted file mode 100755 index 0b3368a0..00000000 --- a/third-party/yoga/src/yoga/Yoga-internal.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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/third-party/yoga/src/yoga/Yoga.cpp b/third-party/yoga/src/yoga/Yoga.cpp deleted file mode 100755 index e10e777e..00000000 --- a/third-party/yoga/src/yoga/Yoga.cpp +++ /dev/null @@ -1,4406 +0,0 @@ -/* - * 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); - node->getLayout().didUseCustomMeasure() = true; - - // 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/third-party/yoga/src/yoga/Yoga.h b/third-party/yoga/src/yoga/Yoga.h old mode 100755 new mode 100644 index 68ed0a62..97f05ed3 --- a/third-party/yoga/src/yoga/Yoga.h +++ b/third-party/yoga/src/yoga/Yoga.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Facebook, Inc. and its affiliates. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. @@ -7,363 +7,15 @@ #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); +/** + * `#include ` includes all of Yoga's public headers. + */ -#endif +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export diff --git a/third-party/yoga/src/yoga/algorithm/AbsoluteLayout.cpp b/third-party/yoga/src/yoga/algorithm/AbsoluteLayout.cpp new file mode 100644 index 00000000..181dfcb1 --- /dev/null +++ b/third-party/yoga/src/yoga/algorithm/AbsoluteLayout.cpp @@ -0,0 +1,593 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include +#include +#include + +namespace facebook::yoga { + +static inline void setFlexStartLayoutPosition( + const yoga::Node* const parent, + yoga::Node* child, + const Direction direction, + const FlexDirection axis, + const float containingBlockWidth) { + float position = child->style().computeFlexStartMargin( + axis, direction, containingBlockWidth) + + parent->getLayout().border(flexStartEdge(axis)); + + // https://www.w3.org/TR/css-grid-1/#abspos + // absolute positioned grid items are positioned relative to the padding edge + // of the grid container + if (!child->hasErrata(Errata::AbsolutePositionWithoutInsetsExcludesPadding) && + parent->style().display() != Display::Grid) { + position += parent->getLayout().padding(flexStartEdge(axis)); + } + + child->setLayoutPosition(position, flexStartEdge(axis)); +} + +static inline void setFlexEndLayoutPosition( + const yoga::Node* const parent, + yoga::Node* child, + const Direction direction, + const FlexDirection axis, + const float containingBlockWidth) { + float flexEndPosition = parent->getLayout().border(flexEndEdge(axis)) + + child->style().computeFlexEndMargin( + axis, direction, containingBlockWidth); + + // https://www.w3.org/TR/css-grid-1/#abspos + // absolute positioned grid items are positioned relative to the padding edge + // of the grid container + if (!child->hasErrata(Errata::AbsolutePositionWithoutInsetsExcludesPadding) && + parent->style().display() != Display::Grid) { + flexEndPosition += parent->getLayout().padding(flexEndEdge(axis)); + } + + child->setLayoutPosition( + getPositionOfOppositeEdge(flexEndPosition, axis, parent, child), + flexStartEdge(axis)); +} + +static inline void setCenterLayoutPosition( + const yoga::Node* const parent, + yoga::Node* child, + const Direction direction, + const FlexDirection axis, + const float containingBlockWidth) { + float parentContentBoxSize = + parent->getLayout().measuredDimension(dimension(axis)) - + parent->getLayout().border(flexStartEdge(axis)) - + parent->getLayout().border(flexEndEdge(axis)); + + // https://www.w3.org/TR/css-grid-1/#abspos + // absolute positioned grid items are positioned relative to the padding edge + // of the grid container + if (!child->hasErrata(Errata::AbsolutePositionWithoutInsetsExcludesPadding) && + parent->style().display() != Display::Grid) { + parentContentBoxSize -= parent->getLayout().padding(flexStartEdge(axis)); + parentContentBoxSize -= parent->getLayout().padding(flexEndEdge(axis)); + } + + const float childOuterSize = + child->getLayout().measuredDimension(dimension(axis)) + + child->style().computeMarginForAxis(axis, containingBlockWidth); + + float position = (parentContentBoxSize - childOuterSize) / 2.0f + + parent->getLayout().border(flexStartEdge(axis)) + + child->style().computeFlexStartMargin( + axis, direction, containingBlockWidth); + + // https://www.w3.org/TR/css-grid-1/#abspos + // absolute positioned grid items are positioned relative to the padding edge + // of the grid container + if (!child->hasErrata(Errata::AbsolutePositionWithoutInsetsExcludesPadding) && + parent->style().display() != Display::Grid) { + position += parent->getLayout().padding(flexStartEdge(axis)); + } + + child->setLayoutPosition(position, flexStartEdge(axis)); +} + +static void justifyAbsoluteChild( + const yoga::Node* const parent, + yoga::Node* child, + const Direction direction, + const FlexDirection mainAxis, + const float containingBlockWidth) { + const Justify justify = parent->style().display() == Display::Grid + ? resolveChildJustification(parent, child) + : parent->style().justifyContent(); + switch (justify) { + case Justify::Start: + case Justify::Auto: + case Justify::Stretch: + case Justify::FlexStart: + case Justify::SpaceBetween: + setFlexStartLayoutPosition( + parent, child, direction, mainAxis, containingBlockWidth); + break; + case Justify::End: + case Justify::FlexEnd: + setFlexEndLayoutPosition( + parent, child, direction, mainAxis, containingBlockWidth); + break; + case Justify::Center: + case Justify::SpaceAround: + case Justify::SpaceEvenly: + setCenterLayoutPosition( + parent, child, direction, mainAxis, containingBlockWidth); + break; + } +} + +static void alignAbsoluteChild( + const yoga::Node* const parent, + yoga::Node* child, + const Direction direction, + const FlexDirection crossAxis, + const float containingBlockWidth) { + Align itemAlign = resolveChildAlignment(parent, child); + const Wrap parentWrap = parent->style().flexWrap(); + if (parentWrap == Wrap::WrapReverse) { + if (itemAlign == Align::FlexEnd) { + itemAlign = Align::FlexStart; + } else if (itemAlign != Align::Center) { + itemAlign = Align::FlexEnd; + } + } + + switch (itemAlign) { + case Align::Start: + case Align::Auto: + case Align::FlexStart: + case Align::Baseline: + case Align::SpaceAround: + case Align::SpaceBetween: + case Align::Stretch: + case Align::SpaceEvenly: + setFlexStartLayoutPosition( + parent, child, direction, crossAxis, containingBlockWidth); + break; + case Align::End: + case Align::FlexEnd: + setFlexEndLayoutPosition( + parent, child, direction, crossAxis, containingBlockWidth); + break; + case Align::Center: + setCenterLayoutPosition( + parent, child, direction, crossAxis, containingBlockWidth); + break; + } +} + +/* + * Absolutely positioned nodes do not participate in flex layout and thus their + * positions can be determined independently from the rest of their siblings. + * For each axis there are essentially two cases: + * + * 1) The node has insets defined. In this case we can just use these to + * determine the position of the node. + * 2) The node does not have insets defined. In this case we look at the style + * of the parent to position the node. Things like justify content and + * align content will move absolute children around. If none of these + * special properties are defined, the child is positioned at the start + * (defined by flex direction) of the leading flex line. + * + * This function does that positioning for the given axis. The spec has more + * information on this topic: https://www.w3.org/TR/css-flexbox-1/#abspos-items + */ +static void positionAbsoluteChild( + const yoga::Node* const containingNode, + const yoga::Node* const parent, + yoga::Node* child, + const Direction direction, + const FlexDirection axis, + const bool isMainAxis, + const float containingBlockWidth, + const float containingBlockHeight) { + const bool isAxisRow = isRow(axis); + const float containingBlockSize = + isAxisRow ? containingBlockWidth : containingBlockHeight; + + // The inline-start position takes priority over the end position in the case + // that they are both set and the node has a fixed width. Thus we only have 2 + // cases here: if inline-start is defined and if inline-end is defined. + // + // Despite checking inline-start to honor prioritization of insets, we write + // to the flex-start edge because this algorithm works by positioning on the + // flex-start edge and then filling in the flex-end direction at the end if + // necessary. + if (child->style().isInlineStartPositionDefined(axis, direction) && + !child->style().isInlineStartPositionAuto(axis, direction)) { + const float positionRelativeToInlineStart = + child->style().computeInlineStartPosition( + axis, direction, containingBlockSize) + + containingNode->style().computeInlineStartBorder(axis, direction) + + child->style().computeInlineStartMargin( + axis, direction, containingBlockSize); + const float positionRelativeToFlexStart = + inlineStartEdge(axis, direction) != flexStartEdge(axis) + ? getPositionOfOppositeEdge( + positionRelativeToInlineStart, axis, containingNode, child) + : positionRelativeToInlineStart; + + child->setLayoutPosition(positionRelativeToFlexStart, flexStartEdge(axis)); + } else if ( + child->style().isInlineEndPositionDefined(axis, direction) && + !child->style().isInlineEndPositionAuto(axis, direction)) { + const float positionRelativeToInlineStart = + containingNode->getLayout().measuredDimension(dimension(axis)) - + child->getLayout().measuredDimension(dimension(axis)) - + containingNode->style().computeInlineEndBorder(axis, direction) - + child->style().computeInlineEndMargin( + axis, direction, containingBlockSize) - + child->style().computeInlineEndPosition( + axis, direction, containingBlockSize); + const float positionRelativeToFlexStart = + inlineStartEdge(axis, direction) != flexStartEdge(axis) + ? getPositionOfOppositeEdge( + positionRelativeToInlineStart, axis, containingNode, child) + : positionRelativeToInlineStart; + + child->setLayoutPosition(positionRelativeToFlexStart, flexStartEdge(axis)); + } else { + isMainAxis ? justifyAbsoluteChild( + parent, child, direction, axis, containingBlockWidth) + : alignAbsoluteChild( + parent, child, direction, axis, containingBlockWidth); + } +} + +void layoutAbsoluteChild( + const yoga::Node* const containingNode, + const yoga::Node* const node, + yoga::Node* const child, + const float containingBlockWidth, + const float containingBlockHeight, + const SizingMode widthMode, + const Direction direction, + LayoutData& layoutMarkerData, + const uint32_t depth, + const uint32_t generationCount) { + // For grid containers, use inline (Row) and block (Column) axes for + // positioning, since grid alignment properties (justify-self, align-self) + // operate on inline/block axes, not main/cross axes based on flex-direction. + const FlexDirection mainAxis = node->style().display() == Display::Grid + ? resolveDirection(FlexDirection::Row, direction) + : resolveDirection(node->style().flexDirection(), direction); + const FlexDirection crossAxis = node->style().display() == Display::Grid + ? FlexDirection::Column + : resolveCrossDirection(mainAxis, direction); + const bool isMainAxisRow = isRow(mainAxis); + + float childWidth = YGUndefined; + float childHeight = YGUndefined; + SizingMode childWidthSizingMode = SizingMode::MaxContent; + SizingMode childHeightSizingMode = SizingMode::MaxContent; + + auto marginRow = child->style().computeMarginForAxis( + FlexDirection::Row, containingBlockWidth); + auto marginColumn = child->style().computeMarginForAxis( + FlexDirection::Column, containingBlockWidth); + + if (child->hasDefiniteLength(Dimension::Width, containingBlockWidth)) { + childWidth = child + ->getResolvedDimension( + direction, + Dimension::Width, + containingBlockWidth, + containingBlockWidth) + .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->style().isFlexStartPositionDefined( + FlexDirection::Row, direction) && + child->style().isFlexEndPositionDefined( + FlexDirection::Row, direction) && + !child->style().isFlexStartPositionAuto( + FlexDirection::Row, direction) && + !child->style().isFlexEndPositionAuto(FlexDirection::Row, direction)) { + childWidth = + containingNode->getLayout().measuredDimension(Dimension::Width) - + (containingNode->style().computeFlexStartBorder( + FlexDirection::Row, direction) + + containingNode->style().computeFlexEndBorder( + FlexDirection::Row, direction)) - + (child->style().computeFlexStartPosition( + FlexDirection::Row, direction, containingBlockWidth) + + child->style().computeFlexEndPosition( + FlexDirection::Row, direction, containingBlockWidth)); + childWidth = boundAxis( + child, + FlexDirection::Row, + direction, + childWidth, + containingBlockWidth, + containingBlockWidth); + } + } + + if (child->hasDefiniteLength(Dimension::Height, containingBlockHeight)) { + childHeight = child + ->getResolvedDimension( + direction, + Dimension::Height, + containingBlockHeight, + containingBlockWidth) + .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->style().isFlexStartPositionDefined( + FlexDirection::Column, direction) && + child->style().isFlexEndPositionDefined( + FlexDirection::Column, direction) && + !child->style().isFlexStartPositionAuto( + FlexDirection::Column, direction) && + !child->style().isFlexEndPositionAuto( + FlexDirection::Column, direction)) { + childHeight = + containingNode->getLayout().measuredDimension(Dimension::Height) - + (containingNode->style().computeFlexStartBorder( + FlexDirection::Column, direction) + + containingNode->style().computeFlexEndBorder( + FlexDirection::Column, direction)) - + (child->style().computeFlexStartPosition( + FlexDirection::Column, direction, containingBlockHeight) + + child->style().computeFlexEndPosition( + FlexDirection::Column, direction, containingBlockHeight)); + childHeight = boundAxis( + child, + FlexDirection::Column, + direction, + childHeight, + containingBlockHeight, + containingBlockWidth); + } + } + + // 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->style(); + if (yoga::isUndefined(childWidth) ^ yoga::isUndefined(childHeight)) { + if (childStyle.aspectRatio().isDefined()) { + if (yoga::isUndefined(childWidth)) { + childWidth = marginRow + + (childHeight - marginColumn) * childStyle.aspectRatio().unwrap(); + } else if (yoga::isUndefined(childHeight)) { + childHeight = marginColumn + + (childWidth - marginRow) / childStyle.aspectRatio().unwrap(); + } + } + } + + // If we're still missing one or the other dimension, measure the content. + if (yoga::isUndefined(childWidth) || yoga::isUndefined(childHeight)) { + childWidthSizingMode = yoga::isUndefined(childWidth) + ? SizingMode::MaxContent + : SizingMode::StretchFit; + childHeightSizingMode = yoga::isUndefined(childHeight) + ? SizingMode::MaxContent + : SizingMode::StretchFit; + + // 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 && yoga::isUndefined(childWidth) && + widthMode != SizingMode::MaxContent && + yoga::isDefined(containingBlockWidth) && containingBlockWidth > 0) { + childWidth = containingBlockWidth; + childWidthSizingMode = SizingMode::FitContent; + } + + calculateLayoutInternal( + child, + childWidth, + childHeight, + direction, + childWidthSizingMode, + childHeightSizingMode, + containingBlockWidth, + containingBlockHeight, + false, + LayoutPassReason::kAbsMeasureChild, + layoutMarkerData, + depth, + generationCount); + childWidth = child->getLayout().measuredDimension(Dimension::Width) + + child->style().computeMarginForAxis( + FlexDirection::Row, containingBlockWidth); + childHeight = child->getLayout().measuredDimension(Dimension::Height) + + child->style().computeMarginForAxis( + FlexDirection::Column, containingBlockWidth); + } + + calculateLayoutInternal( + child, + childWidth, + childHeight, + direction, + SizingMode::StretchFit, + SizingMode::StretchFit, + containingBlockWidth, + containingBlockHeight, + true, + LayoutPassReason::kAbsLayout, + layoutMarkerData, + depth, + generationCount); + + positionAbsoluteChild( + containingNode, + node, + child, + direction, + mainAxis, + true /*isMainAxis*/, + containingBlockWidth, + containingBlockHeight); + positionAbsoluteChild( + containingNode, + node, + child, + direction, + crossAxis, + false /*isMainAxis*/, + containingBlockWidth, + containingBlockHeight); +} + +bool layoutAbsoluteDescendants( + yoga::Node* containingNode, + yoga::Node* currentNode, + SizingMode widthSizingMode, + Direction currentNodeDirection, + LayoutData& layoutMarkerData, + uint32_t currentDepth, + uint32_t generationCount, + float currentNodeLeftOffsetFromContainingBlock, + float currentNodeTopOffsetFromContainingBlock, + float containingNodeAvailableInnerWidth, + float containingNodeAvailableInnerHeight) { + bool hasNewLayout = false; + for (auto child : currentNode->getLayoutChildren()) { + if (child->style().display() == Display::None) { + continue; + } else if (child->style().positionType() == PositionType::Absolute) { + const bool absoluteErrata = + currentNode->hasErrata(Errata::AbsolutePercentAgainstInnerSize); + const float containingBlockWidth = absoluteErrata + ? containingNodeAvailableInnerWidth + : containingNode->getLayout().measuredDimension(Dimension::Width) - + containingNode->style().computeBorderForAxis(FlexDirection::Row); + const float containingBlockHeight = absoluteErrata + ? containingNodeAvailableInnerHeight + : containingNode->getLayout().measuredDimension(Dimension::Height) - + containingNode->style().computeBorderForAxis( + FlexDirection::Column); + + layoutAbsoluteChild( + containingNode, + currentNode, + child, + containingBlockWidth, + containingBlockHeight, + widthSizingMode, + currentNodeDirection, + layoutMarkerData, + currentDepth, + generationCount); + + hasNewLayout = hasNewLayout || child->getHasNewLayout(); + + /* + * At this point the child has its position set but only on its the + * parent's flexStart edge. Additionally, this position should be + * interpreted relative to the containing block of the child if it had + * insets defined. So we need to adjust the position by subtracting the + * the parents offset from the containing block. However, getting that + * offset is complicated since the two nodes can have different main/cross + * axes. + */ + const FlexDirection parentMainAxis = resolveDirection( + currentNode->style().flexDirection(), currentNodeDirection); + const FlexDirection parentCrossAxis = + resolveCrossDirection(parentMainAxis, currentNodeDirection); + + if (needsTrailingPosition(parentMainAxis)) { + const bool mainInsetsDefined = isRow(parentMainAxis) + ? child->style().horizontalInsetsDefined() + : child->style().verticalInsetsDefined(); + setChildTrailingPosition( + mainInsetsDefined ? containingNode : currentNode, + child, + parentMainAxis); + } + if (needsTrailingPosition(parentCrossAxis)) { + const bool crossInsetsDefined = isRow(parentCrossAxis) + ? child->style().horizontalInsetsDefined() + : child->style().verticalInsetsDefined(); + setChildTrailingPosition( + crossInsetsDefined ? containingNode : currentNode, + child, + parentCrossAxis); + } + + /* + * At this point we know the left and top physical edges of the child are + * set with positions that are relative to the containing block if insets + * are defined + */ + const float childLeftPosition = + child->getLayout().position(PhysicalEdge::Left); + const float childTopPosition = + child->getLayout().position(PhysicalEdge::Top); + + const float childLeftOffsetFromParent = + child->style().horizontalInsetsDefined() + ? (childLeftPosition - currentNodeLeftOffsetFromContainingBlock) + : childLeftPosition; + const float childTopOffsetFromParent = + child->style().verticalInsetsDefined() + ? (childTopPosition - currentNodeTopOffsetFromContainingBlock) + : childTopPosition; + + child->setLayoutPosition(childLeftOffsetFromParent, PhysicalEdge::Left); + child->setLayoutPosition(childTopOffsetFromParent, PhysicalEdge::Top); + } else if ( + child->style().positionType() == PositionType::Static && + !child->alwaysFormsContainingBlock()) { + // We may write new layout results for absolute descendants of "child" + // which are positioned relative to the current containing block instead + // of their parent. "child" may not be dirty, or have new constraints, so + // absolute positioning may be the first time during this layout pass that + // we need to mutate these descendents. Make sure the path of + // nodes to them is mutable before positioning. + child->cloneChildrenIfNeeded(); + const Direction childDirection = + child->resolveDirection(currentNodeDirection); + // By now all descendants of the containing block that are not absolute + // will have their positions set for left and top. + const float childLeftOffsetFromContainingBlock = + currentNodeLeftOffsetFromContainingBlock + + child->getLayout().position(PhysicalEdge::Left); + const float childTopOffsetFromContainingBlock = + currentNodeTopOffsetFromContainingBlock + + child->getLayout().position(PhysicalEdge::Top); + + hasNewLayout = layoutAbsoluteDescendants( + containingNode, + child, + widthSizingMode, + childDirection, + layoutMarkerData, + currentDepth + 1, + generationCount, + childLeftOffsetFromContainingBlock, + childTopOffsetFromContainingBlock, + containingNodeAvailableInnerWidth, + containingNodeAvailableInnerHeight) || + hasNewLayout; + + if (hasNewLayout) { + child->setHasNewLayout(hasNewLayout); + } + } + } + return hasNewLayout; +} +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/algorithm/AbsoluteLayout.h b/third-party/yoga/src/yoga/algorithm/AbsoluteLayout.h new file mode 100644 index 00000000..584a586a --- /dev/null +++ b/third-party/yoga/src/yoga/algorithm/AbsoluteLayout.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook::yoga { + +void layoutAbsoluteChild( + const yoga::Node* containingNode, + const yoga::Node* node, + yoga::Node* child, + float containingBlockWidth, + float containingBlockHeight, + SizingMode widthMode, + Direction direction, + LayoutData& layoutMarkerData, + uint32_t depth, + uint32_t generationCount); + +// Returns if some absolute descendant has new layout +bool layoutAbsoluteDescendants( + yoga::Node* containingNode, + yoga::Node* currentNode, + SizingMode widthSizingMode, + Direction currentNodeDirection, + LayoutData& layoutMarkerData, + uint32_t currentDepth, + uint32_t generationCount, + float currentNodeMainOffsetFromContainingBlock, + float currentNodeCrossOffsetFromContainingBlock, + float containingNodeAvailableInnerWidth, + float containingNodeAvailableInnerHeight); + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/algorithm/Align.h b/third-party/yoga/src/yoga/algorithm/Align.h new file mode 100644 index 00000000..b12ae7fd --- /dev/null +++ b/third-party/yoga/src/yoga/algorithm/Align.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include +#include + +namespace facebook::yoga { + +inline Align resolveChildAlignment( + const yoga::Node* node, + const yoga::Node* child) { + const Align align = child->style().alignSelf() == Align::Auto + ? node->style().alignItems() + : child->style().alignSelf(); + + if (node->style().display() == Display::Flex && align == Align::Baseline && + isColumn(node->style().flexDirection())) { + return Align::FlexStart; + } + + return align; +} + +inline Justify resolveChildJustification( + const yoga::Node* node, + const yoga::Node* child) { + return child->style().justifySelf() == Justify::Auto + ? node->style().justifyItems() + : child->style().justifySelf(); +} + +/** + * Fallback alignment to use on overflow + * https://www.w3.org/TR/css-align-3/#distribution-values + */ +constexpr Align fallbackAlignment(Align align) { + switch (align) { + // Fallback to flex-start + case Align::SpaceBetween: + case Align::Stretch: + return Align::FlexStart; + + // Fallback to safe center. TODO (T208209388): This should be aligned to + // Start instead of FlexStart (for row-reverse containers) + case Align::SpaceAround: + case Align::SpaceEvenly: + return Align::FlexStart; + default: + return align; + } +} + +/** + * Fallback alignment to use on overflow + * https://www.w3.org/TR/css-align-3/#distribution-values + */ +constexpr Justify fallbackAlignment(Justify align) { + switch (align) { + // Fallback to flex-start + case Justify::SpaceBetween: + // TODO: Support `justify-content: stretch` + // case Justify::Stretch: + return Justify::FlexStart; + + // Fallback to safe center. TODO (T208209388): This should be aligned to + // Start instead of FlexStart (for row-reverse containers) + case Justify::SpaceAround: + case Justify::SpaceEvenly: + return Justify::FlexStart; + default: + return align; + } +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/algorithm/Baseline.cpp b/third-party/yoga/src/yoga/algorithm/Baseline.cpp new file mode 100644 index 00000000..b3002012 --- /dev/null +++ b/third-party/yoga/src/yoga/algorithm/Baseline.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include +#include +#include +#include + +namespace facebook::yoga { + +float calculateBaseline(const yoga::Node* node) { + if (node->hasBaselineFunc()) { + Event::publish(node); + + const float baseline = node->baseline( + node->getLayout().measuredDimension(Dimension::Width), + node->getLayout().measuredDimension(Dimension::Height)); + + Event::publish(node); + + yoga::assertFatalWithNode( + node, + !std::isnan(baseline), + "Expect custom baseline function to not return NaN"); + return baseline; + } + + yoga::Node* baselineChild = nullptr; + for (auto child : node->getLayoutChildren()) { + if (child->getLineIndex() > 0) { + break; + } + if (child->style().positionType() == PositionType::Absolute) { + continue; + } + if (resolveChildAlignment(node, child) == Align::Baseline || + child->isReferenceBaseline()) { + baselineChild = child; + break; + } + + if (baselineChild == nullptr) { + baselineChild = child; + } + } + + if (baselineChild == nullptr) { + return node->getLayout().measuredDimension(Dimension::Height); + } + + const float baseline = calculateBaseline(baselineChild); + return baseline + baselineChild->getLayout().position(PhysicalEdge::Top); +} + +bool isBaselineLayout(const yoga::Node* node) { + if (isColumn(node->style().flexDirection())) { + return false; + } + if (node->style().alignItems() == Align::Baseline) { + return true; + } + for (auto child : node->getLayoutChildren()) { + if (child->style().positionType() != PositionType::Absolute && + child->style().alignSelf() == Align::Baseline) { + return true; + } + } + + return false; +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/algorithm/Baseline.h b/third-party/yoga/src/yoga/algorithm/Baseline.h new file mode 100644 index 00000000..c95ffd0f --- /dev/null +++ b/third-party/yoga/src/yoga/algorithm/Baseline.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook::yoga { + +// Calculate baseline represented as an offset from the top edge of the node. +float calculateBaseline(const yoga::Node* node); + +// Whether any of the children of this node participate in baseline alignment +bool isBaselineLayout(const yoga::Node* node); + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/algorithm/BoundAxis.h b/third-party/yoga/src/yoga/algorithm/BoundAxis.h new file mode 100644 index 00000000..d9ebc68c --- /dev/null +++ b/third-party/yoga/src/yoga/algorithm/BoundAxis.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace facebook::yoga { + +inline float paddingAndBorderForAxis( + const yoga::Node* const node, + const FlexDirection axis, + const Direction direction, + const float widthSize) { + return node->style().computeInlineStartPaddingAndBorder( + axis, direction, widthSize) + + node->style().computeInlineEndPaddingAndBorder( + axis, direction, widthSize); +} + +inline FloatOptional boundAxisWithinMinAndMax( + const yoga::Node* const node, + const Direction direction, + const FlexDirection axis, + const FloatOptional value, + const float axisSize, + const float widthSize) { + FloatOptional min; + FloatOptional max; + + if (isColumn(axis)) { + min = node->style().resolvedMinDimension( + direction, Dimension::Height, axisSize, widthSize); + max = node->style().resolvedMaxDimension( + direction, Dimension::Height, axisSize, widthSize); + } else if (isRow(axis)) { + min = node->style().resolvedMinDimension( + direction, Dimension::Width, axisSize, widthSize); + max = node->style().resolvedMaxDimension( + direction, Dimension::Width, axisSize, widthSize); + } + + if (max >= FloatOptional{0} && value > max) { + return max; + } + + if (min >= FloatOptional{0} && value < min) { + return min; + } + + return value; +} + +// Like boundAxisWithinMinAndMax but also ensures that the value doesn't +// go below the padding and border amount. +inline float boundAxis( + const yoga::Node* const node, + const FlexDirection axis, + const Direction direction, + const float value, + const float axisSize, + const float widthSize) { + return yoga::maxOrDefined( + boundAxisWithinMinAndMax( + node, direction, axis, FloatOptional{value}, axisSize, widthSize) + .unwrap(), + paddingAndBorderForAxis(node, axis, direction, widthSize)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/algorithm/Cache.cpp b/third-party/yoga/src/yoga/algorithm/Cache.cpp new file mode 100644 index 00000000..9154fc6c --- /dev/null +++ b/third-party/yoga/src/yoga/algorithm/Cache.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +namespace facebook::yoga { + +static inline bool sizeIsExactAndMatchesOldMeasuredSize( + SizingMode sizeMode, + float size, + float lastComputedSize) { + return sizeMode == SizingMode::StretchFit && + yoga::inexactEquals(size, lastComputedSize); +} + +static inline bool oldSizeIsMaxContentAndStillFits( + SizingMode sizeMode, + float size, + SizingMode lastSizeMode, + float lastComputedSize) { + return sizeMode == SizingMode::FitContent && + lastSizeMode == SizingMode::MaxContent && + (size >= lastComputedSize || yoga::inexactEquals(size, lastComputedSize)); +} + +static inline bool newSizeIsStricterAndStillValid( + SizingMode sizeMode, + float size, + SizingMode lastSizeMode, + float lastSize, + float lastComputedSize) { + return lastSizeMode == SizingMode::FitContent && + sizeMode == SizingMode::FitContent && yoga::isDefined(lastSize) && + yoga::isDefined(size) && yoga::isDefined(lastComputedSize) && + lastSize > size && + (lastComputedSize <= size || yoga::inexactEquals(size, lastComputedSize)); +} + +bool canUseCachedMeasurement( + const SizingMode widthMode, + const float availableWidth, + const SizingMode heightMode, + const float availableHeight, + const SizingMode lastWidthMode, + const float lastAvailableWidth, + const SizingMode lastHeightMode, + const float lastAvailableHeight, + const float lastComputedWidth, + const float lastComputedHeight, + const float marginRow, + const float marginColumn, + const yoga::Config* const config) { + if ((yoga::isDefined(lastComputedHeight) && lastComputedHeight < 0) || + ((yoga::isDefined(lastComputedWidth)) && lastComputedWidth < 0)) { + return false; + } + + const float pointScaleFactor = config->getPointScaleFactor(); + + bool useRoundedComparison = config != nullptr && pointScaleFactor != 0; + const float effectiveWidth = useRoundedComparison + ? roundValueToPixelGrid(availableWidth, pointScaleFactor, false, false) + : availableWidth; + const float effectiveHeight = useRoundedComparison + ? roundValueToPixelGrid(availableHeight, pointScaleFactor, false, false) + : availableHeight; + const float effectiveLastWidth = useRoundedComparison + ? roundValueToPixelGrid( + lastAvailableWidth, pointScaleFactor, false, false) + : lastAvailableWidth; + const float effectiveLastHeight = useRoundedComparison + ? roundValueToPixelGrid( + lastAvailableHeight, pointScaleFactor, false, false) + : lastAvailableHeight; + + const bool hasSameWidthSpec = lastWidthMode == widthMode && + yoga::inexactEquals(effectiveLastWidth, effectiveWidth); + const bool hasSameHeightSpec = lastHeightMode == heightMode && + yoga::inexactEquals(effectiveLastHeight, effectiveHeight); + + const bool widthIsCompatible = + hasSameWidthSpec || + sizeIsExactAndMatchesOldMeasuredSize( + widthMode, availableWidth - marginRow, lastComputedWidth) || + oldSizeIsMaxContentAndStillFits( + widthMode, + availableWidth - marginRow, + lastWidthMode, + lastComputedWidth) || + newSizeIsStricterAndStillValid( + widthMode, + availableWidth - marginRow, + lastWidthMode, + lastAvailableWidth, + lastComputedWidth); + + const bool heightIsCompatible = hasSameHeightSpec || + sizeIsExactAndMatchesOldMeasuredSize( + heightMode, + availableHeight - marginColumn, + lastComputedHeight) || + oldSizeIsMaxContentAndStillFits(heightMode, + availableHeight - marginColumn, + lastHeightMode, + lastComputedHeight) || + newSizeIsStricterAndStillValid(heightMode, + availableHeight - marginColumn, + lastHeightMode, + lastAvailableHeight, + lastComputedHeight); + + return widthIsCompatible && heightIsCompatible; +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/algorithm/Cache.h b/third-party/yoga/src/yoga/algorithm/Cache.h new file mode 100644 index 00000000..f8857eaf --- /dev/null +++ b/third-party/yoga/src/yoga/algorithm/Cache.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook::yoga { + +bool canUseCachedMeasurement( + SizingMode widthMode, + float availableWidth, + SizingMode heightMode, + float availableHeight, + SizingMode lastWidthMode, + float lastAvailableWidth, + SizingMode lastHeightMode, + float lastAvailableHeight, + float lastComputedWidth, + float lastComputedHeight, + float marginRow, + float marginColumn, + const yoga::Config* config); + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/algorithm/CalculateLayout.cpp b/third-party/yoga/src/yoga/algorithm/CalculateLayout.cpp new file mode 100644 index 00000000..6c90ddce --- /dev/null +++ b/third-party/yoga/src/yoga/algorithm/CalculateLayout.cpp @@ -0,0 +1,2528 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace facebook::yoga { + +std::atomic gCurrentGenerationCount(0); + +void constrainMaxSizeForMode( + const yoga::Node* node, + Direction direction, + FlexDirection axis, + float ownerAxisSize, + float ownerWidth, + /*in_out*/ SizingMode* mode, + /*in_out*/ float* size) { + const FloatOptional maxSize = + node->style().resolvedMaxDimension( + direction, dimension(axis), ownerAxisSize, ownerWidth) + + FloatOptional(node->style().computeMarginForAxis(axis, ownerWidth)); + switch (*mode) { + case SizingMode::StretchFit: + case SizingMode::FitContent: + *size = (maxSize.isUndefined() || *size < maxSize.unwrap()) + ? *size + : maxSize.unwrap(); + break; + case SizingMode::MaxContent: + if (maxSize.isDefined()) { + *mode = SizingMode::FitContent; + *size = maxSize.unwrap(); + } + break; + } +} + +static void computeFlexBasisForChild( + const yoga::Node* const node, + yoga::Node* const child, + const float width, + const SizingMode widthMode, + const float height, + const float ownerWidth, + const float ownerHeight, + const SizingMode heightMode, + const Direction direction, + LayoutData& layoutMarkerData, + const uint32_t depth, + const uint32_t generationCount) { + const FlexDirection mainAxis = + resolveDirection(node->style().flexDirection(), direction); + const bool isMainAxisRow = isRow(mainAxis); + const float mainAxisSize = isMainAxisRow ? width : height; + const float mainAxisOwnerSize = isMainAxisRow ? ownerWidth : ownerHeight; + + float childWidth = YGUndefined; + float childHeight = YGUndefined; + SizingMode childWidthSizingMode; + SizingMode childHeightSizingMode; + + const FloatOptional resolvedFlexBasis = child->resolveFlexBasis( + direction, mainAxis, mainAxisOwnerSize, ownerWidth); + const bool isRowStyleDimDefined = + child->hasDefiniteLength(Dimension::Width, ownerWidth); + const bool isColumnStyleDimDefined = + child->hasDefiniteLength(Dimension::Height, ownerHeight); + + const bool fixFlexBasisFitContent = + node->getConfig()->isExperimentalFeatureEnabled( + ExperimentalFeature::FixFlexBasisFitContent); + + bool useResolvedFlexBasis = + resolvedFlexBasis.isDefined() && yoga::isDefined(mainAxisSize); + if (fixFlexBasisFitContent && resolvedFlexBasis.isDefined() && + resolvedFlexBasis.unwrap() > 0) { + useResolvedFlexBasis = true; + } + + if (useResolvedFlexBasis) { + if (child->getLayout().computedFlexBasis.isUndefined() || + (child->getConfig()->isExperimentalFeatureEnabled( + ExperimentalFeature::WebFlexBasis) && + child->getLayout().computedFlexBasisGeneration != generationCount)) { + const FloatOptional paddingAndBorder = FloatOptional( + paddingAndBorderForAxis(child, mainAxis, direction, ownerWidth)); + child->setLayoutComputedFlexBasis( + yoga::maxOrDefined(resolvedFlexBasis, paddingAndBorder)); + } + } else if (isMainAxisRow && isRowStyleDimDefined) { + // The width is definite, so use that as the flex basis. + const FloatOptional paddingAndBorder = + FloatOptional(paddingAndBorderForAxis( + child, FlexDirection::Row, direction, ownerWidth)); + + child->setLayoutComputedFlexBasis( + yoga::maxOrDefined( + child->getResolvedDimension( + direction, Dimension::Width, ownerWidth, ownerWidth), + paddingAndBorder)); + } else if (!isMainAxisRow && isColumnStyleDimDefined) { + // The height is definite, so use that as the flex basis. + const FloatOptional paddingAndBorder = + FloatOptional(paddingAndBorderForAxis( + child, FlexDirection::Column, direction, ownerWidth)); + child->setLayoutComputedFlexBasis( + yoga::maxOrDefined( + child->getResolvedDimension( + direction, Dimension::Height, ownerHeight, ownerWidth), + paddingAndBorder)); + } else { + // Compute the flex basis and hypothetical main size (i.e. the clamped flex + // basis). + childWidthSizingMode = SizingMode::MaxContent; + childHeightSizingMode = SizingMode::MaxContent; + + auto marginRow = + child->style().computeMarginForAxis(FlexDirection::Row, ownerWidth); + auto marginColumn = + child->style().computeMarginForAxis(FlexDirection::Column, ownerWidth); + + if (isRowStyleDimDefined) { + childWidth = child + ->getResolvedDimension( + direction, Dimension::Width, ownerWidth, ownerWidth) + .unwrap() + + marginRow; + childWidthSizingMode = SizingMode::StretchFit; + } + if (isColumnStyleDimDefined) { + childHeight = + child + ->getResolvedDimension( + direction, Dimension::Height, ownerHeight, ownerWidth) + .unwrap() + + marginColumn; + childHeightSizingMode = SizingMode::StretchFit; + } + + // The W3C spec doesn't say anything about the 'overflow' property, but all + // major browsers appear to implement the following logic. + if ((!isMainAxisRow && node->style().overflow() == Overflow::Scroll) || + node->style().overflow() != Overflow::Scroll) { + if (yoga::isUndefined(childWidth) && yoga::isDefined(width)) { + childWidth = width; + childWidthSizingMode = SizingMode::FitContent; + } + } + + // For height in the main axis (column direction): when the + // FixFlexBasisFitContent feature is enabled, skip FitContent for + // non-measure container children. This makes the flex basis independent + // of the parent's content-determined height, preventing unnecessary + // re-measurement cascades when a sibling changes size in a ScrollView. + // + // We only optimize the height (column) axis because text wrapping depends + // on width constraints propagating through container nodes. Removing + // FitContent from the width axis would cause text inside nested + // containers to stop wrapping. + bool applyHeightFitContent = + isMainAxisRow || node->style().overflow() != Overflow::Scroll; + if (fixFlexBasisFitContent) { + applyHeightFitContent = isMainAxisRow || + (child->hasMeasureFunc() && + node->style().overflow() != Overflow::Scroll); + } + if (applyHeightFitContent && yoga::isUndefined(childHeight) && + yoga::isDefined(height)) { + childHeight = height; + childHeightSizingMode = SizingMode::FitContent; + } + + const auto& childStyle = child->style(); + if (childStyle.aspectRatio().isDefined()) { + if (!isMainAxisRow && childWidthSizingMode == SizingMode::StretchFit) { + childHeight = marginColumn + + (childWidth - marginRow) / childStyle.aspectRatio().unwrap(); + childHeightSizingMode = SizingMode::StretchFit; + } else if ( + isMainAxisRow && childHeightSizingMode == SizingMode::StretchFit) { + childWidth = marginRow + + (childHeight - marginColumn) * childStyle.aspectRatio().unwrap(); + childWidthSizingMode = SizingMode::StretchFit; + } + } + + // 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 = + yoga::isDefined(width) && widthMode == SizingMode::StretchFit; + const bool childWidthStretch = + resolveChildAlignment(node, child) == Align::Stretch && + childWidthSizingMode != SizingMode::StretchFit; + if (!isMainAxisRow && !isRowStyleDimDefined && hasExactWidth && + childWidthStretch) { + childWidth = width; + childWidthSizingMode = SizingMode::StretchFit; + if (childStyle.aspectRatio().isDefined()) { + childHeight = + (childWidth - marginRow) / childStyle.aspectRatio().unwrap(); + childHeightSizingMode = SizingMode::StretchFit; + } + } + + const bool hasExactHeight = + yoga::isDefined(height) && heightMode == SizingMode::StretchFit; + const bool childHeightStretch = + resolveChildAlignment(node, child) == Align::Stretch && + childHeightSizingMode != SizingMode::StretchFit; + if (isMainAxisRow && !isColumnStyleDimDefined && hasExactHeight && + childHeightStretch) { + childHeight = height; + childHeightSizingMode = SizingMode::StretchFit; + + if (childStyle.aspectRatio().isDefined()) { + childWidth = + (childHeight - marginColumn) * childStyle.aspectRatio().unwrap(); + childWidthSizingMode = SizingMode::StretchFit; + } + } + + constrainMaxSizeForMode( + child, + direction, + FlexDirection::Row, + ownerWidth, + ownerWidth, + &childWidthSizingMode, + &childWidth); + constrainMaxSizeForMode( + child, + direction, + FlexDirection::Column, + ownerHeight, + ownerWidth, + &childHeightSizingMode, + &childHeight); + + // Measure the child + calculateLayoutInternal( + child, + childWidth, + childHeight, + direction, + childWidthSizingMode, + childHeightSizingMode, + ownerWidth, + ownerHeight, + false, + LayoutPassReason::kMeasureChild, + layoutMarkerData, + depth, + generationCount); + + child->setLayoutComputedFlexBasis(FloatOptional( + yoga::maxOrDefined( + child->getLayout().measuredDimension(dimension(mainAxis)), + paddingAndBorderForAxis(child, mainAxis, direction, ownerWidth)))); + } + child->setLayoutComputedFlexBasisGeneration(generationCount); +} + +static void measureNodeWithMeasureFunc( + yoga::Node* const node, + const Direction direction, + float availableWidth, + float availableHeight, + const SizingMode widthSizingMode, + const SizingMode heightSizingMode, + const float ownerWidth, + const float ownerHeight, + LayoutData& layoutMarkerData, + const LayoutPassReason reason) { + yoga::assertFatalWithNode( + node, + node->hasMeasureFunc(), + "Expected node to have custom measure function"); + + if (widthSizingMode == SizingMode::MaxContent) { + availableWidth = YGUndefined; + } + if (heightSizingMode == SizingMode::MaxContent) { + availableHeight = YGUndefined; + } + + const auto& layout = node->getLayout(); + const float paddingAndBorderAxisRow = layout.padding(PhysicalEdge::Left) + + layout.padding(PhysicalEdge::Right) + layout.border(PhysicalEdge::Left) + + layout.border(PhysicalEdge::Right); + const float paddingAndBorderAxisColumn = layout.padding(PhysicalEdge::Top) + + layout.padding(PhysicalEdge::Bottom) + layout.border(PhysicalEdge::Top) + + layout.border(PhysicalEdge::Bottom); + + // We want to make sure we don't call measure with negative size + const float innerWidth = yoga::isUndefined(availableWidth) + ? availableWidth + : yoga::maxOrDefined(0.0f, availableWidth - paddingAndBorderAxisRow); + const float innerHeight = yoga::isUndefined(availableHeight) + ? availableHeight + : yoga::maxOrDefined(0.0f, availableHeight - paddingAndBorderAxisColumn); + + if (widthSizingMode == SizingMode::StretchFit && + heightSizingMode == SizingMode::StretchFit) { + // Don't bother sizing the text if both dimensions are already defined. + node->setLayoutMeasuredDimension( + boundAxis( + node, + FlexDirection::Row, + direction, + availableWidth, + ownerWidth, + ownerWidth), + Dimension::Width); + node->setLayoutMeasuredDimension( + boundAxis( + node, + FlexDirection::Column, + direction, + availableHeight, + ownerHeight, + ownerWidth), + Dimension::Height); + } else { + Event::publish(node); + + // Measure the text under the current constraints. + const YGSize measuredSize = node->measure( + innerWidth, + measureMode(widthSizingMode), + innerHeight, + measureMode(heightSizingMode)); + + layoutMarkerData.measureCallbacks += 1; + layoutMarkerData.measureCallbackReasonsCount[static_cast(reason)] += + 1; + + Event::publish( + node, + {.width = innerWidth, + .widthMeasureMode = unscopedEnum(measureMode(widthSizingMode)), + .height = innerHeight, + .heightMeasureMode = unscopedEnum(measureMode(heightSizingMode)), + .measuredWidth = measuredSize.width, + .measuredHeight = measuredSize.height, + .reason = reason}); + + node->setLayoutMeasuredDimension( + boundAxis( + node, + FlexDirection::Row, + direction, + (widthSizingMode == SizingMode::MaxContent || + widthSizingMode == SizingMode::FitContent) + ? measuredSize.width + paddingAndBorderAxisRow + : availableWidth, + ownerWidth, + ownerWidth), + Dimension::Width); + + node->setLayoutMeasuredDimension( + boundAxis( + node, + FlexDirection::Column, + direction, + (heightSizingMode == SizingMode::MaxContent || + heightSizingMode == SizingMode::FitContent) + ? measuredSize.height + paddingAndBorderAxisColumn + : availableHeight, + ownerHeight, + ownerWidth), + Dimension::Height); + } +} + +// 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 measureNodeWithoutChildren( + yoga::Node* const node, + const Direction direction, + const float availableWidth, + const float availableHeight, + const SizingMode widthSizingMode, + const SizingMode heightSizingMode, + const float ownerWidth, + const float ownerHeight) { + const auto& layout = node->getLayout(); + + float width = availableWidth; + if (widthSizingMode == SizingMode::MaxContent || + widthSizingMode == SizingMode::FitContent) { + width = layout.padding(PhysicalEdge::Left) + + layout.padding(PhysicalEdge::Right) + + layout.border(PhysicalEdge::Left) + layout.border(PhysicalEdge::Right); + } + node->setLayoutMeasuredDimension( + boundAxis( + node, FlexDirection::Row, direction, width, ownerWidth, ownerWidth), + Dimension::Width); + + float height = availableHeight; + if (heightSizingMode == SizingMode::MaxContent || + heightSizingMode == SizingMode::FitContent) { + height = layout.padding(PhysicalEdge::Top) + + layout.padding(PhysicalEdge::Bottom) + + layout.border(PhysicalEdge::Top) + layout.border(PhysicalEdge::Bottom); + } + node->setLayoutMeasuredDimension( + boundAxis( + node, + FlexDirection::Column, + direction, + height, + ownerHeight, + ownerWidth), + Dimension::Height); +} + +inline bool isFixedSize(float dim, SizingMode sizingMode) { + return sizingMode == SizingMode::StretchFit || + (yoga::isDefined(dim) && sizingMode == SizingMode::FitContent && + dim <= 0.0); +} + +static bool measureNodeWithFixedSize( + yoga::Node* const node, + const Direction direction, + const float availableWidth, + const float availableHeight, + const SizingMode widthSizingMode, + const SizingMode heightSizingMode, + const float ownerWidth, + const float ownerHeight) { + if (isFixedSize(availableWidth, widthSizingMode) && + isFixedSize(availableHeight, heightSizingMode)) { + node->setLayoutMeasuredDimension( + boundAxis( + node, + FlexDirection::Row, + direction, + yoga::isUndefined(availableWidth) || + (widthSizingMode == SizingMode::FitContent && + availableWidth < 0.0f) + ? 0.0f + : availableWidth, + ownerWidth, + ownerWidth), + Dimension::Width); + + node->setLayoutMeasuredDimension( + boundAxis( + node, + FlexDirection::Column, + direction, + yoga::isUndefined(availableHeight) || + (heightSizingMode == SizingMode::FitContent && + availableHeight < 0.0f) + ? 0.0f + : availableHeight, + ownerHeight, + ownerWidth), + Dimension::Height); + return true; + } + + return false; +} + +void zeroOutLayoutRecursively(yoga::Node* const node) { + node->getLayout() = {}; + node->setLayoutDimension(0, Dimension::Width); + node->setLayoutDimension(0, Dimension::Height); + node->setHasNewLayout(true); + + node->cloneChildrenIfNeeded(); + for (const auto child : node->getChildren()) { + zeroOutLayoutRecursively(child); + } +} + +void cleanupContentsNodesRecursively(yoga::Node* const node) { + if (node->hasContentsChildren()) [[unlikely]] { + node->cloneContentsChildrenIfNeeded(); + for (auto child : node->getChildren()) { + if (child->style().display() == Display::Contents) { + child->getLayout() = {}; + child->setLayoutDimension(0, Dimension::Width); + child->setLayoutDimension(0, Dimension::Height); + child->setHasNewLayout(true); + child->setDirty(false); + child->cloneChildrenIfNeeded(); + + cleanupContentsNodesRecursively(child); + } + } + } +} + +float calculateAvailableInnerDimension( + const yoga::Node* const node, + const Direction direction, + const Dimension dimension, + const float availableDim, + const float paddingAndBorder, + const float ownerDim, + const float ownerWidth) { + float availableInnerDim = availableDim - paddingAndBorder; + // Max dimension overrides predefined dimension value; Min dimension in turn + // overrides both of the above + if (yoga::isDefined(availableInnerDim)) { + // We want to make sure our available height does not violate min and max + // constraints + const FloatOptional minDimensionOptional = + node->style().resolvedMinDimension( + direction, dimension, ownerDim, ownerWidth); + const float minInnerDim = minDimensionOptional.isUndefined() + ? 0.0f + : minDimensionOptional.unwrap() - paddingAndBorder; + + const FloatOptional maxDimensionOptional = + node->style().resolvedMaxDimension( + direction, dimension, ownerDim, ownerWidth); + + const float maxInnerDim = maxDimensionOptional.isUndefined() + ? FLT_MAX + : maxDimensionOptional.unwrap() - paddingAndBorder; + availableInnerDim = yoga::maxOrDefined( + yoga::minOrDefined(availableInnerDim, maxInnerDim), minInnerDim); + } + + return availableInnerDim; +} + +static float computeFlexBasisForChildren( + yoga::Node* const node, + const float availableInnerWidth, + const float availableInnerHeight, + const float ownerWidth, + const float ownerHeight, + SizingMode widthSizingMode, + SizingMode heightSizingMode, + Direction direction, + FlexDirection mainAxis, + bool performLayout, + LayoutData& layoutMarkerData, + const uint32_t depth, + const uint32_t generationCount) { + float totalOuterFlexBasis = 0.0f; + YGNodeRef singleFlexChild = nullptr; + auto children = node->getLayoutChildren(); + SizingMode sizingModeMainDim = + isRow(mainAxis) ? widthSizingMode : heightSizingMode; + // 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 (sizingModeMainDim == SizingMode::StretchFit) { + for (auto child : children) { + if (child->isNodeFlexible()) { + if (singleFlexChild != nullptr || + yoga::inexactEquals(child->resolveFlexGrow(), 0.0f) || + yoga::inexactEquals(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->processDimensions(); + if (child->style().display() == Display::None) { + zeroOutLayoutRecursively(child); + child->setHasNewLayout(true); + child->setDirty(false); + continue; + } + if (performLayout) { + // Set the initial position (relative to the owner). + const Direction childDirection = child->resolveDirection(direction); + child->setPosition( + childDirection, availableInnerWidth, availableInnerHeight); + } + + if (child->style().positionType() == PositionType::Absolute) { + continue; + } + if (child == singleFlexChild) { + child->setLayoutComputedFlexBasisGeneration(generationCount); + child->setLayoutComputedFlexBasis(FloatOptional(0)); + } else { + computeFlexBasisForChild( + node, + child, + availableInnerWidth, + widthSizingMode, + availableInnerHeight, + ownerWidth, + ownerHeight, + heightSizingMode, + direction, + layoutMarkerData, + depth, + generationCount); + } + + totalOuterFlexBasis += + (child->getLayout().computedFlexBasis.unwrap() + + child->style().computeMarginForAxis(mainAxis, availableInnerWidth)); + } + + return totalOuterFlexBasis; +} + +// 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 distributeFreeSpaceFirstPass is called. +static float distributeFreeSpaceSecondPass( + FlexLine& flexLine, + yoga::Node* const node, + const FlexDirection mainAxis, + const FlexDirection crossAxis, + const Direction direction, + const float ownerWidth, + const float mainAxisOwnerSize, + const float availableInnerMainDim, + const float availableInnerCrossDim, + const float availableInnerWidth, + const float availableInnerHeight, + const bool mainAxisOverflows, + const SizingMode sizingModeCrossDim, + const bool performLayout, + LayoutData& layoutMarkerData, + const uint32_t depth, + const uint32_t generationCount) { + float childFlexBasis = 0; + float flexShrinkScaledFactor = 0; + float flexGrowFactor = 0; + float deltaFreeSpace = 0; + const bool isMainAxisRow = isRow(mainAxis); + const bool isNodeFlexWrap = node->style().flexWrap() != Wrap::NoWrap; + + for (auto currentLineChild : flexLine.itemsInFlow) { + childFlexBasis = boundAxisWithinMinAndMax( + currentLineChild, + direction, + mainAxis, + currentLineChild->getLayout().computedFlexBasis, + mainAxisOwnerSize, + ownerWidth) + .unwrap(); + float updatedMainSize = childFlexBasis; + + if (yoga::isDefined(flexLine.layout.remainingFreeSpace) && + flexLine.layout.remainingFreeSpace < 0) { + flexShrinkScaledFactor = + -currentLineChild->resolveFlexShrink() * childFlexBasis; + // Is this child able to shrink? + if (flexShrinkScaledFactor != 0) { + float childSize = YGUndefined; + + if (yoga::isDefined(flexLine.layout.totalFlexShrinkScaledFactors) && + flexLine.layout.totalFlexShrinkScaledFactors == 0) { + childSize = childFlexBasis + flexShrinkScaledFactor; + } else { + childSize = childFlexBasis + + (flexLine.layout.remainingFreeSpace / + flexLine.layout.totalFlexShrinkScaledFactors) * + flexShrinkScaledFactor; + } + + updatedMainSize = boundAxis( + currentLineChild, + mainAxis, + direction, + childSize, + availableInnerMainDim, + availableInnerWidth); + } + } else if ( + yoga::isDefined(flexLine.layout.remainingFreeSpace) && + flexLine.layout.remainingFreeSpace > 0) { + flexGrowFactor = currentLineChild->resolveFlexGrow(); + + // Is this child able to grow? + if (!std::isnan(flexGrowFactor) && flexGrowFactor != 0) { + updatedMainSize = boundAxis( + currentLineChild, + mainAxis, + direction, + childFlexBasis + + flexLine.layout.remainingFreeSpace / + flexLine.layout.totalFlexGrowFactors * flexGrowFactor, + availableInnerMainDim, + availableInnerWidth); + } + } + + deltaFreeSpace += updatedMainSize - childFlexBasis; + + const float marginMain = currentLineChild->style().computeMarginForAxis( + mainAxis, availableInnerWidth); + const float marginCross = currentLineChild->style().computeMarginForAxis( + crossAxis, availableInnerWidth); + + float childCrossSize = YGUndefined; + float childMainSize = updatedMainSize + marginMain; + SizingMode childCrossSizingMode; + SizingMode childMainSizingMode = SizingMode::StretchFit; + + const auto& childStyle = currentLineChild->style(); + if (childStyle.aspectRatio().isDefined()) { + childCrossSize = isMainAxisRow + ? (childMainSize - marginMain) / childStyle.aspectRatio().unwrap() + : (childMainSize - marginMain) * childStyle.aspectRatio().unwrap(); + childCrossSizingMode = SizingMode::StretchFit; + + childCrossSize += marginCross; + } else if ( + !std::isnan(availableInnerCrossDim) && + !currentLineChild->hasDefiniteLength( + dimension(crossAxis), availableInnerCrossDim) && + sizingModeCrossDim == SizingMode::StretchFit && + !(isNodeFlexWrap && mainAxisOverflows) && + resolveChildAlignment(node, currentLineChild) == Align::Stretch && + !currentLineChild->style().flexStartMarginIsAuto( + crossAxis, direction) && + !currentLineChild->style().flexEndMarginIsAuto(crossAxis, direction)) { + childCrossSize = availableInnerCrossDim; + childCrossSizingMode = SizingMode::StretchFit; + } else if (!currentLineChild->hasDefiniteLength( + dimension(crossAxis), availableInnerCrossDim)) { + childCrossSize = availableInnerCrossDim; + childCrossSizingMode = yoga::isUndefined(childCrossSize) + ? SizingMode::MaxContent + : SizingMode::FitContent; + } else { + childCrossSize = currentLineChild + ->getResolvedDimension( + direction, + dimension(crossAxis), + availableInnerCrossDim, + availableInnerWidth) + .unwrap() + + marginCross; + const bool isLoosePercentageMeasurement = + currentLineChild->getProcessedDimension(dimension(crossAxis)) + .isPercent() && + sizingModeCrossDim != SizingMode::StretchFit; + childCrossSizingMode = + yoga::isUndefined(childCrossSize) || isLoosePercentageMeasurement + ? SizingMode::MaxContent + : SizingMode::StretchFit; + } + + constrainMaxSizeForMode( + currentLineChild, + direction, + mainAxis, + availableInnerMainDim, + availableInnerWidth, + &childMainSizingMode, + &childMainSize); + constrainMaxSizeForMode( + currentLineChild, + direction, + crossAxis, + availableInnerCrossDim, + availableInnerWidth, + &childCrossSizingMode, + &childCrossSize); + + const bool requiresStretchLayout = + !currentLineChild->hasDefiniteLength( + dimension(crossAxis), availableInnerCrossDim) && + resolveChildAlignment(node, currentLineChild) == Align::Stretch && + !currentLineChild->style().flexStartMarginIsAuto( + crossAxis, direction) && + !currentLineChild->style().flexEndMarginIsAuto(crossAxis, direction); + + const float childWidth = isMainAxisRow ? childMainSize : childCrossSize; + const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize; + + const SizingMode childWidthSizingMode = + isMainAxisRow ? childMainSizingMode : childCrossSizingMode; + const SizingMode childHeightSizingMode = + !isMainAxisRow ? childMainSizingMode : childCrossSizingMode; + + const bool isLayoutPass = performLayout && !requiresStretchLayout; + // Recursively call the layout algorithm for this child with the updated + // main size. + calculateLayoutInternal( + currentLineChild, + childWidth, + childHeight, + node->getLayout().direction(), + childWidthSizingMode, + childHeightSizingMode, + availableInnerWidth, + availableInnerHeight, + isLayoutPass, + isLayoutPass ? LayoutPassReason::kFlexLayout + : LayoutPassReason::kFlexMeasure, + layoutMarkerData, + depth, + generationCount); + node->setLayoutHadOverflow( + node->getLayout().hadOverflow() || + currentLineChild->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 distributeFreeSpaceFirstPass( + FlexLine& flexLine, + const Direction direction, + const FlexDirection mainAxis, + const float ownerWidth, + 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 currentLineChild : flexLine.itemsInFlow) { + float childFlexBasis = boundAxisWithinMinAndMax( + currentLineChild, + direction, + mainAxis, + currentLineChild->getLayout().computedFlexBasis, + mainAxisOwnerSize, + ownerWidth) + .unwrap(); + + if (flexLine.layout.remainingFreeSpace < 0) { + flexShrinkScaledFactor = + -currentLineChild->resolveFlexShrink() * childFlexBasis; + + // Is this child able to shrink? + if (yoga::isDefined(flexShrinkScaledFactor) && + flexShrinkScaledFactor != 0) { + baseMainSize = childFlexBasis + + flexLine.layout.remainingFreeSpace / + flexLine.layout.totalFlexShrinkScaledFactors * + flexShrinkScaledFactor; + boundMainSize = boundAxis( + currentLineChild, + mainAxis, + direction, + baseMainSize, + availableInnerMainDim, + availableInnerWidth); + if (yoga::isDefined(baseMainSize) && yoga::isDefined(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; + flexLine.layout.totalFlexShrinkScaledFactors -= + (-currentLineChild->resolveFlexShrink() * + currentLineChild->getLayout().computedFlexBasis.unwrap()); + } + } + } else if ( + yoga::isDefined(flexLine.layout.remainingFreeSpace) && + flexLine.layout.remainingFreeSpace > 0) { + flexGrowFactor = currentLineChild->resolveFlexGrow(); + + // Is this child able to grow? + if (yoga::isDefined(flexGrowFactor) && flexGrowFactor != 0) { + baseMainSize = childFlexBasis + + flexLine.layout.remainingFreeSpace / + flexLine.layout.totalFlexGrowFactors * flexGrowFactor; + boundMainSize = boundAxis( + currentLineChild, + mainAxis, + direction, + baseMainSize, + availableInnerMainDim, + availableInnerWidth); + + if (yoga::isDefined(baseMainSize) && yoga::isDefined(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; + flexLine.layout.totalFlexGrowFactors -= flexGrowFactor; + } + } + } + } + flexLine.layout.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 resolveFlexibleLength( + yoga::Node* const node, + FlexLine& flexLine, + const FlexDirection mainAxis, + const FlexDirection crossAxis, + const Direction direction, + const float ownerWidth, + const float mainAxisOwnerSize, + const float availableInnerMainDim, + const float availableInnerCrossDim, + const float availableInnerWidth, + const float availableInnerHeight, + const bool mainAxisOverflows, + const SizingMode sizingModeCrossDim, + const bool performLayout, + LayoutData& layoutMarkerData, + const uint32_t depth, + const uint32_t generationCount) { + const float originalFreeSpace = flexLine.layout.remainingFreeSpace; + // First pass: detect the flex items whose min/max constraints trigger + distributeFreeSpaceFirstPass( + flexLine, + direction, + mainAxis, + ownerWidth, + mainAxisOwnerSize, + availableInnerMainDim, + availableInnerWidth); + + // Second pass: resolve the sizes of the flexible items + const float distributedFreeSpace = distributeFreeSpaceSecondPass( + flexLine, + node, + mainAxis, + crossAxis, + direction, + ownerWidth, + mainAxisOwnerSize, + availableInnerMainDim, + availableInnerCrossDim, + availableInnerWidth, + availableInnerHeight, + mainAxisOverflows, + sizingModeCrossDim, + performLayout, + layoutMarkerData, + depth, + generationCount); + + flexLine.layout.remainingFreeSpace = originalFreeSpace - distributedFreeSpace; +} + +static void justifyMainAxis( + yoga::Node* const node, + FlexLine& flexLine, + const FlexDirection mainAxis, + const FlexDirection crossAxis, + const Direction direction, + const SizingMode sizingModeMainDim, + const SizingMode sizingModeCrossDim, + const float mainAxisOwnerSize, + const float ownerWidth, + const float availableInnerMainDim, + const float availableInnerCrossDim, + const float availableInnerWidth, + const bool performLayout) { + const auto& style = node->style(); + + const float leadingPaddingAndBorderMain = + node->style().computeFlexStartPaddingAndBorder( + mainAxis, direction, ownerWidth); + const float trailingPaddingAndBorderMain = + node->style().computeFlexEndPaddingAndBorder( + mainAxis, direction, ownerWidth); + + const float gap = + node->style().computeGapForAxis(mainAxis, availableInnerMainDim); + // 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 (sizingModeMainDim == SizingMode::FitContent && + flexLine.layout.remainingFreeSpace > 0) { + if (style.minDimension(dimension(mainAxis)).isDefined() && + style + .resolvedMinDimension( + direction, dimension(mainAxis), mainAxisOwnerSize, ownerWidth) + .isDefined()) { + // 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 = + style + .resolvedMinDimension( + direction, dimension(mainAxis), mainAxisOwnerSize, ownerWidth) + .unwrap() - + leadingPaddingAndBorderMain - trailingPaddingAndBorderMain; + const float occupiedSpaceByChildNodes = + availableInnerMainDim - flexLine.layout.remainingFreeSpace; + flexLine.layout.remainingFreeSpace = yoga::maxOrDefined( + 0.0f, minAvailableMainDim - occupiedSpaceByChildNodes); + } else { + flexLine.layout.remainingFreeSpace = 0; + } + } + + // 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 = gap; + const Justify justifyContent = flexLine.layout.remainingFreeSpace >= 0 + ? node->style().justifyContent() + : fallbackAlignment(node->style().justifyContent()); + + if (flexLine.numberOfAutoMargins == 0) { + switch (justifyContent) { + case Justify::Start: + case Justify::End: + case Justify::Auto: + // No-Op + break; + case Justify::Stretch: + // No-Op + break; + case Justify::Center: + leadingMainDim = flexLine.layout.remainingFreeSpace / 2; + break; + case Justify::FlexEnd: + leadingMainDim = flexLine.layout.remainingFreeSpace; + break; + case Justify::SpaceBetween: + if (flexLine.itemsInFlow.size() > 1) { + betweenMainDim += flexLine.layout.remainingFreeSpace / + static_cast(flexLine.itemsInFlow.size() - 1); + } + break; + case Justify::SpaceEvenly: + // Space is distributed evenly across all elements + leadingMainDim = flexLine.layout.remainingFreeSpace / + static_cast(flexLine.itemsInFlow.size() + 1); + betweenMainDim += leadingMainDim; + break; + case Justify::SpaceAround: + // Space on the edges is half of the space between elements + leadingMainDim = 0.5f * flexLine.layout.remainingFreeSpace / + static_cast(flexLine.itemsInFlow.size()); + betweenMainDim += leadingMainDim * 2; + break; + case Justify::FlexStart: + break; + } + } + + flexLine.layout.mainDim = leadingPaddingAndBorderMain + leadingMainDim; + flexLine.layout.crossDim = 0; + + float maxAscentForCurrentLine = 0; + float maxDescentForCurrentLine = 0; + bool isNodeBaselineLayout = isBaselineLayout(node); + for (auto child : flexLine.itemsInFlow) { + const LayoutResults& childLayout = child->getLayout(); + if (child->style().flexStartMarginIsAuto(mainAxis, direction) && + flexLine.layout.remainingFreeSpace > 0.0f) { + flexLine.layout.mainDim += flexLine.layout.remainingFreeSpace / + static_cast(flexLine.numberOfAutoMargins); + } + + if (performLayout) { + child->setLayoutPosition( + childLayout.position(flexStartEdge(mainAxis)) + + flexLine.layout.mainDim, + flexStartEdge(mainAxis)); + } + + if (child != flexLine.itemsInFlow.back()) { + flexLine.layout.mainDim += betweenMainDim; + } + + if (child->style().flexEndMarginIsAuto(mainAxis, direction) && + flexLine.layout.remainingFreeSpace > 0.0f) { + flexLine.layout.mainDim += flexLine.layout.remainingFreeSpace / + static_cast(flexLine.numberOfAutoMargins); + } + bool canSkipFlex = + !performLayout && sizingModeCrossDim == SizingMode::StretchFit; + 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 + // dimensionWithMargin. + flexLine.layout.mainDim += + child->style().computeMarginForAxis(mainAxis, availableInnerWidth) + + boundAxisWithinMinAndMax( + child, + direction, + mainAxis, + childLayout.computedFlexBasis, + mainAxisOwnerSize, + ownerWidth) + .unwrap(); + flexLine.layout.crossDim = availableInnerCrossDim; + } else { + // The main dimension is the sum of all the elements dimension plus + // the spacing. + flexLine.layout.mainDim += + child->dimensionWithMargin(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 = calculateBaseline(child) + + child->style().computeFlexStartMargin( + FlexDirection::Column, direction, availableInnerWidth); + const float descent = + child->getLayout().measuredDimension(Dimension::Height) + + child->style().computeMarginForAxis( + FlexDirection::Column, availableInnerWidth) - + ascent; + + maxAscentForCurrentLine = + yoga::maxOrDefined(maxAscentForCurrentLine, ascent); + maxDescentForCurrentLine = + yoga::maxOrDefined(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 + flexLine.layout.crossDim = yoga::maxOrDefined( + flexLine.layout.crossDim, + child->dimensionWithMargin(crossAxis, availableInnerWidth)); + } + } + } + flexLine.layout.mainDim += trailingPaddingAndBorderMain; + + if (isNodeBaselineLayout) { + flexLine.layout.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 laid 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) +// - widthSizingMode: indicates the sizing rules for the width (see below +// for explanation) +// - heightSizingMode: 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 laid 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. +// +// When calling calculateLayoutImpl and calculateLayoutInternal, if the +// caller passes an available size of undefined then it must also pass a +// measure mode of SizingMode::MaxContent in that dimension. +// +static void calculateLayoutImpl( + yoga::Node* const node, + const float availableWidth, + const float availableHeight, + const Direction ownerDirection, + const SizingMode widthSizingMode, + const SizingMode heightSizingMode, + const float ownerWidth, + const float ownerHeight, + const bool performLayout, + const LayoutPassReason reason, + LayoutData& layoutMarkerData, + const uint32_t depth, + const uint32_t generationCount) { + yoga::assertFatalWithNode( + node, + yoga::isUndefined(availableWidth) + ? widthSizingMode == SizingMode::MaxContent + : true, + "availableWidth is indefinite so widthSizingMode must be " + "SizingMode::MaxContent"); + yoga::assertFatalWithNode( + node, + yoga::isUndefined(availableHeight) + ? heightSizingMode == SizingMode::MaxContent + : true, + "availableHeight is indefinite so heightSizingMode must be " + "SizingMode::MaxContent"); + + (performLayout ? layoutMarkerData.layouts : layoutMarkerData.measures) += 1; + + // Set the resolved resolution in the node's layout. + const Direction direction = node->resolveDirection(ownerDirection); + node->setLayoutDirection(direction); + + const FlexDirection flexRowDirection = + resolveDirection(FlexDirection::Row, direction); + const FlexDirection flexColumnDirection = + resolveDirection(FlexDirection::Column, direction); + + const auto startEdge = + direction == Direction::LTR ? PhysicalEdge::Left : PhysicalEdge::Right; + const auto endEdge = + direction == Direction::LTR ? PhysicalEdge::Right : PhysicalEdge::Left; + + const float marginRowLeading = node->style().computeInlineStartMargin( + flexRowDirection, direction, ownerWidth); + node->setLayoutMargin(marginRowLeading, startEdge); + const float marginRowTrailing = node->style().computeInlineEndMargin( + flexRowDirection, direction, ownerWidth); + node->setLayoutMargin(marginRowTrailing, endEdge); + const float marginColumnLeading = node->style().computeInlineStartMargin( + flexColumnDirection, direction, ownerWidth); + node->setLayoutMargin(marginColumnLeading, PhysicalEdge::Top); + const float marginColumnTrailing = node->style().computeInlineEndMargin( + flexColumnDirection, direction, ownerWidth); + node->setLayoutMargin(marginColumnTrailing, PhysicalEdge::Bottom); + + const float marginAxisRow = marginRowLeading + marginRowTrailing; + const float marginAxisColumn = marginColumnLeading + marginColumnTrailing; + + node->setLayoutBorder( + node->style().computeInlineStartBorder(flexRowDirection, direction), + startEdge); + node->setLayoutBorder( + node->style().computeInlineEndBorder(flexRowDirection, direction), + endEdge); + node->setLayoutBorder( + node->style().computeInlineStartBorder(flexColumnDirection, direction), + PhysicalEdge::Top); + node->setLayoutBorder( + node->style().computeInlineEndBorder(flexColumnDirection, direction), + PhysicalEdge::Bottom); + + node->setLayoutPadding( + node->style().computeInlineStartPadding( + flexRowDirection, direction, ownerWidth), + startEdge); + node->setLayoutPadding( + node->style().computeInlineEndPadding( + flexRowDirection, direction, ownerWidth), + endEdge); + node->setLayoutPadding( + node->style().computeInlineStartPadding( + flexColumnDirection, direction, ownerWidth), + PhysicalEdge::Top); + node->setLayoutPadding( + node->style().computeInlineEndPadding( + flexColumnDirection, direction, ownerWidth), + PhysicalEdge::Bottom); + + if (node->hasMeasureFunc()) { + measureNodeWithMeasureFunc( + node, + direction, + availableWidth - marginAxisRow, + availableHeight - marginAxisColumn, + widthSizingMode, + heightSizingMode, + ownerWidth, + ownerHeight, + layoutMarkerData, + reason); + + // Clean and update all display: contents nodes with a direct path to the + // current node as they will not be traversed + cleanupContentsNodesRecursively(node); + return; + } + + const auto childCount = node->getLayoutChildCount(); + if (childCount == 0) { + measureNodeWithoutChildren( + node, + direction, + availableWidth - marginAxisRow, + availableHeight - marginAxisColumn, + widthSizingMode, + heightSizingMode, + ownerWidth, + ownerHeight); + + // Clean and update all display: contents nodes with a direct path to the + // current node as they will not be traversed + cleanupContentsNodesRecursively(node); + 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 && + measureNodeWithFixedSize( + node, + direction, + availableWidth - marginAxisRow, + availableHeight - marginAxisColumn, + widthSizingMode, + heightSizingMode, + ownerWidth, + ownerHeight)) { + // Clean and update all display: contents nodes with a direct path to the + // current node as they will not be traversed + cleanupContentsNodesRecursively(node); + return; + } + + // At this point we know we're going to perform work. Ensure that each child + // has a mutable copy. + node->cloneChildrenIfNeeded(); + // Reset layout flags, as they could have changed. + node->setLayoutHadOverflow(false); + + // Clean and update all display: contents nodes with a direct path to the + // current node as they will not be traversed + cleanupContentsNodesRecursively(node); + + // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM + const FlexDirection mainAxis = + resolveDirection(node->style().flexDirection(), direction); + const FlexDirection crossAxis = resolveCrossDirection(mainAxis, direction); + const bool isMainAxisRow = isRow(mainAxis); + const bool isNodeFlexWrap = node->style().flexWrap() != Wrap::NoWrap; + + const float mainAxisOwnerSize = isMainAxisRow ? ownerWidth : ownerHeight; + const float crossAxisOwnerSize = isMainAxisRow ? ownerHeight : ownerWidth; + + const float paddingAndBorderAxisMain = + paddingAndBorderForAxis(node, mainAxis, direction, ownerWidth); + const float paddingAndBorderAxisCross = + paddingAndBorderForAxis(node, crossAxis, direction, ownerWidth); + const float leadingPaddingAndBorderCross = + node->style().computeFlexStartPaddingAndBorder( + crossAxis, direction, ownerWidth); + + SizingMode sizingModeMainDim = + isMainAxisRow ? widthSizingMode : heightSizingMode; + SizingMode sizingModeCrossDim = + isMainAxisRow ? heightSizingMode : widthSizingMode; + + const float paddingAndBorderAxisRow = + isMainAxisRow ? paddingAndBorderAxisMain : paddingAndBorderAxisCross; + const float paddingAndBorderAxisColumn = + isMainAxisRow ? paddingAndBorderAxisCross : paddingAndBorderAxisMain; + + // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS + + float availableInnerWidth = calculateAvailableInnerDimension( + node, + direction, + Dimension::Width, + availableWidth - marginAxisRow, + paddingAndBorderAxisRow, + ownerWidth, + ownerWidth); + float availableInnerHeight = calculateAvailableInnerDimension( + node, + direction, + Dimension::Height, + availableHeight - marginAxisColumn, + paddingAndBorderAxisColumn, + ownerHeight, + ownerWidth); + + float availableInnerMainDim = + isMainAxisRow ? availableInnerWidth : availableInnerHeight; + const float availableInnerCrossDim = + isMainAxisRow ? availableInnerHeight : availableInnerWidth; + + // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM + + // When this node is measured with MaxContent (FixFlexBasisFitContent + // behavior), availableInnerHeight is NaN. + // To preserve percentage resolution for descendants, derive a definite + // owner-size from the parent-provided ownerHeight. + float ownerWidthForChildren = availableInnerWidth; + float ownerHeightForChildren = availableInnerHeight; + + if (node->getConfig()->isExperimentalFeatureEnabled( + ExperimentalFeature::FixFlexBasisFitContent)) { + const auto* owner = node->getOwner(); + const bool isChildOfScrollContainer = + owner != nullptr && owner->style().overflow() == Overflow::Scroll; + + if (!isChildOfScrollContainer) { + if (yoga::isUndefined(ownerWidthForChildren) && + yoga::isDefined(ownerWidth)) { + ownerWidthForChildren = calculateAvailableInnerDimension( + node, + direction, + Dimension::Width, + ownerWidth - marginAxisRow, + paddingAndBorderAxisRow, + ownerWidth, + ownerWidth); + } + if (yoga::isUndefined(ownerHeightForChildren) && + yoga::isDefined(ownerHeight)) { + ownerHeightForChildren = calculateAvailableInnerDimension( + node, + direction, + Dimension::Height, + ownerHeight - marginAxisColumn, + paddingAndBorderAxisColumn, + ownerHeight, + ownerWidth); + } + } + } + + // Computed basis + margins + gap + float totalMainDim = 0; + totalMainDim += computeFlexBasisForChildren( + node, + availableInnerWidth, + availableInnerHeight, + ownerWidthForChildren, + ownerHeightForChildren, + widthSizingMode, + heightSizingMode, + direction, + mainAxis, + performLayout, + layoutMarkerData, + depth, + generationCount); + + if (childCount > 1) { + totalMainDim += + node->style().computeGapForAxis(mainAxis, availableInnerMainDim) * + static_cast(childCount - 1); + } + + const bool mainAxisOverflows = + (sizingModeMainDim != SizingMode::MaxContent) && + totalMainDim > availableInnerMainDim; + + if (isNodeFlexWrap && mainAxisOverflows && + sizingModeMainDim == SizingMode::FitContent) { + sizingModeMainDim = SizingMode::StretchFit; + } + // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES + + // Iterator representing the beginning of the current line + Node::LayoutableChildren::Iterator startOfLineIterator = + node->getLayoutChildren().begin(); + + // Number of lines. + size_t lineCount = 0; + + // Accumulated cross dimensions of all lines so far. + float totalLineCrossDim = 0; + + const float crossAxisGap = + node->style().computeGapForAxis(crossAxis, availableInnerCrossDim); + + // Max main dimension of all the lines. + float maxLineMainDim = 0; + for (; startOfLineIterator != node->getLayoutChildren().end(); lineCount++) { + auto flexLine = calculateFlexLine( + node, + ownerDirection, + ownerWidth, + mainAxisOwnerSize, + availableInnerWidth, + availableInnerMainDim, + startOfLineIterator, + lineCount); + + // If we don't need to measure the cross axis, we can skip the entire flex + // step. + const bool canSkipFlex = + !performLayout && sizingModeCrossDim == SizingMode::StretchFit; + + // 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 (sizingModeMainDim != SizingMode::StretchFit) { + const auto& style = node->style(); + const float minInnerWidth = + style + .resolvedMinDimension( + direction, Dimension::Width, ownerWidth, ownerWidth) + .unwrap() - + paddingAndBorderAxisRow; + const float maxInnerWidth = + style + .resolvedMaxDimension( + direction, Dimension::Width, ownerWidth, ownerWidth) + .unwrap() - + paddingAndBorderAxisRow; + const float minInnerHeight = + style + .resolvedMinDimension( + direction, Dimension::Height, ownerHeight, ownerWidth) + .unwrap() - + paddingAndBorderAxisColumn; + const float maxInnerHeight = + style + .resolvedMaxDimension( + direction, Dimension::Height, ownerHeight, ownerWidth) + .unwrap() - + paddingAndBorderAxisColumn; + + const float minInnerMainDim = + isMainAxisRow ? minInnerWidth : minInnerHeight; + const float maxInnerMainDim = + isMainAxisRow ? maxInnerWidth : maxInnerHeight; + + if (yoga::isDefined(minInnerMainDim) && + flexLine.sizeConsumed < minInnerMainDim) { + availableInnerMainDim = minInnerMainDim; + } else if ( + yoga::isDefined(maxInnerMainDim) && + flexLine.sizeConsumed > maxInnerMainDim) { + availableInnerMainDim = maxInnerMainDim; + } else { + bool useLegacyStretchBehaviour = + node->hasErrata(Errata::StretchFlexBasis); + + if (!useLegacyStretchBehaviour && + ((yoga::isDefined(flexLine.layout.totalFlexGrowFactors) && + flexLine.layout.totalFlexGrowFactors == 0) || + (yoga::isDefined(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 = flexLine.sizeConsumed; + } + + sizeBasedOnContent = !useLegacyStretchBehaviour; + } + } + + if (!sizeBasedOnContent && yoga::isDefined(availableInnerMainDim)) { + flexLine.layout.remainingFreeSpace = + availableInnerMainDim - flexLine.sizeConsumed; + } else if (flexLine.sizeConsumed < 0) { + // availableInnerMainDim is indefinite which means the node is being sized + // based on its content. sizeConsumed is negative which means + // the node will allocate 0 points for its content. Consequently, + // remainingFreeSpace is 0 - sizeConsumed. + flexLine.layout.remainingFreeSpace = -flexLine.sizeConsumed; + } + + if (!canSkipFlex) { + resolveFlexibleLength( + node, + flexLine, + mainAxis, + crossAxis, + direction, + ownerWidth, + mainAxisOwnerSize, + availableInnerMainDim, + availableInnerCrossDim, + availableInnerWidth, + availableInnerHeight, + mainAxisOverflows, + sizingModeCrossDim, + performLayout, + layoutMarkerData, + depth, + generationCount); + } + + node->setLayoutHadOverflow( + node->getLayout().hadOverflow() || + (flexLine.layout.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. + + justifyMainAxis( + node, + flexLine, + mainAxis, + crossAxis, + direction, + sizingModeMainDim, + sizingModeCrossDim, + mainAxisOwnerSize, + ownerWidth, + availableInnerMainDim, + availableInnerCrossDim, + availableInnerWidth, + performLayout); + + float containerCrossAxis = availableInnerCrossDim; + if (sizingModeCrossDim == SizingMode::MaxContent || + sizingModeCrossDim == SizingMode::FitContent) { + // Compute the cross axis from the max cross dimension of the children. + containerCrossAxis = + boundAxis( + node, + crossAxis, + direction, + flexLine.layout.crossDim + paddingAndBorderAxisCross, + crossAxisOwnerSize, + ownerWidth) - + paddingAndBorderAxisCross; + } + + // If there's no flex wrap, the cross dimension is defined by the container. + if (!isNodeFlexWrap && sizingModeCrossDim == SizingMode::StretchFit) { + flexLine.layout.crossDim = availableInnerCrossDim; + } + + // As-per https://www.w3.org/TR/css-flexbox-1/#cross-sizing, the + // cross-size of the line within a single-line container should be bound to + // min/max constraints before alignment within the line. In a multi-line + // container, affecting alignment between the lines. + if (!isNodeFlexWrap) { + flexLine.layout.crossDim = + boundAxis( + node, + crossAxis, + direction, + flexLine.layout.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 (auto child : flexLine.itemsInFlow) { + 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 Align alignItem = resolveChildAlignment(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 == Align::Stretch && + !child->style().flexStartMarginIsAuto(crossAxis, direction) && + !child->style().flexEndMarginIsAuto(crossAxis, direction)) { + // If the child defines a definite size for its cross axis, there's + // no need to stretch. + if (!child->hasDefiniteLength( + dimension(crossAxis), availableInnerCrossDim)) { + float childMainSize = + child->getLayout().measuredDimension(dimension(mainAxis)); + const auto& childStyle = child->style(); + float childCrossSize = childStyle.aspectRatio().isDefined() + ? child->style().computeMarginForAxis( + crossAxis, availableInnerWidth) + + (isMainAxisRow + ? childMainSize / childStyle.aspectRatio().unwrap() + : childMainSize * childStyle.aspectRatio().unwrap()) + : flexLine.layout.crossDim; + + childMainSize += child->style().computeMarginForAxis( + mainAxis, availableInnerWidth); + + SizingMode childMainSizingMode = SizingMode::StretchFit; + SizingMode childCrossSizingMode = SizingMode::StretchFit; + constrainMaxSizeForMode( + child, + direction, + mainAxis, + availableInnerMainDim, + availableInnerWidth, + &childMainSizingMode, + &childMainSize); + constrainMaxSizeForMode( + child, + direction, + crossAxis, + availableInnerCrossDim, + availableInnerWidth, + &childCrossSizingMode, + &childCrossSize); + + const float childWidth = + isMainAxisRow ? childMainSize : childCrossSize; + const float childHeight = + !isMainAxisRow ? childMainSize : childCrossSize; + + auto alignContent = node->style().alignContent(); + auto crossAxisDoesNotGrow = + alignContent != Align::Stretch && isNodeFlexWrap; + const SizingMode childWidthSizingMode = + yoga::isUndefined(childWidth) || + (!isMainAxisRow && crossAxisDoesNotGrow) + ? SizingMode::MaxContent + : SizingMode::StretchFit; + const SizingMode childHeightSizingMode = + yoga::isUndefined(childHeight) || + (isMainAxisRow && crossAxisDoesNotGrow) + ? SizingMode::MaxContent + : SizingMode::StretchFit; + + calculateLayoutInternal( + child, + childWidth, + childHeight, + direction, + childWidthSizingMode, + childHeightSizingMode, + availableInnerWidth, + availableInnerHeight, + true, + LayoutPassReason::kStretch, + layoutMarkerData, + depth, + generationCount); + } + } else { + const float remainingCrossDim = containerCrossAxis - + child->dimensionWithMargin(crossAxis, availableInnerWidth); + + if (child->style().flexStartMarginIsAuto(crossAxis, direction) && + child->style().flexEndMarginIsAuto(crossAxis, direction)) { + leadingCrossDim += yoga::maxOrDefined(0.0f, remainingCrossDim / 2); + } else if (child->style().flexEndMarginIsAuto(crossAxis, direction)) { + // No-Op + } else if (child->style().flexStartMarginIsAuto( + crossAxis, direction)) { + leadingCrossDim += yoga::maxOrDefined(0.0f, remainingCrossDim); + } else if (alignItem == Align::FlexStart) { + // No-Op + } else if (alignItem == Align::Center) { + leadingCrossDim += remainingCrossDim / 2; + } else { + leadingCrossDim += remainingCrossDim; + } + } + // And we apply the position + child->setLayoutPosition( + child->getLayout().position(flexStartEdge(crossAxis)) + + totalLineCrossDim + leadingCrossDim, + flexStartEdge(crossAxis)); + } + } + + const float appliedCrossGap = lineCount != 0 ? crossAxisGap : 0.0f; + totalLineCrossDim += flexLine.layout.crossDim + appliedCrossGap; + maxLineMainDim = + yoga::maxOrDefined(maxLineMainDim, flexLine.layout.mainDim); + } + + // STEP 8: MULTI-LINE CONTENT ALIGNMENT + // currentLead stores the size of the cross dim + if (performLayout && (isNodeFlexWrap || isBaselineLayout(node))) { + float leadPerLine = 0; + float currentLead = leadingPaddingAndBorderCross; + float extraSpacePerLine = 0; + + const float unclampedCrossDim = sizingModeCrossDim == SizingMode::StretchFit + ? availableInnerCrossDim + paddingAndBorderAxisCross + : node->hasDefiniteLength(dimension(crossAxis), crossAxisOwnerSize) + ? node->getResolvedDimension( + direction, + dimension(crossAxis), + crossAxisOwnerSize, + ownerWidth) + .unwrap() + : totalLineCrossDim + paddingAndBorderAxisCross; + + const float innerCrossDim = boundAxis( + node, + crossAxis, + direction, + unclampedCrossDim, + crossAxisOwnerSize, + ownerWidth) - + paddingAndBorderAxisCross; + + const float remainingAlignContentDim = innerCrossDim - totalLineCrossDim; + + const auto alignContent = remainingAlignContentDim >= 0 + ? node->style().alignContent() + : fallbackAlignment(node->style().alignContent()); + + switch (alignContent) { + case Align::Start: + case Align::End: + // No-Op + break; + case Align::FlexEnd: + currentLead += remainingAlignContentDim; + break; + case Align::Center: + currentLead += remainingAlignContentDim / 2; + break; + case Align::Stretch: + extraSpacePerLine = + remainingAlignContentDim / static_cast(lineCount); + break; + case Align::SpaceAround: + currentLead += + remainingAlignContentDim / (2 * static_cast(lineCount)); + leadPerLine = remainingAlignContentDim / static_cast(lineCount); + break; + case Align::SpaceEvenly: + currentLead += + remainingAlignContentDim / static_cast(lineCount + 1); + leadPerLine = + remainingAlignContentDim / static_cast(lineCount + 1); + break; + case Align::SpaceBetween: + if (lineCount > 1) { + leadPerLine = + remainingAlignContentDim / static_cast(lineCount - 1); + } + break; + case Align::Auto: + case Align::FlexStart: + case Align::Baseline: + break; + } + Node::LayoutableChildren::Iterator endIterator = + node->getLayoutChildren().begin(); + for (size_t i = 0; i < lineCount; i++) { + const Node::LayoutableChildren::Iterator startIterator = endIterator; + auto iterator = startIterator; + + // compute the line's height and find the endIndex + float lineHeight = 0; + float maxAscentForCurrentLine = 0; + float maxDescentForCurrentLine = 0; + for (; iterator != node->getLayoutChildren().end(); iterator++) { + const auto child = *iterator; + if (child->style().display() == Display::None) { + continue; + } + if (child->style().positionType() != PositionType::Absolute) { + if (child->getLineIndex() != i) { + break; + } + if (child->isLayoutDimensionDefined(crossAxis)) { + lineHeight = yoga::maxOrDefined( + lineHeight, + child->getLayout().measuredDimension(dimension(crossAxis)) + + child->style().computeMarginForAxis( + crossAxis, availableInnerWidth)); + } + if (resolveChildAlignment(node, child) == Align::Baseline) { + const float ascent = calculateBaseline(child) + + child->style().computeFlexStartMargin( + FlexDirection::Column, direction, availableInnerWidth); + const float descent = + child->getLayout().measuredDimension(Dimension::Height) + + child->style().computeMarginForAxis( + FlexDirection::Column, availableInnerWidth) - + ascent; + maxAscentForCurrentLine = + yoga::maxOrDefined(maxAscentForCurrentLine, ascent); + maxDescentForCurrentLine = + yoga::maxOrDefined(maxDescentForCurrentLine, descent); + lineHeight = yoga::maxOrDefined( + lineHeight, maxAscentForCurrentLine + maxDescentForCurrentLine); + } + } + } + endIterator = iterator; + currentLead += i != 0 ? crossAxisGap : 0; + lineHeight += extraSpacePerLine; + + for (iterator = startIterator; iterator != endIterator; iterator++) { + const auto child = *iterator; + if (child->style().display() == Display::None) { + continue; + } + if (child->style().positionType() != PositionType::Absolute) { + switch (resolveChildAlignment(node, child)) { + case Align::Start: + case Align::End: + // Not yet implemented + break; + case Align::FlexStart: { + child->setLayoutPosition( + currentLead + + child->style().computeFlexStartPosition( + crossAxis, direction, availableInnerWidth), + flexStartEdge(crossAxis)); + break; + } + case Align::FlexEnd: { + child->setLayoutPosition( + currentLead + lineHeight - + child->style().computeFlexEndMargin( + crossAxis, direction, availableInnerWidth) - + child->getLayout().measuredDimension( + dimension(crossAxis)), + flexStartEdge(crossAxis)); + break; + } + case Align::Center: { + float childHeight = + child->getLayout().measuredDimension(dimension(crossAxis)); + + child->setLayoutPosition( + currentLead + (lineHeight - childHeight) / 2, + flexStartEdge(crossAxis)); + break; + } + case Align::Stretch: { + child->setLayoutPosition( + currentLead + + child->style().computeFlexStartMargin( + crossAxis, direction, availableInnerWidth), + flexStartEdge(crossAxis)); + + // Remeasure child with the line height as it as been only + // measured with the owners height yet. + if (!child->hasDefiniteLength( + dimension(crossAxis), availableInnerCrossDim)) { + const float childWidth = isMainAxisRow + ? (child->getLayout().measuredDimension(Dimension::Width) + + child->style().computeMarginForAxis( + mainAxis, availableInnerWidth)) + : leadPerLine + lineHeight; + + const float childHeight = !isMainAxisRow + ? (child->getLayout().measuredDimension(Dimension::Height) + + child->style().computeMarginForAxis( + crossAxis, availableInnerWidth)) + : leadPerLine + lineHeight; + + if (!(yoga::inexactEquals( + childWidth, + child->getLayout().measuredDimension( + Dimension::Width)) && + yoga::inexactEquals( + childHeight, + child->getLayout().measuredDimension( + Dimension::Height)))) { + calculateLayoutInternal( + child, + childWidth, + childHeight, + direction, + SizingMode::StretchFit, + SizingMode::StretchFit, + availableInnerWidth, + availableInnerHeight, + true, + LayoutPassReason::kMultilineStretch, + layoutMarkerData, + depth, + generationCount); + } + } + break; + } + case Align::Baseline: { + child->setLayoutPosition( + currentLead + maxAscentForCurrentLine - + calculateBaseline(child) + + child->style().computeFlexStartPosition( + FlexDirection::Column, + direction, + availableInnerCrossDim), + PhysicalEdge::Top); + + break; + } + case Align::Auto: + case Align::SpaceBetween: + case Align::SpaceAround: + case Align::SpaceEvenly: + break; + } + } + } + + currentLead = currentLead + leadPerLine + lineHeight; + } + } + + // STEP 9: COMPUTING FINAL DIMENSIONS + + node->setLayoutMeasuredDimension( + boundAxis( + node, + FlexDirection::Row, + direction, + availableWidth - marginAxisRow, + ownerWidth, + ownerWidth), + Dimension::Width); + + node->setLayoutMeasuredDimension( + boundAxis( + node, + FlexDirection::Column, + direction, + availableHeight - marginAxisColumn, + ownerHeight, + ownerWidth), + Dimension::Height); + + // If the user didn't specify a width or height for the node, set the + // dimensions based on the children. + if (sizingModeMainDim == SizingMode::MaxContent || + (node->style().overflow() != Overflow::Scroll && + sizingModeMainDim == SizingMode::FitContent)) { + // 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( + boundAxis( + node, + mainAxis, + direction, + maxLineMainDim, + mainAxisOwnerSize, + ownerWidth), + dimension(mainAxis)); + + } else if ( + sizingModeMainDim == SizingMode::FitContent && + node->style().overflow() == Overflow::Scroll) { + node->setLayoutMeasuredDimension( + yoga::maxOrDefined( + yoga::minOrDefined( + availableInnerMainDim + paddingAndBorderAxisMain, + boundAxisWithinMinAndMax( + node, + direction, + mainAxis, + FloatOptional{maxLineMainDim}, + mainAxisOwnerSize, + ownerWidth) + .unwrap()), + paddingAndBorderAxisMain), + dimension(mainAxis)); + } + + if (sizingModeCrossDim == SizingMode::MaxContent || + (node->style().overflow() != Overflow::Scroll && + sizingModeCrossDim == SizingMode::FitContent)) { + // 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( + boundAxis( + node, + crossAxis, + direction, + totalLineCrossDim + paddingAndBorderAxisCross, + crossAxisOwnerSize, + ownerWidth), + dimension(crossAxis)); + + } else if ( + sizingModeCrossDim == SizingMode::FitContent && + node->style().overflow() == Overflow::Scroll) { + node->setLayoutMeasuredDimension( + yoga::maxOrDefined( + yoga::minOrDefined( + availableInnerCrossDim + paddingAndBorderAxisCross, + boundAxisWithinMinAndMax( + node, + direction, + crossAxis, + FloatOptional{ + totalLineCrossDim + paddingAndBorderAxisCross}, + crossAxisOwnerSize, + ownerWidth) + .unwrap()), + paddingAndBorderAxisCross), + dimension(crossAxis)); + } + + // As we only wrapped in normal direction yet, we need to reverse the + // positions on wrap-reverse. + if (performLayout && node->style().flexWrap() == Wrap::WrapReverse) { + for (auto child : node->getLayoutChildren()) { + if (child->style().positionType() != PositionType::Absolute) { + child->setLayoutPosition( + node->getLayout().measuredDimension(dimension(crossAxis)) - + child->getLayout().position(flexStartEdge(crossAxis)) - + child->getLayout().measuredDimension(dimension(crossAxis)), + flexStartEdge(crossAxis)); + } + } + } + + if (performLayout) { + // STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN + const bool needsMainTrailingPos = needsTrailingPosition(mainAxis); + const bool needsCrossTrailingPos = needsTrailingPosition(crossAxis); + + if (needsMainTrailingPos || needsCrossTrailingPos) { + for (auto child : node->getLayoutChildren()) { + // Absolute children will be handled by their containing block since we + // cannot guarantee that their positions are set when their parents are + // done with layout. + if (child->style().display() == Display::None || + child->style().positionType() == PositionType::Absolute) { + continue; + } + if (needsMainTrailingPos) { + setChildTrailingPosition(node, child, mainAxis); + } + + if (needsCrossTrailingPos) { + setChildTrailingPosition(node, child, crossAxis); + } + } + } + + // STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN + // Let the containing block layout its absolute descendants. + if (node->style().positionType() != PositionType::Static || + node->alwaysFormsContainingBlock() || depth == 1) { + layoutAbsoluteDescendants( + node, + node, + isMainAxisRow ? sizingModeMainDim : sizingModeCrossDim, + direction, + layoutMarkerData, + depth, + generationCount, + 0.0f, + 0.0f, + availableInnerWidth, + availableInnerHeight); + } + } +} + +// +// This is a wrapper around the calculateLayoutImpl function. It determines +// whether the layout request is redundant and can be skipped. +// +// Parameters: +// Input parameters are the same as calculateLayoutImpl (see above) +// Return parameter is true if layout was performed, false if skipped +// +bool calculateLayoutInternal( + yoga::Node* const node, + const float availableWidth, + const float availableHeight, + const Direction ownerDirection, + const SizingMode widthSizingMode, + const SizingMode heightSizingMode, + const float ownerWidth, + const float ownerHeight, + const bool performLayout, + const LayoutPassReason reason, + LayoutData& layoutMarkerData, + uint32_t depth, + const uint32_t generationCount) { + LayoutResults* layout = &node->getLayout(); + + depth++; + + const bool needToVisitNode = + (node->isDirty() && layout->generationCount != generationCount) || + layout->configVersion != node->getConfig()->getVersion() || + layout->lastOwnerDirection != ownerDirection; + + if (needToVisitNode) { + // Invalidate the cached results. + layout->nextCachedMeasurementsIndex = 0; + layout->cachedLayout.availableWidth = -1; + layout->cachedLayout.availableHeight = -1; + layout->cachedLayout.widthSizingMode = SizingMode::MaxContent; + layout->cachedLayout.heightSizingMode = SizingMode::MaxContent; + layout->cachedLayout.computedWidth = -1; + layout->cachedLayout.computedHeight = -1; + } + + CachedMeasurement* 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 laid 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->style().computeMarginForAxis(FlexDirection::Row, ownerWidth); + const float marginAxisColumn = + node->style().computeMarginForAxis(FlexDirection::Column, ownerWidth); + + // First, try to use the layout cache. + if (canUseCachedMeasurement( + widthSizingMode, + availableWidth, + heightSizingMode, + availableHeight, + layout->cachedLayout.widthSizingMode, + layout->cachedLayout.availableWidth, + layout->cachedLayout.heightSizingMode, + layout->cachedLayout.availableHeight, + layout->cachedLayout.computedWidth, + layout->cachedLayout.computedHeight, + marginAxisRow, + marginAxisColumn, + node->getConfig())) { + cachedResults = &layout->cachedLayout; + } else { + // Try to use the measurement cache. + for (size_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) { + if (canUseCachedMeasurement( + widthSizingMode, + availableWidth, + heightSizingMode, + availableHeight, + layout->cachedMeasurements[i].widthSizingMode, + layout->cachedMeasurements[i].availableWidth, + layout->cachedMeasurements[i].heightSizingMode, + layout->cachedMeasurements[i].availableHeight, + layout->cachedMeasurements[i].computedWidth, + layout->cachedMeasurements[i].computedHeight, + marginAxisRow, + marginAxisColumn, + node->getConfig())) { + cachedResults = &layout->cachedMeasurements[i]; + break; + } + } + } + } else if (performLayout) { + if (yoga::inexactEquals( + layout->cachedLayout.availableWidth, availableWidth) && + yoga::inexactEquals( + layout->cachedLayout.availableHeight, availableHeight) && + layout->cachedLayout.widthSizingMode == widthSizingMode && + layout->cachedLayout.heightSizingMode == heightSizingMode) { + cachedResults = &layout->cachedLayout; + } + } else { + for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) { + if (yoga::inexactEquals( + layout->cachedMeasurements[i].availableWidth, availableWidth) && + yoga::inexactEquals( + layout->cachedMeasurements[i].availableHeight, availableHeight) && + layout->cachedMeasurements[i].widthSizingMode == widthSizingMode && + layout->cachedMeasurements[i].heightSizingMode == heightSizingMode) { + cachedResults = &layout->cachedMeasurements[i]; + break; + } + } + } + + if (!needToVisitNode && cachedResults != nullptr) { + layout->setMeasuredDimension( + Dimension::Width, cachedResults->computedWidth); + layout->setMeasuredDimension( + Dimension::Height, cachedResults->computedHeight); + + (performLayout ? layoutMarkerData.cachedLayouts + : layoutMarkerData.cachedMeasures) += 1; + } else { + calculateLayoutImpl( + node, + availableWidth, + availableHeight, + ownerDirection, + widthSizingMode, + heightSizingMode, + ownerWidth, + ownerHeight, + performLayout, + reason, + layoutMarkerData, + depth, + generationCount); + + layout->lastOwnerDirection = ownerDirection; + layout->configVersion = node->getConfig()->getVersion(); + + if (cachedResults == nullptr) { + layoutMarkerData.maxMeasureCache = std::max( + layoutMarkerData.maxMeasureCache, + layout->nextCachedMeasurementsIndex + 1u); + + if (layout->nextCachedMeasurementsIndex == + LayoutResults::MaxCachedMeasurements) { + layout->nextCachedMeasurementsIndex = 0; + } + + CachedMeasurement* newCacheEntry = nullptr; + 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->widthSizingMode = widthSizingMode; + newCacheEntry->heightSizingMode = heightSizingMode; + newCacheEntry->computedWidth = + layout->measuredDimension(Dimension::Width); + newCacheEntry->computedHeight = + layout->measuredDimension(Dimension::Height); + } + } + + if (performLayout) { + node->setLayoutDimension( + node->getLayout().measuredDimension(Dimension::Width), + Dimension::Width); + node->setLayoutDimension( + node->getLayout().measuredDimension(Dimension::Height), + Dimension::Height); + + 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}); + + return (needToVisitNode || cachedResults == nullptr); +} + +void calculateLayout( + yoga::Node* const node, + const float ownerWidth, + const float ownerHeight, + const Direction ownerDirection) { + Event::publish(node); + 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->processDimensions(); + const Direction direction = node->resolveDirection(ownerDirection); + float width = YGUndefined; + SizingMode widthSizingMode = SizingMode::MaxContent; + const auto& style = node->style(); + if (node->hasDefiniteLength(Dimension::Width, ownerWidth)) { + width = + (node->getResolvedDimension( + direction, + dimension(FlexDirection::Row), + ownerWidth, + ownerWidth) + .unwrap() + + node->style().computeMarginForAxis(FlexDirection::Row, ownerWidth)); + widthSizingMode = SizingMode::StretchFit; + } else if (style + .resolvedMaxDimension( + direction, Dimension::Width, ownerWidth, ownerWidth) + .isDefined()) { + width = style + .resolvedMaxDimension( + direction, Dimension::Width, ownerWidth, ownerWidth) + .unwrap(); + widthSizingMode = SizingMode::FitContent; + } else { + width = ownerWidth; + widthSizingMode = yoga::isUndefined(width) ? SizingMode::MaxContent + : SizingMode::StretchFit; + } + + float height = YGUndefined; + SizingMode heightSizingMode = SizingMode::MaxContent; + if (node->hasDefiniteLength(Dimension::Height, ownerHeight)) { + height = + (node->getResolvedDimension( + direction, + dimension(FlexDirection::Column), + ownerHeight, + ownerWidth) + .unwrap() + + node->style().computeMarginForAxis(FlexDirection::Column, ownerWidth)); + heightSizingMode = SizingMode::StretchFit; + } else if (style + .resolvedMaxDimension( + direction, Dimension::Height, ownerHeight, ownerWidth) + .isDefined()) { + height = style + .resolvedMaxDimension( + direction, Dimension::Height, ownerHeight, ownerWidth) + .unwrap(); + heightSizingMode = SizingMode::FitContent; + } else { + height = ownerHeight; + heightSizingMode = yoga::isUndefined(height) ? SizingMode::MaxContent + : SizingMode::StretchFit; + } + if (calculateLayoutInternal( + node, + width, + height, + ownerDirection, + widthSizingMode, + heightSizingMode, + ownerWidth, + ownerHeight, + true, + LayoutPassReason::kInitial, + markerData, + 0, // tree root + gCurrentGenerationCount.load(std::memory_order_relaxed))) { + node->setPosition(node->getLayout().direction(), ownerWidth, ownerHeight); + roundLayoutResultsToPixelGrid(node, 0.0f, 0.0f); + } + + Event::publish(node, {&markerData}); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/algorithm/CalculateLayout.h b/third-party/yoga/src/yoga/algorithm/CalculateLayout.h new file mode 100644 index 00000000..2314bce0 --- /dev/null +++ b/third-party/yoga/src/yoga/algorithm/CalculateLayout.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include + +namespace facebook::yoga { + +void calculateLayout( + yoga::Node* node, + float ownerWidth, + float ownerHeight, + Direction ownerDirection); + +bool calculateLayoutInternal( + yoga::Node* node, + float availableWidth, + float availableHeight, + Direction ownerDirection, + SizingMode widthSizingMode, + SizingMode heightSizingMode, + float ownerWidth, + float ownerHeight, + bool performLayout, + LayoutPassReason reason, + LayoutData& layoutMarkerData, + uint32_t depth, + uint32_t generationCount); + +void constrainMaxSizeForMode( + const yoga::Node* node, + Direction direction, + FlexDirection axis, + float ownerAxisSize, + float ownerWidth, + /*in_out*/ SizingMode* mode, + /*in_out*/ float* size); + +float calculateAvailableInnerDimension( + const yoga::Node* const node, + const Direction direction, + const Dimension dimension, + const float availableDim, + float paddingAndBorder, + const float ownerDim, + const float ownerWidth); + +void zeroOutLayoutRecursively(yoga::Node* const node); + +void cleanupContentsNodesRecursively(yoga::Node* const node); + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/algorithm/FlexDirection.h b/third-party/yoga/src/yoga/algorithm/FlexDirection.h new file mode 100644 index 00000000..e76d0d7f --- /dev/null +++ b/third-party/yoga/src/yoga/algorithm/FlexDirection.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +namespace facebook::yoga { + +inline bool isRow(const FlexDirection flexDirection) { + return flexDirection == FlexDirection::Row || + flexDirection == FlexDirection::RowReverse; +} + +inline bool isColumn(const FlexDirection flexDirection) { + return flexDirection == FlexDirection::Column || + flexDirection == FlexDirection::ColumnReverse; +} + +inline FlexDirection resolveDirection( + const FlexDirection flexDirection, + const Direction direction) { + if (direction == Direction::RTL) { + if (flexDirection == FlexDirection::Row) { + return FlexDirection::RowReverse; + } else if (flexDirection == FlexDirection::RowReverse) { + return FlexDirection::Row; + } + } + + return flexDirection; +} + +inline FlexDirection resolveCrossDirection( + const FlexDirection flexDirection, + const Direction direction) { + return isColumn(flexDirection) + ? resolveDirection(FlexDirection::Row, direction) + : FlexDirection::Column; +} + +inline PhysicalEdge flexStartEdge(FlexDirection flexDirection) { + switch (flexDirection) { + case FlexDirection::Column: + return PhysicalEdge::Top; + case FlexDirection::ColumnReverse: + return PhysicalEdge::Bottom; + case FlexDirection::Row: + return PhysicalEdge::Left; + case FlexDirection::RowReverse: + return PhysicalEdge::Right; + default: + fatalWithMessage("Invalid FlexDirection"); + } +} + +inline PhysicalEdge flexEndEdge(FlexDirection flexDirection) { + switch (flexDirection) { + case FlexDirection::Column: + return PhysicalEdge::Bottom; + case FlexDirection::ColumnReverse: + return PhysicalEdge::Top; + case FlexDirection::Row: + return PhysicalEdge::Right; + case FlexDirection::RowReverse: + return PhysicalEdge::Left; + default: + fatalWithMessage("Invalid FlexDirection"); + } +} + +inline PhysicalEdge inlineStartEdge( + FlexDirection flexDirection, + Direction direction) { + if (isRow(flexDirection)) { + return direction == Direction::RTL ? PhysicalEdge::Right + : PhysicalEdge::Left; + } + + return PhysicalEdge::Top; +} + +inline PhysicalEdge inlineEndEdge( + FlexDirection flexDirection, + Direction direction) { + if (isRow(flexDirection)) { + return direction == Direction::RTL ? PhysicalEdge::Left + : PhysicalEdge::Right; + } + + return PhysicalEdge::Bottom; +} + +inline Dimension dimension(FlexDirection flexDirection) { + switch (flexDirection) { + case FlexDirection::Column: + return Dimension::Height; + case FlexDirection::ColumnReverse: + return Dimension::Height; + case FlexDirection::Row: + return Dimension::Width; + case FlexDirection::RowReverse: + return Dimension::Width; + default: + fatalWithMessage("Invalid FlexDirection"); + } +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/algorithm/FlexLine.cpp b/third-party/yoga/src/yoga/algorithm/FlexLine.cpp new file mode 100644 index 00000000..dc0a300a --- /dev/null +++ b/third-party/yoga/src/yoga/algorithm/FlexLine.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include +#include +#include + +namespace facebook::yoga { + +FlexLine calculateFlexLine( + yoga::Node* const node, + const Direction ownerDirection, + const float ownerWidth, + const float mainAxisOwnerSize, + const float availableInnerWidth, + const float availableInnerMainDim, + Node::LayoutableChildren::Iterator& iterator, + const size_t lineCount) { + std::vector itemsInFlow; + itemsInFlow.reserve(node->getChildCount()); + + float sizeConsumed = 0.0f; + float totalFlexGrowFactors = 0.0f; + float totalFlexShrinkScaledFactors = 0.0f; + size_t numberOfAutoMargins = 0; + yoga::Node* firstElementInLine = nullptr; + + float sizeConsumedIncludingMinConstraint = 0; + const Direction direction = node->resolveDirection(ownerDirection); + const FlexDirection mainAxis = + resolveDirection(node->style().flexDirection(), direction); + const bool isNodeFlexWrap = node->style().flexWrap() != Wrap::NoWrap; + const float gap = + node->style().computeGapForAxis(mainAxis, availableInnerMainDim); + + const auto childrenEnd = node->getLayoutChildren().end(); + // Add items to the current line until it's full or we run out of items. + for (; iterator != childrenEnd; iterator++) { + auto child = *iterator; + if (child->style().display() == Display::None || + child->style().positionType() == PositionType::Absolute) { + continue; + } + + if (firstElementInLine == nullptr) { + firstElementInLine = child; + } + + if (child->style().flexStartMarginIsAuto(mainAxis, ownerDirection)) { + numberOfAutoMargins++; + } + if (child->style().flexEndMarginIsAuto(mainAxis, ownerDirection)) { + numberOfAutoMargins++; + } + + child->setLineIndex(lineCount); + const float childMarginMainAxis = + child->style().computeMarginForAxis(mainAxis, availableInnerWidth); + const float childLeadingGapMainAxis = + child == firstElementInLine ? 0.0f : gap; + const float flexBasisWithMinAndMaxConstraints = + boundAxisWithinMinAndMax( + child, + direction, + mainAxis, + child->getLayout().computedFlexBasis, + mainAxisOwnerSize, + ownerWidth) + .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 (sizeConsumedIncludingMinConstraint + flexBasisWithMinAndMaxConstraints + + childMarginMainAxis + childLeadingGapMainAxis > + availableInnerMainDim && + isNodeFlexWrap && !itemsInFlow.empty()) { + break; + } + + sizeConsumedIncludingMinConstraint += flexBasisWithMinAndMaxConstraints + + childMarginMainAxis + childLeadingGapMainAxis; + sizeConsumed += flexBasisWithMinAndMaxConstraints + childMarginMainAxis + + childLeadingGapMainAxis; + + if (child->isNodeFlexible()) { + totalFlexGrowFactors += child->resolveFlexGrow(); + + // Unlike the grow factor, the shrink factor is scaled relative to the + // child dimension. + totalFlexShrinkScaledFactors += -child->resolveFlexShrink() * + child->getLayout().computedFlexBasis.unwrap(); + } + + itemsInFlow.push_back(child); + } + + // The total flex factor needs to be floored to 1. + if (totalFlexGrowFactors > 0 && totalFlexGrowFactors < 1) { + totalFlexGrowFactors = 1; + } + + // The total flex shrink factor needs to be floored to 1. + if (totalFlexShrinkScaledFactors > 0 && totalFlexShrinkScaledFactors < 1) { + totalFlexShrinkScaledFactors = 1; + } + + return FlexLine{ + .itemsInFlow = std::move(itemsInFlow), + .sizeConsumed = sizeConsumed, + .numberOfAutoMargins = numberOfAutoMargins, + .layout = FlexLineRunningLayout{ + totalFlexGrowFactors, + totalFlexShrinkScaledFactors, + }}; +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/algorithm/FlexLine.h b/third-party/yoga/src/yoga/algorithm/FlexLine.h new file mode 100644 index 00000000..3f2ef2f1 --- /dev/null +++ b/third-party/yoga/src/yoga/algorithm/FlexLine.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include +#include + +namespace facebook::yoga { + +struct FlexLineRunningLayout { + // Total flex grow factors of flex items which are to be laid in the current + // line. This is decremented as free space is distributed. + float totalFlexGrowFactors{0.0f}; + + // Total flex shrink factors of flex items which are to be laid in the current + // line. This is decremented as free space is distributed. + float totalFlexShrinkScaledFactors{0.0f}; + + // The amount of available space within inner dimensions of the line which may + // still be distributed. + float remainingFreeSpace{0.0f}; + + // 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{0.0f}; + + // 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{0.0f}; +}; + +struct FlexLine { + // List of children which are part of the line flow. This means they are not + // positioned absolutely, or with `display: "none"`, and do not overflow the + // available dimensions. + const std::vector itemsInFlow{}; + + // 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. + const float sizeConsumed{0.0f}; + + // Number of edges along the line flow with an auto margin. + const size_t numberOfAutoMargins{0}; + + // Layout information about the line computed in steps after line-breaking + FlexLineRunningLayout layout{}; +}; + +// Calculates where a line starting at a given index should break, returning +// information about the collective children on the liune. +// +// This function assumes that all the children of node have their +// computedFlexBasis properly computed(To do this use +// computeFlexBasisForChildren function). +FlexLine calculateFlexLine( + yoga::Node* node, + Direction ownerDirection, + float ownerWidth, + float mainAxisOwnerSize, + float availableInnerWidth, + float availableInnerMainDim, + Node::LayoutableChildren::Iterator& iterator, + size_t lineCount); + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/algorithm/PixelGrid.cpp b/third-party/yoga/src/yoga/algorithm/PixelGrid.cpp new file mode 100644 index 00000000..61de2be2 --- /dev/null +++ b/third-party/yoga/src/yoga/algorithm/PixelGrid.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include +#include + +namespace facebook::yoga { + +float roundValueToPixelGrid( + const double value, + const double pointScaleFactor, + const bool forceCeil, + const bool forceFloor) { + double scaledValue = value * pointScaleFactor; + // We want to calculate `fractial` such that `floor(scaledValue) = scaledValue + // - fractial`. + double fractial = fmod(scaledValue, 1.0); + 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 (yoga::inexactEquals(fractial, 0)) { + // First we check if the value is already rounded + scaledValue = scaledValue - fractial; + } else if (yoga::inexactEquals(fractial, 1.0)) { + scaledValue = scaledValue - fractial + 1.0; + } else if (forceCeil) { + // Next we check if we need to use forced rounding + scaledValue = scaledValue - fractial + 1.0; + } else if (forceFloor) { + scaledValue = scaledValue - fractial; + } else { + // Finally we just round the value + scaledValue = scaledValue - fractial + + (!std::isnan(fractial) && + (fractial > 0.5 || yoga::inexactEquals(fractial, 0.5)) + ? 1.0 + : 0.0); + } + return (std::isnan(scaledValue) || std::isnan(pointScaleFactor)) + ? YGUndefined + : (float)(scaledValue / pointScaleFactor); +} + +void roundLayoutResultsToPixelGrid( + yoga::Node* const node, + const double absoluteLeft, + const double absoluteTop) { + const auto pointScaleFactor = + static_cast(node->getConfig()->getPointScaleFactor()); + + const double nodeLeft = node->getLayout().position(PhysicalEdge::Left); + const double nodeTop = node->getLayout().position(PhysicalEdge::Top); + + const double nodeWidth = node->getLayout().dimension(Dimension::Width); + const double nodeHeight = node->getLayout().dimension(Dimension::Height); + + const double absoluteNodeLeft = absoluteLeft + nodeLeft; + const double absoluteNodeTop = absoluteTop + nodeTop; + + const double absoluteNodeRight = absoluteNodeLeft + nodeWidth; + const double absoluteNodeBottom = absoluteNodeTop + nodeHeight; + + if (pointScaleFactor != 0.0) { + // 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() == NodeType::Text; + + node->setLayoutPosition( + roundValueToPixelGrid(nodeLeft, pointScaleFactor, false, textRounding), + PhysicalEdge::Left); + + node->setLayoutPosition( + roundValueToPixelGrid(nodeTop, pointScaleFactor, false, textRounding), + PhysicalEdge::Top); + + // 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 double scaledNodeWith = nodeWidth * pointScaleFactor; + const bool hasFractionalWidth = + !yoga::inexactEquals(round(scaledNodeWith), scaledNodeWith); + + const double scaledNodeHeight = nodeHeight * pointScaleFactor; + const bool hasFractionalHeight = + !yoga::inexactEquals(round(scaledNodeHeight), scaledNodeHeight); + + node->getLayout().setDimension( + Dimension::Width, + roundValueToPixelGrid( + absoluteNodeRight, + pointScaleFactor, + (textRounding && hasFractionalWidth), + (textRounding && !hasFractionalWidth)) - + roundValueToPixelGrid( + absoluteNodeLeft, pointScaleFactor, false, textRounding)); + + node->getLayout().setDimension( + Dimension::Height, + roundValueToPixelGrid( + absoluteNodeBottom, + pointScaleFactor, + (textRounding && hasFractionalHeight), + (textRounding && !hasFractionalHeight)) - + roundValueToPixelGrid( + absoluteNodeTop, pointScaleFactor, false, textRounding)); + } + + for (yoga::Node* child : node->getChildren()) { + roundLayoutResultsToPixelGrid(child, absoluteNodeLeft, absoluteNodeTop); + } +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/algorithm/PixelGrid.h b/third-party/yoga/src/yoga/algorithm/PixelGrid.h new file mode 100644 index 00000000..f3bf79f9 --- /dev/null +++ b/third-party/yoga/src/yoga/algorithm/PixelGrid.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook::yoga { + +// Round a point value to the nearest physical pixel based on DPI +// (pointScaleFactor) +float roundValueToPixelGrid( + double value, + double pointScaleFactor, + bool forceCeil, + bool forceFloor); + +// Round the layout results of a node and its subtree to the pixel grid. +void roundLayoutResultsToPixelGrid( + yoga::Node* node, + double absoluteLeft, + double absoluteTop); + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/algorithm/SizingMode.h b/third-party/yoga/src/yoga/algorithm/SizingMode.h new file mode 100644 index 00000000..2b308744 --- /dev/null +++ b/third-party/yoga/src/yoga/algorithm/SizingMode.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook::yoga { + +/** + * Corresponds to a CSS auto box sizes. Missing "min-content", as Yoga does not + * current support automatic minimum sizes. + * https://www.w3.org/TR/css-sizing-3/#auto-box-sizes + * https://www.w3.org/TR/css-flexbox-1/#min-size-auto + */ +enum class SizingMode { + /** + * The size a box would take if its outer size filled the available space in + * the given axis; in other words, the stretch fit into the available space, + * if that is definite. Undefined if the available space is indefinite. + */ + StretchFit, + + /** + * A box’s “ideal” size in a given axis when given infinite available space. + * Usually this is the smallest size the box could take in that axis while + * still fitting around its contents, i.e. minimizing unfilled space while + * avoiding overflow. + */ + MaxContent, + + /** + * If the available space in a given axis is definite, equal to + * clamp(min-content size, stretch-fit size, max-content size) (i.e. + * max(min-content size, min(max-content size, stretch-fit size))). When + * sizing under a min-content constraint, equal to the min-content size. + * Otherwise, equal to the max-content size in that axis. + */ + FitContent, +}; + +inline MeasureMode measureMode(SizingMode mode) { + switch (mode) { + case SizingMode::StretchFit: + return MeasureMode::Exactly; + case SizingMode::MaxContent: + return MeasureMode::Undefined; + case SizingMode::FitContent: + return MeasureMode::AtMost; + default: + fatalWithMessage("Invalid SizingMode"); + } +} + +inline SizingMode sizingMode(MeasureMode mode) { + switch (mode) { + case MeasureMode::Exactly: + return SizingMode::StretchFit; + case MeasureMode::Undefined: + return SizingMode::MaxContent; + case MeasureMode::AtMost: + return SizingMode::FitContent; + default: + fatalWithMessage("Invalid MeasureMode"); + } +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/algorithm/TrailingPosition.h b/third-party/yoga/src/yoga/algorithm/TrailingPosition.h new file mode 100644 index 00000000..84d00edb --- /dev/null +++ b/third-party/yoga/src/yoga/algorithm/TrailingPosition.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include + +namespace facebook::yoga { + +// Given an offset to an edge, returns the offset to the opposite edge on the +// same axis. This assumes that the width/height of both nodes is determined at +// this point. +inline float getPositionOfOppositeEdge( + float position, + FlexDirection axis, + const yoga::Node* const containingNode, + const yoga::Node* const node) { + return containingNode->getLayout().measuredDimension(dimension(axis)) - + node->getLayout().measuredDimension(dimension(axis)) - position; +} + +inline void setChildTrailingPosition( + const yoga::Node* const node, + yoga::Node* const child, + const FlexDirection axis) { + child->setLayoutPosition( + getPositionOfOppositeEdge( + child->getLayout().position(flexStartEdge(axis)), axis, node, child), + flexEndEdge(axis)); +} + +inline bool needsTrailingPosition(const FlexDirection axis) { + return axis == FlexDirection::RowReverse || + axis == FlexDirection::ColumnReverse; +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/config/Config.cpp b/third-party/yoga/src/yoga/config/Config.cpp new file mode 100644 index 00000000..0f5fb019 --- /dev/null +++ b/third-party/yoga/src/yoga/config/Config.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +namespace facebook::yoga { + +bool configUpdateInvalidatesLayout( + const Config& oldConfig, + const Config& newConfig) { + return oldConfig.getErrata() != newConfig.getErrata() || + oldConfig.getEnabledExperiments() != newConfig.getEnabledExperiments() || + oldConfig.getPointScaleFactor() != newConfig.getPointScaleFactor() || + oldConfig.useWebDefaults() != newConfig.useWebDefaults(); +} + +void Config::setUseWebDefaults(bool useWebDefaults) { + useWebDefaults_ = useWebDefaults; +} + +bool Config::useWebDefaults() const { + return useWebDefaults_; +} + +void Config::setExperimentalFeatureEnabled( + ExperimentalFeature feature, + bool enabled) { + if (isExperimentalFeatureEnabled(feature) != enabled) { + experimentalFeatures_.set(static_cast(feature), enabled); + version_++; + } +} + +bool Config::isExperimentalFeatureEnabled(ExperimentalFeature feature) const { + return experimentalFeatures_.test(static_cast(feature)); +} + +ExperimentalFeatureSet Config::getEnabledExperiments() const { + return experimentalFeatures_; +} + +void Config::setErrata(Errata errata) { + if (errata_ != errata) { + errata_ = errata; + version_++; + } +} + +void Config::addErrata(Errata errata) { + if (!hasErrata(errata)) { + errata_ |= errata; + version_++; + } +} + +void Config::removeErrata(Errata errata) { + if (hasErrata(errata)) { + errata_ &= (~errata); + version_++; + } +} + +Errata Config::getErrata() const { + return errata_; +} + +bool Config::hasErrata(Errata errata) const { + return (errata_ & errata) != Errata::None; +} + +void Config::setPointScaleFactor(float pointScaleFactor) { + if (pointScaleFactor_ != pointScaleFactor) { + pointScaleFactor_ = pointScaleFactor; + version_++; + } +} + +float Config::getPointScaleFactor() const { + return pointScaleFactor_; +} + +void Config::setContext(void* context) { + context_ = context; +} + +void* Config::getContext() const { + return context_; +} + +uint32_t Config::getVersion() const noexcept { + return version_; +} + +void Config::setLogger(YGLogger logger) { + logger_ = logger; +} + +void Config::log( + const yoga::Node* node, + LogLevel logLevel, + const char* format, + va_list args) const { + logger_(this, node, unscopedEnum(logLevel), format, args); +} + +void Config::setCloneNodeCallback(YGCloneNodeFunc cloneNode) { + cloneNodeCallback_ = cloneNode; +} + +YGNodeRef Config::cloneNode( + YGNodeConstRef node, + YGNodeConstRef owner, + size_t childIndex) const { + YGNodeRef clone = nullptr; + if (cloneNodeCallback_ != nullptr) { + clone = cloneNodeCallback_(node, owner, childIndex); + } + if (clone == nullptr) { + clone = YGNodeClone(node); + } + return clone; +} + +/*static*/ const Config& Config::getDefault() { + static Config config{getDefaultLogger()}; + return config; +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/config/Config.h b/third-party/yoga/src/yoga/config/Config.h new file mode 100644 index 00000000..7bcffd14 --- /dev/null +++ b/third-party/yoga/src/yoga/config/Config.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include +#include +#include +#include + +// Tag struct used to form the opaque YGConfigRef for the public C API +struct YGConfig {}; + +namespace facebook::yoga { + +class Config; +class Node; + +using ExperimentalFeatureSet = std::bitset()>; + +// Whether moving a node from an old to new config should dirty previously +// calculated layout results. +bool configUpdateInvalidatesLayout( + const Config& oldConfig, + const Config& newConfig); + +class YG_EXPORT Config : public ::YGConfig { + public: + explicit Config(YGLogger logger) : logger_{logger} {} + + void setUseWebDefaults(bool useWebDefaults); + bool useWebDefaults() const; + + void setExperimentalFeatureEnabled(ExperimentalFeature feature, bool enabled); + bool isExperimentalFeatureEnabled(ExperimentalFeature feature) const; + ExperimentalFeatureSet getEnabledExperiments() const; + + void setErrata(Errata errata); + void addErrata(Errata errata); + void removeErrata(Errata errata); + Errata getErrata() const; + bool hasErrata(Errata errata) const; + + void setPointScaleFactor(float pointScaleFactor); + float getPointScaleFactor() const; + + void setContext(void* context); + void* getContext() const; + + uint32_t getVersion() const noexcept; + + void setLogger(YGLogger logger); + void log( + const yoga::Node* node, + LogLevel logLevel, + const char* format, + va_list args) const; + + void setCloneNodeCallback(YGCloneNodeFunc cloneNode); + YGNodeRef + cloneNode(YGNodeConstRef node, YGNodeConstRef owner, size_t childIndex) const; + + static const Config& getDefault(); + + private: + YGCloneNodeFunc cloneNodeCallback_{nullptr}; + YGLogger logger_{}; + + bool useWebDefaults_ : 1 = false; + + uint32_t version_ = 0; + ExperimentalFeatureSet experimentalFeatures_{}; + Errata errata_ = Errata::None; + float pointScaleFactor_ = 1.0f; + void* context_ = nullptr; +}; + +inline Config* resolveRef(const YGConfigRef ref) { + return static_cast(ref); +} + +inline const Config* resolveRef(const YGConfigConstRef ref) { + return static_cast(ref); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/debug/AssertFatal.cpp b/third-party/yoga/src/yoga/debug/AssertFatal.cpp new file mode 100644 index 00000000..e36af650 --- /dev/null +++ b/third-party/yoga/src/yoga/debug/AssertFatal.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +#include +#include +#include +#include + +namespace facebook::yoga { + +[[noreturn]] void fatalWithMessage(const char* message) { +#if defined(__cpp_exceptions) + throw std::logic_error(message); +#else + static_cast(message); // Unused + std::terminate(); +#endif +} + +void assertFatal(const bool condition, const char* message) { + if (!condition) { + yoga::log(LogLevel::Fatal, "%s\n", message); + fatalWithMessage(message); + } +} + +void assertFatalWithNode( + const yoga::Node* const node, + const bool condition, + const char* message) { + if (!condition) { + yoga::log(node, LogLevel::Fatal, "%s\n", message); + fatalWithMessage(message); + } +} + +void assertFatalWithConfig( + const yoga::Config* const config, + const bool condition, + const char* message) { + if (!condition) { + yoga::log(config, LogLevel::Fatal, "%s\n", message); + fatalWithMessage(message); + } +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/debug/AssertFatal.h b/third-party/yoga/src/yoga/debug/AssertFatal.h new file mode 100644 index 00000000..bbd8ad19 --- /dev/null +++ b/third-party/yoga/src/yoga/debug/AssertFatal.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook::yoga { + +class Node; +class Config; + +[[noreturn]] void fatalWithMessage(const char* message); + +void assertFatal(bool condition, const char* message); +void assertFatalWithNode( + const yoga::Node* node, + bool condition, + const char* message); +void assertFatalWithConfig( + const yoga::Config* config, + bool condition, + const char* message); + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/debug/Log.cpp b/third-party/yoga/src/yoga/debug/Log.cpp new file mode 100644 index 00000000..9b525d8a --- /dev/null +++ b/third-party/yoga/src/yoga/debug/Log.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#ifdef ANDROID +#include +#endif + +namespace facebook::yoga { + +namespace { + +void vlog( + const yoga::Config* config, + const yoga::Node* node, + LogLevel level, + const char* format, + va_list args) { + if (config == nullptr) { + getDefaultLogger()(nullptr, node, unscopedEnum(level), format, args); + } else { + config->log(node, level, format, args); + } +} +} // namespace + +void log(LogLevel level, const char* format, ...) noexcept { + va_list args; + va_start(args, format); + vlog(nullptr, nullptr, level, format, args); + va_end(args); +} + +void log( + const yoga::Node* node, + LogLevel level, + const char* format, + ...) noexcept { + va_list args; + va_start(args, format); + vlog( + node == nullptr ? nullptr : node->getConfig(), node, level, format, args); + va_end(args); +} + +void log( + const yoga::Config* config, + LogLevel level, + const char* format, + ...) noexcept { + va_list args; + va_start(args, format); + vlog(config, nullptr, level, format, args); + va_end(args); +} + +YGLogger getDefaultLogger() { + return [](const YGConfigConstRef /*config*/, + const YGNodeConstRef /*node*/, + YGLogLevel level, + const char* format, + va_list args) -> int { +#ifdef ANDROID + 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; + } + return __android_log_vprint(androidLevel, "yoga", format, args); +#else + switch (level) { + case YGLogLevelError: + case YGLogLevelFatal: + return vfprintf(stderr, format, args); + case YGLogLevelWarn: + case YGLogLevelInfo: + case YGLogLevelDebug: + case YGLogLevelVerbose: + default: + return vprintf(format, args); + } +#endif + }; +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/debug/Log.h b/third-party/yoga/src/yoga/debug/Log.h new file mode 100644 index 00000000..0a932d80 --- /dev/null +++ b/third-party/yoga/src/yoga/debug/Log.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace facebook::yoga { + +void log(LogLevel level, const char* format, ...) noexcept; + +void log( + const yoga::Node* node, + LogLevel level, + const char* format, + ...) noexcept; + +void log( + const yoga::Config* config, + LogLevel level, + const char* format, + ...) noexcept; + +YGLogger getDefaultLogger(); + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/Align.h b/third-party/yoga/src/yoga/enums/Align.h new file mode 100644 index 00000000..e1b8b29b --- /dev/null +++ b/third-party/yoga/src/yoga/enums/Align.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class Align : uint8_t { + Auto = YGAlignAuto, + FlexStart = YGAlignFlexStart, + Center = YGAlignCenter, + FlexEnd = YGAlignFlexEnd, + Stretch = YGAlignStretch, + Baseline = YGAlignBaseline, + SpaceBetween = YGAlignSpaceBetween, + SpaceAround = YGAlignSpaceAround, + SpaceEvenly = YGAlignSpaceEvenly, + Start = YGAlignStart, + End = YGAlignEnd, +}; + +template <> +constexpr int32_t ordinalCount() { + return 11; +} + +constexpr Align scopedEnum(YGAlign unscoped) { + return static_cast(unscoped); +} + +constexpr YGAlign unscopedEnum(Align scoped) { + return static_cast(scoped); +} + +inline const char* toString(Align e) { + return YGAlignToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/BoxSizing.h b/third-party/yoga/src/yoga/enums/BoxSizing.h new file mode 100644 index 00000000..eb18b99a --- /dev/null +++ b/third-party/yoga/src/yoga/enums/BoxSizing.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class BoxSizing : uint8_t { + BorderBox = YGBoxSizingBorderBox, + ContentBox = YGBoxSizingContentBox, +}; + +template <> +constexpr int32_t ordinalCount() { + return 2; +} + +constexpr BoxSizing scopedEnum(YGBoxSizing unscoped) { + return static_cast(unscoped); +} + +constexpr YGBoxSizing unscopedEnum(BoxSizing scoped) { + return static_cast(scoped); +} + +inline const char* toString(BoxSizing e) { + return YGBoxSizingToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/Dimension.h b/third-party/yoga/src/yoga/enums/Dimension.h new file mode 100644 index 00000000..96590152 --- /dev/null +++ b/third-party/yoga/src/yoga/enums/Dimension.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class Dimension : uint8_t { + Width = YGDimensionWidth, + Height = YGDimensionHeight, +}; + +template <> +constexpr int32_t ordinalCount() { + return 2; +} + +constexpr Dimension scopedEnum(YGDimension unscoped) { + return static_cast(unscoped); +} + +constexpr YGDimension unscopedEnum(Dimension scoped) { + return static_cast(scoped); +} + +inline const char* toString(Dimension e) { + return YGDimensionToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/Direction.h b/third-party/yoga/src/yoga/enums/Direction.h new file mode 100644 index 00000000..5bbabaec --- /dev/null +++ b/third-party/yoga/src/yoga/enums/Direction.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class Direction : uint8_t { + Inherit = YGDirectionInherit, + LTR = YGDirectionLTR, + RTL = YGDirectionRTL, +}; + +template <> +constexpr int32_t ordinalCount() { + return 3; +} + +constexpr Direction scopedEnum(YGDirection unscoped) { + return static_cast(unscoped); +} + +constexpr YGDirection unscopedEnum(Direction scoped) { + return static_cast(scoped); +} + +inline const char* toString(Direction e) { + return YGDirectionToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/Display.h b/third-party/yoga/src/yoga/enums/Display.h new file mode 100644 index 00000000..9edbee80 --- /dev/null +++ b/third-party/yoga/src/yoga/enums/Display.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class Display : uint8_t { + Flex = YGDisplayFlex, + None = YGDisplayNone, + Contents = YGDisplayContents, + Grid = YGDisplayGrid, +}; + +template <> +constexpr int32_t ordinalCount() { + return 4; +} + +constexpr Display scopedEnum(YGDisplay unscoped) { + return static_cast(unscoped); +} + +constexpr YGDisplay unscopedEnum(Display scoped) { + return static_cast(scoped); +} + +inline const char* toString(Display e) { + return YGDisplayToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/Edge.h b/third-party/yoga/src/yoga/enums/Edge.h new file mode 100644 index 00000000..3ab6a33f --- /dev/null +++ b/third-party/yoga/src/yoga/enums/Edge.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class Edge : uint8_t { + Left = YGEdgeLeft, + Top = YGEdgeTop, + Right = YGEdgeRight, + Bottom = YGEdgeBottom, + Start = YGEdgeStart, + End = YGEdgeEnd, + Horizontal = YGEdgeHorizontal, + Vertical = YGEdgeVertical, + All = YGEdgeAll, +}; + +template <> +constexpr int32_t ordinalCount() { + return 9; +} + +constexpr Edge scopedEnum(YGEdge unscoped) { + return static_cast(unscoped); +} + +constexpr YGEdge unscopedEnum(Edge scoped) { + return static_cast(scoped); +} + +inline const char* toString(Edge e) { + return YGEdgeToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/Errata.h b/third-party/yoga/src/yoga/enums/Errata.h new file mode 100644 index 00000000..2f47a941 --- /dev/null +++ b/third-party/yoga/src/yoga/enums/Errata.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class Errata : uint32_t { + None = YGErrataNone, + StretchFlexBasis = YGErrataStretchFlexBasis, + AbsolutePositionWithoutInsetsExcludesPadding = YGErrataAbsolutePositionWithoutInsetsExcludesPadding, + AbsolutePercentAgainstInnerSize = YGErrataAbsolutePercentAgainstInnerSize, + All = YGErrataAll, + Classic = YGErrataClassic, +}; + +YG_DEFINE_ENUM_FLAG_OPERATORS(Errata) + +constexpr Errata scopedEnum(YGErrata unscoped) { + return static_cast(unscoped); +} + +constexpr YGErrata unscopedEnum(Errata scoped) { + return static_cast(scoped); +} + +inline const char* toString(Errata e) { + return YGErrataToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/ExperimentalFeature.h b/third-party/yoga/src/yoga/enums/ExperimentalFeature.h new file mode 100644 index 00000000..7e6c5eb8 --- /dev/null +++ b/third-party/yoga/src/yoga/enums/ExperimentalFeature.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class ExperimentalFeature : uint8_t { + WebFlexBasis = YGExperimentalFeatureWebFlexBasis, + FixFlexBasisFitContent = YGExperimentalFeatureFixFlexBasisFitContent, +}; + +template <> +constexpr int32_t ordinalCount() { + return 2; +} + +constexpr ExperimentalFeature scopedEnum(YGExperimentalFeature unscoped) { + return static_cast(unscoped); +} + +constexpr YGExperimentalFeature unscopedEnum(ExperimentalFeature scoped) { + return static_cast(scoped); +} + +inline const char* toString(ExperimentalFeature e) { + return YGExperimentalFeatureToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/FlexDirection.h b/third-party/yoga/src/yoga/enums/FlexDirection.h new file mode 100644 index 00000000..d2d3f610 --- /dev/null +++ b/third-party/yoga/src/yoga/enums/FlexDirection.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class FlexDirection : uint8_t { + Column = YGFlexDirectionColumn, + ColumnReverse = YGFlexDirectionColumnReverse, + Row = YGFlexDirectionRow, + RowReverse = YGFlexDirectionRowReverse, +}; + +template <> +constexpr int32_t ordinalCount() { + return 4; +} + +constexpr FlexDirection scopedEnum(YGFlexDirection unscoped) { + return static_cast(unscoped); +} + +constexpr YGFlexDirection unscopedEnum(FlexDirection scoped) { + return static_cast(scoped); +} + +inline const char* toString(FlexDirection e) { + return YGFlexDirectionToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/GridTrackType.h b/third-party/yoga/src/yoga/enums/GridTrackType.h new file mode 100644 index 00000000..eb6e8332 --- /dev/null +++ b/third-party/yoga/src/yoga/enums/GridTrackType.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class GridTrackType : uint8_t { + Auto = YGGridTrackTypeAuto, + Points = YGGridTrackTypePoints, + Percent = YGGridTrackTypePercent, + Fr = YGGridTrackTypeFr, + Minmax = YGGridTrackTypeMinmax, +}; + +template <> +constexpr int32_t ordinalCount() { + return 5; +} + +constexpr GridTrackType scopedEnum(YGGridTrackType unscoped) { + return static_cast(unscoped); +} + +constexpr YGGridTrackType unscopedEnum(GridTrackType scoped) { + return static_cast(scoped); +} + +inline const char* toString(GridTrackType e) { + return YGGridTrackTypeToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/Gutter.h b/third-party/yoga/src/yoga/enums/Gutter.h new file mode 100644 index 00000000..f16bbbcd --- /dev/null +++ b/third-party/yoga/src/yoga/enums/Gutter.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class Gutter : uint8_t { + Column = YGGutterColumn, + Row = YGGutterRow, + All = YGGutterAll, +}; + +template <> +constexpr int32_t ordinalCount() { + return 3; +} + +constexpr Gutter scopedEnum(YGGutter unscoped) { + return static_cast(unscoped); +} + +constexpr YGGutter unscopedEnum(Gutter scoped) { + return static_cast(scoped); +} + +inline const char* toString(Gutter e) { + return YGGutterToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/Justify.h b/third-party/yoga/src/yoga/enums/Justify.h new file mode 100644 index 00000000..db4afece --- /dev/null +++ b/third-party/yoga/src/yoga/enums/Justify.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class Justify : uint8_t { + Auto = YGJustifyAuto, + FlexStart = YGJustifyFlexStart, + Center = YGJustifyCenter, + FlexEnd = YGJustifyFlexEnd, + SpaceBetween = YGJustifySpaceBetween, + SpaceAround = YGJustifySpaceAround, + SpaceEvenly = YGJustifySpaceEvenly, + Stretch = YGJustifyStretch, + Start = YGJustifyStart, + End = YGJustifyEnd, +}; + +template <> +constexpr int32_t ordinalCount() { + return 10; +} + +constexpr Justify scopedEnum(YGJustify unscoped) { + return static_cast(unscoped); +} + +constexpr YGJustify unscopedEnum(Justify scoped) { + return static_cast(scoped); +} + +inline const char* toString(Justify e) { + return YGJustifyToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/LogLevel.h b/third-party/yoga/src/yoga/enums/LogLevel.h new file mode 100644 index 00000000..d4b2c360 --- /dev/null +++ b/third-party/yoga/src/yoga/enums/LogLevel.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class LogLevel : uint8_t { + Error = YGLogLevelError, + Warn = YGLogLevelWarn, + Info = YGLogLevelInfo, + Debug = YGLogLevelDebug, + Verbose = YGLogLevelVerbose, + Fatal = YGLogLevelFatal, +}; + +template <> +constexpr int32_t ordinalCount() { + return 6; +} + +constexpr LogLevel scopedEnum(YGLogLevel unscoped) { + return static_cast(unscoped); +} + +constexpr YGLogLevel unscopedEnum(LogLevel scoped) { + return static_cast(scoped); +} + +inline const char* toString(LogLevel e) { + return YGLogLevelToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/MeasureMode.h b/third-party/yoga/src/yoga/enums/MeasureMode.h new file mode 100644 index 00000000..39e2102f --- /dev/null +++ b/third-party/yoga/src/yoga/enums/MeasureMode.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class MeasureMode : uint8_t { + Undefined = YGMeasureModeUndefined, + Exactly = YGMeasureModeExactly, + AtMost = YGMeasureModeAtMost, +}; + +template <> +constexpr int32_t ordinalCount() { + return 3; +} + +constexpr MeasureMode scopedEnum(YGMeasureMode unscoped) { + return static_cast(unscoped); +} + +constexpr YGMeasureMode unscopedEnum(MeasureMode scoped) { + return static_cast(scoped); +} + +inline const char* toString(MeasureMode e) { + return YGMeasureModeToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/NodeType.h b/third-party/yoga/src/yoga/enums/NodeType.h new file mode 100644 index 00000000..dc881be8 --- /dev/null +++ b/third-party/yoga/src/yoga/enums/NodeType.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class NodeType : uint8_t { + Default = YGNodeTypeDefault, + Text = YGNodeTypeText, +}; + +template <> +constexpr int32_t ordinalCount() { + return 2; +} + +constexpr NodeType scopedEnum(YGNodeType unscoped) { + return static_cast(unscoped); +} + +constexpr YGNodeType unscopedEnum(NodeType scoped) { + return static_cast(scoped); +} + +inline const char* toString(NodeType e) { + return YGNodeTypeToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/Overflow.h b/third-party/yoga/src/yoga/enums/Overflow.h new file mode 100644 index 00000000..20901b02 --- /dev/null +++ b/third-party/yoga/src/yoga/enums/Overflow.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class Overflow : uint8_t { + Visible = YGOverflowVisible, + Hidden = YGOverflowHidden, + Scroll = YGOverflowScroll, +}; + +template <> +constexpr int32_t ordinalCount() { + return 3; +} + +constexpr Overflow scopedEnum(YGOverflow unscoped) { + return static_cast(unscoped); +} + +constexpr YGOverflow unscopedEnum(Overflow scoped) { + return static_cast(scoped); +} + +inline const char* toString(Overflow e) { + return YGOverflowToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/PhysicalEdge.h b/third-party/yoga/src/yoga/enums/PhysicalEdge.h new file mode 100644 index 00000000..21e37d2c --- /dev/null +++ b/third-party/yoga/src/yoga/enums/PhysicalEdge.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook::yoga { + +enum class PhysicalEdge : uint32_t { + Left = yoga::to_underlying(Edge::Left), + Top = yoga::to_underlying(Edge::Top), + Right = yoga::to_underlying(Edge::Right), + Bottom = yoga::to_underlying(Edge::Bottom), +}; + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/PositionType.h b/third-party/yoga/src/yoga/enums/PositionType.h new file mode 100644 index 00000000..8a552aac --- /dev/null +++ b/third-party/yoga/src/yoga/enums/PositionType.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class PositionType : uint8_t { + Static = YGPositionTypeStatic, + Relative = YGPositionTypeRelative, + Absolute = YGPositionTypeAbsolute, +}; + +template <> +constexpr int32_t ordinalCount() { + return 3; +} + +constexpr PositionType scopedEnum(YGPositionType unscoped) { + return static_cast(unscoped); +} + +constexpr YGPositionType unscopedEnum(PositionType scoped) { + return static_cast(scoped); +} + +inline const char* toString(PositionType e) { + return YGPositionTypeToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/Unit.h b/third-party/yoga/src/yoga/enums/Unit.h new file mode 100644 index 00000000..685b1cae --- /dev/null +++ b/third-party/yoga/src/yoga/enums/Unit.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class Unit : uint8_t { + Undefined = YGUnitUndefined, + Point = YGUnitPoint, + Percent = YGUnitPercent, + Auto = YGUnitAuto, + MaxContent = YGUnitMaxContent, + FitContent = YGUnitFitContent, + Stretch = YGUnitStretch, +}; + +template <> +constexpr int32_t ordinalCount() { + return 7; +} + +constexpr Unit scopedEnum(YGUnit unscoped) { + return static_cast(unscoped); +} + +constexpr YGUnit unscopedEnum(Unit scoped) { + return static_cast(scoped); +} + +inline const char* toString(Unit e) { + return YGUnitToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/Wrap.h b/third-party/yoga/src/yoga/enums/Wrap.h new file mode 100644 index 00000000..96552b7b --- /dev/null +++ b/third-party/yoga/src/yoga/enums/Wrap.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +enum class Wrap : uint8_t { + NoWrap = YGWrapNoWrap, + Wrap = YGWrapWrap, + WrapReverse = YGWrapWrapReverse, +}; + +template <> +constexpr int32_t ordinalCount() { + return 3; +} + +constexpr Wrap scopedEnum(YGWrap unscoped) { + return static_cast(unscoped); +} + +constexpr YGWrap unscopedEnum(Wrap scoped) { + return static_cast(scoped); +} + +inline const char* toString(Wrap e) { + return YGWrapToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/enums/YogaEnums.h b/third-party/yoga/src/yoga/enums/YogaEnums.h new file mode 100644 index 00000000..521d9f7a --- /dev/null +++ b/third-party/yoga/src/yoga/enums/YogaEnums.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +/** + * Concept for any enum/enum class + */ +template +concept Enumeration = std::is_enum_v; + +/** + * Count of ordinals in a Yoga enum which is sequential + */ +template +constexpr int32_t ordinalCount(); + +/** + * Concept for a yoga enum which is sequential + */ +template +concept HasOrdinality = (ordinalCount() > 0); + +/** + * Count of bits needed to represent every ordinal + */ +template +constexpr int32_t bitCount() { + return std::bit_width( + static_cast>(ordinalCount() - 1)); +} + +/** + * Polyfill of C++ 23 to_underlying() + * https://en.cppreference.com/w/cpp/utility/to_underlying + */ +constexpr auto to_underlying(Enumeration auto e) noexcept { + return static_cast>(e); +} + +/** + * Convenience function to iterate through every value in a Yoga enum as part of + * a range-based for loop. + */ +template +auto ordinals() { + struct Iterator { + EnumT e{}; + + EnumT operator*() const { + return e; + } + + Iterator& operator++() { + e = static_cast(to_underlying(e) + 1); + return *this; + } + + bool operator==(const Iterator& other) const = default; + }; + + struct Range { + Iterator begin() const { + return Iterator{}; + } + Iterator end() const { + return Iterator{static_cast(ordinalCount())}; + } + }; + + return Range{}; +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/event/event.cpp b/third-party/yoga/src/yoga/event/event.cpp old mode 100755 new mode 100644 index 2b07e381..ac6735d6 --- a/third-party/yoga/src/yoga/event/event.cpp +++ b/third-party/yoga/src/yoga/event/event.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) Facebook, Inc. and its affiliates. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. @@ -8,10 +8,8 @@ #include "event.h" #include #include -#include -namespace facebook { -namespace yoga { +namespace facebook::yoga { const char* LayoutPassReasonToString(const LayoutPassReason value) { switch (value) { @@ -31,6 +29,8 @@ const char* LayoutPassReasonToString(const LayoutPassReason value) { return "abs_measure"; case LayoutPassReason::kFlexMeasure: return "flex_measure"; + case LayoutPassReason::kGridLayout: + return "grid_layout"; default: return "unknown"; } @@ -42,14 +42,14 @@ struct Node { std::function subscriber = nullptr; Node* next = nullptr; - Node(std::function&& subscriber) + explicit Node(std::function&& subscriber) : subscriber{std::move(subscriber)} {} }; std::atomic subscribers{nullptr}; Node* push(Node* newHead) { - Node* oldHead; + Node* oldHead = nullptr; do { oldHead = subscribers.load(std::memory_order_relaxed); if (newHead != nullptr) { @@ -75,7 +75,10 @@ void Event::subscribe(std::function&& subscriber) { push(new Node{std::move(subscriber)}); } -void Event::publish(const YGNode& node, Type eventType, const Data& eventData) { +void Event::publish( + YGNodeConstRef node, + Type eventType, + const Data& eventData) { for (auto subscriber = subscribers.load(std::memory_order_relaxed); subscriber != nullptr; subscriber = subscriber->next) { @@ -83,5 +86,4 @@ void Event::publish(const YGNode& node, Type eventType, const Data& eventData) { } } -} // namespace yoga -} // namespace facebook +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/event/event.h b/third-party/yoga/src/yoga/event/event.h old mode 100755 new mode 100644 index 309dacb5..0d032240 --- a/third-party/yoga/src/yoga/event/event.h +++ b/third-party/yoga/src/yoga/event/event.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Facebook, Inc. and its affiliates. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. @@ -7,16 +7,14 @@ #pragma once +#include + +#include +#include #include #include -#include -#include -struct YGConfig; -struct YGNode; - -namespace facebook { -namespace yoga { +namespace facebook::yoga { enum struct LayoutType : int { kLayout = 0, @@ -34,23 +32,24 @@ enum struct LayoutPassReason : int { kMeasureChild = 5, kAbsMeasureChild = 6, kFlexMeasure = 7, + kGridLayout = 8, COUNT }; struct LayoutData { - int layouts; - int measures; - int maxMeasureCache; - int cachedLayouts; - int cachedMeasures; - int measureCallbacks; + int layouts = 0; + int measures = 0; + uint32_t maxMeasureCache = 0; + int cachedLayouts = 0; + int cachedMeasures = 0; + int measureCallbacks = 0; std::array(LayoutPassReason::COUNT)> measureCallbackReasonsCount; }; -const char* LayoutPassReasonToString(const LayoutPassReason value); +const char* LayoutPassReasonToString(LayoutPassReason value); -struct YOGA_EXPORT Event { +struct YG_EXPORT Event { enum Type { NodeAllocation, NodeDeallocation, @@ -63,7 +62,7 @@ struct YOGA_EXPORT Event { NodeBaselineEnd, }; class Data; - using Subscriber = void(const YGNode&, Type, Data); + using Subscriber = void(YGNodeConstRef, Type, Data); using Subscribers = std::vector>; template @@ -72,14 +71,14 @@ struct YOGA_EXPORT Event { class Data { const void* data_; - public: + public: template - Data(const TypedData& data) : data_{&data} {} + explicit Data(const TypedData& data) : data_{&data} {} template const TypedData& get() const { return *static_cast*>(data_); - }; + } }; static void reset(); @@ -87,45 +86,34 @@ struct YOGA_EXPORT Event { static void subscribe(std::function&& subscriber); template - static void publish(const YGNode& node, const TypedData& eventData = {}) { -#ifdef YG_ENABLE_EVENTS + static void publish(YGNodeConstRef node, const TypedData& eventData = {}) { 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&); + private: + static void publish( + YGNodeConstRef /*node*/, + Type /*eventType*/, + const Data& /*eventData*/); }; template <> struct Event::TypedData { - YGConfig* config; + YGConfigConstRef config; }; template <> struct Event::TypedData { - YGConfig* config; -}; - -template <> -struct Event::TypedData { - void* layoutContext; + YGConfigConstRef config; }; template <> struct Event::TypedData { - void* layoutContext; LayoutData* layoutData; }; template <> struct Event::TypedData { - void* layoutContext; float width; YGMeasureMode widthMeasureMode; float height; @@ -138,8 +126,6 @@ struct Event::TypedData { template <> struct Event::TypedData { LayoutType layoutType; - void* layoutContext; }; -} // namespace yoga -} // namespace facebook +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/internal/experiments-inl.h b/third-party/yoga/src/yoga/internal/experiments-inl.h deleted file mode 100755 index 959d9c33..00000000 --- a/third-party/yoga/src/yoga/internal/experiments-inl.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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/third-party/yoga/src/yoga/internal/experiments.cpp b/third-party/yoga/src/yoga/internal/experiments.cpp deleted file mode 100755 index 16f196d5..00000000 --- a/third-party/yoga/src/yoga/internal/experiments.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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/third-party/yoga/src/yoga/internal/experiments.h b/third-party/yoga/src/yoga/internal/experiments.h deleted file mode 100755 index 1bdb7014..00000000 --- a/third-party/yoga/src/yoga/internal/experiments.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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/third-party/yoga/src/yoga/log.cpp b/third-party/yoga/src/yoga/log.cpp deleted file mode 100755 index fe6fbbc6..00000000 --- a/third-party/yoga/src/yoga/log.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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/third-party/yoga/src/yoga/log.h b/third-party/yoga/src/yoga/log.h deleted file mode 100755 index ae33744c..00000000 --- a/third-party/yoga/src/yoga/log.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 diff --git a/third-party/yoga/src/yoga/module.modulemap b/third-party/yoga/src/yoga/module.modulemap new file mode 100644 index 00000000..6a0f4603 --- /dev/null +++ b/third-party/yoga/src/yoga/module.modulemap @@ -0,0 +1,21 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module yoga [system] { + module core { + header "YGConfig.h" + header "YGEnums.h" + header "YGMacros.h" + header "YGNode.h" + header "YGNodeLayout.h" + header "YGNodeStyle.h" + header "YGPixelGrid.h" + header "YGValue.h" + header "Yoga.h" + export * + } +} diff --git a/third-party/yoga/src/yoga/node/CachedMeasurement.h b/third-party/yoga/src/yoga/node/CachedMeasurement.h new file mode 100644 index 00000000..9c76280a --- /dev/null +++ b/third-party/yoga/src/yoga/node/CachedMeasurement.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include + +#include +#include + +namespace facebook::yoga { + +struct CachedMeasurement { + float availableWidth{-1}; + float availableHeight{-1}; + SizingMode widthSizingMode{SizingMode::MaxContent}; + SizingMode heightSizingMode{SizingMode::MaxContent}; + + float computedWidth{-1}; + float computedHeight{-1}; + + bool operator==(CachedMeasurement measurement) const { + bool isEqual = widthSizingMode == measurement.widthSizingMode && + heightSizingMode == measurement.heightSizingMode; + + 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; + } +}; + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/node/LayoutResults.cpp b/third-party/yoga/src/yoga/node/LayoutResults.cpp new file mode 100644 index 00000000..acc37a1b --- /dev/null +++ b/third-party/yoga/src/yoga/node/LayoutResults.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include +#include + +namespace facebook::yoga { + +bool LayoutResults::operator==(LayoutResults layout) const { + bool isEqual = yoga::inexactEquals(position_, layout.position_) && + yoga::inexactEquals(dimensions_, layout.dimensions_) && + yoga::inexactEquals(margin_, layout.margin_) && + yoga::inexactEquals(border_, layout.border_) && + yoga::inexactEquals(padding_, layout.padding_) && + direction() == layout.direction() && + hadOverflow() == layout.hadOverflow() && + lastOwnerDirection == layout.lastOwnerDirection && + configVersion == layout.configVersion && + nextCachedMeasurementsIndex == layout.nextCachedMeasurementsIndex && + cachedLayout == layout.cachedLayout && + computedFlexBasis == layout.computedFlexBasis; + + for (uint32_t i = 0; i < LayoutResults::MaxCachedMeasurements && 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; +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/node/LayoutResults.h b/third-party/yoga/src/yoga/node/LayoutResults.h new file mode 100644 index 00000000..833dc927 --- /dev/null +++ b/third-party/yoga/src/yoga/node/LayoutResults.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace facebook::yoga { + +struct LayoutResults { + // This value was chosen based on empirical data: + // 98% of analyzed layouts require less than 8 entries. + static constexpr int32_t MaxCachedMeasurements = 8; + + uint32_t computedFlexBasisGeneration = 0; + FloatOptional computedFlexBasis = {}; + + // Instead of recomputing the entire layout every single time, we cache some + // information to break early when nothing changed + uint32_t generationCount = 0; + uint32_t configVersion = 0; + Direction lastOwnerDirection = Direction::Inherit; + + uint32_t nextCachedMeasurementsIndex = 0; + std::array cachedMeasurements = {}; + + CachedMeasurement cachedLayout{}; + + Direction direction() const { + return direction_; + } + + void setDirection(Direction direction) { + direction_ = direction; + } + + bool hadOverflow() const { + return hadOverflow_; + } + + void setHadOverflow(bool hadOverflow) { + hadOverflow_ = hadOverflow; + } + + float dimension(Dimension axis) const { + return dimensions_[yoga::to_underlying(axis)]; + } + + void setDimension(Dimension axis, float dimension) { + dimensions_[yoga::to_underlying(axis)] = dimension; + } + + float measuredDimension(Dimension axis) const { + return measuredDimensions_[yoga::to_underlying(axis)]; + } + + float rawDimension(Dimension axis) const { + return rawDimensions_[yoga::to_underlying(axis)]; + } + + void setMeasuredDimension(Dimension axis, float dimension) { + measuredDimensions_[yoga::to_underlying(axis)] = dimension; + } + + void setRawDimension(Dimension axis, float dimension) { + rawDimensions_[yoga::to_underlying(axis)] = dimension; + } + + float position(PhysicalEdge physicalEdge) const { + return position_[yoga::to_underlying(physicalEdge)]; + } + + void setPosition(PhysicalEdge physicalEdge, float dimension) { + position_[yoga::to_underlying(physicalEdge)] = dimension; + } + + float margin(PhysicalEdge physicalEdge) const { + return margin_[yoga::to_underlying(physicalEdge)]; + } + + void setMargin(PhysicalEdge physicalEdge, float dimension) { + margin_[yoga::to_underlying(physicalEdge)] = dimension; + } + + float border(PhysicalEdge physicalEdge) const { + return border_[yoga::to_underlying(physicalEdge)]; + } + + void setBorder(PhysicalEdge physicalEdge, float dimension) { + border_[yoga::to_underlying(physicalEdge)] = dimension; + } + + float padding(PhysicalEdge physicalEdge) const { + return padding_[yoga::to_underlying(physicalEdge)]; + } + + void setPadding(PhysicalEdge physicalEdge, float dimension) { + padding_[yoga::to_underlying(physicalEdge)] = dimension; + } + + bool operator==(LayoutResults layout) const; + + private: + Direction direction_ : bitCount() = Direction::Inherit; + bool hadOverflow_ : 1 = false; + + std::array dimensions_ = {{YGUndefined, YGUndefined}}; + std::array measuredDimensions_ = {{YGUndefined, YGUndefined}}; + std::array rawDimensions_ = {{YGUndefined, YGUndefined}}; + std::array position_ = {}; + std::array margin_ = {}; + std::array border_ = {}; + std::array padding_ = {}; +}; + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/node/LayoutableChildren.h b/third-party/yoga/src/yoga/node/LayoutableChildren.h new file mode 100644 index 00000000..b158a6a6 --- /dev/null +++ b/third-party/yoga/src/yoga/node/LayoutableChildren.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +#include + +namespace facebook::yoga { + +class Node; + +template +class LayoutableChildren { + public: + struct Iterator { + using iterator_category = std::input_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = T*; + using pointer = T*; + using reference = T*; + + Iterator() = default; + + Iterator(const T* node, size_t childIndex) + : node_(node), childIndex_(childIndex) {} + + T* operator*() const { + return node_->getChild(childIndex_); + } + + Iterator& operator++() { + next(); + return *this; + } + + Iterator operator++(int) { + Iterator tmp = *this; + ++(*this); + return tmp; + } + + friend bool operator==(const Iterator& a, const Iterator& b) { + return a.node_ == b.node_ && a.childIndex_ == b.childIndex_; + } + + friend bool operator!=(const Iterator& a, const Iterator& b) { + return a.node_ != b.node_ || a.childIndex_ != b.childIndex_; + } + + private: + void next() { + if (childIndex_ + 1 >= node_->getChildCount()) { + // if the current node has no more children, try to backtrack and + // visit its successor + if (backtrack_.empty()) [[likely]] { + // if there are no nodes to backtrack to, the last node has been + // visited + *this = Iterator{}; + } else { + // pop and restore the latest backtrack entry + const auto& back = backtrack_.front(); + node_ = back.first; + childIndex_ = back.second; + backtrack_.pop_front(); + + // go to the next node + next(); + } + } else { + // current node has more children to visit, go to next + ++childIndex_; + // skip all display: contents nodes, possibly going deeper into the + // tree + if (node_->getChild(childIndex_)->style().display() == + Display::Contents) [[unlikely]] { + skipContentsNodes(); + } + } + } + + void skipContentsNodes() { + // get the node that would be returned from the iterator + auto currentNode = node_->getChild(childIndex_); + while (currentNode->style().display() == Display::Contents && + currentNode->getChildCount() > 0) { + // if it has display: contents set, it shouldn't be returned but its + // children should in its place push the current node and child index + // so that the current state can be restored when backtracking + backtrack_.push_front({node_, childIndex_}); + // traverse the child + node_ = currentNode; + childIndex_ = 0; + + // repeat until a node without display: contents is found in the + // subtree or a leaf is reached + currentNode = currentNode->getChild(childIndex_); + } + + // if no node without display: contents was found, try to backtrack + if (currentNode->style().display() == Display::Contents) { + next(); + } + } + + const T* node_{nullptr}; + size_t childIndex_{0}; + std::forward_list> backtrack_; + + friend LayoutableChildren; + }; + + explicit LayoutableChildren(const T* node) : node_(node) { + static_assert(std::input_iterator::Iterator>); + static_assert( + std::is_base_of::value, + "Type parameter of LayoutableChildren must derive from yoga::Node"); + } + + Iterator begin() const { + if (node_->getChildCount() > 0) { + auto result = Iterator(node_, 0); + if (node_->getChild(0)->style().display() == Display::Contents) + [[unlikely]] { + result.skipContentsNodes(); + } + return result; + } else { + return Iterator{}; + } + } + + Iterator end() const { + return Iterator{}; + } + + private: + const T* node_; +}; + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/node/Node.cpp b/third-party/yoga/src/yoga/node/Node.cpp new file mode 100644 index 00000000..d42bd5f9 --- /dev/null +++ b/third-party/yoga/src/yoga/node/Node.cpp @@ -0,0 +1,472 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace facebook::yoga { + +Node::Node() : Node{&Config::getDefault()} {} + +Node::Node(const yoga::Config* config) : config_{config} { + yoga::assertFatal( + config != nullptr, "Attempting to construct Node with null config"); + + if (config->useWebDefaults()) { + useWebDefaults(); + } +} + +Node::Node(Node&& node) noexcept + : hasNewLayout_(node.hasNewLayout_), + isReferenceBaseline_(node.isReferenceBaseline_), + isDirty_(node.isDirty_), + alwaysFormsContainingBlock_(node.alwaysFormsContainingBlock_), + nodeType_(node.nodeType_), + context_(node.context_), + measureFunc_(node.measureFunc_), + baselineFunc_(node.baselineFunc_), + dirtiedFunc_(node.dirtiedFunc_), + style_(std::move(node.style_)), + layout_(node.layout_), + lineIndex_(node.lineIndex_), + contentsChildrenCount_(node.contentsChildrenCount_), + owner_(node.owner_), + children_(std::move(node.children_)), + config_(node.config_), + processedDimensions_(node.processedDimensions_) { + for (auto c : children_) { + c->setOwner(this); + } +} + +YGSize Node::measure( + float availableWidth, + MeasureMode widthMode, + float availableHeight, + MeasureMode heightMode) { + const auto size = measureFunc_( + this, + availableWidth, + unscopedEnum(widthMode), + availableHeight, + unscopedEnum(heightMode)); + + if (yoga::isUndefined(size.height) || size.height < 0 || + yoga::isUndefined(size.width) || size.width < 0) { + yoga::log( + this, + LogLevel::Warn, + "Measure function returned an invalid dimension to Yoga: [width=%f, height=%f]", + size.width, + size.height); + return { + .width = maxOrDefined(0.0f, size.width), + .height = maxOrDefined(0.0f, size.height)}; + } + + return size; +} + +float Node::baseline(float width, float height) const { + return baselineFunc_(this, width, height); +} + +float Node::dimensionWithMargin( + const FlexDirection axis, + const float widthSize) { + return getLayout().measuredDimension(dimension(axis)) + + style_.computeMarginForAxis(axis, widthSize); +} + +bool Node::isLayoutDimensionDefined(const FlexDirection axis) { + const float value = getLayout().measuredDimension(dimension(axis)); + return yoga::isDefined(value) && value >= 0.0f; +} + +// Setters + +void Node::setMeasureFunc(YGMeasureFunc measureFunc) { + if (measureFunc == nullptr) { + // TODO: t18095186 Move nodeType to opt-in function and mark appropriate + // places in Litho + setNodeType(NodeType::Default); + } else { + yoga::assertFatalWithNode( + this, + children_.empty(), + "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(NodeType::Text); + } + + measureFunc_ = measureFunc; +} + +void Node::replaceChild(Node* child, size_t index) { + auto previousChild = children_[index]; + if (previousChild->style().display() == Display::Contents && + child->style().display() != Display::Contents) { + contentsChildrenCount_--; + } else if ( + previousChild->style().display() != Display::Contents && + child->style().display() == Display::Contents) { + contentsChildrenCount_++; + } + + children_[index] = child; +} + +void Node::replaceChild(Node* oldChild, Node* newChild) { + if (oldChild->style().display() == Display::Contents && + newChild->style().display() != Display::Contents) { + contentsChildrenCount_--; + } else if ( + oldChild->style().display() != Display::Contents && + newChild->style().display() == Display::Contents) { + contentsChildrenCount_++; + } + + std::replace(children_.begin(), children_.end(), oldChild, newChild); +} + +void Node::insertChild(Node* child, size_t index) { + if (child->style().display() == Display::Contents) { + contentsChildrenCount_++; + } + + children_.insert(children_.begin() + static_cast(index), child); +} + +void Node::setConfig(yoga::Config* config) { + yoga::assertFatal( + config != nullptr, "Attempting to set a null config on a Node"); + yoga::assertFatalWithConfig( + config, + config->useWebDefaults() == config_->useWebDefaults(), + "UseWebDefaults may not be changed after constructing a Node"); + + if (yoga::configUpdateInvalidatesLayout(*config_, *config)) { + markDirtyAndPropagate(); + layout_.configVersion = 0; + } else { + // If the config is functionally the same, then align the configVersion so + // that we can reuse the layout cache + layout_.configVersion = config->getVersion(); + } + + config_ = config; +} + +void Node::setDirty(bool isDirty) { + if (static_cast(isDirty) == isDirty_) { + return; + } + isDirty_ = isDirty; + if (isDirty && (dirtiedFunc_ != nullptr)) { + dirtiedFunc_(this); + } +} + +void Node::setChildren(const std::vector& children) { + children_ = children; + + contentsChildrenCount_ = 0; + for (const auto& child : children) { + if (child->style().display() == Display::Contents) { + contentsChildrenCount_++; + } + } +} + +bool Node::removeChild(Node* child) { + auto p = std::find(children_.begin(), children_.end(), child); + if (p != children_.end()) { + if (child->style().display() == Display::Contents) { + contentsChildrenCount_--; + } + + children_.erase(p); + return true; + } + return false; +} + +void Node::removeChild(size_t index) { + if (children_[index]->style().display() == Display::Contents) { + contentsChildrenCount_--; + } + + children_.erase(children_.begin() + static_cast(index)); +} + +void Node::setLayoutDirection(Direction direction) { + layout_.setDirection(direction); +} + +void Node::setLayoutMargin(float margin, PhysicalEdge edge) { + layout_.setMargin(edge, margin); +} + +void Node::setLayoutBorder(float border, PhysicalEdge edge) { + layout_.setBorder(edge, border); +} + +void Node::setLayoutPadding(float padding, PhysicalEdge edge) { + layout_.setPadding(edge, padding); +} + +void Node::setLayoutLastOwnerDirection(Direction direction) { + layout_.lastOwnerDirection = direction; +} + +void Node::setLayoutComputedFlexBasis(const FloatOptional computedFlexBasis) { + layout_.computedFlexBasis = computedFlexBasis; +} + +void Node::setLayoutPosition(float position, PhysicalEdge edge) { + layout_.setPosition(edge, position); +} + +void Node::setLayoutComputedFlexBasisGeneration( + uint32_t computedFlexBasisGeneration) { + layout_.computedFlexBasisGeneration = computedFlexBasisGeneration; +} + +void Node::setLayoutMeasuredDimension( + float measuredDimension, + Dimension dimension) { + layout_.setMeasuredDimension(dimension, measuredDimension); +} + +void Node::setLayoutHadOverflow(bool hadOverflow) { + layout_.setHadOverflow(hadOverflow); +} + +void Node::setLayoutDimension(float lengthValue, Dimension dimension) { + layout_.setDimension(dimension, lengthValue); + layout_.setRawDimension(dimension, lengthValue); +} + +// If both left and right are defined, then use left. Otherwise return +left or +// -right depending on which is defined. Ignore statically positioned nodes as +// insets do not apply to them. +float Node::relativePosition( + FlexDirection axis, + Direction direction, + float axisSize) const { + if (style_.positionType() == PositionType::Static) { + return 0; + } + if (style_.isInlineStartPositionDefined(axis, direction) && + !style_.isInlineStartPositionAuto(axis, direction)) { + return style_.computeInlineStartPosition(axis, direction, axisSize); + } + + return -1 * style_.computeInlineEndPosition(axis, direction, axisSize); +} + +void Node::setPosition( + const Direction direction, + const float ownerWidth, + const float ownerHeight) { + /* Root nodes should be always layouted as LTR, so we don't return negative + * values. */ + const Direction directionRespectingRoot = + owner_ != nullptr ? direction : Direction::LTR; + const FlexDirection mainAxis = + yoga::resolveDirection(style_.flexDirection(), directionRespectingRoot); + const FlexDirection crossAxis = + yoga::resolveCrossDirection(mainAxis, directionRespectingRoot); + + // In the case of position static these are just 0. See: + // https://www.w3.org/TR/css-position-3/#valdef-position-static + const float relativePositionMain = relativePosition( + mainAxis, + directionRespectingRoot, + isRow(mainAxis) ? ownerWidth : ownerHeight); + const float relativePositionCross = relativePosition( + crossAxis, + directionRespectingRoot, + isRow(mainAxis) ? ownerHeight : ownerWidth); + + const auto mainAxisLeadingEdge = inlineStartEdge(mainAxis, direction); + const auto mainAxisTrailingEdge = inlineEndEdge(mainAxis, direction); + const auto crossAxisLeadingEdge = inlineStartEdge(crossAxis, direction); + const auto crossAxisTrailingEdge = inlineEndEdge(crossAxis, direction); + + setLayoutPosition( + (style_.computeInlineStartMargin(mainAxis, direction, ownerWidth) + + relativePositionMain), + mainAxisLeadingEdge); + setLayoutPosition( + (style_.computeInlineEndMargin(mainAxis, direction, ownerWidth) + + relativePositionMain), + mainAxisTrailingEdge); + setLayoutPosition( + (style_.computeInlineStartMargin(crossAxis, direction, ownerWidth) + + relativePositionCross), + crossAxisLeadingEdge); + setLayoutPosition( + (style_.computeInlineEndMargin(crossAxis, direction, ownerWidth) + + relativePositionCross), + crossAxisTrailingEdge); +} + +Style::SizeLength Node::processFlexBasis() const { + Style::SizeLength flexBasis = style_.flexBasis(); + if (!flexBasis.isAuto() && !flexBasis.isUndefined()) { + return flexBasis; + } + if (style_.flex().isDefined() && style_.flex().unwrap() > 0.0f) { + return config_->useWebDefaults() ? StyleSizeLength::ofAuto() + : StyleSizeLength::points(0); + } + return StyleSizeLength::ofAuto(); +} + +FloatOptional Node::resolveFlexBasis( + Direction direction, + FlexDirection flexDirection, + float referenceLength, + float ownerWidth) const { + FloatOptional value = processFlexBasis().resolve(referenceLength); + if (style_.boxSizing() == BoxSizing::BorderBox) { + return value; + } + + Dimension dim = dimension(flexDirection); + FloatOptional dimensionPaddingAndBorder = FloatOptional{ + style_.computePaddingAndBorderForDimension(direction, dim, ownerWidth)}; + + return value + + (dimensionPaddingAndBorder.isDefined() ? dimensionPaddingAndBorder + : FloatOptional{0.0}); +} + +void Node::processDimensions() { + for (auto dim : {Dimension::Width, Dimension::Height}) { + if (style_.maxDimension(dim).isDefined() && + yoga::inexactEquals( + style_.maxDimension(dim), style_.minDimension(dim))) { + processedDimensions_[yoga::to_underlying(dim)] = style_.maxDimension(dim); + } else { + processedDimensions_[yoga::to_underlying(dim)] = style_.dimension(dim); + } + } +} + +Direction Node::resolveDirection(const Direction ownerDirection) { + if (style_.direction() == Direction::Inherit) { + return ownerDirection != Direction::Inherit ? ownerDirection + : Direction::LTR; + } else { + return style_.direction(); + } +} + +void Node::clearChildren() { + children_.clear(); + children_.shrink_to_fit(); +} + +// Other Methods + +void Node::cloneChildrenIfNeeded() { + size_t i = 0; + for (Node*& child : children_) { + if (child->getOwner() != this) { + child = resolveRef(config_->cloneNode(child, this, i)); + child->setOwner(this); + + if (child->hasContentsChildren()) [[unlikely]] { + child->cloneContentsChildrenIfNeeded(); + } + } + i += 1; + } +} + +void Node::cloneContentsChildrenIfNeeded() { + size_t i = 0; + for (Node*& child : children_) { + if (child->style().display() == Display::Contents && + child->getOwner() != this) { + child = resolveRef(config_->cloneNode(child, this, i)); + child->setOwner(this); + child->cloneChildrenIfNeeded(); + } + i += 1; + } +} + +void Node::markDirtyAndPropagate() { + if (!isDirty_) { + setDirty(true); + setLayoutComputedFlexBasis(FloatOptional()); + if (owner_ != nullptr) { + owner_->markDirtyAndPropagate(); + } + } +} + +float Node::resolveFlexGrow() const { + // Root nodes flexGrow should always be 0 + if (owner_ == nullptr) { + return 0.0; + } + if (style_.flexGrow().isDefined()) { + return style_.flexGrow().unwrap(); + } + if (style_.flex().isDefined() && style_.flex().unwrap() > 0.0f) { + return style_.flex().unwrap(); + } + return Style::DefaultFlexGrow; +} + +float Node::resolveFlexShrink() const { + if (owner_ == nullptr) { + return 0.0; + } + if (style_.flexShrink().isDefined()) { + return style_.flexShrink().unwrap(); + } + if (!config_->useWebDefaults() && style_.flex().isDefined() && + style_.flex().unwrap() < 0.0f) { + return -style_.flex().unwrap(); + } + return config_->useWebDefaults() ? Style::WebDefaultFlexShrink + : Style::DefaultFlexShrink; +} + +bool Node::isNodeFlexible() { + return ( + (style_.positionType() != PositionType::Absolute) && + (resolveFlexGrow() != 0 || resolveFlexShrink() != 0)); +} + +void Node::reset() { + yoga::assertFatalWithNode( + this, + children_.empty(), + "Cannot reset a node which still has children attached"); + yoga::assertFatalWithNode( + this, owner_ == nullptr, "Cannot reset a node still attached to a owner"); + + *this = Node{getConfig()}; +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/node/Node.h b/third-party/yoga/src/yoga/node/Node.h new file mode 100644 index 00000000..d22c4c1e --- /dev/null +++ b/third-party/yoga/src/yoga/node/Node.h @@ -0,0 +1,339 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Tag struct used to form the opaque YGNodeRef for the public C API +struct YGNode {}; + +namespace facebook::yoga { + +class YG_EXPORT Node : public ::YGNode { + public: + using LayoutableChildren = yoga::LayoutableChildren; + Node(); + explicit Node(const Config* config); + + Node(Node&& node) noexcept; + + // Does not expose true value semantics, as children are not cloned eagerly. + // Should we remove this? + Node(const Node& node) = default; + + // assignment means potential leaks of existing children, or alternatively + // freeing unowned memory, double free, or freeing stack memory. + Node& operator=(const Node&) = delete; + + // Getters + void* getContext() const { + return context_; + } + + bool alwaysFormsContainingBlock() const { + return alwaysFormsContainingBlock_; + } + + bool getHasNewLayout() const { + return hasNewLayout_; + } + + NodeType getNodeType() const { + return nodeType_; + } + + bool hasMeasureFunc() const noexcept { + return measureFunc_ != nullptr; + } + + YGSize measure( + float availableWidth, + MeasureMode widthMode, + float availableHeight, + MeasureMode heightMode); + + bool hasBaselineFunc() const noexcept { + return baselineFunc_ != nullptr; + } + + float baseline(float width, float height) const; + + float dimensionWithMargin(FlexDirection axis, float widthSize); + + bool isLayoutDimensionDefined(FlexDirection axis); + + /** + * Whether the node has a "definite length" along the given axis. + * https://www.w3.org/TR/css-sizing-3/#definite + */ + inline bool hasDefiniteLength(Dimension dimension, float ownerSize) { + auto usedValue = getProcessedDimension(dimension).resolve(ownerSize); + return usedValue.isDefined() && usedValue.unwrap() >= 0.0f; + } + + bool hasErrata(Errata errata) const { + return config_->hasErrata(errata); + } + + bool hasContentsChildren() const { + return contentsChildrenCount_ != 0; + } + + YGDirtiedFunc getDirtiedFunc() const { + return dirtiedFunc_; + } + + // For Performance reasons passing as reference. + Style& style() { + return style_; + } + + const Style& style() const { + return style_; + } + + // For Performance reasons passing as reference. + LayoutResults& getLayout() { + return layout_; + } + + const LayoutResults& getLayout() const { + return layout_; + } + + size_t getLineIndex() const { + return lineIndex_; + } + + bool isReferenceBaseline() const { + return isReferenceBaseline_; + } + + // returns the Node that owns this Node. An owner is used to identify + // the YogaTree that a Node belongs to. This method will return the parent + // of the Node when a Node only belongs to one YogaTree or nullptr when + // the Node is shared between two or more YogaTrees. + Node* getOwner() const { + return owner_; + } + + const std::vector& getChildren() const { + return children_; + } + + Node* getChild(size_t index) const { + return children_.at(index); + } + + size_t getChildCount() const { + return children_.size(); + } + + LayoutableChildren getLayoutChildren() const { + return LayoutableChildren(this); + } + + size_t getLayoutChildCount() const { + if (contentsChildrenCount_ == 0) { + return children_.size(); + } else { + size_t count = 0; + for (auto iter = getLayoutChildren().begin(); + iter != getLayoutChildren().end(); + iter++) { + count++; + } + return count; + } + } + + const Config* getConfig() const { + return config_; + } + + bool isDirty() const { + return isDirty_; + } + + Style::SizeLength getProcessedDimension(Dimension dimension) const { + return processedDimensions_[static_cast(dimension)]; + } + + FloatOptional getResolvedDimension( + Direction direction, + Dimension dimension, + float referenceLength, + float ownerWidth) const { + FloatOptional value = + getProcessedDimension(dimension).resolve(referenceLength); + if (style_.boxSizing() == BoxSizing::BorderBox) { + return value; + } + + FloatOptional dimensionPaddingAndBorder = + FloatOptional{style_.computePaddingAndBorderForDimension( + direction, dimension, ownerWidth)}; + + return value + + (dimensionPaddingAndBorder.isDefined() ? dimensionPaddingAndBorder + : FloatOptional{0.0}); + } + + // Setters + + void setContext(void* context) { + context_ = context; + } + + void setAlwaysFormsContainingBlock(bool alwaysFormsContainingBlock) { + alwaysFormsContainingBlock_ = alwaysFormsContainingBlock; + } + + void setHasNewLayout(bool hasNewLayout) { + hasNewLayout_ = hasNewLayout; + } + + void setNodeType(NodeType nodeType) { + nodeType_ = nodeType; + } + + void setMeasureFunc(YGMeasureFunc measureFunc); + + void setBaselineFunc(YGBaselineFunc baseLineFunc) { + baselineFunc_ = baseLineFunc; + } + + void setDirtiedFunc(YGDirtiedFunc dirtiedFunc) { + dirtiedFunc_ = dirtiedFunc; + } + + void setStyle(const Style& style) { + style_ = style; + } + + void setLayout(const LayoutResults& layout) { + layout_ = layout; + } + + void setLineIndex(size_t lineIndex) { + lineIndex_ = lineIndex; + } + + void setIsReferenceBaseline(bool isReferenceBaseline) { + isReferenceBaseline_ = isReferenceBaseline; + } + + void setOwner(Node* owner) { + owner_ = owner; + } + + // TODO: rvalue override for setChildren + + void setConfig(Config* config); + + void setDirty(bool isDirty); + void setChildren(const std::vector& children); + void setLayoutLastOwnerDirection(Direction direction); + void setLayoutComputedFlexBasis(FloatOptional computedFlexBasis); + void setLayoutComputedFlexBasisGeneration( + uint32_t computedFlexBasisGeneration); + void setLayoutMeasuredDimension(float measuredDimension, Dimension dimension); + void setLayoutHadOverflow(bool hadOverflow); + void setLayoutDimension(float lengthValue, Dimension dimension); + void setLayoutDirection(Direction direction); + void setLayoutMargin(float margin, PhysicalEdge edge); + void setLayoutBorder(float border, PhysicalEdge edge); + void setLayoutPadding(float padding, PhysicalEdge edge); + void setLayoutPosition(float position, PhysicalEdge edge); + void setPosition(Direction direction, float ownerWidth, float ownerHeight); + + // Other methods + Style::SizeLength processFlexBasis() const; + FloatOptional resolveFlexBasis( + Direction direction, + FlexDirection flexDirection, + float referenceLength, + float ownerWidth) const; + void processDimensions(); + Direction resolveDirection(Direction ownerDirection); + void clearChildren(); + /// Replaces the occurrences of oldChild with newChild + void replaceChild(Node* oldChild, Node* newChild); + void replaceChild(Node* child, size_t index); + void insertChild(Node* child, size_t index); + /// Removes the first occurrence of child + bool removeChild(Node* child); + void removeChild(size_t index); + + void cloneChildrenIfNeeded(); + void cloneContentsChildrenIfNeeded(); + void markDirtyAndPropagate(); + float resolveFlexGrow() const; + float resolveFlexShrink() const; + bool isNodeFlexible(); + void reset(); + + float relativePosition( + FlexDirection axis, + Direction direction, + float axisSize) const; + + private: + // Used to allow resetting the node + Node& operator=(Node&&) noexcept = default; + + void useWebDefaults() { + style_.setFlexDirection(FlexDirection::Row); + style_.setAlignContent(Align::Stretch); + } + + bool hasNewLayout_ : 1 = true; + bool isReferenceBaseline_ : 1 = false; + bool isDirty_ : 1 = true; + bool alwaysFormsContainingBlock_ : 1 = false; + NodeType nodeType_ : bitCount() = NodeType::Default; + void* context_ = nullptr; + YGMeasureFunc measureFunc_ = nullptr; + YGBaselineFunc baselineFunc_ = nullptr; + YGDirtiedFunc dirtiedFunc_ = nullptr; + Style style_; + LayoutResults layout_; + size_t lineIndex_ = 0; + size_t contentsChildrenCount_ = 0; + Node* owner_ = nullptr; + std::vector children_; + const Config* config_; + std::array processedDimensions_{ + {StyleSizeLength::undefined(), StyleSizeLength::undefined()}}; +}; + +inline Node* resolveRef(const YGNodeRef ref) { + return static_cast(ref); +} + +inline const Node* resolveRef(const YGNodeConstRef ref) { + return static_cast(ref); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/numeric/Comparison.h b/third-party/yoga/src/yoga/numeric/Comparison.h new file mode 100644 index 00000000..0e23a837 --- /dev/null +++ b/third-party/yoga/src/yoga/numeric/Comparison.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace facebook::yoga { + +constexpr bool isUndefined(std::floating_point auto value) { + return value != value; +} + +constexpr bool isDefined(std::floating_point auto value) { + return !isUndefined(value); +} + +/** + * Constexpr version of `std::isinf` before C++ 23 + */ +constexpr bool isinf(auto value) { + return value == +std::numeric_limits::infinity() || + value == -std::numeric_limits::infinity(); +} + +constexpr auto maxOrDefined( + std::floating_point auto a, + std::floating_point auto b) { + if (yoga::isDefined(a) && yoga::isDefined(b)) { + return std::max(a, b); + } + return yoga::isUndefined(a) ? b : a; +} + +constexpr auto minOrDefined( + std::floating_point auto a, + std::floating_point auto b) { + if (yoga::isDefined(a) && yoga::isDefined(b)) { + return std::min(a, b); + } + + return yoga::isUndefined(a) ? b : a; +} + +// Custom equality functions using a hardcoded epsilon of 0.0001f, or returning +// true if both floats are NaN. +inline bool inexactEquals(float a, float b) { + if (yoga::isDefined(a) && yoga::isDefined(b)) { + return std::abs(a - b) < 0.0001f; + } + return yoga::isUndefined(a) && yoga::isUndefined(b); +} + +inline bool inexactEquals(double a, double b) { + if (yoga::isDefined(a) && yoga::isDefined(b)) { + return std::abs(a - b) < 0.0001; + } + return yoga::isUndefined(a) && yoga::isUndefined(b); +} + +template +bool inexactEquals( + const std::array& val1, + const std::array& val2) { + bool areEqual = true; + for (std::size_t i = 0; i < Size && areEqual; ++i) { + areEqual = inexactEquals(val1[i], val2[i]); + } + return areEqual; +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/numeric/FloatOptional.h b/third-party/yoga/src/yoga/numeric/FloatOptional.h new file mode 100644 index 00000000..c255277a --- /dev/null +++ b/third-party/yoga/src/yoga/numeric/FloatOptional.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook::yoga { + +struct FloatOptional { + private: + float value_ = std::numeric_limits::quiet_NaN(); + + public: + explicit constexpr FloatOptional(float value) : value_(value) {} + constexpr FloatOptional() = default; + + // returns the wrapped value, or a value x with YGIsUndefined(x) == true + constexpr float unwrap() const { + return value_; + } + + constexpr float unwrapOrDefault(float defaultValue) const { + return isUndefined() ? defaultValue : value_; + } + + constexpr bool isUndefined() const { + return yoga::isUndefined(value_); + } + + constexpr bool isDefined() const { + return yoga::isDefined(value_); + } +}; + +// operators take FloatOptional by value, as it is a 32bit value + +constexpr bool operator==(FloatOptional lhs, FloatOptional rhs) { + return lhs.unwrap() == rhs.unwrap() || + (lhs.isUndefined() && rhs.isUndefined()); +} + +constexpr bool operator==(FloatOptional lhs, float rhs) { + return lhs == FloatOptional{rhs}; +} + +constexpr bool operator==(float lhs, FloatOptional rhs) { + return rhs == lhs; +} + +constexpr FloatOptional operator+(FloatOptional lhs, FloatOptional rhs) { + return FloatOptional{lhs.unwrap() + rhs.unwrap()}; +} + +constexpr bool operator>(FloatOptional lhs, FloatOptional rhs) { + return lhs.unwrap() > rhs.unwrap(); +} + +constexpr bool operator<(FloatOptional lhs, FloatOptional rhs) { + return lhs.unwrap() < rhs.unwrap(); +} + +constexpr bool operator>=(FloatOptional lhs, FloatOptional rhs) { + return lhs > rhs || lhs == rhs; +} + +constexpr bool operator<=(FloatOptional lhs, FloatOptional rhs) { + return lhs < rhs || lhs == rhs; +} + +constexpr FloatOptional maxOrDefined(FloatOptional lhs, FloatOptional rhs) { + return FloatOptional{yoga::maxOrDefined(lhs.unwrap(), rhs.unwrap())}; +} + +inline bool inexactEquals(FloatOptional lhs, FloatOptional rhs) { + return yoga::inexactEquals(lhs.unwrap(), rhs.unwrap()); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/style/GridLine.h b/third-party/yoga/src/yoga/style/GridLine.h new file mode 100644 index 00000000..0a06c0df --- /dev/null +++ b/third-party/yoga/src/yoga/style/GridLine.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook::yoga { + +// https://www.w3.org/TR/css-grid-1/#typedef-grid-row-start-grid-line +enum class GridLineType : uint8_t { + Auto, + Integer, + Span, +}; + +struct GridLine { + GridLineType type{}; + // Line position (1, 2, -1, -2, etc) + int32_t integer{}; + + constexpr static GridLine auto_() { + return GridLine{.type = GridLineType::Auto, .integer = 0}; + } + + constexpr static GridLine fromInteger(int32_t value) { + return GridLine{.type = GridLineType::Integer, .integer = value}; + } + + constexpr static GridLine span(int32_t value) { + return GridLine{.type = GridLineType::Span, .integer = value}; + } + + constexpr bool isAuto() const { + return type == GridLineType::Auto; + } + + constexpr bool isInteger() const { + return type == GridLineType::Integer; + } + + constexpr bool isSpan() const { + return type == GridLineType::Span; + } + + bool operator==(const GridLine& other) const = default; +}; + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/style/GridTrack.h b/third-party/yoga/src/yoga/style/GridTrack.h new file mode 100644 index 00000000..2a2bafb5 --- /dev/null +++ b/third-party/yoga/src/yoga/style/GridTrack.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook::yoga { +// https://www.w3.org/TR/css-grid-1/#typedef-track-size +struct GridTrackSize { + StyleSizeLength minSizingFunction; + StyleSizeLength maxSizingFunction; + + // These are used in the grid layout algorithm when distributing spaces among + // tracks + // TODO: maybe move them to TrackSizing since these are track states + float baseSize = 0.0f; + float growthLimit = 0.0f; + bool infinitelyGrowable = false; + + // Static factory methods for common cases + constexpr static GridTrackSize auto_() { + return GridTrackSize{ + .minSizingFunction = StyleSizeLength::ofAuto(), + .maxSizingFunction = StyleSizeLength::ofAuto()}; + } + + constexpr static GridTrackSize length(float points) { + auto len = StyleSizeLength::points(points); + return GridTrackSize{.minSizingFunction = len, .maxSizingFunction = len}; + } + + constexpr static GridTrackSize fr(float fraction) { + // Flex sizing function is always a max sizing function + return GridTrackSize{ + .minSizingFunction = StyleSizeLength::ofAuto(), + .maxSizingFunction = StyleSizeLength::stretch(fraction)}; + } + + constexpr static GridTrackSize percent(float percentage) { + return GridTrackSize{ + .minSizingFunction = StyleSizeLength::percent(percentage), + .maxSizingFunction = StyleSizeLength::percent(percentage)}; + } + + constexpr static GridTrackSize minmax( + StyleSizeLength min, + StyleSizeLength max) { + return GridTrackSize{.minSizingFunction = min, .maxSizingFunction = max}; + } + + bool operator==(const GridTrackSize& other) const = default; +}; + +// Grid track list for grid-template-rows/columns properties +using GridTrackList = std::vector; + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/style/SmallValueBuffer.h b/third-party/yoga/src/yoga/style/SmallValueBuffer.h new file mode 100644 index 00000000..8d78a54d --- /dev/null +++ b/third-party/yoga/src/yoga/style/SmallValueBuffer.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace facebook::yoga { + +// Container which allows storing 32 or 64 bit integer values, whose index may +// never change. Values are first stored in a fixed buffer of `BufferSize` +// 32-bit chunks, before falling back to heap allocation. +template +class SmallValueBuffer { + public: + SmallValueBuffer() = default; + SmallValueBuffer(const SmallValueBuffer& other) { + *this = other; + } + SmallValueBuffer(SmallValueBuffer&& other) noexcept = default; + + // Add a new element to the buffer, returning the index of the element + uint16_t push(uint32_t value) { + const auto index = count_++; + assert(index < 4096 && "SmallValueBuffer can only hold up to 4096 chunks"); + if (index < buffer_.size()) { + buffer_[index] = value; + return index; + } + + if (overflow_ == nullptr) { + overflow_ = std::make_unique(); + } + + overflow_->buffer_.push_back(value); + overflow_->wideElements_.push_back(false); + return index; + } + + uint16_t push(uint64_t value) { + const auto lsb = static_cast(value & 0xFFFFFFFF); + const auto msb = static_cast(value >> 32); + + const auto lsbIndex = push(lsb); + [[maybe_unused]] const auto msbIndex = push(msb); + assert( + msbIndex < 4096 && "SmallValueBuffer can only hold up to 4096 chunks"); + + if (lsbIndex < buffer_.size()) { + wideElements_[lsbIndex] = true; + } else { + overflow_->wideElements_[lsbIndex - buffer_.size()] = true; + } + return lsbIndex; + } + + // Replace an existing element in the buffer with a new value. A new index + // may be returned, e.g. if a new value is wider than the previous. + [[nodiscard]] uint16_t replace(uint16_t index, uint32_t value) { + if (index < buffer_.size()) { + buffer_[index] = value; + } else { + overflow_->buffer_.at(index - buffer_.size()) = value; + } + + return index; + } + + [[nodiscard]] uint16_t replace(uint16_t index, uint64_t value) { + const bool isWide = index < wideElements_.size() + ? wideElements_[index] + : overflow_->wideElements_.at(index - buffer_.size()); + + if (isWide) { + const auto lsb = static_cast(value & 0xFFFFFFFF); + const auto msb = static_cast(value >> 32); + + [[maybe_unused]] auto lsbIndex = replace(index, lsb); + [[maybe_unused]] auto msbIndex = replace(index + 1, msb); + return index; + } else { + return push(value); + } + } + + // Get a value of a given width + uint32_t get32(uint16_t index) const { + if (index < buffer_.size()) { + return buffer_[index]; + } else { + return overflow_->buffer_.at(index - buffer_.size()); + } + } + + uint64_t get64(uint16_t index) const { + const auto lsb = get32(index); + const auto msb = get32(index + 1); + return (static_cast(msb) << 32) | lsb; + } + + SmallValueBuffer& operator=(const SmallValueBuffer& other) { + count_ = other.count_; + buffer_ = other.buffer_; + wideElements_ = other.wideElements_; + overflow_ = other.overflow_ ? std::make_unique(*other.overflow_) + : nullptr; + return *this; + } + + SmallValueBuffer& operator=(SmallValueBuffer&& other) noexcept = default; + + private: + struct Overflow { + std::vector buffer_; + std::vector wideElements_; + }; + + uint16_t count_{0}; + std::array buffer_{}; + std::bitset wideElements_; + std::unique_ptr overflow_; +}; + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/style/Style.h b/third-party/yoga/src/yoga/style/Style.h new file mode 100644 index 00000000..a06bd246 --- /dev/null +++ b/third-party/yoga/src/yoga/style/Style.h @@ -0,0 +1,945 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace facebook::yoga { + +class YG_EXPORT Style { + public: + using Length = StyleLength; + using SizeLength = StyleSizeLength; + + static constexpr float DefaultFlexGrow = 0.0f; + static constexpr float DefaultFlexShrink = 0.0f; + static constexpr float WebDefaultFlexShrink = 1.0f; + + Direction direction() const { + return direction_; + } + void setDirection(Direction value) { + direction_ = value; + } + + FlexDirection flexDirection() const { + return flexDirection_; + } + void setFlexDirection(FlexDirection value) { + flexDirection_ = value; + } + + Justify justifyContent() const { + return justifyContent_; + } + void setJustifyContent(Justify value) { + justifyContent_ = value; + } + + Justify justifyItems() const { + return justifyItems_; + } + void setJustifyItems(Justify value) { + justifyItems_ = value; + } + + Justify justifySelf() const { + return justifySelf_; + } + void setJustifySelf(Justify value) { + justifySelf_ = value; + } + + Align alignContent() const { + return alignContent_; + } + void setAlignContent(Align value) { + alignContent_ = value; + } + + Align alignItems() const { + return alignItems_; + } + void setAlignItems(Align value) { + alignItems_ = value; + } + + Align alignSelf() const { + return alignSelf_; + } + void setAlignSelf(Align value) { + alignSelf_ = value; + } + + PositionType positionType() const { + return positionType_; + } + void setPositionType(PositionType value) { + positionType_ = value; + } + + Wrap flexWrap() const { + return flexWrap_; + } + void setFlexWrap(Wrap value) { + flexWrap_ = value; + } + + Overflow overflow() const { + return overflow_; + } + void setOverflow(Overflow value) { + overflow_ = value; + } + + Display display() const { + return display_; + } + void setDisplay(Display value) { + display_ = value; + } + + FloatOptional flex() const { + return pool_.getNumber(flex_); + } + void setFlex(FloatOptional value) { + pool_.store(flex_, value); + } + + FloatOptional flexGrow() const { + return pool_.getNumber(flexGrow_); + } + void setFlexGrow(FloatOptional value) { + pool_.store(flexGrow_, value); + } + + FloatOptional flexShrink() const { + return pool_.getNumber(flexShrink_); + } + void setFlexShrink(FloatOptional value) { + pool_.store(flexShrink_, value); + } + + Style::SizeLength flexBasis() const { + return pool_.getSize(flexBasis_); + } + void setFlexBasis(Style::SizeLength value) { + pool_.store(flexBasis_, value); + } + + Style::Length margin(Edge edge) const { + return pool_.getLength(margin_[yoga::to_underlying(edge)]); + } + void setMargin(Edge edge, Style::Length value) { + pool_.store(margin_[yoga::to_underlying(edge)], value); + } + + Style::Length position(Edge edge) const { + return pool_.getLength(position_[yoga::to_underlying(edge)]); + } + void setPosition(Edge edge, Style::Length value) { + pool_.store(position_[yoga::to_underlying(edge)], value); + } + + Style::Length padding(Edge edge) const { + return pool_.getLength(padding_[yoga::to_underlying(edge)]); + } + void setPadding(Edge edge, Style::Length value) { + pool_.store(padding_[yoga::to_underlying(edge)], value); + } + + Style::Length border(Edge edge) const { + return pool_.getLength(border_[yoga::to_underlying(edge)]); + } + void setBorder(Edge edge, Style::Length value) { + pool_.store(border_[yoga::to_underlying(edge)], value); + } + + Style::Length gap(Gutter gutter) const { + return pool_.getLength(gap_[yoga::to_underlying(gutter)]); + } + void setGap(Gutter gutter, Style::Length value) { + pool_.store(gap_[yoga::to_underlying(gutter)], value); + } + + Style::SizeLength dimension(Dimension axis) const { + return pool_.getSize(dimensions_[yoga::to_underlying(axis)]); + } + void setDimension(Dimension axis, Style::SizeLength value) { + pool_.store(dimensions_[yoga::to_underlying(axis)], value); + } + + Style::SizeLength minDimension(Dimension axis) const { + return pool_.getSize(minDimensions_[yoga::to_underlying(axis)]); + } + void setMinDimension(Dimension axis, Style::SizeLength value) { + pool_.store(minDimensions_[yoga::to_underlying(axis)], value); + } + + // Grid Container Properties + const GridTrackList& gridTemplateColumns() const { + return gridTemplateColumns_; + } + void setGridTemplateColumns(GridTrackList value) { + gridTemplateColumns_ = std::move(value); + } + void resizeGridTemplateColumns(size_t count) { + gridTemplateColumns_.resize(count); + } + void setGridTemplateColumnAt(size_t index, GridTrackSize value) { + gridTemplateColumns_[index] = value; + } + + const GridTrackList& gridTemplateRows() const { + return gridTemplateRows_; + } + void setGridTemplateRows(GridTrackList value) { + gridTemplateRows_ = std::move(value); + } + void resizeGridTemplateRows(size_t count) { + gridTemplateRows_.resize(count); + } + void setGridTemplateRowAt(size_t index, GridTrackSize value) { + gridTemplateRows_[index] = value; + } + + const GridTrackList& gridAutoColumns() const { + return gridAutoColumns_; + } + void setGridAutoColumns(GridTrackList value) { + gridAutoColumns_ = std::move(value); + } + void resizeGridAutoColumns(size_t count) { + gridAutoColumns_.resize(count); + } + void setGridAutoColumnAt(size_t index, GridTrackSize value) { + gridAutoColumns_[index] = value; + } + + const GridTrackList& gridAutoRows() const { + return gridAutoRows_; + } + void setGridAutoRows(GridTrackList value) { + gridAutoRows_ = std::move(value); + } + void resizeGridAutoRows(size_t count) { + gridAutoRows_.resize(count); + } + void setGridAutoRowAt(size_t index, GridTrackSize value) { + gridAutoRows_[index] = value; + } + + // Grid Item Properties + const GridLine& gridColumnStart() const { + return gridColumnStart_; + } + void setGridColumnStart(GridLine value) { + gridColumnStart_ = value; + } + + const GridLine& gridColumnEnd() const { + return gridColumnEnd_; + } + void setGridColumnEnd(GridLine value) { + gridColumnEnd_ = value; + } + + const GridLine& gridRowStart() const { + return gridRowStart_; + } + void setGridRowStart(GridLine value) { + gridRowStart_ = value; + } + + const GridLine& gridRowEnd() const { + return gridRowEnd_; + } + void setGridRowEnd(GridLine value) { + gridRowEnd_ = value; + } + + FloatOptional resolvedMinDimension( + Direction direction, + Dimension axis, + float referenceLength, + float ownerWidth) const { + const auto handle = minDimensions_[yoga::to_underlying(axis)]; + if (handle.isUndefined()) { + return FloatOptional{}; + } + FloatOptional value = resolve(handle, referenceLength); + if (boxSizing() == BoxSizing::BorderBox || !value.isDefined()) { + return value; + } + + FloatOptional dimensionPaddingAndBorder = FloatOptional{ + computePaddingAndBorderForDimension(direction, axis, ownerWidth)}; + + return value + + (dimensionPaddingAndBorder.isDefined() ? dimensionPaddingAndBorder + : FloatOptional{0.0}); + } + + Style::SizeLength maxDimension(Dimension axis) const { + return pool_.getSize(maxDimensions_[yoga::to_underlying(axis)]); + } + void setMaxDimension(Dimension axis, Style::SizeLength value) { + pool_.store(maxDimensions_[yoga::to_underlying(axis)], value); + } + + FloatOptional resolvedMaxDimension( + Direction direction, + Dimension axis, + float referenceLength, + float ownerWidth) const { + const auto handle = maxDimensions_[yoga::to_underlying(axis)]; + if (handle.isUndefined()) { + return FloatOptional{}; + } + FloatOptional value = resolve(handle, referenceLength); + if (boxSizing() == BoxSizing::BorderBox || !value.isDefined()) { + return value; + } + + FloatOptional dimensionPaddingAndBorder = FloatOptional{ + computePaddingAndBorderForDimension(direction, axis, ownerWidth)}; + + return value + + (dimensionPaddingAndBorder.isDefined() ? dimensionPaddingAndBorder + : FloatOptional{0.0}); + } + + FloatOptional aspectRatio() const { + return pool_.getNumber(aspectRatio_); + } + void setAspectRatio(FloatOptional value) { + // degenerate aspect ratios act as auto. + // see https://drafts.csswg.org/css-sizing-4/#valdef-aspect-ratio-ratio + pool_.store( + aspectRatio_, + value == 0.0f || std::isinf(value.unwrap()) ? FloatOptional{} : value); + } + + BoxSizing boxSizing() const { + return boxSizing_; + } + void setBoxSizing(BoxSizing value) { + boxSizing_ = value; + } + + bool horizontalInsetsDefined() const { + return position_[yoga::to_underlying(Edge::Left)].isDefined() || + position_[yoga::to_underlying(Edge::Right)].isDefined() || + position_[yoga::to_underlying(Edge::All)].isDefined() || + position_[yoga::to_underlying(Edge::Horizontal)].isDefined() || + position_[yoga::to_underlying(Edge::Start)].isDefined() || + position_[yoga::to_underlying(Edge::End)].isDefined(); + } + + bool verticalInsetsDefined() const { + return position_[yoga::to_underlying(Edge::Top)].isDefined() || + position_[yoga::to_underlying(Edge::Bottom)].isDefined() || + position_[yoga::to_underlying(Edge::All)].isDefined() || + position_[yoga::to_underlying(Edge::Vertical)].isDefined(); + } + + bool isFlexStartPositionDefined(FlexDirection axis, Direction direction) + const { + return computePosition(flexStartEdge(axis), direction).isDefined(); + } + + bool isFlexStartPositionAuto(FlexDirection axis, Direction direction) const { + return computePosition(flexStartEdge(axis), direction).isAuto(); + } + + bool isInlineStartPositionDefined(FlexDirection axis, Direction direction) + const { + return computePosition(inlineStartEdge(axis, direction), direction) + .isDefined(); + } + + bool isInlineStartPositionAuto(FlexDirection axis, Direction direction) + const { + return computePosition(inlineStartEdge(axis, direction), direction) + .isAuto(); + } + + bool isFlexEndPositionDefined(FlexDirection axis, Direction direction) const { + return computePosition(flexEndEdge(axis), direction).isDefined(); + } + + bool isFlexEndPositionAuto(FlexDirection axis, Direction direction) const { + return computePosition(flexEndEdge(axis), direction).isAuto(); + } + + bool isInlineEndPositionDefined(FlexDirection axis, Direction direction) + const { + return computePosition(inlineEndEdge(axis, direction), direction) + .isDefined(); + } + + bool isInlineEndPositionAuto(FlexDirection axis, Direction direction) const { + return computePosition(inlineEndEdge(axis, direction), direction).isAuto(); + } + + float computeFlexStartPosition( + FlexDirection axis, + Direction direction, + float axisSize) const { + return resolve(computePosition(flexStartEdge(axis), direction), axisSize) + .unwrapOrDefault(0.0f); + } + + float computeInlineStartPosition( + FlexDirection axis, + Direction direction, + float axisSize) const { + return resolve( + computePosition(inlineStartEdge(axis, direction), direction), + axisSize) + .unwrapOrDefault(0.0f); + } + + float computeFlexEndPosition( + FlexDirection axis, + Direction direction, + float axisSize) const { + return resolve(computePosition(flexEndEdge(axis), direction), axisSize) + .unwrapOrDefault(0.0f); + } + + float computeInlineEndPosition( + FlexDirection axis, + Direction direction, + float axisSize) const { + return resolve( + computePosition(inlineEndEdge(axis, direction), direction), + axisSize) + .unwrapOrDefault(0.0f); + } + + float computeFlexStartMargin( + FlexDirection axis, + Direction direction, + float widthSize) const { + return resolve(computeMargin(flexStartEdge(axis), direction), widthSize) + .unwrapOrDefault(0.0f); + } + + float computeInlineStartMargin( + FlexDirection axis, + Direction direction, + float widthSize) const { + return resolve( + computeMargin(inlineStartEdge(axis, direction), direction), + widthSize) + .unwrapOrDefault(0.0f); + } + + float computeFlexEndMargin( + FlexDirection axis, + Direction direction, + float widthSize) const { + return resolve(computeMargin(flexEndEdge(axis), direction), widthSize) + .unwrapOrDefault(0.0f); + } + + float computeInlineEndMargin( + FlexDirection axis, + Direction direction, + float widthSize) const { + return resolve( + computeMargin(inlineEndEdge(axis, direction), direction), + widthSize) + .unwrapOrDefault(0.0f); + } + + float computeFlexStartBorder(FlexDirection axis, Direction direction) const { + return maxOrDefined( + resolve(computeBorder(flexStartEdge(axis), direction), 0.0f).unwrap(), + 0.0f); + } + + float computeInlineStartBorder(FlexDirection axis, Direction direction) + const { + return maxOrDefined( + resolve( + computeBorder(inlineStartEdge(axis, direction), direction), 0.0f) + .unwrap(), + 0.0f); + } + + float computeFlexEndBorder(FlexDirection axis, Direction direction) const { + return maxOrDefined( + resolve(computeBorder(flexEndEdge(axis), direction), 0.0f).unwrap(), + 0.0f); + } + + float computeInlineEndBorder(FlexDirection axis, Direction direction) const { + return maxOrDefined( + resolve(computeBorder(inlineEndEdge(axis, direction), direction), 0.0f) + .unwrap(), + 0.0f); + } + + float computeFlexStartPadding( + FlexDirection axis, + Direction direction, + float widthSize) const { + return maxOrDefined( + resolve(computePadding(flexStartEdge(axis), direction), widthSize) + .unwrap(), + 0.0f); + } + + float computeInlineStartPadding( + FlexDirection axis, + Direction direction, + float widthSize) const { + return maxOrDefined( + resolve( + computePadding(inlineStartEdge(axis, direction), direction), + widthSize) + .unwrap(), + 0.0f); + } + + float computeFlexEndPadding( + FlexDirection axis, + Direction direction, + float widthSize) const { + return maxOrDefined( + resolve(computePadding(flexEndEdge(axis), direction), widthSize) + .unwrap(), + 0.0f); + } + + float computeInlineEndPadding( + FlexDirection axis, + Direction direction, + float widthSize) const { + return maxOrDefined( + resolve( + computePadding(inlineEndEdge(axis, direction), direction), + widthSize) + .unwrap(), + 0.0f); + } + + float computeInlineStartPaddingAndBorder( + FlexDirection axis, + Direction direction, + float widthSize) const { + return computeInlineStartPadding(axis, direction, widthSize) + + computeInlineStartBorder(axis, direction); + } + + float computeFlexStartPaddingAndBorder( + FlexDirection axis, + Direction direction, + float widthSize) const { + return computeFlexStartPadding(axis, direction, widthSize) + + computeFlexStartBorder(axis, direction); + } + + float computeInlineEndPaddingAndBorder( + FlexDirection axis, + Direction direction, + float widthSize) const { + return computeInlineEndPadding(axis, direction, widthSize) + + computeInlineEndBorder(axis, direction); + } + + float computeFlexEndPaddingAndBorder( + FlexDirection axis, + Direction direction, + float widthSize) const { + return computeFlexEndPadding(axis, direction, widthSize) + + computeFlexEndBorder(axis, direction); + } + + float computePaddingAndBorderForDimension( + Direction direction, + Dimension dimension, + float widthSize) const { + FlexDirection flexDirectionForDimension = dimension == Dimension::Width + ? FlexDirection::Row + : FlexDirection::Column; + + return computeFlexStartPaddingAndBorder( + flexDirectionForDimension, direction, widthSize) + + computeFlexEndPaddingAndBorder( + flexDirectionForDimension, direction, widthSize); + } + + float computeBorderForAxis(FlexDirection axis) const { + return computeInlineStartBorder(axis, Direction::LTR) + + computeInlineEndBorder(axis, Direction::LTR); + } + + float computeMarginForAxis(FlexDirection axis, float widthSize) const { + // The total margin for a given axis does not depend on the direction + // so hardcoding LTR here to avoid piping direction to this function + return computeInlineStartMargin(axis, Direction::LTR, widthSize) + + computeInlineEndMargin(axis, Direction::LTR, widthSize); + } + + float computeGapForAxis(FlexDirection axis, float ownerSize) const { + auto gap = isRow(axis) ? computeColumnGap() : computeRowGap(); + return maxOrDefined(resolve(gap, ownerSize).unwrap(), 0.0f); + } + + float computeGapForDimension(Dimension dimension, float ownerSize) const { + auto gap = + dimension == Dimension::Width ? computeColumnGap() : computeRowGap(); + return maxOrDefined(resolve(gap, ownerSize).unwrap(), 0.0f); + } + + bool flexStartMarginIsAuto(FlexDirection axis, Direction direction) const { + return computeMargin(flexStartEdge(axis), direction).isAuto(); + } + + bool flexEndMarginIsAuto(FlexDirection axis, Direction direction) const { + return computeMargin(flexEndEdge(axis), direction).isAuto(); + } + + bool inlineStartMarginIsAuto(FlexDirection axis, Direction direction) const { + return computeMargin(inlineStartEdge(axis, direction), direction).isAuto(); + } + + bool inlineEndMarginIsAuto(FlexDirection axis, Direction direction) const { + return computeMargin(inlineEndEdge(axis, direction), direction).isAuto(); + } + + bool operator==(const Style& other) const { + return direction_ == other.direction_ && + flexDirection_ == other.flexDirection_ && + justifyContent_ == other.justifyContent_ && + justifyItems_ == other.justifyItems_ && + justifySelf_ == other.justifySelf_ && + alignContent_ == other.alignContent_ && + alignItems_ == other.alignItems_ && alignSelf_ == other.alignSelf_ && + positionType_ == other.positionType_ && flexWrap_ == other.flexWrap_ && + overflow_ == other.overflow_ && display_ == other.display_ && + numbersEqual(flex_, pool_, other.flex_, other.pool_) && + numbersEqual(flexGrow_, pool_, other.flexGrow_, other.pool_) && + numbersEqual(flexShrink_, pool_, other.flexShrink_, other.pool_) && + lengthsEqual(flexBasis_, pool_, other.flexBasis_, other.pool_) && + lengthsEqual(margin_, pool_, other.margin_, other.pool_) && + lengthsEqual(position_, pool_, other.position_, other.pool_) && + lengthsEqual(padding_, pool_, other.padding_, other.pool_) && + lengthsEqual(border_, pool_, other.border_, other.pool_) && + lengthsEqual(gap_, pool_, other.gap_, other.pool_) && + sizeLengthsEqual(dimensions_, pool_, other.dimensions_, other.pool_) && + sizeLengthsEqual( + minDimensions_, pool_, other.minDimensions_, other.pool_) && + sizeLengthsEqual( + maxDimensions_, pool_, other.maxDimensions_, other.pool_) && + numbersEqual(aspectRatio_, pool_, other.aspectRatio_, other.pool_) && + gridTemplateColumns_ == other.gridTemplateColumns_ && + gridTemplateRows_ == other.gridTemplateRows_ && + gridAutoColumns_ == other.gridAutoColumns_ && + gridAutoRows_ == other.gridAutoRows_ && + gridColumnStart_ == other.gridColumnStart_ && + gridColumnEnd_ == other.gridColumnEnd_ && + gridRowStart_ == other.gridRowStart_ && + gridRowEnd_ == other.gridRowEnd_; + } + + private: + using Dimensions = std::array()>; + using Edges = std::array()>; + using Gutters = std::array()>; + + static inline bool numbersEqual( + const StyleValueHandle& lhsHandle, + const StyleValuePool& lhsPool, + const StyleValueHandle& rhsHandle, + const StyleValuePool& rhsPool) { + return (lhsHandle.isUndefined() && rhsHandle.isUndefined()) || + (lhsPool.getNumber(lhsHandle) == rhsPool.getNumber(rhsHandle)); + } + + static inline bool lengthsEqual( + const StyleValueHandle& lhsHandle, + const StyleValuePool& lhsPool, + const StyleValueHandle& rhsHandle, + const StyleValuePool& rhsPool) { + return (lhsHandle.isUndefined() && rhsHandle.isUndefined()) || + (lhsPool.getLength(lhsHandle) == rhsPool.getLength(rhsHandle)); + } + + template + static inline bool lengthsEqual( + const std::array& lhs, + const StyleValuePool& lhsPool, + const std::array& rhs, + const StyleValuePool& rhsPool) { + return std::equal( + lhs.begin(), + lhs.end(), + rhs.begin(), + rhs.end(), + [&](const auto& lhs, const auto& rhs) { + return lengthsEqual(lhs, lhsPool, rhs, rhsPool); + }); + } + + static inline bool sizeLengthsEqual( + const StyleValueHandle& lhsHandle, + const StyleValuePool& lhsPool, + const StyleValueHandle& rhsHandle, + const StyleValuePool& rhsPool) { + return (lhsHandle.isUndefined() && rhsHandle.isUndefined()) || + (lhsPool.getSize(lhsHandle) == rhsPool.getSize(rhsHandle)); + } + + template + static inline bool sizeLengthsEqual( + const std::array& lhs, + const StyleValuePool& lhsPool, + const std::array& rhs, + const StyleValuePool& rhsPool) { + return std::equal( + lhs.begin(), + lhs.end(), + rhs.begin(), + rhs.end(), + [&](const auto& lhs, const auto& rhs) { + return sizeLengthsEqual(lhs, lhsPool, rhs, rhsPool); + }); + } + + StyleValueHandle computeColumnGap() const { + if (gap_[yoga::to_underlying(Gutter::Column)].isDefined()) { + return gap_[yoga::to_underlying(Gutter::Column)]; + } else { + return gap_[yoga::to_underlying(Gutter::All)]; + } + } + + StyleValueHandle computeRowGap() const { + if (gap_[yoga::to_underlying(Gutter::Row)].isDefined()) { + return gap_[yoga::to_underlying(Gutter::Row)]; + } else { + return gap_[yoga::to_underlying(Gutter::All)]; + } + } + + StyleValueHandle computeLeftEdge( + const Edges& edges, + Direction layoutDirection) const { + if (layoutDirection == Direction::LTR && + edges[yoga::to_underlying(Edge::Start)].isDefined()) { + return edges[yoga::to_underlying(Edge::Start)]; + } else if ( + layoutDirection == Direction::RTL && + edges[yoga::to_underlying(Edge::End)].isDefined()) { + return edges[yoga::to_underlying(Edge::End)]; + } else if (edges[yoga::to_underlying(Edge::Left)].isDefined()) { + return edges[yoga::to_underlying(Edge::Left)]; + } else if (edges[yoga::to_underlying(Edge::Horizontal)].isDefined()) { + return edges[yoga::to_underlying(Edge::Horizontal)]; + } else { + return edges[yoga::to_underlying(Edge::All)]; + } + } + + StyleValueHandle computeTopEdge(const Edges& edges) const { + if (edges[yoga::to_underlying(Edge::Top)].isDefined()) { + return edges[yoga::to_underlying(Edge::Top)]; + } else if (edges[yoga::to_underlying(Edge::Vertical)].isDefined()) { + return edges[yoga::to_underlying(Edge::Vertical)]; + } else { + return edges[yoga::to_underlying(Edge::All)]; + } + } + + StyleValueHandle computeRightEdge( + const Edges& edges, + Direction layoutDirection) const { + if (layoutDirection == Direction::LTR && + edges[yoga::to_underlying(Edge::End)].isDefined()) { + return edges[yoga::to_underlying(Edge::End)]; + } else if ( + layoutDirection == Direction::RTL && + edges[yoga::to_underlying(Edge::Start)].isDefined()) { + return edges[yoga::to_underlying(Edge::Start)]; + } else if (edges[yoga::to_underlying(Edge::Right)].isDefined()) { + return edges[yoga::to_underlying(Edge::Right)]; + } else if (edges[yoga::to_underlying(Edge::Horizontal)].isDefined()) { + return edges[yoga::to_underlying(Edge::Horizontal)]; + } else { + return edges[yoga::to_underlying(Edge::All)]; + } + } + + StyleValueHandle computeBottomEdge(const Edges& edges) const { + if (edges[yoga::to_underlying(Edge::Bottom)].isDefined()) { + return edges[yoga::to_underlying(Edge::Bottom)]; + } else if (edges[yoga::to_underlying(Edge::Vertical)].isDefined()) { + return edges[yoga::to_underlying(Edge::Vertical)]; + } else { + return edges[yoga::to_underlying(Edge::All)]; + } + } + + StyleValueHandle computePosition(PhysicalEdge edge, Direction direction) + const { + switch (edge) { + case PhysicalEdge::Left: + return computeLeftEdge(position_, direction); + case PhysicalEdge::Top: + return computeTopEdge(position_); + case PhysicalEdge::Right: + return computeRightEdge(position_, direction); + case PhysicalEdge::Bottom: + return computeBottomEdge(position_); + default: + fatalWithMessage("Invalid physical edge"); + } + } + + StyleValueHandle computeMargin(PhysicalEdge edge, Direction direction) const { + switch (edge) { + case PhysicalEdge::Left: + return computeLeftEdge(margin_, direction); + case PhysicalEdge::Top: + return computeTopEdge(margin_); + case PhysicalEdge::Right: + return computeRightEdge(margin_, direction); + case PhysicalEdge::Bottom: + return computeBottomEdge(margin_); + default: + fatalWithMessage("Invalid physical edge"); + } + } + + StyleValueHandle computePadding(PhysicalEdge edge, Direction direction) + const { + switch (edge) { + case PhysicalEdge::Left: + return computeLeftEdge(padding_, direction); + case PhysicalEdge::Top: + return computeTopEdge(padding_); + case PhysicalEdge::Right: + return computeRightEdge(padding_, direction); + case PhysicalEdge::Bottom: + return computeBottomEdge(padding_); + default: + fatalWithMessage("Invalid physical edge"); + } + } + + StyleValueHandle computeBorder(PhysicalEdge edge, Direction direction) const { + switch (edge) { + case PhysicalEdge::Left: + return computeLeftEdge(border_, direction); + case PhysicalEdge::Top: + return computeTopEdge(border_); + case PhysicalEdge::Right: + return computeRightEdge(border_, direction); + case PhysicalEdge::Bottom: + return computeBottomEdge(border_); + default: + fatalWithMessage("Invalid physical edge"); + } + } + + /** + * Internal resolution of a StyleValueHandle. + * + * Part of the handle-based optimization, this function allows the layout + * engine to resolve stored values (Points, Percents) directly from the pool + * via handles. This avoids the overhead of materializing an intermediate + * StyleLength/StyleSizeLength object on the stack during hot-path overhead + * calculations. + */ + FloatOptional resolve(StyleValueHandle handle, float referenceLength) const { + if (handle.isPoint()) { + return FloatOptional{pool_.getStoredValue(handle)}; + } + if (handle.isPercent()) { + return FloatOptional{ + pool_.getStoredValue(handle) * referenceLength * 0.01f}; + } + return FloatOptional{}; + } + + Direction direction_ : bitCount() = Direction::Inherit; + FlexDirection flexDirection_ + : bitCount() = FlexDirection::Column; + Justify justifyContent_ : bitCount() = Justify::FlexStart; + Justify justifyItems_ : bitCount() = Justify::Stretch; + Justify justifySelf_ : bitCount() = Justify::Auto; + Align alignContent_ : bitCount() = Align::FlexStart; + Align alignItems_ : bitCount() = Align::Stretch; + Align alignSelf_ : bitCount() = Align::Auto; + PositionType positionType_ + : bitCount() = PositionType::Relative; + Wrap flexWrap_ : bitCount() = Wrap::NoWrap; + Overflow overflow_ : bitCount() = Overflow::Visible; + Display display_ : bitCount() = Display::Flex; + BoxSizing boxSizing_ : bitCount() = BoxSizing::BorderBox; + + StyleValueHandle flex_{}; + StyleValueHandle flexGrow_{}; + StyleValueHandle flexShrink_{}; + StyleValueHandle flexBasis_{StyleValueHandle::ofAuto()}; + Edges margin_{}; + Edges position_{}; + Edges padding_{}; + Edges border_{}; + Gutters gap_{}; + Dimensions dimensions_{ + StyleValueHandle::ofAuto(), + StyleValueHandle::ofAuto()}; + Dimensions minDimensions_{}; + Dimensions maxDimensions_{}; + StyleValueHandle aspectRatio_{}; + + // Grid properties + GridTrackList gridTemplateColumns_{}; + GridTrackList gridTemplateRows_{}; + GridTrackList gridAutoColumns_{}; + GridTrackList gridAutoRows_{}; + GridLine gridColumnStart_{}; + GridLine gridColumnEnd_{}; + GridLine gridRowStart_{}; + GridLine gridRowEnd_{}; + + StyleValuePool pool_; +}; + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/style/StyleLength.h b/third-party/yoga/src/yoga/style/StyleLength.h new file mode 100644 index 00000000..8099ce7d --- /dev/null +++ b/third-party/yoga/src/yoga/style/StyleLength.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook::yoga { + +/** + * Style::Length represents a CSS Value which may be one of: + * 1. Undefined + * 2. A keyword (e.g. auto) + * 3. A CSS value: + * a. value (e.g. 10px) + * b. value of a reference + * + * References: + * 1. https://www.w3.org/TR/css-values-4/#lengths + * 2. https://www.w3.org/TR/css-values-4/#percentage-value + * 3. https://www.w3.org/TR/css-values-4/#mixed-percentages + */ +class StyleLength { + public: + constexpr StyleLength() = default; + + constexpr static StyleLength points(float value) { + return yoga::isUndefined(value) || yoga::isinf(value) + ? undefined() + : StyleLength{FloatOptional{value}, Unit::Point}; + } + + constexpr static StyleLength percent(float value) { + return yoga::isUndefined(value) || yoga::isinf(value) + ? undefined() + : StyleLength{FloatOptional{value}, Unit::Percent}; + } + + constexpr static StyleLength ofAuto() { + return StyleLength{{}, Unit::Auto}; + } + + constexpr static StyleLength undefined() { + return StyleLength{{}, Unit::Undefined}; + } + + constexpr bool isAuto() const { + return unit_ == Unit::Auto; + } + + constexpr bool isUndefined() const { + return unit_ == Unit::Undefined; + } + + constexpr bool isPoints() const { + return unit_ == Unit::Point; + } + + constexpr bool isPercent() const { + return unit_ == Unit::Percent; + } + + constexpr bool isDefined() const { + return !isUndefined(); + } + + constexpr FloatOptional value() const { + return value_; + } + + constexpr FloatOptional resolve(float referenceLength) { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif + switch (unit_) { +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + case Unit::Point: + return value_; + case Unit::Percent: + return FloatOptional{value_.unwrap() * referenceLength * 0.01f}; + default: + return FloatOptional{}; + } + } + + explicit constexpr operator YGValue() const { + return YGValue{value_.unwrap(), unscopedEnum(unit_)}; + } + + constexpr bool operator==(const StyleLength& rhs) const { + return value_ == rhs.value_ && unit_ == rhs.unit_; + } + + constexpr bool inexactEquals(const StyleLength& other) const { + return unit_ == other.unit_ && + facebook::yoga::inexactEquals(value_, other.value_); + } + + private: + // We intentionally do not allow direct construction using value and unit, to + // avoid invalid, or redundant combinations. + constexpr StyleLength(FloatOptional value, Unit unit) + : value_(value), unit_(unit) {} + + FloatOptional value_{}; + Unit unit_{Unit::Undefined}; +}; + +inline bool inexactEquals(const StyleLength& a, const StyleLength& b) { + return a.inexactEquals(b); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/style/StyleSizeLength.h b/third-party/yoga/src/yoga/style/StyleSizeLength.h new file mode 100644 index 00000000..76e079b2 --- /dev/null +++ b/third-party/yoga/src/yoga/style/StyleSizeLength.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook::yoga { + +/** + * This class represents a CSS Value for sizes (e.g. width, height, min-width, + * etc.). It may be one of: + * 1. Undefined + * 2. A keyword (e.g. auto, max-content, stretch, etc.) + * 3. A CSS value: + * a. value (e.g. 10px) + * b. value of a reference + * + * References: + * 1. https://www.w3.org/TR/css-values-4/#lengths + * 2. https://www.w3.org/TR/css-values-4/#percentage-value + * 3. https://www.w3.org/TR/css-values-4/#mixed-percentages + */ +class StyleSizeLength { + public: + constexpr StyleSizeLength() = default; + + constexpr static StyleSizeLength points(float value) { + return yoga::isUndefined(value) || yoga::isinf(value) + ? undefined() + : StyleSizeLength{FloatOptional{value}, Unit::Point}; + } + + constexpr static StyleSizeLength percent(float value) { + return yoga::isUndefined(value) || yoga::isinf(value) + ? undefined() + : StyleSizeLength{FloatOptional{value}, Unit::Percent}; + } + + constexpr static StyleSizeLength stretch(float fraction) { + return yoga::isUndefined(fraction) || yoga::isinf(fraction) + ? undefined() + : StyleSizeLength{FloatOptional{fraction}, Unit::Stretch}; + } + + constexpr static StyleSizeLength ofAuto() { + return StyleSizeLength{{}, Unit::Auto}; + } + + constexpr static StyleSizeLength ofMaxContent() { + return StyleSizeLength{{}, Unit::MaxContent}; + } + + constexpr static StyleSizeLength ofFitContent() { + return StyleSizeLength{{}, Unit::FitContent}; + } + + constexpr static StyleSizeLength ofStretch() { + return StyleSizeLength{{}, Unit::Stretch}; + } + + constexpr static StyleSizeLength undefined() { + return StyleSizeLength{{}, Unit::Undefined}; + } + + constexpr bool isAuto() const { + return unit_ == Unit::Auto; + } + + constexpr bool isMaxContent() const { + return unit_ == Unit::MaxContent; + } + + constexpr bool isFitContent() const { + return unit_ == Unit::FitContent; + } + + constexpr bool isStretch() const { + return unit_ == Unit::Stretch; + } + + constexpr bool isUndefined() const { + return unit_ == Unit::Undefined; + } + + constexpr bool isDefined() const { + return !isUndefined(); + } + + constexpr bool isPoints() const { + return unit_ == Unit::Point; + } + + constexpr bool isPercent() const { + return unit_ == Unit::Percent; + } + + constexpr FloatOptional value() const { + return value_; + } + + constexpr FloatOptional resolve(float referenceLength) const { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif + switch (unit_) { +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + case Unit::Point: + return value_; + case Unit::Percent: + return FloatOptional{value_.unwrap() * referenceLength * 0.01f}; + default: + return FloatOptional{}; + } + } + + explicit constexpr operator YGValue() const { + return YGValue{value_.unwrap(), unscopedEnum(unit_)}; + } + + constexpr bool operator==(const StyleSizeLength& rhs) const { + return value_ == rhs.value_ && unit_ == rhs.unit_; + } + + constexpr bool inexactEquals(const StyleSizeLength& other) const { + return unit_ == other.unit_ && + facebook::yoga::inexactEquals(value_, other.value_); + } + + private: + // We intentionally do not allow direct construction using value and unit, to + // avoid invalid, or redundant combinations. + constexpr StyleSizeLength(FloatOptional value, Unit unit) + : value_(value), unit_(unit) {} + + FloatOptional value_{}; + Unit unit_{Unit::Undefined}; +}; + +inline bool inexactEquals(const StyleSizeLength& a, const StyleSizeLength& b) { + return a.inexactEquals(b); +} + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/style/StyleValueHandle.h b/third-party/yoga/src/yoga/style/StyleValueHandle.h new file mode 100644 index 00000000..6a2b5890 --- /dev/null +++ b/third-party/yoga/src/yoga/style/StyleValueHandle.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include + +#include +#include +#include + +namespace facebook::yoga { + +#pragma pack(push) +#pragma pack(1) + +/** + * StyleValueHandle is a small (16-bit) handle to a length or number in a style. + * The value may be embedded directly in the handle if simple, or the handle may + * instead point to an index within a StyleValuePool. + * + * To read or write a value from a StyleValueHandle, use + * `StyleValuePool::store()`, and `StyleValuePool::getLength()`/ + * `StyleValuePool::getNumber()`. + */ +class StyleValueHandle { + public: + static constexpr StyleValueHandle ofAuto() { + StyleValueHandle handle; + handle.setType(Type::Auto); + return handle; + } + + constexpr bool isUndefined() const { + return type() == Type::Undefined; + } + + constexpr bool isDefined() const { + return !isUndefined(); + } + + constexpr bool isAuto() const { + return type() == Type::Auto; + } + + constexpr bool isPercent() const { + return type() == Type::Percent; + } + + constexpr bool isPoint() const { + return type() == Type::Point; + } + + private: + friend class StyleValuePool; + + static constexpr uint16_t kHandleTypeMask = 0b0000'0000'0000'0111; + static constexpr uint16_t kHandleIndexedMask = 0b0000'0000'0000'1000; + static constexpr uint16_t kHandleValueMask = 0b1111'1111'1111'0000; + + enum class Type : uint8_t { + Undefined, + Point, + Percent, + Number, + Auto, + Keyword + }; + + // Intentionally leaving out auto as a fast path + enum class Keyword : uint8_t { MaxContent, FitContent, Stretch }; + + constexpr bool isKeyword(Keyword keyword) const { + return type() == Type::Keyword && value() == static_cast(keyword); + } + + constexpr Type type() const { + return static_cast(repr_ & kHandleTypeMask); + } + + constexpr void setType(Type handleType) { + repr_ &= (~kHandleTypeMask); + repr_ |= static_cast(handleType); + } + + constexpr uint16_t value() const { + return repr_ >> 4; + } + + constexpr void setValue(uint16_t value) { + repr_ &= (~kHandleValueMask); + repr_ |= (value << 4); + } + + constexpr bool isValueIndexed() const { + return (repr_ & kHandleIndexedMask) != 0; + } + + constexpr void setValueIsIndexed() { + repr_ |= kHandleIndexedMask; + } + + uint16_t repr_{0}; +}; + +#pragma pack(pop) + +} // namespace facebook::yoga diff --git a/third-party/yoga/src/yoga/style/StyleValuePool.h b/third-party/yoga/src/yoga/style/StyleValuePool.h new file mode 100644 index 00000000..fb31193b --- /dev/null +++ b/third-party/yoga/src/yoga/style/StyleValuePool.h @@ -0,0 +1,194 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +namespace facebook::yoga { + +/** + * StyleValuePool allows compact storage for a sparse collection of assigned + * lengths and numbers. Values are referred to using StyleValueHandle. In most + * cases StyleValueHandle can embed the value directly, but if not, the value is + * stored within a buffer provided by the pool. The pool contains a fixed number + * of inline slots before falling back to heap allocating additional slots. + */ +class StyleValuePool { + public: + void store(StyleValueHandle& handle, StyleLength length) { + if (length.isUndefined()) { + handle.setType(StyleValueHandle::Type::Undefined); + } else if (length.isAuto()) { + handle.setType(StyleValueHandle::Type::Auto); + } else { + auto type = length.isPoints() ? StyleValueHandle::Type::Point + : StyleValueHandle::Type::Percent; + storeValue(handle, length.value().unwrap(), type); + } + } + + void store(StyleValueHandle& handle, StyleSizeLength sizeValue) { + if (sizeValue.isUndefined()) { + handle.setType(StyleValueHandle::Type::Undefined); + } else if (sizeValue.isAuto()) { + handle.setType(StyleValueHandle::Type::Auto); + } else if (sizeValue.isMaxContent()) { + storeKeyword(handle, StyleValueHandle::Keyword::MaxContent); + } else if (sizeValue.isStretch()) { + storeKeyword(handle, StyleValueHandle::Keyword::Stretch); + } else if (sizeValue.isFitContent()) { + storeKeyword(handle, StyleValueHandle::Keyword::FitContent); + } else { + auto type = sizeValue.isPoints() ? StyleValueHandle::Type::Point + : StyleValueHandle::Type::Percent; + storeValue(handle, sizeValue.value().unwrap(), type); + } + } + + void store(StyleValueHandle& handle, FloatOptional number) { + if (number.isUndefined()) { + handle.setType(StyleValueHandle::Type::Undefined); + } else { + storeValue(handle, number.unwrap(), StyleValueHandle::Type::Number); + } + } + + StyleLength getLength(StyleValueHandle handle) const { + if (handle.isUndefined()) { + return StyleLength::undefined(); + } else if (handle.isAuto()) { + return StyleLength::ofAuto(); + } else { + assert( + handle.type() == StyleValueHandle::Type::Point || + handle.type() == StyleValueHandle::Type::Percent); + float value = (handle.isValueIndexed()) + ? std::bit_cast(buffer_.get32(handle.value())) + : unpackInlineInteger(handle.value()); + + return handle.type() == StyleValueHandle::Type::Point + ? StyleLength::points(value) + : StyleLength::percent(value); + } + } + + StyleSizeLength getSize(StyleValueHandle handle) const { + if (handle.isUndefined()) { + return StyleSizeLength::undefined(); + } else if (handle.isAuto()) { + return StyleSizeLength::ofAuto(); + } else if (handle.isKeyword(StyleValueHandle::Keyword::MaxContent)) { + return StyleSizeLength::ofMaxContent(); + } else if (handle.isKeyword(StyleValueHandle::Keyword::FitContent)) { + return StyleSizeLength::ofFitContent(); + } else if (handle.isKeyword(StyleValueHandle::Keyword::Stretch)) { + return StyleSizeLength::ofStretch(); + } else { + assert( + handle.type() == StyleValueHandle::Type::Point || + handle.type() == StyleValueHandle::Type::Percent); + float value = (handle.isValueIndexed()) + ? std::bit_cast(buffer_.get32(handle.value())) + : unpackInlineInteger(handle.value()); + + return handle.type() == StyleValueHandle::Type::Point + ? StyleSizeLength::points(value) + : StyleSizeLength::percent(value); + } + } + + FloatOptional getNumber(StyleValueHandle handle) const { + if (handle.isUndefined()) { + return FloatOptional{}; + } else { + assert(handle.type() == StyleValueHandle::Type::Number); + float value = (handle.isValueIndexed()) + ? std::bit_cast(buffer_.get32(handle.value())) + : unpackInlineInteger(handle.value()); + return FloatOptional{value}; + } + } + + float getStoredValue(StyleValueHandle handle) const { + assert( + handle.type() == StyleValueHandle::Type::Point || + handle.type() == StyleValueHandle::Type::Percent || + handle.type() == StyleValueHandle::Type::Number); + return handle.isValueIndexed() + ? std::bit_cast(buffer_.get32(handle.value())) + : unpackInlineInteger(handle.value()); + } + + private: + void storeValue( + StyleValueHandle& handle, + float value, + StyleValueHandle::Type type) { + handle.setType(type); + + if (handle.isValueIndexed()) { + auto newIndex = + buffer_.replace(handle.value(), std::bit_cast(value)); + handle.setValue(newIndex); + } else if (isIntegerPackable(value)) { + handle.setValue(packInlineInteger(value)); + } else { + auto newIndex = buffer_.push(std::bit_cast(value)); + handle.setValue(newIndex); + handle.setValueIsIndexed(); + } + } + + void storeKeyword( + StyleValueHandle& handle, + StyleValueHandle::Keyword keyword) { + handle.setType(StyleValueHandle::Type::Keyword); + + if (handle.isValueIndexed()) { + auto newIndex = + buffer_.replace(handle.value(), static_cast(keyword)); + handle.setValue(newIndex); + } else { + handle.setValue(static_cast(keyword)); + } + } + + static constexpr bool isIntegerPackable(float f) { + constexpr uint16_t kMaxInlineAbsValue = (1 << 11) - 1; + + auto i = static_cast(f); + return static_cast(i) == f && i >= -kMaxInlineAbsValue && + i <= +kMaxInlineAbsValue; + } + + static constexpr uint16_t packInlineInteger(float value) { + uint16_t isNegative = value < 0 ? 1 : 0; + return static_cast( + (isNegative << 11) | + (static_cast(value) * (isNegative != 0u ? -1 : 1))); + } + + static constexpr float unpackInlineInteger(uint16_t value) { + constexpr uint16_t kValueSignMask = 0b0000'1000'0000'0000; + constexpr uint16_t kValueMagnitudeMask = 0b0000'0111'1111'1111; + const bool isNegative = (value & kValueSignMask) != 0; + return static_cast( + (value & kValueMagnitudeMask) * (isNegative ? -1 : 1)); + } + + SmallValueBuffer<4> buffer_; +}; + +} // namespace facebook::yoga From 3d53aef03c7c8f727b155cd8730a38d690bc6932 Mon Sep 17 00:00:00 2001 From: Simon Corsin Date: Fri, 17 Apr 2026 12:27:30 -0700 Subject: [PATCH 2/9] updated snap drawing (cherry picked from commit 220da1abb23be9fd1b6c355d87244ebe72997b8b) --- .../snap_drawing/cpp/Layers/FlexboxLayer.cpp | 117 +++++++++++------- .../snap_drawing/cpp/Layers/FlexboxLayer.hpp | 20 +-- 2 files changed, 87 insertions(+), 50 deletions(-) diff --git a/snap_drawing/src/snap_drawing/cpp/Layers/FlexboxLayer.cpp b/snap_drawing/src/snap_drawing/cpp/Layers/FlexboxLayer.cpp index 8fde64d4..0cd6e388 100644 --- a/snap_drawing/src/snap_drawing/cpp/Layers/FlexboxLayer.cpp +++ b/snap_drawing/src/snap_drawing/cpp/Layers/FlexboxLayer.cpp @@ -6,7 +6,9 @@ // #include "snap_drawing/cpp/Layers/FlexboxLayer.hpp" -#include +#include +#include +#include namespace snap::drawing { @@ -41,7 +43,7 @@ struct FlexboxNode : public Valdi::SimpleRefCountable { } void setLayoutDirty() const { - if (yogaNode->hasMeasureFunc()) { + if (facebook::yoga::resolveRef(yogaNode)->hasMeasureFunc()) { YGNodeMarkDirty(yogaNode); } } @@ -63,143 +65,174 @@ struct FlexboxNode : public Valdi::SimpleRefCountable { } }; -static YGFloatOptional toOptional(const std::optional& value) { - if (value) { - return YGFloatOptional(value.value()); - } else { - return YGFloatOptional(); +static facebook::yoga::FloatOptional toFloatOptional(const std::optional& value) { + return value ? facebook::yoga::FloatOptional(value.value()) : facebook::yoga::FloatOptional(); +} + +static facebook::yoga::StyleLength toStyleLength(FlexValue value) { + switch (value.value.unit) { + case YGUnitAuto: + return facebook::yoga::StyleLength::ofAuto(); + case YGUnitPercent: + return facebook::yoga::StyleLength::percent(value.value.value); + case YGUnitPoint: + return facebook::yoga::StyleLength::points(value.value.value); + case YGUnitUndefined: + case YGUnitMaxContent: + case YGUnitFitContent: + case YGUnitStretch: + return facebook::yoga::StyleLength::undefined(); } } -FlexboxAttributes::FlexboxAttributes(YGStyle* style) : _style(style) {} +static facebook::yoga::StyleSizeLength toStyleSizeLength(FlexValue value) { + switch (value.value.unit) { + case YGUnitAuto: + return facebook::yoga::StyleSizeLength::ofAuto(); + case YGUnitPercent: + return facebook::yoga::StyleSizeLength::percent(value.value.value); + case YGUnitPoint: + return facebook::yoga::StyleSizeLength::points(value.value.value); + case YGUnitMaxContent: + return facebook::yoga::StyleSizeLength::ofMaxContent(); + case YGUnitFitContent: + return facebook::yoga::StyleSizeLength::ofFitContent(); + case YGUnitStretch: + return facebook::yoga::StyleSizeLength::ofStretch(); + case YGUnitUndefined: + return facebook::yoga::StyleSizeLength::undefined(); + } +} + +FlexboxAttributes::FlexboxAttributes(facebook::yoga::Style* style) : _style(style) {} FlexboxAttributes& FlexboxAttributes::setDirection(YGDirection value) { - _style->direction() = value; + _style->setDirection(facebook::yoga::scopedEnum(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setFlexDirection(YGFlexDirection value) { - _style->flexDirection() = value; + _style->setFlexDirection(facebook::yoga::scopedEnum(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setJustifyContent(YGJustify value) { - _style->justifyContent() = value; + _style->setJustifyContent(facebook::yoga::scopedEnum(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setAlignItems(YGAlign value) { - _style->alignItems() = value; + _style->setAlignItems(facebook::yoga::scopedEnum(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setAlignContent(YGAlign value) { - _style->alignContent() = value; + _style->setAlignContent(facebook::yoga::scopedEnum(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setAlignSelf(YGAlign value) { - _style->alignSelf() = value; + _style->setAlignSelf(facebook::yoga::scopedEnum(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setPadding(YGEdge edge, FlexValue value) { - _style->padding()[edge] = value.value; + _style->setPadding(facebook::yoga::scopedEnum(edge), toStyleLength(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setMargin(YGEdge edge, FlexValue value) { - _style->margin()[edge] = value.value; + _style->setMargin(facebook::yoga::scopedEnum(edge), toStyleLength(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setBorder(YGEdge edge, FlexValue value) { - _style->border()[edge] = value.value; + _style->setBorder(facebook::yoga::scopedEnum(edge), toStyleLength(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setPosition(YGEdge edge, FlexValue value) { - _style->position()[edge] = value.value; + _style->setPosition(facebook::yoga::scopedEnum(edge), toStyleLength(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setPositionType(YGPositionType value) { - _style->positionType() = value; + _style->setPositionType(facebook::yoga::scopedEnum(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setFlexWrap(YGWrap value) { - _style->flexWrap() = value; + _style->setFlexWrap(facebook::yoga::scopedEnum(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setOverflow(YGOverflow value) { - _style->overflow() = value; + _style->setOverflow(facebook::yoga::scopedEnum(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setDisplay(YGDisplay value) { - _style->display() = value; + _style->setDisplay(facebook::yoga::scopedEnum(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setFlex(std::optional value) { - _style->flex() = toOptional(value); + _style->setFlex(toFloatOptional(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setFlexGrow(std::optional value) { - _style->flexGrow() = toOptional(value); + _style->setFlexGrow(toFloatOptional(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setFlexShrink(std::optional value) { - _style->flexShrink() = toOptional(value); + _style->setFlexShrink(toFloatOptional(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setFlexBasis(FlexValue value) { - _style->flexBasis() = value.value; + _style->setFlexBasis(toStyleSizeLength(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setWidth(FlexValue value) { - _style->dimensions()[YGDimensionWidth] = value.value; + _style->setDimension(facebook::yoga::Dimension::Width, toStyleSizeLength(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setHeight(FlexValue value) { - _style->dimensions()[YGDimensionHeight] = value.value; + _style->setDimension(facebook::yoga::Dimension::Height, toStyleSizeLength(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setMinWidth(FlexValue value) { - _style->minDimensions()[YGDimensionWidth] = value.value; + _style->setMinDimension(facebook::yoga::Dimension::Width, toStyleSizeLength(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setMinHeight(FlexValue value) { - _style->minDimensions()[YGDimensionHeight] = value.value; + _style->setMinDimension(facebook::yoga::Dimension::Height, toStyleSizeLength(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setMaxWidth(FlexValue value) { - _style->maxDimensions()[YGDimensionWidth] = value.value; + _style->setMaxDimension(facebook::yoga::Dimension::Width, toStyleSizeLength(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setMaxHeight(FlexValue value) { - _style->maxDimensions()[YGDimensionHeight] = value.value; + _style->setMaxDimension(facebook::yoga::Dimension::Height, toStyleSizeLength(value)); return *this; } FlexboxAttributes& FlexboxAttributes::setAspectRatio(std::optional value) { - _style->aspectRatio() = toOptional(value); + _style->setAspectRatio(toFloatOptional(value)); return *this; } -void onYogaNodeDirty(YGNodeRef node) { - auto* layer = reinterpret_cast(node->getContext()); +void onYogaNodeDirty(YGNodeConstRef node) { + auto* layer = reinterpret_cast(facebook::yoga::resolveRef(node)->getContext()); if (layer == nullptr) { return; } @@ -207,8 +240,8 @@ void onYogaNodeDirty(YGNodeRef node) { layer->setNeedsLayout(); } -YGSize onYogaMeasure(YGNodeRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode) { - auto* layer = reinterpret_cast(node->getContext()); +YGSize onYogaMeasure(YGNodeConstRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode) { + auto* layer = reinterpret_cast(facebook::yoga::resolveRef(node)->getContext()); if (layer == nullptr) { return {.width = 0, .height = 0}; } @@ -224,11 +257,12 @@ static Ref createAndAssociateFlexboxNode(Layer* layer, bool isOwner auto flexboxNode = Valdi::makeShared(); layer->setAttachedData(flexboxNode); - flexboxNode->yogaNode->setContext(static_cast(layer)); - flexboxNode->yogaNode->setDirtiedFunc(&onYogaNodeDirty); + auto* yogaNode = facebook::yoga::resolveRef(flexboxNode->yogaNode); + yogaNode->setContext(static_cast(layer)); + yogaNode->setDirtiedFunc(&onYogaNodeDirty); if (!isOwner) { - flexboxNode->yogaNode->setMeasureFunc(&onYogaMeasure); + yogaNode->setMeasureFunc(&onYogaMeasure); } return flexboxNode; @@ -302,11 +336,10 @@ FlexboxAttributes FlexboxLayer::updateLayoutAttributesForLayer(const Ref& // NOLINTNEXTLINE(readability-convert-member-functions-to-static) FlexboxAttributes FlexboxLayer::updateLayoutAttributesForLayer(Layer* layer) { auto flexboxNode = mustGetFlexboxNode(layer); - auto& style = flexboxNode->yogaNode->getStyle(); flexboxNode->setLayoutDirty(); - return FlexboxAttributes(&style); + return FlexboxAttributes(&facebook::yoga::resolveRef(flexboxNode->yogaNode)->style()); } void FlexboxLayer::requestLayout(ILayer* layer) { diff --git a/snap_drawing/src/snap_drawing/cpp/Layers/FlexboxLayer.hpp b/snap_drawing/src/snap_drawing/cpp/Layers/FlexboxLayer.hpp index 56bd4973..33abe038 100644 --- a/snap_drawing/src/snap_drawing/cpp/Layers/FlexboxLayer.hpp +++ b/snap_drawing/src/snap_drawing/cpp/Layers/FlexboxLayer.hpp @@ -9,33 +9,37 @@ #include "snap_drawing/cpp/Layers/Layer.hpp" -#include +#include struct YGNode; +namespace facebook::yoga { +class Style; +} + namespace snap::drawing { struct FlexValue { - facebook::yoga::detail::CompactValue value; + YGValue value; - constexpr FlexValue(facebook::yoga::detail::CompactValue value) : value(value) {} + constexpr explicit FlexValue(YGValue value) : value(value) {} inline static FlexValue point(Scalar value) { - return facebook::yoga::detail::CompactValue::of(value); + return FlexValue(YGValue{.value = value, .unit = YGUnitPoint}); } inline static FlexValue percent(Scalar value) { - return facebook::yoga::detail::CompactValue::of(value); + return FlexValue(YGValue{.value = value, .unit = YGUnitPercent}); } inline static FlexValue undefined() { - return facebook::yoga::detail::CompactValue::ofUndefined(); + return FlexValue(YGValue{.value = YGUndefined, .unit = YGUnitUndefined}); } }; class FlexboxAttributes { public: - FlexboxAttributes(YGStyle* _style); + FlexboxAttributes(facebook::yoga::Style* style); FlexboxAttributes& setDirection(YGDirection value); @@ -72,7 +76,7 @@ class FlexboxAttributes { FlexboxAttributes& setAspectRatio(std::optional value); private: - YGStyle* _style; + facebook::yoga::Style* _style; }; /** From 6badcf5b288f5ad7790c8f0017da007013b5417b Mon Sep 17 00:00:00 2001 From: Simon Corsin Date: Fri, 17 Apr 2026 16:36:41 -0500 Subject: [PATCH 3/9] updated valdi runtime with new yoga version (cherry picked from commit 530b50b44a29a7b603c286366622aa52a0d271c0) --- .../Yoga/YGEdgesAttributeHandlerDelegate.cpp | 39 +- .../Yoga/YGEdgesAttributeHandlerDelegate.hpp | 36 +- .../Yoga/YGValueAttributeHandlerDelegate.cpp | 28 +- .../Yoga/YGValueAttributeHandlerDelegate.hpp | 16 +- .../valdi/runtime/Attributes/Yoga/Yoga.cpp | 14 +- .../Yoga/YogaAttributeHandlerDelegate.cpp | 56 ++- .../Yoga/YogaAttributeHandlerDelegate.hpp | 13 +- .../Attributes/Yoga/YogaAttributes.cpp | 467 +++++++++++++----- .../Attributes/Yoga/YogaAttributes.hpp | 22 +- ...gaGetterSetterAttributeHandlerDelegate.hpp | 12 +- valdi/src/valdi/runtime/Context/ViewNode.cpp | 207 ++++---- valdi/src/valdi/runtime/Runtime.cpp | 1 + 12 files changed, 609 insertions(+), 302 deletions(-) diff --git a/valdi/src/valdi/runtime/Attributes/Yoga/YGEdgesAttributeHandlerDelegate.cpp b/valdi/src/valdi/runtime/Attributes/Yoga/YGEdgesAttributeHandlerDelegate.cpp index 56b460d6..8cb0044c 100644 --- a/valdi/src/valdi/runtime/Attributes/Yoga/YGEdgesAttributeHandlerDelegate.cpp +++ b/valdi/src/valdi/runtime/Attributes/Yoga/YGEdgesAttributeHandlerDelegate.cpp @@ -10,6 +10,8 @@ #include "valdi_core/cpp/Attributes/AttributeUtils.hpp" +#include + namespace Valdi { Result YGEdgesBaseAttributeHandlerDelegate::onApply(YGNodeRef node, const Value& value) { @@ -19,10 +21,10 @@ Result YGEdgesBaseAttributeHandlerDelegate::onApply(YGNodeRef node, const const auto& parts = *value.getArray(); - auto topEdge = YGCompactValue::ofUndefined(); - auto endEdge = YGCompactValue::ofUndefined(); - auto bottomEdge = YGCompactValue::ofUndefined(); - auto startEdge = YGCompactValue::ofUndefined(); + auto topEdge = facebook::yoga::StyleLength::undefined(); + auto endEdge = facebook::yoga::StyleLength::undefined(); + auto bottomEdge = facebook::yoga::StyleLength::undefined(); + auto startEdge = facebook::yoga::StyleLength::undefined(); // Our behavior here is a bit different than how web CSS work. // We fill the values using the shorthand value if it is set, @@ -65,53 +67,54 @@ Result YGEdgesBaseAttributeHandlerDelegate::onApply(YGNodeRef node, const } if (!parts[1].isNull()) { - auto result = valueToYGValue(parts[1]); + auto result = valueToYGStyleLength(parts[1]); if (!result) { return result.moveError(); } topEdge = result.value(); } if (!parts[2].isNull()) { - auto result = valueToYGValue(parts[2]); + auto result = valueToYGStyleLength(parts[2]); if (!result) { return result.moveError(); } endEdge = result.value(); } if (!parts[3].isNull()) { - auto result = valueToYGValue(parts[3]); + auto result = valueToYGStyleLength(parts[3]); if (!result) { return result.moveError(); } bottomEdge = result.value(); } if (!parts[4].isNull()) { - auto result = valueToYGValue(parts[4]); + auto result = valueToYGStyleLength(parts[4]); if (!result) { return result.moveError(); } startEdge = result.value(); } - setEdges(node, topEdge, endEdge, bottomEdge, startEdge); + setEdges(facebook::yoga::resolveRef(node)->style(), topEdge, endEdge, bottomEdge, startEdge); return Void(); } void YGEdgesBaseAttributeHandlerDelegate::onReset(YGNodeRef node, YGNodeRef /*defaultYogaNode*/) { - setEdges(node, - YGCompactValue::ofUndefined(), - YGCompactValue::ofUndefined(), - YGCompactValue::ofUndefined(), - YGCompactValue::ofUndefined()); + setEdges(facebook::yoga::resolveRef(node)->style(), + facebook::yoga::StyleLength::undefined(), + facebook::yoga::StyleLength::undefined(), + facebook::yoga::StyleLength::undefined(), + facebook::yoga::StyleLength::undefined()); } -Result> YGEdgesBaseAttributeHandlerDelegate::parseFlexBoxShorthand(const Value& value) { - std::vector values; +Result> YGEdgesBaseAttributeHandlerDelegate::parseFlexBoxShorthand( + const Value& value) { + std::vector values; if (value.isNumber()) { // Case where we pass a number directly - values.emplace_back(valueToYGValue(value).value()); + values.emplace_back(valueToYGStyleLength(value).value()); return std::move(values); } else if (value.isString()) { @@ -122,7 +125,7 @@ Result> YGEdgesBaseAttributeHandlerDelegate::parseFl AttributeParser parser(strBox.toStringView()); while (!parser.isAtEnd()) { - auto value = parseYGValue(parser); + auto value = parseYGStyleLength(parser); if (!value) { return parser.getError(); } diff --git a/valdi/src/valdi/runtime/Attributes/Yoga/YGEdgesAttributeHandlerDelegate.hpp b/valdi/src/valdi/runtime/Attributes/Yoga/YGEdgesAttributeHandlerDelegate.hpp index a1093d76..cbe8de69 100644 --- a/valdi/src/valdi/runtime/Attributes/Yoga/YGEdgesAttributeHandlerDelegate.hpp +++ b/valdi/src/valdi/runtime/Attributes/Yoga/YGEdgesAttributeHandlerDelegate.hpp @@ -9,6 +9,10 @@ #include "valdi/runtime/Attributes/Yoga/YogaAttributeHandlerDelegate.hpp" +namespace facebook::yoga { +class Style; +} + namespace Valdi { class YGEdgesBaseAttributeHandlerDelegate : public YogaAttributeHandlerDelegate { @@ -17,36 +21,32 @@ class YGEdgesBaseAttributeHandlerDelegate : public YogaAttributeHandlerDelegate void onReset(YGNodeRef node, YGNodeRef defaultYogaNode) override; protected: - virtual void setEdges(YGNodeRef node, - const YGCompactValue& top, - const YGCompactValue& end, - const YGCompactValue& bottom, - const YGCompactValue& start) = 0; + virtual void setEdges(facebook::yoga::Style& style, + const facebook::yoga::StyleLength& top, + const facebook::yoga::StyleLength& end, + const facebook::yoga::StyleLength& bottom, + const facebook::yoga::StyleLength& start) = 0; private: - Result> parseFlexBoxShorthand(const Value& value); + Result> parseFlexBoxShorthand(const Value& value); }; template class YGEdgesAttributeHandlerDelegate : public YGEdgesBaseAttributeHandlerDelegate { public: - explicit YGEdgesAttributeHandlerDelegate(T&& getEdges) : _getEdges(std::move(getEdges)) {} + explicit YGEdgesAttributeHandlerDelegate(T&& setEdges) : _setEdges(std::move(setEdges)) {} protected: - void setEdges(YGNodeRef node, - const YGCompactValue& top, - const YGCompactValue& end, - const YGCompactValue& bottom, - const YGCompactValue& start) override { - auto edges = _getEdges(node); - edges[YGEdgeTop] = top; - edges[YGEdgeEnd] = end; - edges[YGEdgeBottom] = bottom; - edges[YGEdgeStart] = start; + void setEdges(facebook::yoga::Style& style, + const facebook::yoga::StyleLength& top, + const facebook::yoga::StyleLength& end, + const facebook::yoga::StyleLength& bottom, + const facebook::yoga::StyleLength& start) override { + _setEdges(style, top, end, bottom, start); } private: - T _getEdges; + T _setEdges; }; } // namespace Valdi diff --git a/valdi/src/valdi/runtime/Attributes/Yoga/YGValueAttributeHandlerDelegate.cpp b/valdi/src/valdi/runtime/Attributes/Yoga/YGValueAttributeHandlerDelegate.cpp index 1eece9c6..6e00e5ca 100644 --- a/valdi/src/valdi/runtime/Attributes/Yoga/YGValueAttributeHandlerDelegate.cpp +++ b/valdi/src/valdi/runtime/Attributes/Yoga/YGValueAttributeHandlerDelegate.cpp @@ -9,16 +9,30 @@ namespace Valdi { -YGValueAttributeHandlerDelegate::YGValueAttributeHandlerDelegate(YGNodeValueGetterSetter getterSetter) - : YogaGetterSetterAttributeHandlerDelegate(getterSetter) {} +YGStyleLengthAttributeHandlerDelegate::YGStyleLengthAttributeHandlerDelegate( + YGNodeValueGetterSetter getterSetter) + : YogaGetterSetterAttributeHandlerDelegate(getterSetter) {} -Result YGValueAttributeHandlerDelegate::onApply(YGNodeRef node, const Value& value) { - auto ygValue = valueToYGValue(value); - if (!ygValue) { - return ygValue.moveError(); +Result YGStyleLengthAttributeHandlerDelegate::onApply(YGNodeRef node, const Value& value) { + auto styleLength = valueToYGStyleLength(value); + if (!styleLength) { + return styleLength.moveError(); } - return setValue(node, ygValue.value()); + return setValue(node, styleLength.value()); +} + +YGStyleSizeLengthAttributeHandlerDelegate::YGStyleSizeLengthAttributeHandlerDelegate( + YGNodeValueGetterSetter getterSetter) + : YogaGetterSetterAttributeHandlerDelegate(getterSetter) {} + +Result YGStyleSizeLengthAttributeHandlerDelegate::onApply(YGNodeRef node, const Value& value) { + auto styleSizeLength = valueToYGStyleSizeLength(value); + if (!styleSizeLength) { + return styleSizeLength.moveError(); + } + + return setValue(node, styleSizeLength.value()); } } // namespace Valdi diff --git a/valdi/src/valdi/runtime/Attributes/Yoga/YGValueAttributeHandlerDelegate.hpp b/valdi/src/valdi/runtime/Attributes/Yoga/YGValueAttributeHandlerDelegate.hpp index 1e504858..624b892d 100644 --- a/valdi/src/valdi/runtime/Attributes/Yoga/YGValueAttributeHandlerDelegate.hpp +++ b/valdi/src/valdi/runtime/Attributes/Yoga/YGValueAttributeHandlerDelegate.hpp @@ -11,9 +11,21 @@ namespace Valdi { -class YGValueAttributeHandlerDelegate : public YogaGetterSetterAttributeHandlerDelegate { +class YGStyleLengthAttributeHandlerDelegate + : public YogaGetterSetterAttributeHandlerDelegate { public: - explicit YGValueAttributeHandlerDelegate(YGNodeValueGetterSetter getterSetter); + explicit YGStyleLengthAttributeHandlerDelegate( + YGNodeValueGetterSetter getterSetter); + +protected: + Result onApply(YGNodeRef node, const Value& value) override; +}; + +class YGStyleSizeLengthAttributeHandlerDelegate + : public YogaGetterSetterAttributeHandlerDelegate { +public: + explicit YGStyleSizeLengthAttributeHandlerDelegate( + YGNodeValueGetterSetter getterSetter); protected: Result onApply(YGNodeRef node, const Value& value) override; diff --git a/valdi/src/valdi/runtime/Attributes/Yoga/Yoga.cpp b/valdi/src/valdi/runtime/Attributes/Yoga/Yoga.cpp index caa46900..47c24192 100644 --- a/valdi/src/valdi/runtime/Attributes/Yoga/Yoga.cpp +++ b/valdi/src/valdi/runtime/Attributes/Yoga/Yoga.cpp @@ -7,8 +7,8 @@ #include "valdi/runtime/Attributes/Yoga/Yoga.hpp" #include "valdi/runtime/Views/Frame.hpp" -#include -#include +#include +#include namespace Valdi { @@ -29,20 +29,20 @@ void Yoga::destroyNode(YGNode* node) { } void Yoga::attachViewNode(YGNode* node, ViewNode* viewNode) { - node->setContext(viewNode); + facebook::yoga::resolveRef(node)->setContext(viewNode); } ViewNode* Yoga::getAttachedViewNode(YGNode* node) { - return reinterpret_cast(node->getContext()); + return reinterpret_cast(facebook::yoga::resolveRef(node)->getContext()); } void Yoga::detachViewNode(YGNode* node) { - node->setContext(nullptr); + facebook::yoga::resolveRef(node)->setContext(nullptr); } void Yoga::markNodeDirty(YGNode* node) { - if (node->hasMeasureFunc()) { - node->markDirtyAndPropogate(); + if (node != nullptr) { + facebook::yoga::resolveRef(node)->markDirtyAndPropagate(); } } diff --git a/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributeHandlerDelegate.cpp b/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributeHandlerDelegate.cpp index f6ca1518..1b50e657 100644 --- a/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributeHandlerDelegate.cpp +++ b/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributeHandlerDelegate.cpp @@ -57,11 +57,11 @@ float YogaAttributeHandlerDelegate::roundToPixelGrid(double value) const { return Valdi::roundToPixelGrid(static_cast(value), _yogaAttributes->_pointScale); } -std::optional YogaAttributeHandlerDelegate::parseYGValue(AttributeParser& parser) { +std::optional YogaAttributeHandlerDelegate::parseYGStyleLength(AttributeParser& parser) { parser.tryParseWhitespaces(); if (parser.tryParse("auto")) { - return YGCompactValue::ofAuto(); + return facebook::yoga::StyleLength::ofAuto(); } else { auto d = parser.parseDimension(); if (!d) { @@ -69,26 +69,64 @@ std::optional YogaAttributeHandlerDelegate::parseYGValue(Attribu } if (d.value().unit == Dimension::Unit::Percent) { - return YGCompactValue::of(static_cast(d.value().value)); + return facebook::yoga::StyleLength::percent(static_cast(d.value().value)); } else { - return YGCompactValue::of(roundToPixelGrid(d.value().value)); + return facebook::yoga::StyleLength::points(roundToPixelGrid(d.value().value)); } } } -Result YogaAttributeHandlerDelegate::valueToYGValue(const Value& value) { +Result YogaAttributeHandlerDelegate::valueToYGStyleLength(const Value& value) { if (value.isNumber()) { - return YGCompactValue::of(roundToPixelGrid(value.toDouble())); + return facebook::yoga::StyleLength::points(roundToPixelGrid(value.toDouble())); } else if (value.isString()) { auto strBox = value.toStringBox(); AttributeParser parser(strBox.toStringView()); - auto ygValue = parseYGValue(parser); - if (!ygValue || !parser.ensureIsAtEnd()) { + auto styleLength = parseYGStyleLength(parser); + if (!styleLength || !parser.ensureIsAtEnd()) { return parser.getError(); } - return ygValue.value(); + return styleLength.value(); + } else { + return ValueConverter::invalidTypeFailure(value, ValueType::Double); + } +} + +std::optional +YogaAttributeHandlerDelegate::parseYGStyleSizeLength(AttributeParser& parser) { + parser.tryParseWhitespaces(); + + if (parser.tryParse("auto")) { + return facebook::yoga::StyleSizeLength::ofAuto(); + } else { + auto d = parser.parseDimension(); + if (!d) { + return std::nullopt; + } + + if (d.value().unit == Dimension::Unit::Percent) { + return facebook::yoga::StyleSizeLength::percent(static_cast(d.value().value)); + } else { + return facebook::yoga::StyleSizeLength::points(roundToPixelGrid(d.value().value)); + } + } +} + +Result YogaAttributeHandlerDelegate::valueToYGStyleSizeLength(const Value& value) { + if (value.isNumber()) { + return facebook::yoga::StyleSizeLength::points(roundToPixelGrid(value.toDouble())); + } else if (value.isString()) { + auto strBox = value.toStringBox(); + AttributeParser parser(strBox.toStringView()); + + auto styleSizeLength = parseYGStyleSizeLength(parser); + if (!styleSizeLength || !parser.ensureIsAtEnd()) { + return parser.getError(); + } + + return styleSizeLength.value(); } else { return ValueConverter::invalidTypeFailure(value, ValueType::Double); } diff --git a/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributeHandlerDelegate.hpp b/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributeHandlerDelegate.hpp index 38dae7f1..3f7daa94 100644 --- a/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributeHandlerDelegate.hpp +++ b/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributeHandlerDelegate.hpp @@ -9,13 +9,16 @@ #include "valdi/runtime/Attributes/AttributeHandlerDelegate.hpp" -#include +#include +#include +#include +#include namespace Valdi { class AttributeParser; class YogaAttributes; -using YGCompactValue = facebook::yoga::detail::CompactValue; +using YGFloatOptional = facebook::yoga::FloatOptional; class YogaAttributeHandlerDelegate : public AttributeHandlerDelegate { public: @@ -39,8 +42,10 @@ class YogaAttributeHandlerDelegate : public AttributeHandlerDelegate { virtual Result onApply(YGNodeRef node, const Value& value) = 0; virtual void onReset(YGNodeRef node, YGNodeRef defaultYogaNode) = 0; - std::optional parseYGValue(AttributeParser& parser); - Result valueToYGValue(const Value& value); + std::optional parseYGStyleLength(AttributeParser& parser); + Result valueToYGStyleLength(const Value& value); + std::optional parseYGStyleSizeLength(AttributeParser& parser); + Result valueToYGStyleSizeLength(const Value& value); private: Ref _yogaAttributes; diff --git a/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributes.cpp b/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributes.cpp index b57e38b9..304a8741 100644 --- a/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributes.cpp +++ b/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributes.cpp @@ -17,37 +17,120 @@ #include "valdi/runtime/Context/ViewNode.hpp" #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace Valdi { -static void populateAssociativeEnumMap(FlatMap& associativeMap, - int enumCount, - const char* (*toStringFunction)(int)) { - associativeMap.reserve(static_cast(enumCount)); - - for (int i = 0; i < enumCount; i++) { - auto key = STRING_LITERAL(toStringFunction(i)); - associativeMap[key] = i; +template +static void populateAssociativeEnumMap(FlatMap& associateMap, + std::initializer_list enumValues, + ToStringFunction toStringFunction) { + associateMap.reserve(enumValues.size()); + for (auto enumValue : enumValues) { + associateMap[STRING_LITERAL(toStringFunction(enumValue))] = static_cast(enumValue); } } -template -static void populateAssociativeEnumMap(FlatMap& associateMap) { - populateAssociativeEnumMap(associateMap, facebook::yoga::enums::count(), [](int index) { - return toStringFunction(static_cast(index)); - }); +template +static int toAttributeEnum(Type value) { + return static_cast(value); +} + +template +static Type fromAttributeEnum(int value) { + return static_cast(value); +} + +static void setYGPosition(facebook::yoga::Style& style, YGEdge edge, facebook::yoga::StyleLength value) { + style.setPosition(facebook::yoga::scopedEnum(edge), value); +} + +static void setYGMargin(facebook::yoga::Style& style, YGEdge edge, facebook::yoga::StyleLength value) { + style.setMargin(facebook::yoga::scopedEnum(edge), value); +} + +static void setYGPadding(facebook::yoga::Style& style, YGEdge edge, facebook::yoga::StyleLength value) { + style.setPadding(facebook::yoga::scopedEnum(edge), value); +} + +static void setYGBorder(facebook::yoga::Style& style, YGEdge edge, facebook::yoga::StyleLength value) { + style.setBorder(facebook::yoga::scopedEnum(edge), value); +} + +static void setYGFlexBasis(facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { + style.setFlexBasis(value); } YogaAttributes::YogaAttributes(YGConfig* const yogaConfig, AttributeIds& attributeIds, float pointScale) : _defaultYogaNode(Yoga::createNode(yogaConfig)), _attributeIds(attributeIds), _pointScale(pointScale) { - populateAssociativeEnumMap(_directionToEnum); - populateAssociativeEnumMap(_flexDirectionToEnum); - populateAssociativeEnumMap(_justifyToEnum); - populateAssociativeEnumMap(_alignToEnum); - populateAssociativeEnumMap(_positionTypeToEnum); - populateAssociativeEnumMap(_wrapToEnum); - populateAssociativeEnumMap(_overflowToEnum); - populateAssociativeEnumMap(_displayToEnum); + populateAssociativeEnumMap( + _directionToEnum, + {facebook::yoga::Direction::Inherit, facebook::yoga::Direction::LTR, facebook::yoga::Direction::RTL}, + [](facebook::yoga::Direction value) { return facebook::yoga::toString(value); }); + populateAssociativeEnumMap( + _flexDirectionToEnum, + {facebook::yoga::FlexDirection::Column, + facebook::yoga::FlexDirection::ColumnReverse, + facebook::yoga::FlexDirection::Row, + facebook::yoga::FlexDirection::RowReverse}, + [](facebook::yoga::FlexDirection value) { return facebook::yoga::toString(value); }); + populateAssociativeEnumMap( + _justifyToEnum, + {facebook::yoga::Justify::Auto, + facebook::yoga::Justify::FlexStart, + facebook::yoga::Justify::Center, + facebook::yoga::Justify::FlexEnd, + facebook::yoga::Justify::SpaceBetween, + facebook::yoga::Justify::SpaceAround, + facebook::yoga::Justify::SpaceEvenly, + facebook::yoga::Justify::Stretch, + facebook::yoga::Justify::Start, + facebook::yoga::Justify::End}, + [](facebook::yoga::Justify value) { return facebook::yoga::toString(value); }); + populateAssociativeEnumMap( + _alignToEnum, + {facebook::yoga::Align::Auto, + facebook::yoga::Align::FlexStart, + facebook::yoga::Align::Center, + facebook::yoga::Align::FlexEnd, + facebook::yoga::Align::Stretch, + facebook::yoga::Align::Baseline, + facebook::yoga::Align::SpaceBetween, + facebook::yoga::Align::SpaceAround, + facebook::yoga::Align::SpaceEvenly, + facebook::yoga::Align::Start, + facebook::yoga::Align::End}, + [](facebook::yoga::Align value) { return facebook::yoga::toString(value); }); + populateAssociativeEnumMap( + _positionTypeToEnum, + {facebook::yoga::PositionType::Static, + facebook::yoga::PositionType::Relative, + facebook::yoga::PositionType::Absolute}, + [](facebook::yoga::PositionType value) { return facebook::yoga::toString(value); }); + populateAssociativeEnumMap( + _wrapToEnum, + {facebook::yoga::Wrap::NoWrap, facebook::yoga::Wrap::Wrap, facebook::yoga::Wrap::WrapReverse}, + [](facebook::yoga::Wrap value) { return facebook::yoga::toString(value); }); + populateAssociativeEnumMap( + _overflowToEnum, + {facebook::yoga::Overflow::Visible, facebook::yoga::Overflow::Hidden, facebook::yoga::Overflow::Scroll}, + [](facebook::yoga::Overflow value) { return facebook::yoga::toString(value); }); + populateAssociativeEnumMap( + _displayToEnum, + {facebook::yoga::Display::Flex, + facebook::yoga::Display::None, + facebook::yoga::Display::Contents, + facebook::yoga::Display::Grid}, + [](facebook::yoga::Display value) { return facebook::yoga::toString(value); }); } YogaAttributes::~YogaAttributes() { @@ -65,17 +148,19 @@ void YogaAttributes::bindAttribute(const char* name, } template -static YGNodeValueGetterSetter makePositionEdgeGetterSetter() { - return YGNodeValueGetterSetter( - [](YGNodeRef node) -> YGCompactValue { return node->getStyle().position()[edge]; }, - [](YGNodeRef node, YGCompactValue value) { node->getStyle().position()[edge] = value; }); +static YGNodeValueGetterSetter makePositionEdgeGetterSetter() { + return YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleLength { + return style.position(facebook::yoga::scopedEnum(edge)); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleLength value) { setYGPosition(style, edge, value); }); } void YogaAttributes::bindPositionAttributes(AttributeHandlerById& attributes) { - bindYGValueAttribute("top", false, attributes, makePositionEdgeGetterSetter()); - bindYGValueAttribute("right", false, attributes, makePositionEdgeGetterSetter()); - bindYGValueAttribute("bottom", false, attributes, makePositionEdgeGetterSetter()); - bindYGValueAttribute("left", false, attributes, makePositionEdgeGetterSetter()); + bindYGStyleLengthAttribute("top", false, attributes, makePositionEdgeGetterSetter()); + bindYGStyleLengthAttribute("right", false, attributes, makePositionEdgeGetterSetter()); + bindYGStyleLengthAttribute("bottom", false, attributes, makePositionEdgeGetterSetter()); + bindYGStyleLengthAttribute("left", false, attributes, makePositionEdgeGetterSetter()); } static std::array, 4> getAllEdges() { @@ -104,22 +189,31 @@ void YogaAttributes::bindYGOptionalAttribute(const char* name, name, isForChildrenNode, attributes, makeShared(getterSetter)); } -void YogaAttributes::bindYGValueAttribute(const char* name, - bool isForChildrenNode, - AttributeHandlerById& attributes, - YGNodeValueGetterSetter getterSetter) { - bindAttribute(name, isForChildrenNode, attributes, makeShared(getterSetter)); +void YogaAttributes::bindYGStyleLengthAttribute(const char* name, + bool isForChildrenNode, + AttributeHandlerById& attributes, + YGNodeValueGetterSetter getterSetter) { + bindAttribute(name, isForChildrenNode, attributes, makeShared(getterSetter)); +} + +void YogaAttributes::bindYGStyleSizeLengthAttribute( + const char* name, + bool isForChildrenNode, + AttributeHandlerById& attributes, + YGNodeValueGetterSetter getterSetter) { + bindAttribute( + name, isForChildrenNode, attributes, makeShared(getterSetter)); } -template +template void YogaAttributes::bindPaddingOrMarginAttributes(std::string_view name, bool isForChildrenNode, AttributeHandlerById& attributes, - GetEdges&& getEdges) { + SetEdges&& setEdges) { bindEdgeAttributes(name, isForChildrenNode, attributes, - makeShared>(std::forward(getEdges))); + makeShared>(std::forward(setEdges))); } void YogaAttributes::bindEdgeAttributes(std::string_view name, @@ -160,17 +254,22 @@ void YogaAttributes::bind(AttributeHandlerById& attributes) { attributes, _directionToEnum, YGNodeValueGetterSetter( - [](YGNodeRef node) { return static_cast(node->getStyle().direction()); }, - [](YGNodeRef node, int enumValue) { node->getStyle().direction() = static_cast(enumValue); })); + [](facebook::yoga::Style& style) { return toAttributeEnum(style.direction()); }, + [](facebook::yoga::Style& style, int enumValue) { + style.setDirection(fromAttributeEnum(enumValue)); + })); bindEnumAttribute( "flexDirection", true, attributes, _flexDirectionToEnum, - YGNodeValueGetterSetter([](YGNodeRef node) { return static_cast(node->getStyle().flexDirection()); }, - [](YGNodeRef node, int enumValue) { - node->getStyle().flexDirection() = static_cast(enumValue); + YGNodeValueGetterSetter([](facebook::yoga::Style& style) { + return toAttributeEnum(style.flexDirection()); + }, + [](facebook::yoga::Style& style, int enumValue) { + style.setFlexDirection( + fromAttributeEnum(enumValue)); })); bindEnumAttribute( @@ -178,9 +277,12 @@ void YogaAttributes::bind(AttributeHandlerById& attributes) { true, attributes, _justifyToEnum, - YGNodeValueGetterSetter([](YGNodeRef node) { return static_cast(node->getStyle().justifyContent()); }, - [](YGNodeRef node, int enumValue) { - node->getStyle().justifyContent() = static_cast(enumValue); + YGNodeValueGetterSetter([](facebook::yoga::Style& style) { + return toAttributeEnum(style.justifyContent()); + }, + [](facebook::yoga::Style& style, int enumValue) { + style.setJustifyContent( + fromAttributeEnum(enumValue)); })); bindEnumAttribute( @@ -189,8 +291,12 @@ void YogaAttributes::bind(AttributeHandlerById& attributes) { attributes, _alignToEnum, YGNodeValueGetterSetter( - [](YGNodeRef node) { return static_cast(node->getStyle().alignContent()); }, - [](YGNodeRef node, int enumValue) { node->getStyle().alignContent() = static_cast(enumValue); })); + [](facebook::yoga::Style& style) { + return toAttributeEnum(style.alignContent()); + }, + [](facebook::yoga::Style& style, int enumValue) { + style.setAlignContent(fromAttributeEnum(enumValue)); + })); bindEnumAttribute( "alignItems", @@ -198,8 +304,12 @@ void YogaAttributes::bind(AttributeHandlerById& attributes) { attributes, _alignToEnum, YGNodeValueGetterSetter( - [](YGNodeRef node) { return static_cast(node->getStyle().alignItems()); }, - [](YGNodeRef node, int enumValue) { node->getStyle().alignItems() = static_cast(enumValue); })); + [](facebook::yoga::Style& style) { + return toAttributeEnum(style.alignItems()); + }, + [](facebook::yoga::Style& style, int enumValue) { + style.setAlignItems(fromAttributeEnum(enumValue)); + })); bindEnumAttribute( "alignSelf", @@ -207,17 +317,24 @@ void YogaAttributes::bind(AttributeHandlerById& attributes) { attributes, _alignToEnum, YGNodeValueGetterSetter( - [](YGNodeRef node) { return static_cast(node->getStyle().alignSelf()); }, - [](YGNodeRef node, int enumValue) { node->getStyle().alignSelf() = static_cast(enumValue); })); + [](facebook::yoga::Style& style) { + return toAttributeEnum(style.alignSelf()); + }, + [](facebook::yoga::Style& style, int enumValue) { + style.setAlignSelf(fromAttributeEnum(enumValue)); + })); bindEnumAttribute( "position", false, attributes, _positionTypeToEnum, - YGNodeValueGetterSetter([](YGNodeRef node) { return static_cast(node->getStyle().positionType()); }, - [](YGNodeRef node, int enumValue) { - node->getStyle().positionType() = static_cast(enumValue); + YGNodeValueGetterSetter([](facebook::yoga::Style& style) { + return toAttributeEnum(style.positionType()); + }, + [](facebook::yoga::Style& style, int enumValue) { + style.setPositionType( + fromAttributeEnum(enumValue)); })); bindEnumAttribute( @@ -226,8 +343,12 @@ void YogaAttributes::bind(AttributeHandlerById& attributes) { attributes, _wrapToEnum, YGNodeValueGetterSetter( - [](YGNodeRef node) { return static_cast(node->getStyle().flexWrap()); }, - [](YGNodeRef node, int enumValue) { node->getStyle().flexWrap() = static_cast(enumValue); })); + [](facebook::yoga::Style& style) { + return toAttributeEnum(style.flexWrap()); + }, + [](facebook::yoga::Style& style, int enumValue) { + style.setFlexWrap(fromAttributeEnum(enumValue)); + })); bindEnumAttribute( "overflow", @@ -235,8 +356,12 @@ void YogaAttributes::bind(AttributeHandlerById& attributes) { attributes, _overflowToEnum, YGNodeValueGetterSetter( - [](YGNodeRef node) { return static_cast(node->getStyle().overflow()); }, - [](YGNodeRef node, int enumValue) { node->getStyle().overflow() = static_cast(enumValue); })); + [](facebook::yoga::Style& style) { + return toAttributeEnum(style.overflow()); + }, + [](facebook::yoga::Style& style, int enumValue) { + style.setOverflow(fromAttributeEnum(enumValue)); + })); bindEnumAttribute( "display", @@ -244,136 +369,214 @@ void YogaAttributes::bind(AttributeHandlerById& attributes) { attributes, _displayToEnum, YGNodeValueGetterSetter( - [](YGNodeRef node) { return static_cast(node->getStyle().display()); }, - [](YGNodeRef node, int enumValue) { node->getStyle().display() = static_cast(enumValue); })); + [](facebook::yoga::Style& style) { + return toAttributeEnum(style.display()); + }, + [](facebook::yoga::Style& style, int enumValue) { + style.setDisplay(fromAttributeEnum(enumValue)); + })); bindYGOptionalAttribute("flexGrow", false, attributes, YGNodeValueGetterSetter( - [](YGNodeRef node) -> YGFloatOptional { return node->getStyle().flexGrow(); }, - [](YGNodeRef node, YGFloatOptional value) { node->getStyle().flexGrow() = value; })); + [](facebook::yoga::Style& style) -> YGFloatOptional { + return style.flexGrow(); + }, + [](facebook::yoga::Style& style, YGFloatOptional value) { + style.setFlexGrow(value); + })); bindYGOptionalAttribute("flexShrink", false, attributes, YGNodeValueGetterSetter( - [](YGNodeRef node) -> YGFloatOptional { return node->getStyle().flexShrink(); }, - [](YGNodeRef node, YGFloatOptional value) { node->getStyle().flexShrink() = value; })); + [](facebook::yoga::Style& style) -> YGFloatOptional { + return style.flexShrink(); + }, + [](facebook::yoga::Style& style, YGFloatOptional value) { + style.setFlexShrink(value); + })); - bindYGValueAttribute("flexBasis", - false, - attributes, - YGNodeValueGetterSetter( - [](YGNodeRef node) -> YGCompactValue { return node->getStyle().flexBasis(); }, - [](YGNodeRef node, YGCompactValue value) { node->getStyle().flexBasis() = value; })); + bindYGStyleSizeLengthAttribute("flexBasis", + false, + attributes, + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { + return style.flexBasis(); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { + setYGFlexBasis(style, value); + })); bindPositionAttributes(attributes); - bindPaddingOrMarginAttributes( - "margin", false, attributes, [](YGNodeRef node) -> auto { return node->getStyle().margin(); }); - bindPaddingOrMarginAttributes( - "padding", true, attributes, [](YGNodeRef node) -> auto { return node->getStyle().padding(); }); + bindPaddingOrMarginAttributes("margin", false, attributes, [](facebook::yoga::Style& style, + const facebook::yoga::StyleLength& top, + const facebook::yoga::StyleLength& end, + const facebook::yoga::StyleLength& bottom, + const facebook::yoga::StyleLength& start) { + setYGMargin(style, YGEdgeTop, top); + setYGMargin(style, YGEdgeEnd, end); + setYGMargin(style, YGEdgeBottom, bottom); + setYGMargin(style, YGEdgeStart, start); + }); + bindPaddingOrMarginAttributes("padding", true, attributes, [](facebook::yoga::Style& style, + const facebook::yoga::StyleLength& top, + const facebook::yoga::StyleLength& end, + const facebook::yoga::StyleLength& bottom, + const facebook::yoga::StyleLength& start) { + setYGPadding(style, YGEdgeTop, top); + setYGPadding(style, YGEdgeEnd, end); + setYGPadding(style, YGEdgeBottom, bottom); + setYGPadding(style, YGEdgeStart, start); + }); - bindYGValueAttribute( + bindYGStyleLengthAttribute( "borderTopWidth", false, attributes, - YGNodeValueGetterSetter( - [](YGNodeRef node) -> YGCompactValue { return node->getStyle().border()[YGEdgeTop]; }, - [](YGNodeRef node, YGCompactValue value) { node->getStyle().border()[YGEdgeTop] = value; })); - - bindYGValueAttribute( + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleLength { + return style.border(facebook::yoga::scopedEnum(YGEdgeTop)); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleLength value) { + setYGBorder(style, YGEdgeTop, value); + })); + + bindYGStyleLengthAttribute( "borderRightWidth", false, attributes, - YGNodeValueGetterSetter( - [](YGNodeRef node) -> YGCompactValue { return node->getStyle().border()[YGEdgeEnd]; }, - [](YGNodeRef node, YGCompactValue value) { node->getStyle().border()[YGEdgeEnd] = value; })); - - bindYGValueAttribute( + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleLength { + return style.border(facebook::yoga::scopedEnum(YGEdgeEnd)); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleLength value) { + setYGBorder(style, YGEdgeEnd, value); + })); + + bindYGStyleLengthAttribute( "borderBottomWidth", false, attributes, - YGNodeValueGetterSetter( - [](YGNodeRef node) -> YGCompactValue { return node->getStyle().border()[YGEdgeBottom]; }, - [](YGNodeRef node, YGCompactValue value) { node->getStyle().border()[YGEdgeBottom] = value; })); - - bindYGValueAttribute( + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleLength { + return style.border(facebook::yoga::scopedEnum(YGEdgeBottom)); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleLength value) { + setYGBorder(style, YGEdgeBottom, value); + })); + + bindYGStyleLengthAttribute( "borderLeftWidth", false, attributes, - YGNodeValueGetterSetter( - [](YGNodeRef node) -> YGCompactValue { return node->getStyle().border()[YGEdgeStart]; }, - [](YGNodeRef node, YGCompactValue value) { node->getStyle().border()[YGEdgeStart] = value; })); - - bindYGValueAttribute( + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleLength { + return style.border(facebook::yoga::scopedEnum(YGEdgeStart)); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleLength value) { + setYGBorder(style, YGEdgeStart, value); + })); + + bindYGStyleLengthAttribute( "borderWidth", false, attributes, - YGNodeValueGetterSetter( - [](YGNodeRef node) -> YGCompactValue { return node->getStyle().border()[YGEdgeAll]; }, - [](YGNodeRef node, YGCompactValue value) { node->getStyle().border()[YGEdgeAll] = value; })); - - bindYGValueAttribute( + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleLength { + return style.border(facebook::yoga::scopedEnum(YGEdgeAll)); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleLength value) { + setYGBorder(style, YGEdgeAll, value); + })); + + bindYGStyleSizeLengthAttribute( "width", false, attributes, - YGNodeValueGetterSetter( - [](YGNodeRef node) -> YGCompactValue { return node->getStyle().dimensions()[YGDimensionWidth]; }, - [](YGNodeRef node, YGCompactValue value) { node->getStyle().dimensions()[YGDimensionWidth] = value; })); - - bindYGValueAttribute( + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { + return style.dimension(facebook::yoga::Dimension::Width); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { + style.setDimension(facebook::yoga::Dimension::Width, value); + })); + + bindYGStyleSizeLengthAttribute( "height", false, attributes, - YGNodeValueGetterSetter( - [](YGNodeRef node) -> YGCompactValue { return node->getStyle().dimensions()[YGDimensionHeight]; }, - [](YGNodeRef node, YGCompactValue value) { node->getStyle().dimensions()[YGDimensionHeight] = value; })); - - bindYGValueAttribute( + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { + return style.dimension(facebook::yoga::Dimension::Height); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { + style.setDimension(facebook::yoga::Dimension::Height, value); + })); + + bindYGStyleSizeLengthAttribute( "minWidth", false, attributes, - YGNodeValueGetterSetter( - [](YGNodeRef node) -> YGCompactValue { return node->getStyle().minDimensions()[YGDimensionWidth]; }, - [](YGNodeRef node, YGCompactValue value) { node->getStyle().minDimensions()[YGDimensionWidth] = value; })); - - bindYGValueAttribute( + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { + return style.minDimension(facebook::yoga::Dimension::Width); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { + style.setMinDimension(facebook::yoga::Dimension::Width, value); + })); + + bindYGStyleSizeLengthAttribute( "minHeight", false, attributes, - YGNodeValueGetterSetter( - [](YGNodeRef node) -> YGCompactValue { return node->getStyle().minDimensions()[YGDimensionHeight]; }, - [](YGNodeRef node, YGCompactValue value) { node->getStyle().minDimensions()[YGDimensionHeight] = value; })); - - bindYGValueAttribute( + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { + return style.minDimension(facebook::yoga::Dimension::Height); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { + style.setMinDimension(facebook::yoga::Dimension::Height, value); + })); + + bindYGStyleSizeLengthAttribute( "maxWidth", false, attributes, - YGNodeValueGetterSetter( - [](YGNodeRef node) -> YGCompactValue { return node->getStyle().maxDimensions()[YGDimensionWidth]; }, - [](YGNodeRef node, YGCompactValue value) { node->getStyle().maxDimensions()[YGDimensionWidth] = value; })); - - bindYGValueAttribute( + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { + return style.maxDimension(facebook::yoga::Dimension::Width); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { + style.setMaxDimension(facebook::yoga::Dimension::Width, value); + })); + + bindYGStyleSizeLengthAttribute( "maxHeight", false, attributes, - YGNodeValueGetterSetter( - [](YGNodeRef node) -> YGCompactValue { return node->getStyle().maxDimensions()[YGDimensionHeight]; }, - [](YGNodeRef node, YGCompactValue value) { node->getStyle().maxDimensions()[YGDimensionHeight] = value; })); + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { + return style.maxDimension(facebook::yoga::Dimension::Height); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { + style.setMaxDimension(facebook::yoga::Dimension::Height, value); + })); bindYGOptionalAttribute("aspectRatio", false, attributes, YGNodeValueGetterSetter( - [](YGNodeRef node) -> YGFloatOptional { return node->getStyle().aspectRatio(); }, - [](YGNodeRef node, YGFloatOptional value) { + [](facebook::yoga::Style& style) -> YGFloatOptional { + return style.aspectRatio(); + }, + [](facebook::yoga::Style& style, YGFloatOptional value) { if (!value.isUndefined() && value.unwrap() == 0) { // safety for aspectRatio 0, we make it undefined instead. - node->getStyle().aspectRatio() = YGFloatOptional(); + style.setAspectRatio(YGFloatOptional()); } else { - node->getStyle().aspectRatio() = value; + style.setAspectRatio(value); } })); } diff --git a/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributes.hpp b/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributes.hpp index 5b3f29eb..c8507b33 100644 --- a/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributes.hpp +++ b/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributes.hpp @@ -22,13 +22,14 @@ #include #include -#include +#include +#include struct YGConfig; namespace Valdi { -using YGCompactValue = facebook::yoga::detail::CompactValue; +using YGFloatOptional = facebook::yoga::FloatOptional; class YogaAttributeHandlerDelegate; @@ -71,16 +72,21 @@ class YogaAttributes : public SimpleRefCountable { AttributeHandlerById& attributes, YGNodeValueGetterSetter getterSetter); - void bindYGValueAttribute(const char* name, - bool isForChildrenNode, - AttributeHandlerById& attributes, - YGNodeValueGetterSetter getterSetter); + void bindYGStyleLengthAttribute(const char* name, + bool isForChildrenNode, + AttributeHandlerById& attributes, + YGNodeValueGetterSetter getterSetter); - template + void bindYGStyleSizeLengthAttribute(const char* name, + bool isForChildrenNode, + AttributeHandlerById& attributes, + YGNodeValueGetterSetter getterSetter); + + template void bindPaddingOrMarginAttributes(std::string_view name, bool isForChildrenNode, AttributeHandlerById& attributes, - GetEdges&& getEdges); + SetEdges&& setEdges); void bindEdgeAttributes(std::string_view name, bool isForChildrenNode, diff --git a/valdi/src/valdi/runtime/Attributes/Yoga/YogaGetterSetterAttributeHandlerDelegate.hpp b/valdi/src/valdi/runtime/Attributes/Yoga/YogaGetterSetterAttributeHandlerDelegate.hpp index ca6ff786..2f3add33 100644 --- a/valdi/src/valdi/runtime/Attributes/Yoga/YogaGetterSetterAttributeHandlerDelegate.hpp +++ b/valdi/src/valdi/runtime/Attributes/Yoga/YogaGetterSetterAttributeHandlerDelegate.hpp @@ -9,12 +9,14 @@ #include "valdi/runtime/Attributes/Yoga/YogaAttributeHandlerDelegate.hpp" +#include + namespace Valdi { template struct YGNodeValueGetterSetter { - using Setter = void (*)(YGNodeRef, T); - using Getter = T (*)(YGNodeRef); + using Setter = void (*)(facebook::yoga::Style&, T); + using Getter = T (*)(facebook::yoga::Style&); Getter get; Setter set; @@ -30,12 +32,12 @@ class YogaGetterSetterAttributeHandlerDelegate : public YogaAttributeHandlerDele protected: void onReset(YGNodeRef node, YGNodeRef defaultYogaNode) override { - auto defaultValue = _getterSetter.get(defaultYogaNode); - _getterSetter.set(node, defaultValue); + auto defaultValue = _getterSetter.get(facebook::yoga::resolveRef(defaultYogaNode)->style()); + _getterSetter.set(facebook::yoga::resolveRef(node)->style(), defaultValue); } inline Result setValue(YGNodeRef node, const T& value) { - _getterSetter.set(node, value); + _getterSetter.set(facebook::yoga::resolveRef(node)->style(), value); return Void(); } diff --git a/valdi/src/valdi/runtime/Context/ViewNode.cpp b/valdi/src/valdi/runtime/Context/ViewNode.cpp index 94143011..7016388f 100644 --- a/valdi/src/valdi/runtime/Context/ViewNode.cpp +++ b/valdi/src/valdi/runtime/Context/ViewNode.cpp @@ -41,28 +41,45 @@ #include #include #include -#include +#include +#include +#include +#include +#include namespace Valdi { -YGSize ygMeasureYoga( - YGNodeRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode, void* context); -void ygDirtiedCallback(YGNodeRef node); +YGSize ygMeasureYoga(YGNodeConstRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode); +void ygDirtiedCallback(YGNodeConstRef node); void destroyYogaNode(YGNode* yogaNode) { if (yogaNode == nullptr) { return; } - yogaNode->setContext(nullptr); - yogaNode->setDirtiedFunc(nullptr); - yogaNode->setMeasureFunc(nullptr); + auto* node = facebook::yoga::resolveRef(yogaNode); + node->setContext(nullptr); + node->setDirtiedFunc(nullptr); + node->setMeasureFunc(nullptr); YGNodeFree(yogaNode); } void setupYogaNode(YGNode* yogaNode, ViewNode* viewNode) { Yoga::attachViewNode(yogaNode, viewNode); - yogaNode->setDirty(true); - yogaNode->setDirtiedFunc(ygDirtiedCallback); + auto* node = facebook::yoga::resolveRef(yogaNode); + node->setDirty(true); + node->setDirtiedFunc(ygDirtiedCallback); +} + +static facebook::yoga::Style& getYogaStyle(YGNodeRef node) { + return facebook::yoga::resolveRef(node)->style(); +} + +static facebook::yoga::Node* resolveYogaNode(YGNodeRef node) { + return facebook::yoga::resolveRef(node); +} + +static const facebook::yoga::Node* resolveYogaNode(YGNodeConstRef node) { + return facebook::yoga::resolveRef(node); } static auto getBackend(ViewNodeTree* tree) { @@ -86,10 +103,11 @@ static inline float sanitizeYogaValue(float yogaValue) { } static Frame ygNodeGetFrame(YGNode* yogaNode, float offsetX) { - return Frame(sanitizeYogaValue(YGNodeLayoutGetLeft(yogaNode) + offsetX), - sanitizeYogaValue(YGNodeLayoutGetTop(yogaNode)), - sanitizeYogaValue(YGNodeLayoutGetWidth(yogaNode)), - sanitizeYogaValue(YGNodeLayoutGetHeight(yogaNode))); + const auto& layout = resolveYogaNode(yogaNode)->getLayout(); + return Frame(sanitizeYogaValue(layout.position(facebook::yoga::PhysicalEdge::Left) + offsetX), + sanitizeYogaValue(layout.position(facebook::yoga::PhysicalEdge::Top)), + sanitizeYogaValue(layout.dimension(facebook::yoga::Dimension::Width)), + sanitizeYogaValue(layout.dimension(facebook::yoga::Dimension::Height))); } LazyLayoutData::~LazyLayoutData() { @@ -1278,7 +1296,7 @@ void ViewNode::setViewFactory(ViewTransactionScope& viewTransactionScope, const (boundAttributes != nullptr && boundAttributes->isBackingClassScrollable()); if (_flags[kScrollAttributesBound]) { - getYogaNodeForInsertingChildren()->getStyle().overflow() = YGOverflowScroll; + getYogaStyle(getYogaNodeForInsertingChildren()).setOverflow(facebook::yoga::Overflow::Scroll); } } else { setIsLayout(true); @@ -1338,7 +1356,7 @@ void ViewNode::removeFromParent(ViewTransactionScope& viewTransactionScope) { } auto parent = getParent(); - auto* owner = YGNodeGetOwner(_yogaNode); + auto* owner = resolveYogaNode(_yogaNode)->getOwner(); if (owner != nullptr) { YGNodeRemoveChild(owner, _yogaNode); } @@ -1364,11 +1382,12 @@ void ViewNode::insertChildAt(ViewTransactionScope& viewTransactionScope, const R child->setHasParent(true); auto* yogaContainer = getYogaNodeForInsertingChildren(); - if (static_cast(YGNodeGetChildCount(yogaContainer)) < index) { + auto* yogaContainerNode = resolveYogaNode(yogaContainer); + if (yogaContainerNode->getChildCount() < index) { VALDI_ERROR(getLogger(), "Cannot insert child at index {} into container with size {} (child had parent: {})", index, - YGNodeGetChildCount(yogaContainer), + yogaContainerNode->getChildCount(), childHadParent); VALDI_ERROR(getLogger(), "Root component path: {}", getViewNodeTree()->getContext()->getPath().toString()); VALDI_ERROR(getLogger(), "Parent XML: {}", toXML()); @@ -1377,8 +1396,8 @@ void ViewNode::insertChildAt(ViewTransactionScope& viewTransactionScope, const R return; } - yogaContainer->setMeasureFunc(nullptr); - YGNodeInsertChild(yogaContainer, child->_yogaNode, static_cast(index)); + yogaContainerNode->setMeasureFunc(nullptr); + YGNodeInsertChild(yogaContainer, child->_yogaNode, index); child->getCSSAttributesManager().setParent(&getCSSAttributesManager()); @@ -1418,7 +1437,7 @@ bool ViewNode::invalidateMeasuredSize() { return false; } - if (!_yogaNode->getLayout().didUseCustomMeasure()) { + if (!facebook::yoga::resolveRef(_yogaNode)->hasMeasureFunc()) { // If we did not use a custom measure, there is no need to dirty our yoga node, because it would not have // any impact on the layout. return false; @@ -1436,12 +1455,12 @@ bool ViewNode::markLayoutDirty() { return false; } - _yogaNode->markDirtyAndPropogate(); + facebook::yoga::resolveRef(_yogaNode)->markDirtyAndPropagate(); return true; } bool ViewNode::isFlexLayoutDirty() const { - return _yogaNode->isDirty(); + return resolveYogaNode(_yogaNode)->isDirty(); } bool ViewNode::isLazyLayoutDirty() const { @@ -1548,7 +1567,7 @@ void ViewNode::setIsLayout(bool isLayout) { } bool ViewNode::needsYogaMeasureFunc() const { - if (!hasParent() || !_yogaNode->getChildren().empty()) { + if (!hasParent() || resolveYogaNode(_yogaNode)->getChildCount() > 0) { // No measure func if we are the root or if we are a container return false; } @@ -1568,10 +1587,11 @@ void ViewNode::updateYogaMeasureFunc() { return; } + auto* node = facebook::yoga::resolveRef(_yogaNode); if (needsYogaMeasureFunc()) { - _yogaNode->setMeasureFunc(ygMeasureYoga); + node->setMeasureFunc(ygMeasureYoga); } else { - _yogaNode->setMeasureFunc(nullptr); + node->setMeasureFunc(nullptr); } } @@ -1639,7 +1659,8 @@ YGNode* ViewNode::getYogaNodeForInsertingChildren() { } auto& lazyLayoutData = getOrCreateLazyLayoutData(); if (lazyLayoutData.yogaNode == nullptr) { - lazyLayoutData.yogaNode = Yoga::createNode(_yogaNode->getConfig()); + lazyLayoutData.yogaNode = + Yoga::createNode(const_cast(facebook::yoga::resolveRef(_yogaNode)->getConfig())); setupYogaNode(lazyLayoutData.yogaNode, this); // First time we are inserting in this lazyLayout, schedule a lazyLayout pass since our node // will be already dirty and won't trigger the yoga dirtied callbacks. @@ -1655,14 +1676,14 @@ size_t ViewNode::getChildCount() const { return 0; } - return yogaNode->getChildren().size(); + return resolveYogaNode(yogaNode)->getChildCount(); } ViewNode* ViewNode::getChildAt(size_t index) const { const auto* yogaNode = getContainerYogaNode(); SC_ASSERT_NOTNULL(yogaNode); - auto* childYogaNode = yogaNode->getChildren()[index]; + auto* childYogaNode = resolveYogaNode(yogaNode)->getChild(index); auto* childViewNode = reinterpret_cast(Yoga::getAttachedViewNode(childYogaNode)); return childViewNode; } @@ -2018,27 +2039,20 @@ struct MeasureMetrics { uint32_t totalMeasure = 0; }; -YGValue resolveYogaValue(float containerSize, YGValue appliedValue) { +static thread_local MeasureMetrics* currentMeasureMetrics = nullptr; + +facebook::yoga::StyleSizeLength resolveYogaValue(float containerSize, facebook::yoga::StyleSizeLength appliedValue) { float resolvedValue; - switch (appliedValue.unit) { - case YGUnitUndefined: - case YGUnitAuto: - resolvedValue = containerSize; - break; - case YGUnitPoint: - resolvedValue = std::min(appliedValue.value, containerSize); - break; - case YGUnitPercent: - resolvedValue = appliedValue.value * containerSize * 0.01f; - break; + if (appliedValue.isPoints()) { + resolvedValue = std::min(appliedValue.value().unwrap(), containerSize); + } else if (appliedValue.isPercent()) { + resolvedValue = appliedValue.value().unwrap() * containerSize * 0.01f; + } else { + resolvedValue = containerSize; } - YGValue out; - out.value = resolvedValue; - out.unit = YGUnitPoint; - - return out; + return facebook::yoga::StyleSizeLength::points(resolvedValue); } static void doCalculateLayoutOnNode(YGNode* yogaNode, @@ -2048,13 +2062,13 @@ static void doCalculateLayoutOnNode(YGNode* yogaNode, MeasureMode heightMode, LayoutDirection direction, MeasureMetrics& measureCount) { - YGDirection yogaDirection; + facebook::yoga::Direction yogaDirection; switch (direction) { case LayoutDirectionLTR: - yogaDirection = YGDirectionLTR; + yogaDirection = facebook::yoga::Direction::LTR; break; case LayoutDirectionRTL: - yogaDirection = YGDirectionRTL; + yogaDirection = facebook::yoga::Direction::RTL; break; } @@ -2066,8 +2080,8 @@ static void doCalculateLayoutOnNode(YGNode* yogaNode, heightMode = MeasureModeUnspecified; } - YGValue savedMaxWidth = yogaNode->getStyle().maxDimensions()[YGDimensionWidth]; - YGValue savedMaxHeight = yogaNode->getStyle().maxDimensions()[YGDimensionHeight]; + auto savedMaxWidth = getYogaStyle(yogaNode).maxDimension(facebook::yoga::Dimension::Width); + auto savedMaxHeight = getYogaStyle(yogaNode).maxDimension(facebook::yoga::Dimension::Height); float ownerWidth; switch (widthMode) { @@ -2079,7 +2093,8 @@ static void doCalculateLayoutOnNode(YGNode* yogaNode, break; case MeasureModeAtMost: ownerWidth = width; - yogaNode->getStyle().maxDimensions()[YGDimensionWidth] = resolveYogaValue(width, savedMaxWidth); + getYogaStyle(yogaNode).setMaxDimension(facebook::yoga::Dimension::Width, + resolveYogaValue(width, savedMaxWidth)); break; } @@ -2093,15 +2108,18 @@ static void doCalculateLayoutOnNode(YGNode* yogaNode, break; case MeasureModeAtMost: ownerHeight = height; - yogaNode->getStyle().maxDimensions()[YGDimensionHeight] = resolveYogaValue(height, savedMaxHeight); + getYogaStyle(yogaNode).setMaxDimension(facebook::yoga::Dimension::Height, + resolveYogaValue(height, savedMaxHeight)); break; } - YGNodeCalculateLayoutWithContext( - yogaNode, ownerWidth, ownerHeight, yogaDirection, static_cast(&measureCount)); + auto* previousMeasureMetrics = currentMeasureMetrics; + currentMeasureMetrics = &measureCount; + facebook::yoga::calculateLayout(facebook::yoga::resolveRef(yogaNode), ownerWidth, ownerHeight, yogaDirection); + currentMeasureMetrics = previousMeasureMetrics; - yogaNode->getStyle().maxDimensions()[YGDimensionWidth] = savedMaxWidth; - yogaNode->getStyle().maxDimensions()[YGDimensionHeight] = savedMaxHeight; + getYogaStyle(yogaNode).setMaxDimension(facebook::yoga::Dimension::Width, savedMaxWidth); + getYogaStyle(yogaNode).setMaxDimension(facebook::yoga::Dimension::Height, savedMaxHeight); } bool ViewNode::calculateLayoutOnNodeIfNeeded(YGNode* yogaNode, @@ -2112,7 +2130,7 @@ bool ViewNode::calculateLayoutOnNodeIfNeeded(YGNode* yogaNode, LayoutDirection direction, bool forceLayout, bool isFromLazyLayout) const { - if (!yogaNode->isDirty() && !forceLayout) { + if (!resolveYogaNode(yogaNode)->isDirty() && !forceLayout) { return false; } @@ -2253,7 +2271,7 @@ bool ViewNode::updateCalculatedFrame(float viewOffsetX, auto newFrame = ygNodeGetFrame(_yogaNode, rtlOffsetX); auto newViewFrame = newFrame.withOffset(viewOffsetX, viewOffsetY); - auto hasNewLayout = _yogaNode->getHasNewLayout(); + auto hasNewLayout = resolveYogaNode(_yogaNode)->getHasNewLayout(); auto hadLayout = _flags[kLayoutDidCompleteOnceFlag]; auto layoutIsRightToLeft = isRightToLeft(); @@ -2282,7 +2300,7 @@ bool ViewNode::updateCalculatedFrame(float viewOffsetX, _flags[kLayoutDidCompleteOnceFlag] = true; - _yogaNode->setHasNewLayout(false); + resolveYogaNode(_yogaNode)->setHasNewLayout(false); return true; } @@ -2291,10 +2309,10 @@ bool ViewNode::updateLazyLayout() { // Using lazy-layout creates a new "detached" yoga subtree, so the device-level RTL/LTR style that // we set on the root node doesn't propagate to that detached subtree. // Need to manually set the Direction style on the detached subtree. - auto direction = _yogaNode->getLayout().direction(); - auto previousDirection = _lazyLayoutData->yogaNode->getLayout().direction(); + auto direction = facebook::yoga::resolveRef(_yogaNode)->getLayout().direction(); + auto previousDirection = facebook::yoga::resolveRef(_lazyLayoutData->yogaNode)->getLayout().direction(); auto directionHasChanged = direction != previousDirection; - YGNodeStyleSetDirection(_lazyLayoutData->yogaNode, direction); + getYogaStyle(_lazyLayoutData->yogaNode).setDirection(direction); auto sizeHasChanged = _lazyLayoutData->availableWidth != _calculatedFrame.width || _lazyLayoutData->availableHeight != _calculatedFrame.height; @@ -2320,26 +2338,29 @@ void ViewNode::updateScrollState() { auto& scrollState = getOrCreateScrollState(); scrollState.setInScrollMode(true); - auto hadOverflow = _yogaNode->getLayout().hadOverflow(); + auto hadOverflow = resolveYogaNode(_yogaNode)->getLayout().hadOverflow(); auto* yogaContainer = getYogaNodeForInsertingChildren(); + auto* yogaContainerNode = resolveYogaNode(yogaContainer); float rtlOffsetX = 0; if (isRightToLeft()) { - if (hadOverflow && _yogaNode->getStyle().flexDirection() == YGFlexDirectionRow) { + if (hadOverflow && getYogaStyle(_yogaNode).flexDirection() == facebook::yoga::FlexDirection::Row) { // On RTL horizontal, we need to adjust the calculated frames as yoga doesn't move the items to the // right of the container. float lowestLeft = 0; - for (auto* childYogaNode : yogaContainer->getChildren()) { - auto left = - ygNodeGetFrame(childYogaNode, -sanitizeYogaValue(childYogaNode->getLayout().margin[YGEdgeLeft])) - .getLeft(); + for (auto* childYogaNode : yogaContainerNode->getChildren()) { + auto left = ygNodeGetFrame( + childYogaNode, + -sanitizeYogaValue(childYogaNode->getLayout().margin(facebook::yoga::PhysicalEdge::Left))) + .getLeft(); lowestLeft = std::min(left, lowestLeft); } - lowestLeft -= sanitizeYogaValue(_yogaNode->getLayout().padding[YGEdgeLeft]); + lowestLeft -= + sanitizeYogaValue(resolveYogaNode(_yogaNode)->getLayout().padding(facebook::yoga::PhysicalEdge::Left)); rtlOffsetX = -lowestLeft; } else { @@ -2352,18 +2373,18 @@ void ViewNode::updateScrollState() { float highestWidth = 0; float highestHeight = 0; - for (auto* childYogaNode : yogaContainer->getChildren()) { + for (auto* childYogaNode : yogaContainerNode->getChildren()) { auto frame = ygNodeGetFrame(childYogaNode, rtlOffsetX); - auto right = frame.getRight() + childYogaNode->getLayout().margin[YGEdgeRight]; - auto bottom = frame.getBottom() + childYogaNode->getLayout().margin[YGEdgeBottom]; + auto right = frame.getRight() + childYogaNode->getLayout().margin(facebook::yoga::PhysicalEdge::Right); + auto bottom = frame.getBottom() + childYogaNode->getLayout().margin(facebook::yoga::PhysicalEdge::Bottom); highestWidth = std::max(right, highestWidth); highestHeight = std::max(bottom, highestHeight); }; - highestWidth += yogaContainer->getLayout().padding[YGEdgeRight]; - highestHeight += yogaContainer->getLayout().padding[YGEdgeBottom]; + highestWidth += yogaContainerNode->getLayout().padding(facebook::yoga::PhysicalEdge::Right); + highestHeight += yogaContainerNode->getLayout().padding(facebook::yoga::PhysicalEdge::Bottom); auto updateResult = scrollState.updateContentSizeAndRtlOffset( Size(highestWidth, highestHeight), _calculatedFrame.size(), rtlOffsetX, getPointScale(), isHorizontal()); @@ -2392,10 +2413,12 @@ void ViewNode::updateScrollState() { int anchorPosition = anchorNode->getScrollAnchorPosition(); // Compute anchor's Y relative to this scroll node - float anchorY = sanitizeYogaValue(YGNodeLayoutGetTop(anchorNode->getYogaNode())); + float anchorY = sanitizeYogaValue( + resolveYogaNode(anchorNode->getYogaNode())->getLayout().position(facebook::yoga::PhysicalEdge::Top)); for (auto* parent = anchorNode->getParent().get(); parent != nullptr && parent != this; parent = parent->getParent().get()) { - anchorY += sanitizeYogaValue(YGNodeLayoutGetTop(parent->getYogaNode())); + anchorY += sanitizeYogaValue( + resolveYogaNode(parent->getYogaNode())->getLayout().position(facebook::yoga::PhysicalEdge::Top)); } // Pin anchor to the specified viewport edge @@ -2634,8 +2657,9 @@ bool ViewNode::handleCSSChange(bool cssChanged) { } bool ViewNode::isHorizontal() { - auto flexDirection = getYogaNodeForInsertingChildren()->getStyle().flexDirection(); - return flexDirection == YGFlexDirectionRow || flexDirection == YGFlexDirectionRowReverse; + auto flexDirection = getYogaStyle(getYogaNodeForInsertingChildren()).flexDirection(); + return flexDirection == facebook::yoga::FlexDirection::Row || + flexDirection == facebook::yoga::FlexDirection::RowReverse; } bool ViewNode::setAttribute(ViewTransactionScope& viewTransactionScope, @@ -2802,7 +2826,7 @@ bool ViewNode::isRightToLeft() const { if (_yogaNode == nullptr) { return false; } - return _yogaNode->getLayout().direction() == YGDirectionRTL; + return facebook::yoga::resolveRef(_yogaNode)->getLayout().direction() == facebook::yoga::Direction::RTL; } PlatformType ViewNode::getPlatformType() const { @@ -3039,15 +3063,16 @@ void ViewNode::updateCSS(ViewTransactionScope& viewTransactionScope, const RefgetStyle().flexDirection() != newDirection) { - getYogaNodeForInsertingChildren()->getStyle().flexDirection() = newDirection; + auto* yogaNode = getYogaNodeForInsertingChildren(); + if (getYogaStyle(yogaNode).flexDirection() != newDirection) { + getYogaStyle(yogaNode).setFlexDirection(newDirection); markLayoutDirty(); } @@ -3508,15 +3533,13 @@ static MeasureMode yogaMeasureModeToValdiMeasureMode(YGMeasureMode measureMode) } } -YGSize ygMeasureYoga( - YGNodeRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode, void* context) { - auto* viewNode = reinterpret_cast(Yoga::getAttachedViewNode(node)); +YGSize ygMeasureYoga(YGNodeConstRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode) { + auto* viewNode = reinterpret_cast(facebook::yoga::resolveRef(node)->getContext()); if (viewNode == nullptr) { return {.width = 0, .height = 0}; } - SC_ASSERT(context != nullptr); - auto measureCount = reinterpret_cast(context); - measureCount->totalMeasure++; + SC_ASSERT(currentMeasureMetrics != nullptr); + currentMeasureMetrics->totalMeasure++; auto convertedWidthMode = yogaMeasureModeToValdiMeasureMode(widthMode); auto convertedHeightMode = yogaMeasureModeToValdiMeasureMode(heightMode); @@ -3531,8 +3554,8 @@ YGSize ygMeasureYoga( }; } -void ygDirtiedCallback(YGNodeRef node) { - auto* viewNode = reinterpret_cast(Yoga::getAttachedViewNode(node)); +void ygDirtiedCallback(YGNodeConstRef node) { + auto* viewNode = reinterpret_cast(facebook::yoga::resolveRef(node)->getContext()); if (viewNode == nullptr) { return; } diff --git a/valdi/src/valdi/runtime/Runtime.cpp b/valdi/src/valdi/runtime/Runtime.cpp index fb8cec56..0572c2c0 100644 --- a/valdi/src/valdi/runtime/Runtime.cpp +++ b/valdi/src/valdi/runtime/Runtime.cpp @@ -56,6 +56,7 @@ #include #include #include +#include namespace Valdi { From 74224e2b5a40d5dfda955ac3794b33d27b192ee1 Mon Sep 17 00:00:00 2001 From: Simon Corsin Date: Fri, 17 Apr 2026 16:41:23 -0500 Subject: [PATCH 4/9] Added gap, rowGap, columnGap attribute (cherry picked from commit 1a2e639eb8576e8decae223e26c79d55c68cfd45) --- .../valdi_tsx/src/NativeTemplateElements.d.ts | 13 ++++ .../Attributes/Yoga/YogaAttributes.cpp | 12 ++++ valdi/test/runtime/ViewNode_tests.cpp | 62 +++++++++++++++++++ 3 files changed, 87 insertions(+) diff --git a/src/valdi_modules/src/valdi/valdi_tsx/src/NativeTemplateElements.d.ts b/src/valdi_modules/src/valdi/valdi_tsx/src/NativeTemplateElements.d.ts index 6a484996..2040d6ba 100644 --- a/src/valdi_modules/src/valdi/valdi_tsx/src/NativeTemplateElements.d.ts +++ b/src/valdi_modules/src/valdi/valdi_tsx/src/NativeTemplateElements.d.ts @@ -465,6 +465,19 @@ interface LayoutChildrenAttributes { */ paddingLeft?: CSSValue; + /** + * Sets spacing between children on both axes. + */ + gap?: CSSValue; + /** + * Sets spacing between flex rows. + */ + rowGap?: CSSValue; + /** + * Sets spacing between flex columns. + */ + columnGap?: CSSValue; + /** * Layout direction specifies the direction in which children and text in a hierarchy should be laid out. * Layout direction also affects what edge start and end refer to. diff --git a/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributes.cpp b/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributes.cpp index 304a8741..a55d9f71 100644 --- a/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributes.cpp +++ b/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributes.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +71,13 @@ static void setYGFlexBasis(facebook::yoga::Style& style, facebook::yoga::StyleSi style.setFlexBasis(value); } +template +static YGNodeValueGetterSetter makeGapGetterSetter() { + return YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleLength { return style.gap(gutter); }, + [](facebook::yoga::Style& style, facebook::yoga::StyleLength value) { style.setGap(gutter, value); }); +} + YogaAttributes::YogaAttributes(YGConfig* const yogaConfig, AttributeIds& attributeIds, float pointScale) : _defaultYogaNode(Yoga::createNode(yogaConfig)), _attributeIds(attributeIds), _pointScale(pointScale) { populateAssociativeEnumMap( @@ -432,6 +440,10 @@ void YogaAttributes::bind(AttributeHandlerById& attributes) { setYGPadding(style, YGEdgeStart, start); }); + bindYGStyleLengthAttribute("gap", true, attributes, makeGapGetterSetter()); + bindYGStyleLengthAttribute("rowGap", true, attributes, makeGapGetterSetter()); + bindYGStyleLengthAttribute("columnGap", true, attributes, makeGapGetterSetter()); + bindYGStyleLengthAttribute( "borderTopWidth", false, diff --git a/valdi/test/runtime/ViewNode_tests.cpp b/valdi/test/runtime/ViewNode_tests.cpp index a10495fb..f4f12bd8 100644 --- a/valdi/test/runtime/ViewNode_tests.cpp +++ b/valdi/test/runtime/ViewNode_tests.cpp @@ -401,6 +401,68 @@ TEST(ViewNode, supportsAllMeasureModes) { ASSERT_EQ(30.0f, size.height); } +TEST(ViewNode, supportsGap) { + ViewNodeTestsDependencies utils; + + auto root = utils.createRootView(); + auto child1 = utils.createView(); + auto child2 = utils.createView(); + auto child3 = utils.createView(); + + utils.setViewNodeAttribute(root, "flexDirection", Value(STRING_LITERAL("row"))); + utils.setViewNodeAttribute(root, "gap", Value(5.0)); + + utils.setViewNodeAttribute(child1, "width", Value(10.0)); + utils.setViewNodeAttribute(child1, "height", Value(5.0)); + + utils.setViewNodeAttribute(child2, "width", Value(20.0)); + utils.setViewNodeAttribute(child2, "height", Value(10.0)); + + utils.setViewNodeAttribute(child3, "width", Value(20.0)); + utils.setViewNodeAttribute(child3, "height", Value(15.0)); + + root->appendChild(utils.getViewTransactionScope(), child1); + root->appendChild(utils.getViewTransactionScope(), child2); + root->appendChild(utils.getViewTransactionScope(), child3); + + auto size = root->measureLayout(100, MeasureModeUnspecified, 100, MeasureModeUnspecified, LayoutDirectionLTR); + + ASSERT_EQ(60.0f, size.width); + ASSERT_EQ(15.0f, size.height); +} + +TEST(ViewNode, supportsRowAndColumnGap) { + ViewNodeTestsDependencies utils; + + auto root = utils.createRootView(); + auto child1 = utils.createView(); + auto child2 = utils.createView(); + auto child3 = utils.createView(); + + utils.setViewNodeAttribute(root, "flexDirection", Value(STRING_LITERAL("row"))); + utils.setViewNodeAttribute(root, "flexWrap", Value(STRING_LITERAL("wrap"))); + utils.setViewNodeAttribute(root, "columnGap", Value(5.0)); + utils.setViewNodeAttribute(root, "rowGap", Value(7.0)); + + utils.setViewNodeAttribute(child1, "width", Value(10.0)); + utils.setViewNodeAttribute(child1, "height", Value(5.0)); + + utils.setViewNodeAttribute(child2, "width", Value(20.0)); + utils.setViewNodeAttribute(child2, "height", Value(10.0)); + + utils.setViewNodeAttribute(child3, "width", Value(20.0)); + utils.setViewNodeAttribute(child3, "height", Value(15.0)); + + root->appendChild(utils.getViewTransactionScope(), child1); + root->appendChild(utils.getViewTransactionScope(), child2); + root->appendChild(utils.getViewTransactionScope(), child3); + + auto size = root->measureLayout(35, MeasureModeExactly, 100, MeasureModeUnspecified, LayoutDirectionLTR); + + ASSERT_EQ(35.0f, size.width); + ASSERT_EQ(32.0f, size.height); +} + TEST(ViewNode, canCalculateViewport) { ViewNodeTestsDependencies utils; From c093532b2789c6423d7bf852fb1d23c63070856e Mon Sep 17 00:00:00 2001 From: Simon Corsin Date: Fri, 17 Apr 2026 21:24:40 -0500 Subject: [PATCH 5/9] format touched files (cherry picked from commit d4034b1aab6ab45580c9c3aa6cdd7f165c574df5) --- .../snap_drawing/cpp/Layers/FlexboxLayer.cpp | 3 +- .../Yoga/YGValueAttributeHandlerDelegate.hpp | 3 +- .../Yoga/YogaAttributeHandlerDelegate.cpp | 4 +- .../Attributes/Yoga/YogaAttributes.cpp | 467 ++++++++---------- valdi/src/valdi/runtime/Context/ViewNode.cpp | 9 +- 5 files changed, 221 insertions(+), 265 deletions(-) diff --git a/snap_drawing/src/snap_drawing/cpp/Layers/FlexboxLayer.cpp b/snap_drawing/src/snap_drawing/cpp/Layers/FlexboxLayer.cpp index 0cd6e388..08fb2d1f 100644 --- a/snap_drawing/src/snap_drawing/cpp/Layers/FlexboxLayer.cpp +++ b/snap_drawing/src/snap_drawing/cpp/Layers/FlexboxLayer.cpp @@ -240,7 +240,8 @@ void onYogaNodeDirty(YGNodeConstRef node) { layer->setNeedsLayout(); } -YGSize onYogaMeasure(YGNodeConstRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode) { +YGSize onYogaMeasure( + YGNodeConstRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode) { auto* layer = reinterpret_cast(facebook::yoga::resolveRef(node)->getContext()); if (layer == nullptr) { return {.width = 0, .height = 0}; diff --git a/valdi/src/valdi/runtime/Attributes/Yoga/YGValueAttributeHandlerDelegate.hpp b/valdi/src/valdi/runtime/Attributes/Yoga/YGValueAttributeHandlerDelegate.hpp index 624b892d..21e5c9a3 100644 --- a/valdi/src/valdi/runtime/Attributes/Yoga/YGValueAttributeHandlerDelegate.hpp +++ b/valdi/src/valdi/runtime/Attributes/Yoga/YGValueAttributeHandlerDelegate.hpp @@ -14,8 +14,7 @@ namespace Valdi { class YGStyleLengthAttributeHandlerDelegate : public YogaGetterSetterAttributeHandlerDelegate { public: - explicit YGStyleLengthAttributeHandlerDelegate( - YGNodeValueGetterSetter getterSetter); + explicit YGStyleLengthAttributeHandlerDelegate(YGNodeValueGetterSetter getterSetter); protected: Result onApply(YGNodeRef node, const Value& value) override; diff --git a/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributeHandlerDelegate.cpp b/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributeHandlerDelegate.cpp index 1b50e657..3326ff4a 100644 --- a/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributeHandlerDelegate.cpp +++ b/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributeHandlerDelegate.cpp @@ -94,8 +94,8 @@ Result YogaAttributeHandlerDelegate::valueToYGStyle } } -std::optional -YogaAttributeHandlerDelegate::parseYGStyleSizeLength(AttributeParser& parser) { +std::optional YogaAttributeHandlerDelegate::parseYGStyleSizeLength( + AttributeParser& parser) { parser.tryParseWhitespaces(); if (parser.tryParse("auto")) { diff --git a/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributes.cpp b/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributes.cpp index a55d9f71..9fd6b6ec 100644 --- a/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributes.cpp +++ b/valdi/src/valdi/runtime/Attributes/Yoga/YogaAttributes.cpp @@ -261,328 +261,283 @@ void YogaAttributes::bind(AttributeHandlerById& attributes) { true, attributes, _directionToEnum, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) { return toAttributeEnum(style.direction()); }, - [](facebook::yoga::Style& style, int enumValue) { - style.setDirection(fromAttributeEnum(enumValue)); - })); - - bindEnumAttribute( - "flexDirection", - true, - attributes, - _flexDirectionToEnum, - YGNodeValueGetterSetter([](facebook::yoga::Style& style) { - return toAttributeEnum(style.flexDirection()); - }, + YGNodeValueGetterSetter([](facebook::yoga::Style& style) { return toAttributeEnum(style.direction()); }, [](facebook::yoga::Style& style, int enumValue) { - style.setFlexDirection( - fromAttributeEnum(enumValue)); + style.setDirection(fromAttributeEnum(enumValue)); })); - bindEnumAttribute( - "justifyContent", - true, - attributes, - _justifyToEnum, - YGNodeValueGetterSetter([](facebook::yoga::Style& style) { - return toAttributeEnum(style.justifyContent()); - }, - [](facebook::yoga::Style& style, int enumValue) { - style.setJustifyContent( - fromAttributeEnum(enumValue)); - })); + bindEnumAttribute("flexDirection", + true, + attributes, + _flexDirectionToEnum, + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) { return toAttributeEnum(style.flexDirection()); }, + [](facebook::yoga::Style& style, int enumValue) { + style.setFlexDirection(fromAttributeEnum(enumValue)); + })); + + bindEnumAttribute("justifyContent", + true, + attributes, + _justifyToEnum, + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) { return toAttributeEnum(style.justifyContent()); }, + [](facebook::yoga::Style& style, int enumValue) { + style.setJustifyContent(fromAttributeEnum(enumValue)); + })); bindEnumAttribute( "alignContent", true, attributes, _alignToEnum, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) { - return toAttributeEnum(style.alignContent()); - }, - [](facebook::yoga::Style& style, int enumValue) { - style.setAlignContent(fromAttributeEnum(enumValue)); - })); + YGNodeValueGetterSetter([](facebook::yoga::Style& style) { return toAttributeEnum(style.alignContent()); }, + [](facebook::yoga::Style& style, int enumValue) { + style.setAlignContent(fromAttributeEnum(enumValue)); + })); bindEnumAttribute( "alignItems", true, attributes, _alignToEnum, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) { - return toAttributeEnum(style.alignItems()); - }, - [](facebook::yoga::Style& style, int enumValue) { - style.setAlignItems(fromAttributeEnum(enumValue)); - })); + YGNodeValueGetterSetter([](facebook::yoga::Style& style) { return toAttributeEnum(style.alignItems()); }, + [](facebook::yoga::Style& style, int enumValue) { + style.setAlignItems(fromAttributeEnum(enumValue)); + })); bindEnumAttribute( "alignSelf", false, attributes, _alignToEnum, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) { - return toAttributeEnum(style.alignSelf()); - }, - [](facebook::yoga::Style& style, int enumValue) { - style.setAlignSelf(fromAttributeEnum(enumValue)); - })); - - bindEnumAttribute( - "position", - false, - attributes, - _positionTypeToEnum, - YGNodeValueGetterSetter([](facebook::yoga::Style& style) { - return toAttributeEnum(style.positionType()); - }, + YGNodeValueGetterSetter([](facebook::yoga::Style& style) { return toAttributeEnum(style.alignSelf()); }, [](facebook::yoga::Style& style, int enumValue) { - style.setPositionType( - fromAttributeEnum(enumValue)); + style.setAlignSelf(fromAttributeEnum(enumValue)); })); + bindEnumAttribute("position", + false, + attributes, + _positionTypeToEnum, + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) { return toAttributeEnum(style.positionType()); }, + [](facebook::yoga::Style& style, int enumValue) { + style.setPositionType(fromAttributeEnum(enumValue)); + })); + bindEnumAttribute( "flexWrap", true, attributes, _wrapToEnum, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) { - return toAttributeEnum(style.flexWrap()); - }, - [](facebook::yoga::Style& style, int enumValue) { - style.setFlexWrap(fromAttributeEnum(enumValue)); - })); + YGNodeValueGetterSetter([](facebook::yoga::Style& style) { return toAttributeEnum(style.flexWrap()); }, + [](facebook::yoga::Style& style, int enumValue) { + style.setFlexWrap(fromAttributeEnum(enumValue)); + })); bindEnumAttribute( "overflow", true, attributes, _overflowToEnum, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) { - return toAttributeEnum(style.overflow()); - }, - [](facebook::yoga::Style& style, int enumValue) { - style.setOverflow(fromAttributeEnum(enumValue)); - })); + YGNodeValueGetterSetter([](facebook::yoga::Style& style) { return toAttributeEnum(style.overflow()); }, + [](facebook::yoga::Style& style, int enumValue) { + style.setOverflow(fromAttributeEnum(enumValue)); + })); bindEnumAttribute( "display", false, attributes, _displayToEnum, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) { - return toAttributeEnum(style.display()); - }, - [](facebook::yoga::Style& style, int enumValue) { - style.setDisplay(fromAttributeEnum(enumValue)); - })); + YGNodeValueGetterSetter([](facebook::yoga::Style& style) { return toAttributeEnum(style.display()); }, + [](facebook::yoga::Style& style, int enumValue) { + style.setDisplay(fromAttributeEnum(enumValue)); + })); bindYGOptionalAttribute("flexGrow", false, attributes, YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) -> YGFloatOptional { - return style.flexGrow(); - }, - [](facebook::yoga::Style& style, YGFloatOptional value) { - style.setFlexGrow(value); - })); + [](facebook::yoga::Style& style) -> YGFloatOptional { return style.flexGrow(); }, + [](facebook::yoga::Style& style, YGFloatOptional value) { style.setFlexGrow(value); })); - bindYGOptionalAttribute("flexShrink", - false, - attributes, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) -> YGFloatOptional { - return style.flexShrink(); - }, - [](facebook::yoga::Style& style, YGFloatOptional value) { - style.setFlexShrink(value); - })); + bindYGOptionalAttribute( + "flexShrink", + false, + attributes, + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> YGFloatOptional { return style.flexShrink(); }, + [](facebook::yoga::Style& style, YGFloatOptional value) { style.setFlexShrink(value); })); - bindYGStyleSizeLengthAttribute("flexBasis", - false, - attributes, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { - return style.flexBasis(); - }, - [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { - setYGFlexBasis(style, value); - })); + bindYGStyleSizeLengthAttribute( + "flexBasis", + false, + attributes, + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { return style.flexBasis(); }, + [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { setYGFlexBasis(style, value); })); bindPositionAttributes(attributes); - bindPaddingOrMarginAttributes("margin", false, attributes, [](facebook::yoga::Style& style, - const facebook::yoga::StyleLength& top, - const facebook::yoga::StyleLength& end, - const facebook::yoga::StyleLength& bottom, - const facebook::yoga::StyleLength& start) { - setYGMargin(style, YGEdgeTop, top); - setYGMargin(style, YGEdgeEnd, end); - setYGMargin(style, YGEdgeBottom, bottom); - setYGMargin(style, YGEdgeStart, start); - }); - bindPaddingOrMarginAttributes("padding", true, attributes, [](facebook::yoga::Style& style, - const facebook::yoga::StyleLength& top, - const facebook::yoga::StyleLength& end, - const facebook::yoga::StyleLength& bottom, - const facebook::yoga::StyleLength& start) { - setYGPadding(style, YGEdgeTop, top); - setYGPadding(style, YGEdgeEnd, end); - setYGPadding(style, YGEdgeBottom, bottom); - setYGPadding(style, YGEdgeStart, start); - }); + bindPaddingOrMarginAttributes("margin", + false, + attributes, + [](facebook::yoga::Style& style, + const facebook::yoga::StyleLength& top, + const facebook::yoga::StyleLength& end, + const facebook::yoga::StyleLength& bottom, + const facebook::yoga::StyleLength& start) { + setYGMargin(style, YGEdgeTop, top); + setYGMargin(style, YGEdgeEnd, end); + setYGMargin(style, YGEdgeBottom, bottom); + setYGMargin(style, YGEdgeStart, start); + }); + bindPaddingOrMarginAttributes("padding", + true, + attributes, + [](facebook::yoga::Style& style, + const facebook::yoga::StyleLength& top, + const facebook::yoga::StyleLength& end, + const facebook::yoga::StyleLength& bottom, + const facebook::yoga::StyleLength& start) { + setYGPadding(style, YGEdgeTop, top); + setYGPadding(style, YGEdgeEnd, end); + setYGPadding(style, YGEdgeBottom, bottom); + setYGPadding(style, YGEdgeStart, start); + }); bindYGStyleLengthAttribute("gap", true, attributes, makeGapGetterSetter()); bindYGStyleLengthAttribute("rowGap", true, attributes, makeGapGetterSetter()); bindYGStyleLengthAttribute("columnGap", true, attributes, makeGapGetterSetter()); - bindYGStyleLengthAttribute( - "borderTopWidth", - false, - attributes, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) -> facebook::yoga::StyleLength { - return style.border(facebook::yoga::scopedEnum(YGEdgeTop)); - }, - [](facebook::yoga::Style& style, facebook::yoga::StyleLength value) { - setYGBorder(style, YGEdgeTop, value); - })); - - bindYGStyleLengthAttribute( - "borderRightWidth", - false, - attributes, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) -> facebook::yoga::StyleLength { - return style.border(facebook::yoga::scopedEnum(YGEdgeEnd)); - }, - [](facebook::yoga::Style& style, facebook::yoga::StyleLength value) { - setYGBorder(style, YGEdgeEnd, value); - })); - - bindYGStyleLengthAttribute( - "borderBottomWidth", - false, - attributes, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) -> facebook::yoga::StyleLength { - return style.border(facebook::yoga::scopedEnum(YGEdgeBottom)); - }, - [](facebook::yoga::Style& style, facebook::yoga::StyleLength value) { - setYGBorder(style, YGEdgeBottom, value); - })); - - bindYGStyleLengthAttribute( - "borderLeftWidth", - false, - attributes, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) -> facebook::yoga::StyleLength { - return style.border(facebook::yoga::scopedEnum(YGEdgeStart)); - }, - [](facebook::yoga::Style& style, facebook::yoga::StyleLength value) { - setYGBorder(style, YGEdgeStart, value); - })); - - bindYGStyleLengthAttribute( - "borderWidth", - false, - attributes, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) -> facebook::yoga::StyleLength { - return style.border(facebook::yoga::scopedEnum(YGEdgeAll)); - }, - [](facebook::yoga::Style& style, facebook::yoga::StyleLength value) { - setYGBorder(style, YGEdgeAll, value); - })); - - bindYGStyleSizeLengthAttribute( - "width", - false, - attributes, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { - return style.dimension(facebook::yoga::Dimension::Width); - }, - [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { - style.setDimension(facebook::yoga::Dimension::Width, value); - })); + bindYGStyleLengthAttribute("borderTopWidth", + false, + attributes, + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleLength { + return style.border(facebook::yoga::scopedEnum(YGEdgeTop)); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleLength value) { + setYGBorder(style, YGEdgeTop, value); + })); + + bindYGStyleLengthAttribute("borderRightWidth", + false, + attributes, + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleLength { + return style.border(facebook::yoga::scopedEnum(YGEdgeEnd)); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleLength value) { + setYGBorder(style, YGEdgeEnd, value); + })); + + bindYGStyleLengthAttribute("borderBottomWidth", + false, + attributes, + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleLength { + return style.border(facebook::yoga::scopedEnum(YGEdgeBottom)); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleLength value) { + setYGBorder(style, YGEdgeBottom, value); + })); + + bindYGStyleLengthAttribute("borderLeftWidth", + false, + attributes, + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleLength { + return style.border(facebook::yoga::scopedEnum(YGEdgeStart)); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleLength value) { + setYGBorder(style, YGEdgeStart, value); + })); + + bindYGStyleLengthAttribute("borderWidth", + false, + attributes, + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleLength { + return style.border(facebook::yoga::scopedEnum(YGEdgeAll)); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleLength value) { + setYGBorder(style, YGEdgeAll, value); + })); + + bindYGStyleSizeLengthAttribute("width", + false, + attributes, + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { + return style.dimension(facebook::yoga::Dimension::Width); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { + style.setDimension(facebook::yoga::Dimension::Width, value); + })); - bindYGStyleSizeLengthAttribute( - "height", - false, - attributes, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { - return style.dimension(facebook::yoga::Dimension::Height); - }, - [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { - style.setDimension(facebook::yoga::Dimension::Height, value); - })); + bindYGStyleSizeLengthAttribute("height", + false, + attributes, + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { + return style.dimension(facebook::yoga::Dimension::Height); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { + style.setDimension(facebook::yoga::Dimension::Height, value); + })); - bindYGStyleSizeLengthAttribute( - "minWidth", - false, - attributes, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { - return style.minDimension(facebook::yoga::Dimension::Width); - }, - [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { - style.setMinDimension(facebook::yoga::Dimension::Width, value); - })); + bindYGStyleSizeLengthAttribute("minWidth", + false, + attributes, + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { + return style.minDimension(facebook::yoga::Dimension::Width); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { + style.setMinDimension(facebook::yoga::Dimension::Width, value); + })); - bindYGStyleSizeLengthAttribute( - "minHeight", - false, - attributes, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { - return style.minDimension(facebook::yoga::Dimension::Height); - }, - [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { - style.setMinDimension(facebook::yoga::Dimension::Height, value); - })); + bindYGStyleSizeLengthAttribute("minHeight", + false, + attributes, + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { + return style.minDimension(facebook::yoga::Dimension::Height); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { + style.setMinDimension(facebook::yoga::Dimension::Height, value); + })); - bindYGStyleSizeLengthAttribute( - "maxWidth", - false, - attributes, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { - return style.maxDimension(facebook::yoga::Dimension::Width); - }, - [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { - style.setMaxDimension(facebook::yoga::Dimension::Width, value); - })); + bindYGStyleSizeLengthAttribute("maxWidth", + false, + attributes, + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { + return style.maxDimension(facebook::yoga::Dimension::Width); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { + style.setMaxDimension(facebook::yoga::Dimension::Width, value); + })); - bindYGStyleSizeLengthAttribute( - "maxHeight", - false, - attributes, - YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { - return style.maxDimension(facebook::yoga::Dimension::Height); - }, - [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { - style.setMaxDimension(facebook::yoga::Dimension::Height, value); - })); + bindYGStyleSizeLengthAttribute("maxHeight", + false, + attributes, + YGNodeValueGetterSetter( + [](facebook::yoga::Style& style) -> facebook::yoga::StyleSizeLength { + return style.maxDimension(facebook::yoga::Dimension::Height); + }, + [](facebook::yoga::Style& style, facebook::yoga::StyleSizeLength value) { + style.setMaxDimension(facebook::yoga::Dimension::Height, value); + })); bindYGOptionalAttribute("aspectRatio", false, attributes, YGNodeValueGetterSetter( - [](facebook::yoga::Style& style) -> YGFloatOptional { - return style.aspectRatio(); - }, + [](facebook::yoga::Style& style) -> YGFloatOptional { return style.aspectRatio(); }, [](facebook::yoga::Style& style, YGFloatOptional value) { if (!value.isUndefined() && value.unwrap() == 0) { // safety for aspectRatio 0, we make it undefined instead. diff --git a/valdi/src/valdi/runtime/Context/ViewNode.cpp b/valdi/src/valdi/runtime/Context/ViewNode.cpp index 7016388f..5d6e48dc 100644 --- a/valdi/src/valdi/runtime/Context/ViewNode.cpp +++ b/valdi/src/valdi/runtime/Context/ViewNode.cpp @@ -2351,9 +2351,9 @@ void ViewNode::updateScrollState() { float lowestLeft = 0; for (auto* childYogaNode : yogaContainerNode->getChildren()) { - auto left = ygNodeGetFrame( - childYogaNode, - -sanitizeYogaValue(childYogaNode->getLayout().margin(facebook::yoga::PhysicalEdge::Left))) + auto left = ygNodeGetFrame(childYogaNode, + -sanitizeYogaValue( + childYogaNode->getLayout().margin(facebook::yoga::PhysicalEdge::Left))) .getLeft(); lowestLeft = std::min(left, lowestLeft); @@ -3533,7 +3533,8 @@ static MeasureMode yogaMeasureModeToValdiMeasureMode(YGMeasureMode measureMode) } } -YGSize ygMeasureYoga(YGNodeConstRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode) { +YGSize ygMeasureYoga( + YGNodeConstRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode) { auto* viewNode = reinterpret_cast(facebook::yoga::resolveRef(node)->getContext()); if (viewNode == nullptr) { return {.width = 0, .height = 0}; From f0a63af5a95c9189c76ff0970bbeab32c5af93c6 Mon Sep 17 00:00:00 2001 From: Simon Corsin Date: Fri, 17 Apr 2026 21:49:33 -0500 Subject: [PATCH 6/9] removed yoga ios as it's unused (cherry picked from commit 7d2a8e0f32da2c09113f39dd590ca1a06eaf0ec1) --- third-party/yoga/BUILD.bazel | 17 - third-party/yoga/src/ios/yoga/UIView+Yoga.h | 36 -- third-party/yoga/src/ios/yoga/UIView+Yoga.m | 39 -- .../yoga/src/ios/yoga/YGLayout+Private.h | 18 - third-party/yoga/src/ios/yoga/YGLayout.h | 162 ------ third-party/yoga/src/ios/yoga/YGLayout.m | 485 ------------------ valdi/src/valdi/ios/Categories/UIView+Valdi.m | 1 - valdi/src/valdi/ios/SCValdiRuntime.mm | 1 - 8 files changed, 759 deletions(-) delete mode 100755 third-party/yoga/src/ios/yoga/UIView+Yoga.h delete mode 100755 third-party/yoga/src/ios/yoga/UIView+Yoga.m delete mode 100755 third-party/yoga/src/ios/yoga/YGLayout+Private.h delete mode 100755 third-party/yoga/src/ios/yoga/YGLayout.h delete mode 100755 third-party/yoga/src/ios/yoga/YGLayout.m diff --git a/third-party/yoga/BUILD.bazel b/third-party/yoga/BUILD.bazel index 4c4163c6..099ab3dd 100644 --- a/third-party/yoga/BUILD.bazel +++ b/third-party/yoga/BUILD.bazel @@ -16,7 +16,6 @@ cc_library( name = "yoga", visibility = ["//visibility:public"], deps = [":yoga_cc"] + select({ - "//bzl/conditions:ios": [":yoga_ios"], "//bzl/conditions:android": [":yoga_android"], "//conditions:default": [], }), @@ -61,22 +60,6 @@ cc_library( tags = ["exported"], ) -objc_library( - name = "yoga_ios", - srcs = glob([ - "src/ios/yoga/**/*.m", - ]), - hdrs = glob([ - "src/ios/yoga/**/*.h", - ]), - includes = ["src/ios"], - tags = ["exported"], - target_compatible_with = ["@platforms//os:ios"], - deps = [ - ":yoga_cc", - ], -) - cc_library( name = "yoga_android", strip_include_prefix = "src/yoga", diff --git a/third-party/yoga/src/ios/yoga/UIView+Yoga.h b/third-party/yoga/src/ios/yoga/UIView+Yoga.h deleted file mode 100755 index 5a5ae00a..00000000 --- a/third-party/yoga/src/ios/yoga/UIView+Yoga.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "YGLayout.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -typedef void (^YGLayoutConfigurationBlock)(YGLayout *layout); - -@interface UIView (Yoga) - -/** - The YGLayout that is attached to this view. It is lazily created. - */ -@property (nonatomic, readonly, strong) YGLayout *yoga; -/** - Indicates whether or not Yoga is enabled - */ -@property (nonatomic, readonly, assign) BOOL isYogaEnabled; - -/** - In ObjC land, every time you access `view.yoga.*` you are adding another `objc_msgSend` - to your code. If you plan on making multiple changes to YGLayout, it's more performant - to use this method, which uses a single objc_msgSend call. - */ -- (void)configureLayoutWithBlock:(YGLayoutConfigurationBlock)block - NS_SWIFT_NAME(configureLayout(block:)); - -@end - -NS_ASSUME_NONNULL_END diff --git a/third-party/yoga/src/ios/yoga/UIView+Yoga.m b/third-party/yoga/src/ios/yoga/UIView+Yoga.m deleted file mode 100755 index eb360a8c..00000000 --- a/third-party/yoga/src/ios/yoga/UIView+Yoga.m +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "UIView+Yoga.h" -#import "YGLayout+Private.h" -#import - -static const void *kYGYogaAssociatedKey = &kYGYogaAssociatedKey; - -@implementation UIView (YogaKit) - -- (YGLayout *)yoga -{ - YGLayout *yoga = objc_getAssociatedObject(self, kYGYogaAssociatedKey); - if (!yoga) { - yoga = [[YGLayout alloc] initWithView:self]; - objc_setAssociatedObject(self, kYGYogaAssociatedKey, yoga, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } - - return yoga; -} - -- (BOOL)isYogaEnabled -{ - return objc_getAssociatedObject(self, kYGYogaAssociatedKey) != nil; -} - -- (void)configureLayoutWithBlock:(YGLayoutConfigurationBlock)block -{ - if (block != nil) { - block(self.yoga); - } -} - -@end diff --git a/third-party/yoga/src/ios/yoga/YGLayout+Private.h b/third-party/yoga/src/ios/yoga/YGLayout+Private.h deleted file mode 100755 index af6a0615..00000000 --- a/third-party/yoga/src/ios/yoga/YGLayout+Private.h +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "YGLayout.h" -#import - -@interface YGLayout () - -@property (nonatomic, assign, readonly) YGNodeRef node; - -- (instancetype)initWithView:(UIView *)view; -- (instancetype)initWithYogaNode:(YGNodeRef)node; - -@end diff --git a/third-party/yoga/src/ios/yoga/YGLayout.h b/third-party/yoga/src/ios/yoga/YGLayout.h deleted file mode 100755 index 7b9d583a..00000000 --- a/third-party/yoga/src/ios/yoga/YGLayout.h +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import -#import -#import -#import - -YG_EXTERN_C_BEGIN - -extern YGValue YGPointValue(CGFloat value) - NS_SWIFT_UNAVAILABLE("Use the swift Int and FloatingPoint extensions instead"); -extern YGValue YGPercentValue(CGFloat value) - NS_SWIFT_UNAVAILABLE("Use the swift Int and FloatingPoint extensions instead"); - -YG_EXTERN_C_END - -typedef NS_OPTIONS(NSInteger, YGDimensionFlexibility) { - YGDimensionFlexibilityFlexibleWidth = 1 << 0, - YGDimensionFlexibilityFlexibleHeight = 1 << 1, -}; - -@interface YGLayout : NSObject - -/** - Make default init unavailable, as it will not initialise YGNode which is - required for the setters and getters of YGLayout's properties to work properly. -*/ -- (instancetype)init - __attribute__((unavailable("you are not meant to initialise YGLayout"))); - -/** - The property that decides if we should include this view when calculating - layout. Defaults to YES. - */ -@property (nonatomic, readwrite, assign, setter=setIncludedInLayout:) BOOL isIncludedInLayout; - -/** - The property that decides during layout/sizing whether or not styling properties should be applied. - Defaults to NO. - */ -@property (nonatomic, readwrite, assign, setter=setEnabled:) BOOL isEnabled; - -@property (nonatomic, readwrite, assign) YGDirection direction; -@property (nonatomic, readwrite, assign) YGFlexDirection flexDirection; -@property (nonatomic, readwrite, assign) YGJustify justifyContent; -@property (nonatomic, readwrite, assign) YGAlign alignContent; -@property (nonatomic, readwrite, assign) YGAlign alignItems; -@property (nonatomic, readwrite, assign) YGAlign alignSelf; -@property (nonatomic, readwrite, assign) YGPositionType position; -@property (nonatomic, readwrite, assign) YGWrap flexWrap; -@property (nonatomic, readwrite, assign) YGOverflow overflow; -@property (nonatomic, readwrite, assign) YGDisplay display; - -@property (nonatomic, readwrite, assign) CGFloat flexGrow; -@property (nonatomic, readwrite, assign) CGFloat flexShrink; -@property (nonatomic, readwrite, assign) YGValue flexBasis; - -@property (nonatomic, readwrite, assign) YGValue left; -@property (nonatomic, readwrite, assign) YGValue top; -@property (nonatomic, readwrite, assign) YGValue right; -@property (nonatomic, readwrite, assign) YGValue bottom; -@property (nonatomic, readwrite, assign) YGValue start; -@property (nonatomic, readwrite, assign) YGValue end; - -@property (nonatomic, readwrite, assign) YGValue marginLeft; -@property (nonatomic, readwrite, assign) YGValue marginTop; -@property (nonatomic, readwrite, assign) YGValue marginRight; -@property (nonatomic, readwrite, assign) YGValue marginBottom; -@property (nonatomic, readwrite, assign) YGValue marginStart; -@property (nonatomic, readwrite, assign) YGValue marginEnd; -@property (nonatomic, readwrite, assign) YGValue marginHorizontal; -@property (nonatomic, readwrite, assign) YGValue marginVertical; -@property (nonatomic, readwrite, assign) YGValue margin; - -@property (nonatomic, readwrite, assign) YGValue paddingLeft; -@property (nonatomic, readwrite, assign) YGValue paddingTop; -@property (nonatomic, readwrite, assign) YGValue paddingRight; -@property (nonatomic, readwrite, assign) YGValue paddingBottom; -@property (nonatomic, readwrite, assign) YGValue paddingStart; -@property (nonatomic, readwrite, assign) YGValue paddingEnd; -@property (nonatomic, readwrite, assign) YGValue paddingHorizontal; -@property (nonatomic, readwrite, assign) YGValue paddingVertical; -@property (nonatomic, readwrite, assign) YGValue padding; - -@property (nonatomic, readwrite, assign) CGFloat borderLeftWidth; -@property (nonatomic, readwrite, assign) CGFloat borderTopWidth; -@property (nonatomic, readwrite, assign) CGFloat borderRightWidth; -@property (nonatomic, readwrite, assign) CGFloat borderBottomWidth; -@property (nonatomic, readwrite, assign) CGFloat borderStartWidth; -@property (nonatomic, readwrite, assign) CGFloat borderEndWidth; -@property (nonatomic, readwrite, assign) CGFloat borderWidth; - -@property (nonatomic, readwrite, assign) YGValue width; -@property (nonatomic, readwrite, assign) YGValue height; -@property (nonatomic, readwrite, assign) YGValue minWidth; -@property (nonatomic, readwrite, assign) YGValue minHeight; -@property (nonatomic, readwrite, assign) YGValue maxWidth; -@property (nonatomic, readwrite, assign) YGValue maxHeight; - -// Yoga specific properties, not compatible with flexbox specification -@property (nonatomic, readwrite, assign) CGFloat aspectRatio; - -/** - Get the resolved direction of this node. This won't be YGDirectionInherit - */ -@property (nonatomic, readonly, assign) YGDirection resolvedDirection; - -/** - Perform a layout calculation and update the frames of the views in the hierarchy with the results. - If the origin is not preserved, the root view's layout results will applied from {0,0}. - */ -- (void)applyLayoutPreservingOrigin:(BOOL)preserveOrigin - NS_SWIFT_NAME(applyLayout(preservingOrigin:)); - -/** - Perform a layout calculation and update the frames of the views in the hierarchy with the results. - If the origin is not preserved, the root view's layout results will applied from {0,0}. - */ -- (void)applyLayoutPreservingOrigin:(BOOL)preserveOrigin - dimensionFlexibility:(YGDimensionFlexibility)dimensionFlexibility - NS_SWIFT_NAME(applyLayout(preservingOrigin:dimensionFlexibility:)); - -/** - Returns the size of the view if no constraints were given. This could equivalent to calling [self - sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)]; - */ -@property (nonatomic, readonly, assign) CGSize intrinsicSize; - -/** - Returns the size of the view based on provided constraints. Pass NaN for an unconstrained dimension. - */ -- (CGSize)calculateLayoutWithSize:(CGSize)size - NS_SWIFT_NAME(calculateLayout(with:)); - -/** - Returns the number of children that are using Flexbox. - */ -@property (nonatomic, readonly, assign) NSUInteger numberOfChildren; - -/** - Return a BOOL indiciating whether or not we this node contains any subviews that are included in - Yoga's layout. - */ -@property (nonatomic, readonly, assign) BOOL isLeaf; - -/** - Return's a BOOL indicating if a view is dirty. When a node is dirty - it usually indicates that it will be remeasured on the next layout pass. - */ -@property (nonatomic, readonly, assign) BOOL isDirty; - -/** - Mark that a view's layout needs to be recalculated. Only works for leaf views. - */ -- (void)markDirty; - -@end diff --git a/third-party/yoga/src/ios/yoga/YGLayout.m b/third-party/yoga/src/ios/yoga/YGLayout.m deleted file mode 100755 index 699fddfe..00000000 --- a/third-party/yoga/src/ios/yoga/YGLayout.m +++ /dev/null @@ -1,485 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "YGLayout+Private.h" -#import "UIView+Yoga.h" - -#define YG_PROPERTY(type, lowercased_name, capitalized_name) \ -- (type)lowercased_name \ -{ \ - return YGNodeStyleGet##capitalized_name(self.node); \ -} \ - \ -- (void)set##capitalized_name:(type)lowercased_name \ -{ \ - YGNodeStyleSet##capitalized_name(self.node, lowercased_name); \ -} - -#define YG_VALUE_PROPERTY(lowercased_name, capitalized_name) \ -- (YGValue)lowercased_name \ -{ \ - return YGNodeStyleGet##capitalized_name(self.node); \ -} \ - \ -- (void)set##capitalized_name:(YGValue)lowercased_name \ -{ \ - switch (lowercased_name.unit) { \ - case YGUnitUndefined: \ - YGNodeStyleSet##capitalized_name(self.node, lowercased_name.value); \ - break; \ - case YGUnitPoint: \ - YGNodeStyleSet##capitalized_name(self.node, lowercased_name.value); \ - break; \ - case YGUnitPercent: \ - YGNodeStyleSet##capitalized_name##Percent(self.node, lowercased_name.value); \ - break; \ - default: \ - NSAssert(NO, @"Not implemented"); \ - } \ -} - -#define YG_AUTO_VALUE_PROPERTY(lowercased_name, capitalized_name) \ -- (YGValue)lowercased_name \ -{ \ - return YGNodeStyleGet##capitalized_name(self.node); \ -} \ - \ -- (void)set##capitalized_name:(YGValue)lowercased_name \ -{ \ - switch (lowercased_name.unit) { \ - case YGUnitPoint: \ - YGNodeStyleSet##capitalized_name(self.node, lowercased_name.value); \ - break; \ - case YGUnitPercent: \ - YGNodeStyleSet##capitalized_name##Percent(self.node, lowercased_name.value); \ - break; \ - case YGUnitAuto: \ - YGNodeStyleSet##capitalized_name##Auto(self.node); \ - break; \ - default: \ - NSAssert(NO, @"Not implemented"); \ - } \ -} - -#define YG_EDGE_PROPERTY_GETTER(type, lowercased_name, capitalized_name, property, edge) \ -- (type)lowercased_name \ -{ \ - return YGNodeStyleGet##property(self.node, edge); \ -} - -#define YG_EDGE_PROPERTY_SETTER(lowercased_name, capitalized_name, property, edge) \ -- (void)set##capitalized_name:(CGFloat)lowercased_name \ -{ \ - YGNodeStyleSet##property(self.node, edge, lowercased_name); \ -} - -#define YG_EDGE_PROPERTY(lowercased_name, capitalized_name, property, edge) \ -YG_EDGE_PROPERTY_GETTER(CGFloat, lowercased_name, capitalized_name, property, edge) \ -YG_EDGE_PROPERTY_SETTER(lowercased_name, capitalized_name, property, edge) - -#define YG_VALUE_EDGE_PROPERTY_SETTER(objc_lowercased_name, objc_capitalized_name, c_name, edge) \ -- (void)set##objc_capitalized_name:(YGValue)objc_lowercased_name \ -{ \ - switch (objc_lowercased_name.unit) { \ - case YGUnitUndefined: \ - YGNodeStyleSet##c_name(self.node, edge, objc_lowercased_name.value); \ - break; \ - case YGUnitPoint: \ - YGNodeStyleSet##c_name(self.node, edge, objc_lowercased_name.value); \ - break; \ - case YGUnitPercent: \ - YGNodeStyleSet##c_name##Percent(self.node, edge, objc_lowercased_name.value); \ - break; \ - default: \ - NSAssert(NO, @"Not implemented"); \ - } \ -} - -#define YG_VALUE_EDGE_PROPERTY(lowercased_name, capitalized_name, property, edge) \ -YG_EDGE_PROPERTY_GETTER(YGValue, lowercased_name, capitalized_name, property, edge) \ -YG_VALUE_EDGE_PROPERTY_SETTER(lowercased_name, capitalized_name, property, edge) - -#define YG_VALUE_EDGES_PROPERTIES(lowercased_name, capitalized_name) \ -YG_VALUE_EDGE_PROPERTY(lowercased_name##Left, capitalized_name##Left, capitalized_name, YGEdgeLeft) \ -YG_VALUE_EDGE_PROPERTY(lowercased_name##Top, capitalized_name##Top, capitalized_name, YGEdgeTop) \ -YG_VALUE_EDGE_PROPERTY(lowercased_name##Right, capitalized_name##Right, capitalized_name, YGEdgeRight) \ -YG_VALUE_EDGE_PROPERTY(lowercased_name##Bottom, capitalized_name##Bottom, capitalized_name, YGEdgeBottom) \ -YG_VALUE_EDGE_PROPERTY(lowercased_name##Start, capitalized_name##Start, capitalized_name, YGEdgeStart) \ -YG_VALUE_EDGE_PROPERTY(lowercased_name##End, capitalized_name##End, capitalized_name, YGEdgeEnd) \ -YG_VALUE_EDGE_PROPERTY(lowercased_name##Horizontal, capitalized_name##Horizontal, capitalized_name, YGEdgeHorizontal) \ -YG_VALUE_EDGE_PROPERTY(lowercased_name##Vertical, capitalized_name##Vertical, capitalized_name, YGEdgeVertical) \ -YG_VALUE_EDGE_PROPERTY(lowercased_name, capitalized_name, capitalized_name, YGEdgeAll) - -YGValue YGPointValue(CGFloat value) -{ - return (YGValue) { .value = value, .unit = YGUnitPoint }; -} - -YGValue YGPercentValue(CGFloat value) -{ - return (YGValue) { .value = value, .unit = YGUnitPercent }; -} - -static YGConfigRef globalConfig; - -@interface YGLayout () { - BOOL _ownsYogaNode; -} - -@property (nonatomic, weak, readonly) UIView *view; - -@end - -@implementation YGLayout - -@synthesize isEnabled=_isEnabled; -@synthesize isIncludedInLayout=_isIncludedInLayout; -@synthesize node=_node; - -+ (void)initialize -{ - globalConfig = YGConfigNew(); - YGConfigSetExperimentalFeatureEnabled(globalConfig, YGExperimentalFeatureWebFlexBasis, true); - YGConfigSetPointScaleFactor(globalConfig, [UIScreen mainScreen].scale); -} - -- (instancetype)initWithView:(UIView*)view -{ - if (self = [super init]) { - _view = view; - _node = YGNodeNewWithConfig(globalConfig); - YGNodeSetContext(_node, (__bridge void *) view); - _isEnabled = NO; - _isIncludedInLayout = YES; - _ownsYogaNode = YES; - } - - return self; -} - -- (instancetype)initWithYogaNode:(YGNodeRef)node -{ - if (self = [super init]) { - _node = node; - } - - return self; -} - -- (void)dealloc -{ - if (_ownsYogaNode) { - YGNodeFree(self.node); - } -} - -- (BOOL)isDirty -{ - return YGNodeIsDirty(self.node); -} - -- (void)markDirty -{ - if (self.isDirty || !self.isLeaf) { - return; - } - - // Yoga is not happy if we try to mark a node as "dirty" before we have set - // the measure function. Since we already know that this is a leaf, - // this *should* be fine. Forgive me Hack Gods. - const YGNodeRef node = self.node; - if (!YGNodeHasMeasureFunc(node)) { - YGNodeSetMeasureFunc(node, YGMeasureView); - } - - YGNodeMarkDirty(node); -} - -- (NSUInteger)numberOfChildren -{ - return YGNodeGetChildCount(self.node); -} - -- (BOOL)isLeaf -{ - NSAssert([NSThread isMainThread], @"This method must be called on the main thread."); - if (self.isEnabled) { - for (UIView *subview in self.view.subviews) { - YGLayout *const yoga = subview.yoga; - if (yoga.isEnabled && yoga.isIncludedInLayout) { - return NO; - } - } - } - - return YES; -} - -#pragma mark - Style - -- (YGPositionType)position -{ - return YGNodeStyleGetPositionType(self.node); -} - -- (void)setPosition:(YGPositionType)position -{ - YGNodeStyleSetPositionType(self.node, position); -} - -YG_PROPERTY(YGDirection, direction, Direction) -YG_PROPERTY(YGFlexDirection, flexDirection, FlexDirection) -YG_PROPERTY(YGJustify, justifyContent, JustifyContent) -YG_PROPERTY(YGAlign, alignContent, AlignContent) -YG_PROPERTY(YGAlign, alignItems, AlignItems) -YG_PROPERTY(YGAlign, alignSelf, AlignSelf) -YG_PROPERTY(YGWrap, flexWrap, FlexWrap) -YG_PROPERTY(YGOverflow, overflow, Overflow) -YG_PROPERTY(YGDisplay, display, Display) - -YG_PROPERTY(CGFloat, flexGrow, FlexGrow) -YG_PROPERTY(CGFloat, flexShrink, FlexShrink) -YG_AUTO_VALUE_PROPERTY(flexBasis, FlexBasis) - -YG_VALUE_EDGE_PROPERTY(left, Left, Position, YGEdgeLeft) -YG_VALUE_EDGE_PROPERTY(top, Top, Position, YGEdgeTop) -YG_VALUE_EDGE_PROPERTY(right, Right, Position, YGEdgeRight) -YG_VALUE_EDGE_PROPERTY(bottom, Bottom, Position, YGEdgeBottom) -YG_VALUE_EDGE_PROPERTY(start, Start, Position, YGEdgeStart) -YG_VALUE_EDGE_PROPERTY(end, End, Position, YGEdgeEnd) -YG_VALUE_EDGES_PROPERTIES(margin, Margin) -YG_VALUE_EDGES_PROPERTIES(padding, Padding) - -YG_EDGE_PROPERTY(borderLeftWidth, BorderLeftWidth, Border, YGEdgeLeft) -YG_EDGE_PROPERTY(borderTopWidth, BorderTopWidth, Border, YGEdgeTop) -YG_EDGE_PROPERTY(borderRightWidth, BorderRightWidth, Border, YGEdgeRight) -YG_EDGE_PROPERTY(borderBottomWidth, BorderBottomWidth, Border, YGEdgeBottom) -YG_EDGE_PROPERTY(borderStartWidth, BorderStartWidth, Border, YGEdgeStart) -YG_EDGE_PROPERTY(borderEndWidth, BorderEndWidth, Border, YGEdgeEnd) -YG_EDGE_PROPERTY(borderWidth, BorderWidth, Border, YGEdgeAll) - -YG_AUTO_VALUE_PROPERTY(width, Width) -YG_AUTO_VALUE_PROPERTY(height, Height) -YG_VALUE_PROPERTY(minWidth, MinWidth) -YG_VALUE_PROPERTY(minHeight, MinHeight) -YG_VALUE_PROPERTY(maxWidth, MaxWidth) -YG_VALUE_PROPERTY(maxHeight, MaxHeight) -YG_PROPERTY(CGFloat, aspectRatio, AspectRatio) - -#pragma mark - Layout and Sizing - -- (YGDirection)resolvedDirection -{ - return YGNodeLayoutGetDirection(self.node); -} - -- (void)applyLayout -{ - [self calculateLayoutWithSize:self.view.bounds.size]; - YGApplyLayoutToViewHierarchy(self.view, NO); -} - -- (void)applyLayoutPreservingOrigin:(BOOL)preserveOrigin -{ - [self calculateLayoutWithSize:self.view.bounds.size]; - YGApplyLayoutToViewHierarchy(self.view, preserveOrigin); -} - -- (void)applyLayoutPreservingOrigin:(BOOL)preserveOrigin dimensionFlexibility:(YGDimensionFlexibility)dimensionFlexibility -{ - CGSize size = self.view.bounds.size; - if (dimensionFlexibility & YGDimensionFlexibilityFlexibleWidth) { - size.width = YGUndefined; - } - if (dimensionFlexibility & YGDimensionFlexibilityFlexibleHeight) { - size.height = YGUndefined; - } - [self calculateLayoutWithSize:size]; - YGApplyLayoutToViewHierarchy(self.view, preserveOrigin); -} - - -- (CGSize)intrinsicSize -{ - const CGSize constrainedSize = { - .width = YGUndefined, - .height = YGUndefined, - }; - return [self calculateLayoutWithSize:constrainedSize]; -} - -- (CGSize)calculateLayoutWithSize:(CGSize)size -{ - NSAssert([NSThread isMainThread], @"Yoga calculation must be done on main."); - NSAssert(self.isEnabled, @"Yoga is not enabled for this view."); - - YGAttachNodesFromViewHierachy(self.view); - - const YGNodeRef node = self.node; - YGNodeCalculateLayout( - node, - size.width, - size.height, - YGNodeStyleGetDirection(node)); - - return (CGSize) { - .width = YGNodeLayoutGetWidth(node), - .height = YGNodeLayoutGetHeight(node), - }; -} - -#pragma mark - Private - -static YGSize YGMeasureView( - YGNodeRef node, - float width, - YGMeasureMode widthMode, - float height, - YGMeasureMode heightMode) -{ - const CGFloat constrainedWidth = (widthMode == YGMeasureModeUndefined) ? CGFLOAT_MAX : width; - const CGFloat constrainedHeight = (heightMode == YGMeasureModeUndefined) ? CGFLOAT_MAX: height; - - UIView *view = (__bridge UIView*) YGNodeGetContext(node); - const CGSize sizeThatFits = [view sizeThatFits:(CGSize) { - .width = constrainedWidth, - .height = constrainedHeight, - }]; - - return (YGSize) { - .width = YGSanitizeMeasurement(constrainedWidth, sizeThatFits.width, widthMode), - .height = YGSanitizeMeasurement(constrainedHeight, sizeThatFits.height, heightMode), - }; -} - -static CGFloat YGSanitizeMeasurement( - CGFloat constrainedSize, - CGFloat measuredSize, - YGMeasureMode measureMode) -{ - CGFloat result; - if (measureMode == YGMeasureModeExactly) { - result = constrainedSize; - } else if (measureMode == YGMeasureModeAtMost) { - result = MIN(constrainedSize, measuredSize); - } else { - result = measuredSize; - } - - return result; -} - -static BOOL YGNodeHasExactSameChildren(const YGNodeRef node, NSArray *subviews) -{ - if (YGNodeGetChildCount(node) != subviews.count) { - return NO; - } - - for (int i=0; i *subviewsToInclude = [[NSMutableArray alloc] initWithCapacity:view.subviews.count]; - for (UIView *subview in view.subviews) { - if (subview.yoga.isEnabled && subview.yoga.isIncludedInLayout) { - [subviewsToInclude addObject:subview]; - } - } - - if (!YGNodeHasExactSameChildren(node, subviewsToInclude)) { - YGRemoveAllChildren(node); - for (int i=0; i 0) { - YGNodeRemoveChild(node, YGNodeGetChild(node, YGNodeGetChildCount(node) - 1)); - } -} - -static CGFloat YGRoundPixelValue(CGFloat value) -{ - static CGFloat scale; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^(){ - scale = [UIScreen mainScreen].scale; - }); - - return roundf(value * scale) / scale; -} - -static void YGApplyLayoutToViewHierarchy(UIView *view, BOOL preserveOrigin) -{ - NSCAssert([NSThread isMainThread], @"Framesetting should only be done on the main thread."); - - const YGLayout *yoga = view.yoga; - - if (!yoga.isIncludedInLayout) { - return; - } - - YGNodeRef node = yoga.node; - const CGPoint topLeft = { - YGNodeLayoutGetLeft(node), - YGNodeLayoutGetTop(node), - }; - - const CGPoint bottomRight = { - topLeft.x + YGNodeLayoutGetWidth(node), - topLeft.y + YGNodeLayoutGetHeight(node), - }; - - const CGPoint origin = preserveOrigin ? view.frame.origin : CGPointZero; - view.frame = (CGRect) { - .origin = { - .x = YGRoundPixelValue(topLeft.x + origin.x), - .y = YGRoundPixelValue(topLeft.y + origin.y), - }, - .size = { - .width = YGRoundPixelValue(bottomRight.x) - YGRoundPixelValue(topLeft.x), - .height = YGRoundPixelValue(bottomRight.y) - YGRoundPixelValue(topLeft.y), - }, - }; - - if (!yoga.isLeaf) { - for (NSUInteger i=0; i -#import static NSString *const kBackgroundGradientLayoutKey = @"background_gradient"; static NSString *const kShadowPathLayoutKey = @"shadow_path"; diff --git a/valdi/src/valdi/ios/SCValdiRuntime.mm b/valdi/src/valdi/ios/SCValdiRuntime.mm index bd98d402..cd723439 100644 --- a/valdi/src/valdi/ios/SCValdiRuntime.mm +++ b/valdi/src/valdi/ios/SCValdiRuntime.mm @@ -32,7 +32,6 @@ #import "valdi_core/SCNValdiCoreModuleFactory+Private.h" #import "valdi_core/SCNValdiCoreAsset+Private.h" #import "valdi/runtime/Runtime.hpp" -#import #import "SCValdiJSRuntimeImpl.h" #import "valdi_core/SCNValdiCoreJSRuntime+Private.h" From 569f92ce7507db1bc131c8ea0cbfc7241ed04c56 Mon Sep 17 00:00:00 2001 From: Simon Corsin Date: Sat, 18 Apr 2026 12:57:37 -0500 Subject: [PATCH 7/9] android c++ 20 fix (cherry picked from commit cd748087f0ccd5d2b6bcabfb2fa5407ee8ef75b7) --- third-party/yoga/BUILD.bazel | 1 + .../yoga/src/yoga/YogaAndroidCpp20Compat.h | 52 +++++++++++++++++++ third-party/yoga/src/yoga/enums/YogaEnums.h | 2 + .../yoga/src/yoga/numeric/Comparison.h | 1 + .../yoga/src/yoga/style/StyleValuePool.h | 2 + 5 files changed, 58 insertions(+) create mode 100644 third-party/yoga/src/yoga/YogaAndroidCpp20Compat.h diff --git a/third-party/yoga/BUILD.bazel b/third-party/yoga/BUILD.bazel index 099ab3dd..d5166907 100644 --- a/third-party/yoga/BUILD.bazel +++ b/third-party/yoga/BUILD.bazel @@ -1,6 +1,7 @@ load("//bzl:valdi_library.bzl", "COMMON_COMPILE_FLAGS") COMPILER_FLAGS = COMMON_COMPILE_FLAGS + [ + "-std=c++20", "-Os", # Always set optimization on "-Wno-pedantic", "-Wno-sign-compare", diff --git a/third-party/yoga/src/yoga/YogaAndroidCpp20Compat.h b/third-party/yoga/src/yoga/YogaAndroidCpp20Compat.h new file mode 100644 index 00000000..a3dcb4ac --- /dev/null +++ b/third-party/yoga/src/yoga/YogaAndroidCpp20Compat.h @@ -0,0 +1,52 @@ +/* + * Compatibility shims for Android NDK libc++ versions that can compile C++20 + * syntax but do not expose the C++20 library APIs used by upstream Yoga. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#if defined(__ANDROID__) + +namespace std { + +#ifndef __cpp_lib_bitops +template +constexpr int bit_width(T value) noexcept { + using UnsignedT = make_unsigned_t; + return numeric_limits::digits - + countl_zero(static_cast(value)); +} +#endif + +#ifndef __cpp_lib_bit_cast +template +To bit_cast(const From& from) noexcept { + static_assert(sizeof(To) == sizeof(From)); + static_assert(is_trivially_copyable_v); + static_assert(is_trivially_copyable_v); + + To to; + memcpy(&to, &from, sizeof(To)); + return to; +} +#endif + +template +concept floating_point = is_floating_point_v; + +template +concept input_iterator = requires(I i) { + typename I::iterator_category; + { *i }; + ++i; +} && is_base_of_v; + +} // namespace std + +#endif // defined(__ANDROID__) diff --git a/third-party/yoga/src/yoga/enums/YogaEnums.h b/third-party/yoga/src/yoga/enums/YogaEnums.h index 521d9f7a..13d80675 100644 --- a/third-party/yoga/src/yoga/enums/YogaEnums.h +++ b/third-party/yoga/src/yoga/enums/YogaEnums.h @@ -11,6 +11,8 @@ #include #include +#include + namespace facebook::yoga { /** diff --git a/third-party/yoga/src/yoga/numeric/Comparison.h b/third-party/yoga/src/yoga/numeric/Comparison.h index 0e23a837..b168e240 100644 --- a/third-party/yoga/src/yoga/numeric/Comparison.h +++ b/third-party/yoga/src/yoga/numeric/Comparison.h @@ -13,6 +13,7 @@ #include #include +#include namespace facebook::yoga { diff --git a/third-party/yoga/src/yoga/style/StyleValuePool.h b/third-party/yoga/src/yoga/style/StyleValuePool.h index fb31193b..d6f9f1c6 100644 --- a/third-party/yoga/src/yoga/style/StyleValuePool.h +++ b/third-party/yoga/src/yoga/style/StyleValuePool.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include @@ -15,6 +16,7 @@ #include #include #include +#include namespace facebook::yoga { From 25635ccf60d6d6416b6c1a9c1bdc3e2338d3d9a6 Mon Sep 17 00:00:00 2001 From: scorsin-oai Date: Wed, 13 May 2026 09:25:36 -0500 Subject: [PATCH 8/9] Enable Yoga flex basis fit-content fix (#102) ## Description This fixes a case in the recipe widget where a fixed layout causes the widget to not consume its entire available height space. ## Type of Change - [ ] Bug fix (non-breaking change that fixes an issue) - [ ] Documentation improvement - [ ] Performance optimization - [ ] Test improvement - [ ] Other (please describe) ## Testing - [ ] Tests pass locally (`bazel test //...`) - [ ] Added/updated tests for changes (if applicable) - [ ] Tested on multiple platforms (iOS/Android/Web/macOS as applicable) - [ ] Manual testing performed (describe below) ### Testing Details ## Checklist - [ ] Code follows project style guidelines - [ ] Documentation updated (if needed) - [ ] No breaking changes (or documented in description) - [ ] Commit messages follow [conventional format](../CONTRIBUTING.md#commit-messages) - [ ] No secrets, API keys, or internal URLs included ## Related Issues ## Additional Context (cherry picked from commit 62faf3a266a9cc5067a8288fcf6192af2127711b) --- valdi/src/valdi/runtime/Attributes/Yoga/Yoga.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/valdi/src/valdi/runtime/Attributes/Yoga/Yoga.cpp b/valdi/src/valdi/runtime/Attributes/Yoga/Yoga.cpp index 47c24192..b84995a8 100644 --- a/valdi/src/valdi/runtime/Attributes/Yoga/Yoga.cpp +++ b/valdi/src/valdi/runtime/Attributes/Yoga/Yoga.cpp @@ -15,6 +15,7 @@ namespace Valdi { std::shared_ptr Yoga::createConfig(float pointScale) { auto* config = YGConfigNew(); YGConfigSetExperimentalFeatureEnabled(config, YGExperimentalFeatureWebFlexBasis, true); + YGConfigSetExperimentalFeatureEnabled(config, YGExperimentalFeatureFixFlexBasisFitContent, true); YGConfigSetPointScaleFactor(config, pointScale); return std::shared_ptr(config, YGConfigFree); From 34b4ed812e0da582287a0b3f953d0c0f119a21e1 Mon Sep 17 00:00:00 2001 From: Simon Corsin Date: Thu, 14 May 2026 13:51:00 -0500 Subject: [PATCH 9/9] Fix SnapDrawing test data runfiles lookup --- snap_drawing/test/utils/TestDataUtils.cpp | 31 +++++++++++++++++++---- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/snap_drawing/test/utils/TestDataUtils.cpp b/snap_drawing/test/utils/TestDataUtils.cpp index f6c758fe..22081aa4 100644 --- a/snap_drawing/test/utils/TestDataUtils.cpp +++ b/snap_drawing/test/utils/TestDataUtils.cpp @@ -1,24 +1,45 @@ #include "TestDataUtils.hpp" #include "valdi_core/cpp/Utils/DiskUtils.hpp" +#include #include using namespace Valdi; namespace snap::drawing { -Path resolveTestPath(const std::string& path) { +namespace { + +constexpr const char* kTestDataSubDir = "snap_drawing/testdata"; + +Path resolveRunfilesTestPath(std::initializer_list subDirsToCheck) { char cwdBuffer[PATH_MAX]; (void)::getcwd(cwdBuffer, PATH_MAX); - auto basePath = Path(cwdBuffer); + auto cwdPath = Path(cwdBuffer); + Path basePath; + + for (const auto& subDir : subDirsToCheck) { + basePath = cwdPath; + basePath.append(subDir); + basePath.append(kTestDataSubDir); + basePath.normalize(); - basePath.append("../+local_repos+valdi/snap_drawing/testdata"); - basePath.append(path); - basePath.normalize(); + if (DiskUtils::isDirectory(basePath)) { + return basePath; + } + } return basePath; } +} // namespace + +Path resolveTestPath(const std::string& path) { + auto testPath = resolveRunfilesTestPath({".", "../+local_repos+valdi"}); + testPath.append(path); + return testPath; +} + Valdi::Result getTestData(const std::string& filename) { auto path = resolveTestPath(filename); return DiskUtils::load(path);