first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
# 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)
set(CMAKE_VERBOSE_MAKEFILE on)
# Yoga by default does not enable optimizations in debug builds. Enable -O2
# for all builds in RN for faster debug app performance (at the cost of not
# being able to debug inside Yoga)
set(CMAKE_BUILD_TYPE Release)
add_subdirectory(yoga)

View File

@@ -0,0 +1,66 @@
# 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.
package = JSON.parse(File.read(File.expand_path('../../package.json', __dir__)))
version = package['version']
source = { :git => ENV['INSTALL_YOGA_FROM_LOCATION'] || 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1")
else
source[:tag] = "v#{version}"
end
Pod::Spec.new do |spec|
spec.name = 'Yoga'
spec.version = '0.0.0'
spec.license = { :type => 'MIT' }
spec.homepage = 'https://yogalayout.dev'
spec.documentation_url = 'https://yogalayout.dev/docs/'
spec.summary = 'Yoga is a cross-platform layout engine which implements Flexbox.'
spec.description = 'Yoga is a cross-platform layout engine enabling maximum collaboration within your team by implementing an API many designers are familiar with, and opening it up to developers across different platforms.'
spec.authors = 'Facebook'
spec.source = source
spec.module_name = 'yoga'
spec.header_dir = 'yoga'
spec.requires_arc = false
spec.pod_target_xcconfig = {
'DEFINES_MODULE' => 'YES'
}.merge!(ENV['USE_FRAMEWORKS'] != nil ? {
'HEADER_SEARCH_PATHS' => '"$(PODS_TARGET_SRCROOT)"'
} : {})
spec.compiler_flags = [
'-fno-omit-frame-pointer',
'-fexceptions',
'-Wall',
'-Werror',
'-std=c++20',
'-fPIC'
]
# Pinning to the same version as React.podspec.
spec.platforms = min_supported_versions
# Set this environment variable when *not* using the `:path` option to install the pod.
# E.g. when publishing this spec to a spec repo.
source_files = 'yoga/**/*.{cpp,h}'
source_files = File.join('ReactCommon/yoga', source_files) if ENV['INSTALL_YOGA_WITHOUT_PATH_OPTION']
spec.source_files = podspec_sources(source_files, "yoga/**/*.h")
spec.header_mappings_dir = 'yoga'
public_header_files = 'yoga/*.h'
public_header_files = File.join('ReactCommon/yoga', public_header_files) if ENV['INSTALL_YOGA_WITHOUT_PATH_OPTION']
spec.public_header_files = public_header_files
# Fabric must be able to access private headers (which should not be included in the umbrella header)
all_header_files = 'yoga/**/*.h'
all_header_files = File.join('ReactCommon/yoga', all_header_files) if ENV['INSTALL_YOGA_WITHOUT_PATH_OPTION']
spec.private_header_files = Dir.glob(all_header_files) - Dir.glob(public_header_files)
spec.preserve_paths = [all_header_files]
end

View File

@@ -0,0 +1,50 @@
# 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.
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
add_compile_definitions($<$<CONFIG:DEBUG>:DEBUG>)
if(MSVC)
add_compile_options(
# Don't omit frame pointers (e.g. for crash dumps)
/Oy-
# "Standard C++ exception handling" (C++ stack unwinding including extern c)
/EHsc
# Enable warnings and warnings as errors
/W4
/WX
# Enable RTTI
$<$<COMPILE_LANGUAGE:CXX>:/GR>
# Use /O2 (Maximize Speed)
$<$<CONFIG:RELEASE>:/O2>)
else()
add_compile_options(
# Don't omit frame pointers (e.g. for crash dumps)
-fno-omit-frame-pointer
# Enable exception handling
-fexceptions
# Enable warnings and warnings as errors
-Wall
-Werror
# Enable RTTI
$<$<COMPILE_LANGUAGE:CXX>:-frtti>
# Use -O2 (prioritize speed)
$<$<CONFIG:RELEASE>:-O2>
# Enable separate sections per function/data item
$<$<CONFIG:RELEASE>:-ffunction-sections>
$<$<CONFIG:RELEASE>:-fdata-sections>)
add_link_options(
# Discard unused sections
$<$<CONFIG:RELEASE>:$<$<CXX_COMPILER_ID:Clang,GNU>:-Wl,--gc-sections>>
$<$<CONFIG:RELEASE>:$<$<CXX_COMPILER_ID:AppleClang>:-Wl,-dead_strip>>)
endif()

View File

@@ -0,0 +1,10 @@
# 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.
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/yogaTargets.cmake")
check_required_components(yoga)

View File

@@ -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 <android/log> 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
$<BUILD_INTERFACE:${YOGA_ROOT}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include/yoga>)

View File

@@ -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.
*/
#include <yoga/Yoga.h>
#include <yoga/debug/AssertFatal.h>
#include <yoga/debug/Log.h>
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 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 {
resolveRef(config)->setLogger(getDefaultLogger());
}
}
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);
}

View File

@@ -0,0 +1,158 @@
/*
* 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 <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <yoga/YGEnums.h>
#include <yoga/YGMacros.h>
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

View File

@@ -0,0 +1,268 @@
/*
* 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
#include <yoga/YGEnums.h>
const char* YGAlignToString(const YGAlign value) {
switch (value) {
case YGAlignAuto:
return "auto";
case YGAlignFlexStart:
return "flex-start";
case YGAlignCenter:
return "center";
case YGAlignFlexEnd:
return "flex-end";
case YGAlignStretch:
return "stretch";
case YGAlignBaseline:
return "baseline";
case YGAlignSpaceBetween:
return "space-between";
case YGAlignSpaceAround:
return "space-around";
case YGAlignSpaceEvenly:
return "space-evenly";
}
return "unknown";
}
const char* YGBoxSizingToString(const YGBoxSizing value) {
switch (value) {
case YGBoxSizingBorderBox:
return "border-box";
case YGBoxSizingContentBox:
return "content-box";
}
return "unknown";
}
const char* YGDimensionToString(const YGDimension value) {
switch (value) {
case YGDimensionWidth:
return "width";
case YGDimensionHeight:
return "height";
}
return "unknown";
}
const char* YGDirectionToString(const YGDirection value) {
switch (value) {
case YGDirectionInherit:
return "inherit";
case YGDirectionLTR:
return "ltr";
case YGDirectionRTL:
return "rtl";
}
return "unknown";
}
const char* YGDisplayToString(const YGDisplay value) {
switch (value) {
case YGDisplayFlex:
return "flex";
case YGDisplayNone:
return "none";
case YGDisplayContents:
return "contents";
}
return "unknown";
}
const char* YGEdgeToString(const YGEdge value) {
switch (value) {
case YGEdgeLeft:
return "left";
case YGEdgeTop:
return "top";
case YGEdgeRight:
return "right";
case YGEdgeBottom:
return "bottom";
case YGEdgeStart:
return "start";
case YGEdgeEnd:
return "end";
case YGEdgeHorizontal:
return "horizontal";
case YGEdgeVertical:
return "vertical";
case YGEdgeAll:
return "all";
}
return "unknown";
}
const char* 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";
}
return "unknown";
}
const char* YGFlexDirectionToString(const YGFlexDirection value) {
switch (value) {
case YGFlexDirectionColumn:
return "column";
case YGFlexDirectionColumnReverse:
return "column-reverse";
case YGFlexDirectionRow:
return "row";
case YGFlexDirectionRowReverse:
return "row-reverse";
}
return "unknown";
}
const char* 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 YGJustifyFlexStart:
return "flex-start";
case YGJustifyCenter:
return "center";
case YGJustifyFlexEnd:
return "flex-end";
case YGJustifySpaceBetween:
return "space-between";
case YGJustifySpaceAround:
return "space-around";
case YGJustifySpaceEvenly:
return "space-evenly";
}
return "unknown";
}
const char* YGLogLevelToString(const YGLogLevel value) {
switch (value) {
case YGLogLevelError:
return "error";
case YGLogLevelWarn:
return "warn";
case YGLogLevelInfo:
return "info";
case YGLogLevelDebug:
return "debug";
case YGLogLevelVerbose:
return "verbose";
case YGLogLevelFatal:
return "fatal";
}
return "unknown";
}
const char* YGMeasureModeToString(const YGMeasureMode value) {
switch (value) {
case YGMeasureModeUndefined:
return "undefined";
case YGMeasureModeExactly:
return "exactly";
case YGMeasureModeAtMost:
return "at-most";
}
return "unknown";
}
const char* YGNodeTypeToString(const YGNodeType value) {
switch (value) {
case YGNodeTypeDefault:
return "default";
case YGNodeTypeText:
return "text";
}
return "unknown";
}
const char* YGOverflowToString(const YGOverflow value) {
switch (value) {
case YGOverflowVisible:
return "visible";
case YGOverflowHidden:
return "hidden";
case YGOverflowScroll:
return "scroll";
}
return "unknown";
}
const char* YGPositionTypeToString(const YGPositionType value) {
switch (value) {
case YGPositionTypeStatic:
return "static";
case YGPositionTypeRelative:
return "relative";
case YGPositionTypeAbsolute:
return "absolute";
}
return "unknown";
}
const char* YGUnitToString(const YGUnit value) {
switch (value) {
case YGUnitUndefined:
return "undefined";
case YGUnitPoint:
return "point";
case YGUnitPercent:
return "percent";
case YGUnitAuto:
return "auto";
case YGUnitMaxContent:
return "max-content";
case YGUnitFitContent:
return "fit-content";
case YGUnitStretch:
return "stretch";
}
return "unknown";
}
const char* YGWrapToString(const YGWrap value) {
switch (value) {
case YGWrapNoWrap:
return "no-wrap";
case YGWrapWrap:
return "wrap";
case YGWrapWrapReverse:
return "wrap-reverse";
}
return "unknown";
}

View File

@@ -0,0 +1,145 @@
/*
* 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 <yoga/YGMacros.h>
YG_EXTERN_C_BEGIN
YG_ENUM_DECL(
YGAlign,
YGAlignAuto,
YGAlignFlexStart,
YGAlignCenter,
YGAlignFlexEnd,
YGAlignStretch,
YGAlignBaseline,
YGAlignSpaceBetween,
YGAlignSpaceAround,
YGAlignSpaceEvenly)
YG_ENUM_DECL(
YGBoxSizing,
YGBoxSizingBorderBox,
YGBoxSizingContentBox)
YG_ENUM_DECL(
YGDimension,
YGDimensionWidth,
YGDimensionHeight)
YG_ENUM_DECL(
YGDirection,
YGDirectionInherit,
YGDirectionLTR,
YGDirectionRTL)
YG_ENUM_DECL(
YGDisplay,
YGDisplayFlex,
YGDisplayNone,
YGDisplayContents)
YG_ENUM_DECL(
YGEdge,
YGEdgeLeft,
YGEdgeTop,
YGEdgeRight,
YGEdgeBottom,
YGEdgeStart,
YGEdgeEnd,
YGEdgeHorizontal,
YGEdgeVertical,
YGEdgeAll)
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)
YG_ENUM_DECL(
YGFlexDirection,
YGFlexDirectionColumn,
YGFlexDirectionColumnReverse,
YGFlexDirectionRow,
YGFlexDirectionRowReverse)
YG_ENUM_DECL(
YGGutter,
YGGutterColumn,
YGGutterRow,
YGGutterAll)
YG_ENUM_DECL(
YGJustify,
YGJustifyFlexStart,
YGJustifyCenter,
YGJustifyFlexEnd,
YGJustifySpaceBetween,
YGJustifySpaceAround,
YGJustifySpaceEvenly)
YG_ENUM_DECL(
YGLogLevel,
YGLogLevelError,
YGLogLevelWarn,
YGLogLevelInfo,
YGLogLevelDebug,
YGLogLevelVerbose,
YGLogLevelFatal)
YG_ENUM_DECL(
YGMeasureMode,
YGMeasureModeUndefined,
YGMeasureModeExactly,
YGMeasureModeAtMost)
YG_ENUM_DECL(
YGNodeType,
YGNodeTypeDefault,
YGNodeTypeText)
YG_ENUM_DECL(
YGOverflow,
YGOverflowVisible,
YGOverflowHidden,
YGOverflowScroll)
YG_ENUM_DECL(
YGPositionType,
YGPositionTypeStatic,
YGPositionTypeRelative,
YGPositionTypeAbsolute)
YG_ENUM_DECL(
YGUnit,
YGUnitUndefined,
YGUnitPoint,
YGUnitPercent,
YGUnitAuto,
YGUnitMaxContent,
YGUnitFitContent,
YGUnitStretch)
YG_ENUM_DECL(
YGWrap,
YGWrapNoWrap,
YGWrapWrap,
YGWrapWrapReverse)
YG_EXTERN_C_END

View File

@@ -0,0 +1,99 @@
/*
* 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
#ifdef __cplusplus
#include <type_traits>
#endif
#ifdef __cplusplus
#define YG_EXTERN_C_BEGIN extern "C" {
#define YG_EXTERN_C_END }
#else
#define YG_EXTERN_C_BEGIN
#define YG_EXTERN_C_END
#endif
#if defined(__cplusplus)
#define YG_DEPRECATED(message) [[deprecated(message)]]
#elif defined(_MSC_VER)
#define YG_DEPRECATED(message) __declspec(deprecated(message))
#else
#define YG_DEPRECATED(message) __attribute__((deprecated(message)))
#endif
#ifdef _WINDLL
#define YG_EXPORT __declspec(dllexport)
#elif !defined(_MSC_VER)
#define YG_EXPORT __attribute__((visibility("default")))
#else
#define YG_EXPORT
#endif
#ifdef __OBJC__
#if __has_include(<Foundation/Foundation.h>)
#import <Foundation/Foundation.h>
#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 mismatch for the Yoga ABI.
#define YG_ENUM_BEGIN(name) NS_ENUM(int, name)
#define YG_ENUM_END(name)
#else
#define YG_ENUM_BEGIN(name) enum name
#define YG_ENUM_END(name) name
#endif
#ifdef __cplusplus
#define YG_DEFINE_ENUM_FLAG_OPERATORS(name) \
extern "C++" { \
constexpr name operator~(name a) { \
return static_cast<name>( \
~static_cast<std::underlying_type<name>::type>(a)); \
} \
constexpr name operator|(name a, name b) { \
return static_cast<name>( \
static_cast<std::underlying_type<name>::type>(a) | \
static_cast<std::underlying_type<name>::type>(b)); \
} \
constexpr name operator&(name a, name b) { \
return static_cast<name>( \
static_cast<std::underlying_type<name>::type>(a) & \
static_cast<std::underlying_type<name>::type>(b)); \
} \
constexpr name operator^(name a, name b) { \
return static_cast<name>( \
static_cast<std::underlying_type<name>::type>(a) ^ \
static_cast<std::underlying_type<name>::type>(b)); \
} \
inline name& operator|=(name& a, name b) { \
return reinterpret_cast<name&>( \
reinterpret_cast<std::underlying_type<name>::type&>(a) |= \
static_cast<std::underlying_type<name>::type>(b)); \
} \
inline name& operator&=(name& a, name b) { \
return reinterpret_cast<name&>( \
reinterpret_cast<std::underlying_type<name>::type&>(a) &= \
static_cast<std::underlying_type<name>::type>(b)); \
} \
inline name& operator^=(name& a, name b) { \
return reinterpret_cast<name&>( \
reinterpret_cast<std::underlying_type<name>::type&>(a) ^= \
static_cast<std::underlying_type<name>::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);

View File

@@ -0,0 +1,366 @@
/*
* 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 <yoga/Yoga.h>
#include <yoga/algorithm/Cache.h>
#include <yoga/algorithm/CalculateLayout.h>
#include <yoga/debug/AssertFatal.h>
#include <yoga/debug/Log.h>
#include <yoga/event/event.h>
#include <yoga/node/Node.h>
using namespace facebook;
using namespace facebook::yoga;
YGNodeRef YGNodeNew(void) {
return YGNodeNewWithConfig(YGConfigGetDefault());
}
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<Event::NodeAllocation>(node, {config});
return node;
}
YGNodeRef YGNodeClone(YGNodeConstRef oldNodeRef) {
auto oldNode = resolveRef(oldNodeRef);
const auto node = new yoga::Node(*oldNode);
Event::publish<Event::NodeAllocation>(node, {node->getConfig()});
node->setOwner(nullptr);
return node;
}
void YGNodeFree(const YGNodeRef nodeRef) {
const auto node = resolveRef(nodeRef);
if (auto owner = node->getOwner()) {
owner->removeChild(node);
node->setOwner(nullptr);
}
const size_t childCount = node->getChildCount();
for (size_t i = 0; i < childCount; i++) {
auto child = node->getChild(i);
child->setOwner(nullptr);
}
node->clearChildren();
Event::publish<Event::NodeDeallocation>(node, {YGNodeGetConfig(node)});
delete resolveRef(node);
}
void YGNodeFreeRecursive(YGNodeRef rootRef) {
const auto root = resolveRef(rootRef);
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);
}
}
YGNodeFree(root);
}
void YGNodeFinalize(const YGNodeRef node) {
Event::publish<Event::NodeDeallocation>(node, {YGNodeGetConfig(node)});
delete resolveRef(node);
}
void YGNodeReset(YGNodeRef node) {
resolveRef(node)->reset();
}
void YGNodeCalculateLayout(
const YGNodeRef node,
const float ownerWidth,
const float ownerHeight,
const YGDirection ownerDirection) {
yoga::calculateLayout(
resolveRef(node), ownerWidth, ownerHeight, scopedEnum(ownerDirection));
}
bool YGNodeGetHasNewLayout(YGNodeConstRef node) {
return resolveRef(node)->getHasNewLayout();
}
void YGNodeSetHasNewLayout(YGNodeRef node, bool hasNewLayout) {
resolveRef(node)->setHasNewLayout(hasNewLayout);
}
bool YGNodeIsDirty(YGNodeConstRef node) {
return resolveRef(node)->isDirty();
}
void YGNodeMarkDirty(const YGNodeRef nodeRef) {
const auto node = resolveRef(nodeRef);
yoga::assertFatalWithNode(
node,
node->hasMeasureFunc(),
"Only leaf nodes with custom measure functions "
"should manually mark themselves as dirty");
node->markDirtyAndPropagate();
}
void YGNodeSetDirtiedFunc(YGNodeRef node, YGDirtiedFunc dirtiedFunc) {
resolveRef(node)->setDirtiedFunc(dirtiedFunc);
}
YGDirtiedFunc YGNodeGetDirtiedFunc(YGNodeConstRef node) {
return resolveRef(node)->getDirtiedFunc();
}
void YGNodeInsertChild(
const YGNodeRef ownerRef,
const YGNodeRef childRef,
const size_t index) {
auto owner = resolveRef(ownerRef);
auto child = resolveRef(childRef);
yoga::assertFatalWithNode(
owner,
child->getOwner() == nullptr,
"Child already has a owner, it must be removed first.");
yoga::assertFatalWithNode(
owner,
!owner->hasMeasureFunc(),
"Cannot add child: Nodes with measure functions cannot have children.");
owner->insertChild(child, index);
child->setOwner(owner);
owner->markDirtyAndPropagate();
}
void YGNodeSwapChild(
const YGNodeRef ownerRef,
const YGNodeRef childRef,
const size_t index) {
auto owner = resolveRef(ownerRef);
auto child = resolveRef(childRef);
owner->replaceChild(child, index);
child->setOwner(owner);
}
void YGNodeRemoveChild(
const YGNodeRef ownerRef,
const YGNodeRef excludedChildRef) {
auto owner = resolveRef(ownerRef);
auto excludedChild = resolveRef(excludedChildRef);
if (owner->getChildCount() == 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->markDirtyAndPropagate();
}
}
void YGNodeRemoveAllChildren(const YGNodeRef ownerRef) {
auto owner = resolveRef(ownerRef);
const size_t childCount = owner->getChildCount();
if (childCount == 0) {
// This is an empty set already. Nothing to do.
return;
}
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);
}
owner->clearChildren();
owner->markDirtyAndPropagate();
return;
}
// 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 YGNodeSetChildren(
const YGNodeRef ownerRef,
const YGNodeRef* childrenRefs,
const size_t count) {
auto owner = resolveRef(ownerRef);
auto children = reinterpret_cast<yoga::Node* const*>(childrenRefs);
if (owner == nullptr) {
return;
}
const std::vector<yoga::Node*> 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 {
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();
}
}
YGNodeRef YGNodeGetChild(const YGNodeRef nodeRef, const size_t index) {
const auto node = resolveRef(nodeRef);
if (index < node->getChildren().size()) {
return node->getChild(index);
}
return nullptr;
}
size_t YGNodeGetChildCount(const YGNodeConstRef node) {
return resolveRef(node)->getChildren().size();
}
YGNodeRef YGNodeGetOwner(const YGNodeRef node) {
return resolveRef(node)->getOwner();
}
YGNodeRef YGNodeGetParent(const YGNodeRef node) {
return resolveRef(node)->getOwner();
}
void YGNodeSetConfig(YGNodeRef node, YGConfigRef config) {
resolveRef(node)->setConfig(resolveRef(config));
}
YGConfigConstRef YGNodeGetConfig(YGNodeRef node) {
return resolveRef(node)->getConfig();
}
void YGNodeSetContext(YGNodeRef node, void* context) {
return resolveRef(node)->setContext(context);
}
void* YGNodeGetContext(YGNodeConstRef node) {
return resolveRef(node)->getContext();
}
void YGNodeSetMeasureFunc(YGNodeRef node, YGMeasureFunc measureFunc) {
resolveRef(node)->setMeasureFunc(measureFunc);
}
bool YGNodeHasMeasureFunc(YGNodeConstRef node) {
return resolveRef(node)->hasMeasureFunc();
}
void YGNodeSetBaselineFunc(YGNodeRef node, YGBaselineFunc baselineFunc) {
resolveRef(node)->setBaselineFunc(baselineFunc);
}
bool YGNodeHasBaselineFunc(YGNodeConstRef node) {
return resolveRef(node)->hasBaselineFunc();
}
void YGNodeSetIsReferenceBaseline(YGNodeRef nodeRef, bool isReferenceBaseline) {
const auto node = resolveRef(nodeRef);
if (node->isReferenceBaseline() != isReferenceBaseline) {
node->setIsReferenceBaseline(isReferenceBaseline);
node->markDirtyAndPropagate();
}
}
bool YGNodeIsReferenceBaseline(YGNodeConstRef node) {
return resolveRef(node)->isReferenceBaseline();
}
void YGNodeSetNodeType(YGNodeRef node, YGNodeType nodeType) {
return resolveRef(node)->setNodeType(scopedEnum(nodeType));
}
YGNodeType YGNodeGetNodeType(YGNodeConstRef node) {
return unscopedEnum(resolveRef(node)->getNodeType());
}
void YGNodeSetAlwaysFormsContainingBlock(
YGNodeRef node,
bool alwaysFormsContainingBlock) {
resolveRef(node)->setAlwaysFormsContainingBlock(alwaysFormsContainingBlock);
}
bool YGNodeGetAlwaysFormsContainingBlock(YGNodeConstRef node) {
return resolveRef(node)->alwaysFormsContainingBlock();
}
// 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));
}

View File

@@ -0,0 +1,304 @@
/*
* 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 <stdbool.h>
#include <stddef.h>
#include <yoga/YGConfig.h>
#include <yoga/YGEnums.h>
#include <yoga/YGMacros.h>
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);
/**
* Wwhether 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

View File

@@ -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 <yoga/Yoga.h>
#include <yoga/debug/AssertFatal.h>
#include <yoga/enums/Edge.h>
#include <yoga/node/Node.h>
using namespace facebook;
using namespace facebook::yoga;
namespace {
template <auto LayoutMember>
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<PhysicalEdge>(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);
}

View File

@@ -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 <stdbool.h>
#include <yoga/YGConfig.h>
#include <yoga/YGEnums.h>
#include <yoga/YGMacros.h>
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

View File

@@ -0,0 +1,505 @@
/*
* 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 <yoga/Yoga.h>
#include <yoga/debug/AssertFatal.h>
#include <yoga/node/Node.h>
using namespace facebook;
using namespace facebook::yoga;
namespace {
template <auto GetterT, auto SetterT, typename ValueT>
void updateStyle(YGNodeRef node, ValueT value) {
auto& style = resolveRef(node)->style();
if ((style.*GetterT)() != value) {
(style.*SetterT)(value);
resolveRef(node)->markDirtyAndPropagate();
}
}
template <auto GetterT, auto SetterT, typename IdxT, typename ValueT>
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 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<YGValue>(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);
}

View File

@@ -0,0 +1,151 @@
/*
* 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 <stddef.h>
#include <yoga/YGNode.h>
#include <yoga/YGValue.h>
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 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);
YG_EXTERN_C_END

View File

@@ -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 <yoga/Yoga.h>
#include <yoga/algorithm/PixelGrid.h>
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);
}

View File

@@ -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 <stdbool.h>
#include <yoga/YGConfig.h>
#include <yoga/YGEnums.h>
#include <yoga/YGMacros.h>
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

View File

@@ -0,0 +1,20 @@
/*
* 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 <yoga/YGValue.h>
#include <yoga/numeric/Comparison.h>
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);
}

View File

@@ -0,0 +1,87 @@
/*
* 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 <stdbool.h>
#include <yoga/YGEnums.h>
#include <yoga/YGMacros.h>
/**
* Float value to represent "undefined" in style values.
*/
#ifdef __cplusplus
#include <limits>
constexpr float YGUndefined = std::numeric_limits<float>::quiet_NaN();
#else
#include <math.h>
#define YGUndefined NAN
#endif
YG_EXTERN_C_BEGIN
/**
* Structure used to represent a dimension in a style.
*/
typedef struct YGValue {
float value;
YGUnit unit;
} YGValue;
/**
* 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;
}
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;
}
}
inline bool operator!=(const YGValue& lhs, const YGValue& rhs) {
return !(lhs == rhs);
}
inline YGValue operator-(const YGValue& value) {
return {-value.value, value.unit};
}
#endif

21
node_modules/react-native/ReactCommon/yoga/yoga/Yoga.h generated vendored Normal file
View File

@@ -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 <yoga/Yoga.h>` includes all of Yoga's public headers.
*/
#include <yoga/YGConfig.h> // IWYU pragma: export
#include <yoga/YGEnums.h> // IWYU pragma: export
#include <yoga/YGMacros.h> // IWYU pragma: export
#include <yoga/YGNode.h> // IWYU pragma: export
#include <yoga/YGNodeLayout.h> // IWYU pragma: export
#include <yoga/YGNodeStyle.h> // IWYU pragma: export
#include <yoga/YGPixelGrid.h> // IWYU pragma: export
#include <yoga/YGValue.h> // IWYU pragma: export

View File

@@ -0,0 +1,563 @@
/*
* 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 <yoga/algorithm/AbsoluteLayout.h>
#include <yoga/algorithm/Align.h>
#include <yoga/algorithm/BoundAxis.h>
#include <yoga/algorithm/CalculateLayout.h>
#include <yoga/algorithm/TrailingPosition.h>
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));
if (!child->hasErrata(Errata::AbsolutePositionWithoutInsetsExcludesPadding)) {
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);
if (!child->hasErrata(Errata::AbsolutePositionWithoutInsetsExcludesPadding)) {
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));
if (!child->hasErrata(Errata::AbsolutePositionWithoutInsetsExcludesPadding)) {
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);
if (!child->hasErrata(Errata::AbsolutePositionWithoutInsetsExcludesPadding)) {
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 parentJustifyContent = parent->style().justifyContent();
switch (parentJustifyContent) {
case Justify::FlexStart:
case Justify::SpaceBetween:
setFlexStartLayoutPosition(
parent, child, direction, mainAxis, containingBlockWidth);
break;
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::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::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) {
const FlexDirection mainAxis =
resolveDirection(node->style().flexDirection(), direction);
const FlexDirection crossAxis = 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

View File

@@ -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 <yoga/event/event.h>
#include <yoga/node/Node.h>
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

View File

@@ -0,0 +1,72 @@
/*
* 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/Yoga.h>
#include <yoga/algorithm/FlexDirection.h>
#include <yoga/node/Node.h>
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 (align == Align::Baseline && isColumn(node->style().flexDirection())) {
return Align::FlexStart;
}
return align;
}
/**
* 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

View File

@@ -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 <yoga/Yoga.h>
#include <yoga/algorithm/Align.h>
#include <yoga/algorithm/Baseline.h>
#include <yoga/debug/AssertFatal.h>
#include <yoga/event/event.h>
namespace facebook::yoga {
float calculateBaseline(const yoga::Node* node) {
if (node->hasBaselineFunc()) {
Event::publish<Event::NodeBaselineStart>(node);
const float baseline = node->baseline(
node->getLayout().measuredDimension(Dimension::Width),
node->getLayout().measuredDimension(Dimension::Height));
Event::publish<Event::NodeBaselineEnd>(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

View File

@@ -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 <yoga/Yoga.h>
#include <yoga/node/Node.h>
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

View File

@@ -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 <yoga/algorithm/FlexDirection.h>
#include <yoga/enums/Dimension.h>
#include <yoga/enums/FlexDirection.h>
#include <yoga/node/Node.h>
#include <yoga/numeric/Comparison.h>
#include <yoga/numeric/FloatOptional.h>
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

View File

@@ -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 <yoga/algorithm/Cache.h>
#include <yoga/algorithm/PixelGrid.h>
#include <yoga/numeric/Comparison.h>
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

View File

@@ -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 <yoga/algorithm/SizingMode.h>
#include <yoga/config/Config.h>
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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
/*
* 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/Yoga.h>
#include <yoga/algorithm/FlexDirection.h>
#include <yoga/event/event.h>
#include <yoga/node/Node.h>
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);
} // namespace facebook::yoga

View File

@@ -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 <yoga/Yoga.h>
#include <yoga/debug/AssertFatal.h>
#include <yoga/enums/Dimension.h>
#include <yoga/enums/Direction.h>
#include <yoga/enums/Edge.h>
#include <yoga/enums/FlexDirection.h>
#include <yoga/enums/PhysicalEdge.h>
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;
}
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;
}
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;
}
fatalWithMessage("Invalid FlexDirection");
}
} // namespace facebook::yoga

View File

@@ -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 <yoga/Yoga.h>
#include <yoga/algorithm/BoundAxis.h>
#include <yoga/algorithm/FlexDirection.h>
#include <yoga/algorithm/FlexLine.h>
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<yoga::Node*> 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

View File

@@ -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 <vector>
#include <yoga/Yoga.h>
#include <yoga/node/Node.h>
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<yoga::Node*> 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

View File

@@ -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 <yoga/Yoga.h>
#include <yoga/algorithm/PixelGrid.h>
#include <yoga/numeric/Comparison.h>
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<double>(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

View File

@@ -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 <yoga/Yoga.h>
#include <yoga/node/Node.h>
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

View File

@@ -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 <yoga/debug/AssertFatal.h>
#include <yoga/enums/MeasureMode.h>
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 boxs “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;
}
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;
}
fatalWithMessage("Invalid MeasureMode");
}
} // namespace facebook::yoga

View File

@@ -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 <yoga/Yoga.h>
#include <yoga/algorithm/FlexDirection.h>
#include <yoga/event/event.h>
#include <yoga/node/Node.h>
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

View File

@@ -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 <yoga/config/Config.h>
#include <yoga/debug/Log.h>
#include <yoga/node/Node.h>
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<size_t>(feature), enabled);
version_++;
}
}
bool Config::isExperimentalFeatureEnabled(ExperimentalFeature feature) const {
return experimentalFeatures_.test(static_cast<size_t>(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

View File

@@ -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 <bitset>
#include <yoga/Yoga.h>
#include <yoga/enums/Errata.h>
#include <yoga/enums/ExperimentalFeature.h>
#include <yoga/enums/LogLevel.h>
// 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<ordinalCount<ExperimentalFeature>()>;
// 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<Config*>(ref);
}
inline const Config* resolveRef(const YGConfigConstRef ref) {
return static_cast<const Config*>(ref);
}
} // namespace facebook::yoga

View File

@@ -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 <exception>
#include <stdexcept>
#include <yoga/config/Config.h>
#include <yoga/debug/AssertFatal.h>
#include <yoga/debug/Log.h>
#include <yoga/node/Node.h>
namespace facebook::yoga {
[[noreturn]] void fatalWithMessage(const char* message) {
#if defined(__cpp_exceptions)
throw std::logic_error(message);
#else
static_cast<void>(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

View File

@@ -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 <yoga/Yoga.h>
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

View File

@@ -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 <yoga/debug/Log.h>
#ifdef ANDROID
#include <android/log.h>
#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

View File

@@ -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 <yoga/Yoga.h>
#include <yoga/config/Config.h>
#include <yoga/enums/LogLevel.h>
#include <yoga/node/Node.h>
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

View File

@@ -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 <cstdint>
#include <yoga/YGEnums.h>
#include <yoga/enums/YogaEnums.h>
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,
};
template <>
constexpr int32_t ordinalCount<Align>() {
return 9;
}
constexpr Align scopedEnum(YGAlign unscoped) {
return static_cast<Align>(unscoped);
}
constexpr YGAlign unscopedEnum(Align scoped) {
return static_cast<YGAlign>(scoped);
}
inline const char* toString(Align e) {
return YGAlignToString(unscopedEnum(e));
}
} // namespace facebook::yoga

View File

@@ -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 <cstdint>
#include <yoga/YGEnums.h>
#include <yoga/enums/YogaEnums.h>
namespace facebook::yoga {
enum class BoxSizing : uint8_t {
BorderBox = YGBoxSizingBorderBox,
ContentBox = YGBoxSizingContentBox,
};
template <>
constexpr int32_t ordinalCount<BoxSizing>() {
return 2;
}
constexpr BoxSizing scopedEnum(YGBoxSizing unscoped) {
return static_cast<BoxSizing>(unscoped);
}
constexpr YGBoxSizing unscopedEnum(BoxSizing scoped) {
return static_cast<YGBoxSizing>(scoped);
}
inline const char* toString(BoxSizing e) {
return YGBoxSizingToString(unscopedEnum(e));
}
} // namespace facebook::yoga

View File

@@ -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 <cstdint>
#include <yoga/YGEnums.h>
#include <yoga/enums/YogaEnums.h>
namespace facebook::yoga {
enum class Dimension : uint8_t {
Width = YGDimensionWidth,
Height = YGDimensionHeight,
};
template <>
constexpr int32_t ordinalCount<Dimension>() {
return 2;
}
constexpr Dimension scopedEnum(YGDimension unscoped) {
return static_cast<Dimension>(unscoped);
}
constexpr YGDimension unscopedEnum(Dimension scoped) {
return static_cast<YGDimension>(scoped);
}
inline const char* toString(Dimension e) {
return YGDimensionToString(unscopedEnum(e));
}
} // namespace facebook::yoga

View File

@@ -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 <cstdint>
#include <yoga/YGEnums.h>
#include <yoga/enums/YogaEnums.h>
namespace facebook::yoga {
enum class Direction : uint8_t {
Inherit = YGDirectionInherit,
LTR = YGDirectionLTR,
RTL = YGDirectionRTL,
};
template <>
constexpr int32_t ordinalCount<Direction>() {
return 3;
}
constexpr Direction scopedEnum(YGDirection unscoped) {
return static_cast<Direction>(unscoped);
}
constexpr YGDirection unscopedEnum(Direction scoped) {
return static_cast<YGDirection>(scoped);
}
inline const char* toString(Direction e) {
return YGDirectionToString(unscopedEnum(e));
}
} // namespace facebook::yoga

View File

@@ -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 <cstdint>
#include <yoga/YGEnums.h>
#include <yoga/enums/YogaEnums.h>
namespace facebook::yoga {
enum class Display : uint8_t {
Flex = YGDisplayFlex,
None = YGDisplayNone,
Contents = YGDisplayContents,
};
template <>
constexpr int32_t ordinalCount<Display>() {
return 3;
}
constexpr Display scopedEnum(YGDisplay unscoped) {
return static_cast<Display>(unscoped);
}
constexpr YGDisplay unscopedEnum(Display scoped) {
return static_cast<YGDisplay>(scoped);
}
inline const char* toString(Display e) {
return YGDisplayToString(unscopedEnum(e));
}
} // namespace facebook::yoga

View File

@@ -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 <cstdint>
#include <yoga/YGEnums.h>
#include <yoga/enums/YogaEnums.h>
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<Edge>() {
return 9;
}
constexpr Edge scopedEnum(YGEdge unscoped) {
return static_cast<Edge>(unscoped);
}
constexpr YGEdge unscopedEnum(Edge scoped) {
return static_cast<YGEdge>(scoped);
}
inline const char* toString(Edge e) {
return YGEdgeToString(unscopedEnum(e));
}
} // namespace facebook::yoga

View File

@@ -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 <cstdint>
#include <yoga/YGEnums.h>
#include <yoga/enums/YogaEnums.h>
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<Errata>(unscoped);
}
constexpr YGErrata unscopedEnum(Errata scoped) {
return static_cast<YGErrata>(scoped);
}
inline const char* toString(Errata e) {
return YGErrataToString(unscopedEnum(e));
}
} // namespace facebook::yoga

View File

@@ -0,0 +1,39 @@
/*
* 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 <cstdint>
#include <yoga/YGEnums.h>
#include <yoga/enums/YogaEnums.h>
namespace facebook::yoga {
enum class ExperimentalFeature : uint8_t {
WebFlexBasis = YGExperimentalFeatureWebFlexBasis,
};
template <>
constexpr int32_t ordinalCount<ExperimentalFeature>() {
return 1;
}
constexpr ExperimentalFeature scopedEnum(YGExperimentalFeature unscoped) {
return static_cast<ExperimentalFeature>(unscoped);
}
constexpr YGExperimentalFeature unscopedEnum(ExperimentalFeature scoped) {
return static_cast<YGExperimentalFeature>(scoped);
}
inline const char* toString(ExperimentalFeature e) {
return YGExperimentalFeatureToString(unscopedEnum(e));
}
} // namespace facebook::yoga

View File

@@ -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 <cstdint>
#include <yoga/YGEnums.h>
#include <yoga/enums/YogaEnums.h>
namespace facebook::yoga {
enum class FlexDirection : uint8_t {
Column = YGFlexDirectionColumn,
ColumnReverse = YGFlexDirectionColumnReverse,
Row = YGFlexDirectionRow,
RowReverse = YGFlexDirectionRowReverse,
};
template <>
constexpr int32_t ordinalCount<FlexDirection>() {
return 4;
}
constexpr FlexDirection scopedEnum(YGFlexDirection unscoped) {
return static_cast<FlexDirection>(unscoped);
}
constexpr YGFlexDirection unscopedEnum(FlexDirection scoped) {
return static_cast<YGFlexDirection>(scoped);
}
inline const char* toString(FlexDirection e) {
return YGFlexDirectionToString(unscopedEnum(e));
}
} // namespace facebook::yoga

View File

@@ -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 <cstdint>
#include <yoga/YGEnums.h>
#include <yoga/enums/YogaEnums.h>
namespace facebook::yoga {
enum class Gutter : uint8_t {
Column = YGGutterColumn,
Row = YGGutterRow,
All = YGGutterAll,
};
template <>
constexpr int32_t ordinalCount<Gutter>() {
return 3;
}
constexpr Gutter scopedEnum(YGGutter unscoped) {
return static_cast<Gutter>(unscoped);
}
constexpr YGGutter unscopedEnum(Gutter scoped) {
return static_cast<YGGutter>(scoped);
}
inline const char* toString(Gutter e) {
return YGGutterToString(unscopedEnum(e));
}
} // namespace facebook::yoga

View File

@@ -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 <cstdint>
#include <yoga/YGEnums.h>
#include <yoga/enums/YogaEnums.h>
namespace facebook::yoga {
enum class Justify : uint8_t {
FlexStart = YGJustifyFlexStart,
Center = YGJustifyCenter,
FlexEnd = YGJustifyFlexEnd,
SpaceBetween = YGJustifySpaceBetween,
SpaceAround = YGJustifySpaceAround,
SpaceEvenly = YGJustifySpaceEvenly,
};
template <>
constexpr int32_t ordinalCount<Justify>() {
return 6;
}
constexpr Justify scopedEnum(YGJustify unscoped) {
return static_cast<Justify>(unscoped);
}
constexpr YGJustify unscopedEnum(Justify scoped) {
return static_cast<YGJustify>(scoped);
}
inline const char* toString(Justify e) {
return YGJustifyToString(unscopedEnum(e));
}
} // namespace facebook::yoga

View File

@@ -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 <cstdint>
#include <yoga/YGEnums.h>
#include <yoga/enums/YogaEnums.h>
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<LogLevel>() {
return 6;
}
constexpr LogLevel scopedEnum(YGLogLevel unscoped) {
return static_cast<LogLevel>(unscoped);
}
constexpr YGLogLevel unscopedEnum(LogLevel scoped) {
return static_cast<YGLogLevel>(scoped);
}
inline const char* toString(LogLevel e) {
return YGLogLevelToString(unscopedEnum(e));
}
} // namespace facebook::yoga

View File

@@ -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 <cstdint>
#include <yoga/YGEnums.h>
#include <yoga/enums/YogaEnums.h>
namespace facebook::yoga {
enum class MeasureMode : uint8_t {
Undefined = YGMeasureModeUndefined,
Exactly = YGMeasureModeExactly,
AtMost = YGMeasureModeAtMost,
};
template <>
constexpr int32_t ordinalCount<MeasureMode>() {
return 3;
}
constexpr MeasureMode scopedEnum(YGMeasureMode unscoped) {
return static_cast<MeasureMode>(unscoped);
}
constexpr YGMeasureMode unscopedEnum(MeasureMode scoped) {
return static_cast<YGMeasureMode>(scoped);
}
inline const char* toString(MeasureMode e) {
return YGMeasureModeToString(unscopedEnum(e));
}
} // namespace facebook::yoga

View File

@@ -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 <cstdint>
#include <yoga/YGEnums.h>
#include <yoga/enums/YogaEnums.h>
namespace facebook::yoga {
enum class NodeType : uint8_t {
Default = YGNodeTypeDefault,
Text = YGNodeTypeText,
};
template <>
constexpr int32_t ordinalCount<NodeType>() {
return 2;
}
constexpr NodeType scopedEnum(YGNodeType unscoped) {
return static_cast<NodeType>(unscoped);
}
constexpr YGNodeType unscopedEnum(NodeType scoped) {
return static_cast<YGNodeType>(scoped);
}
inline const char* toString(NodeType e) {
return YGNodeTypeToString(unscopedEnum(e));
}
} // namespace facebook::yoga

View File

@@ -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 <cstdint>
#include <yoga/YGEnums.h>
#include <yoga/enums/YogaEnums.h>
namespace facebook::yoga {
enum class Overflow : uint8_t {
Visible = YGOverflowVisible,
Hidden = YGOverflowHidden,
Scroll = YGOverflowScroll,
};
template <>
constexpr int32_t ordinalCount<Overflow>() {
return 3;
}
constexpr Overflow scopedEnum(YGOverflow unscoped) {
return static_cast<Overflow>(unscoped);
}
constexpr YGOverflow unscopedEnum(Overflow scoped) {
return static_cast<YGOverflow>(scoped);
}
inline const char* toString(Overflow e) {
return YGOverflowToString(unscopedEnum(e));
}
} // namespace facebook::yoga

View File

@@ -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 <yoga/enums/Edge.h>
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

View File

@@ -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 <cstdint>
#include <yoga/YGEnums.h>
#include <yoga/enums/YogaEnums.h>
namespace facebook::yoga {
enum class PositionType : uint8_t {
Static = YGPositionTypeStatic,
Relative = YGPositionTypeRelative,
Absolute = YGPositionTypeAbsolute,
};
template <>
constexpr int32_t ordinalCount<PositionType>() {
return 3;
}
constexpr PositionType scopedEnum(YGPositionType unscoped) {
return static_cast<PositionType>(unscoped);
}
constexpr YGPositionType unscopedEnum(PositionType scoped) {
return static_cast<YGPositionType>(scoped);
}
inline const char* toString(PositionType e) {
return YGPositionTypeToString(unscopedEnum(e));
}
} // namespace facebook::yoga

View File

@@ -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 <cstdint>
#include <yoga/YGEnums.h>
#include <yoga/enums/YogaEnums.h>
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<Unit>() {
return 7;
}
constexpr Unit scopedEnum(YGUnit unscoped) {
return static_cast<Unit>(unscoped);
}
constexpr YGUnit unscopedEnum(Unit scoped) {
return static_cast<YGUnit>(scoped);
}
inline const char* toString(Unit e) {
return YGUnitToString(unscopedEnum(e));
}
} // namespace facebook::yoga

View File

@@ -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 <cstdint>
#include <yoga/YGEnums.h>
#include <yoga/enums/YogaEnums.h>
namespace facebook::yoga {
enum class Wrap : uint8_t {
NoWrap = YGWrapNoWrap,
Wrap = YGWrapWrap,
WrapReverse = YGWrapWrapReverse,
};
template <>
constexpr int32_t ordinalCount<Wrap>() {
return 3;
}
constexpr Wrap scopedEnum(YGWrap unscoped) {
return static_cast<Wrap>(unscoped);
}
constexpr YGWrap unscopedEnum(Wrap scoped) {
return static_cast<YGWrap>(scoped);
}
inline const char* toString(Wrap e) {
return YGWrapToString(unscopedEnum(e));
}
} // namespace facebook::yoga

View File

@@ -0,0 +1,85 @@
/*
* 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 <bit>
#include <iterator>
#include <type_traits>
namespace facebook::yoga {
/**
* Concept for any enum/enum class
*/
template <typename EnumT>
concept Enumeration = std::is_enum_v<EnumT>;
/**
* Count of ordinals in a Yoga enum which is sequential
*/
template <Enumeration EnumT>
constexpr int32_t ordinalCount();
/**
* Concept for a yoga enum which is sequential
*/
template <typename EnumT>
concept HasOrdinality = (ordinalCount<EnumT>() > 0);
/**
* Count of bits needed to represent every ordinal
*/
template <HasOrdinality EnumT>
constexpr int32_t bitCount() {
return std::bit_width(
static_cast<std::underlying_type_t<EnumT>>(ordinalCount<EnumT>() - 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<std::underlying_type_t<decltype(e)>>(e);
}
/**
* Convenience function to iterate through every value in a Yoga enum as part of
* a range-based for loop.
*/
template <HasOrdinality EnumT>
auto ordinals() {
struct Iterator {
EnumT e{};
EnumT operator*() const {
return e;
}
Iterator& operator++() {
e = static_cast<EnumT>(to_underlying(e) + 1);
return *this;
}
bool operator==(const Iterator& other) const = default;
bool operator!=(const Iterator& other) const = default;
};
struct Range {
Iterator begin() const {
return Iterator{};
}
Iterator end() const {
return Iterator{static_cast<EnumT>(ordinalCount<EnumT>())};
}
};
return Range{};
}
} // namespace facebook::yoga

View File

@@ -0,0 +1,87 @@
/*
* 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 "event.h"
#include <atomic>
#include <memory>
namespace facebook::yoga {
const char* LayoutPassReasonToString(const LayoutPassReason value) {
switch (value) {
case LayoutPassReason::kInitial:
return "initial";
case LayoutPassReason::kAbsLayout:
return "abs_layout";
case LayoutPassReason::kStretch:
return "stretch";
case LayoutPassReason::kMultilineStretch:
return "multiline_stretch";
case LayoutPassReason::kFlexLayout:
return "flex_layout";
case LayoutPassReason::kMeasureChild:
return "measure";
case LayoutPassReason::kAbsMeasureChild:
return "abs_measure";
case LayoutPassReason::kFlexMeasure:
return "flex_measure";
default:
return "unknown";
}
}
namespace {
struct Node {
std::function<Event::Subscriber> subscriber = nullptr;
Node* next = nullptr;
explicit Node(std::function<Event::Subscriber>&& subscriber)
: subscriber{std::move(subscriber)} {}
};
std::atomic<Node*> subscribers{nullptr};
Node* push(Node* newHead) {
Node* oldHead = nullptr;
do {
oldHead = subscribers.load(std::memory_order_relaxed);
if (newHead != nullptr) {
newHead->next = oldHead;
}
} while (!subscribers.compare_exchange_weak(
oldHead, newHead, std::memory_order_release, std::memory_order_relaxed));
return oldHead;
}
} // namespace
void Event::reset() {
auto head = push(nullptr);
while (head != nullptr) {
auto current = head;
head = head->next;
delete current;
}
}
void Event::subscribe(std::function<Subscriber>&& subscriber) {
push(new Node{std::move(subscriber)});
}
void Event::publish(
YGNodeConstRef node,
Type eventType,
const Data& eventData) {
for (auto subscriber = subscribers.load(std::memory_order_relaxed);
subscriber != nullptr;
subscriber = subscriber->next) {
subscriber->subscriber(node, eventType, eventData);
}
}
} // namespace facebook::yoga

View File

@@ -0,0 +1,130 @@
/*
* 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/Yoga.h>
#include <array>
#include <cstdint>
#include <functional>
#include <vector>
namespace facebook::yoga {
enum struct LayoutType : int {
kLayout = 0,
kMeasure = 1,
kCachedLayout = 2,
kCachedMeasure = 3
};
enum struct LayoutPassReason : int {
kInitial = 0,
kAbsLayout = 1,
kStretch = 2,
kMultilineStretch = 3,
kFlexLayout = 4,
kMeasureChild = 5,
kAbsMeasureChild = 6,
kFlexMeasure = 7,
COUNT
};
struct LayoutData {
int layouts = 0;
int measures = 0;
uint32_t maxMeasureCache = 0;
int cachedLayouts = 0;
int cachedMeasures = 0;
int measureCallbacks = 0;
std::array<int, static_cast<uint8_t>(LayoutPassReason::COUNT)>
measureCallbackReasonsCount;
};
const char* LayoutPassReasonToString(LayoutPassReason value);
struct YG_EXPORT Event {
enum Type {
NodeAllocation,
NodeDeallocation,
NodeLayout,
LayoutPassStart,
LayoutPassEnd,
MeasureCallbackStart,
MeasureCallbackEnd,
NodeBaselineStart,
NodeBaselineEnd,
};
class Data;
using Subscriber = void(YGNodeConstRef, Type, Data);
using Subscribers = std::vector<std::function<Subscriber>>;
template <Type E>
struct TypedData {};
class Data {
const void* data_;
public:
template <Type E>
explicit Data(const TypedData<E>& data) : data_{&data} {}
template <Type E>
const TypedData<E>& get() const {
return *static_cast<const TypedData<E>*>(data_);
}
};
static void reset();
static void subscribe(std::function<Subscriber>&& subscriber);
template <Type E>
static void publish(YGNodeConstRef node, const TypedData<E>& eventData = {}) {
publish(node, E, Data{eventData});
}
private:
static void publish(
YGNodeConstRef /*node*/,
Type /*eventType*/,
const Data& /*eventData*/);
};
template <>
struct Event::TypedData<Event::NodeAllocation> {
YGConfigConstRef config;
};
template <>
struct Event::TypedData<Event::NodeDeallocation> {
YGConfigConstRef config;
};
template <>
struct Event::TypedData<Event::LayoutPassEnd> {
LayoutData* layoutData;
};
template <>
struct Event::TypedData<Event::MeasureCallbackEnd> {
float width;
YGMeasureMode widthMeasureMode;
float height;
YGMeasureMode heightMeasureMode;
float measuredWidth;
float measuredHeight;
const LayoutPassReason reason;
};
template <>
struct Event::TypedData<Event::NodeLayout> {
LayoutType layoutType;
};
} // namespace facebook::yoga

View File

@@ -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 *
}
}

View File

@@ -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 <cmath>
#include <yoga/Yoga.h>
#include <yoga/algorithm/SizingMode.h>
#include <yoga/numeric/Comparison.h>
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

View File

@@ -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 <cmath>
#include <yoga/node/LayoutResults.h>
#include <yoga/numeric/Comparison.h>
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

View File

@@ -0,0 +1,131 @@
/*
* 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 <array>
#include <yoga/debug/AssertFatal.h>
#include <yoga/enums/Dimension.h>
#include <yoga/enums/Direction.h>
#include <yoga/enums/Edge.h>
#include <yoga/enums/PhysicalEdge.h>
#include <yoga/node/CachedMeasurement.h>
#include <yoga/numeric/FloatOptional.h>
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<CachedMeasurement, MaxCachedMeasurements> 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;
bool operator!=(LayoutResults layout) const {
return !(*this == layout);
}
private:
Direction direction_ : bitCount<Direction>() = Direction::Inherit;
bool hadOverflow_ : 1 = false;
std::array<float, 2> dimensions_ = {{YGUndefined, YGUndefined}};
std::array<float, 2> measuredDimensions_ = {{YGUndefined, YGUndefined}};
std::array<float, 2> rawDimensions_ = {{YGUndefined, YGUndefined}};
std::array<float, 4> position_ = {};
std::array<float, 4> margin_ = {};
std::array<float, 4> border_ = {};
std::array<float, 4> padding_ = {};
};
} // namespace facebook::yoga

View File

@@ -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 <cstdint>
#include <forward_list>
#include <utility>
#include <yoga/enums/Display.h>
namespace facebook::yoga {
class Node;
template <typename T>
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<std::pair<const T*, size_t>> backtrack_;
friend LayoutableChildren;
};
explicit LayoutableChildren(const T* node) : node_(node) {
static_assert(std::input_iterator<LayoutableChildren<T>::Iterator>);
static_assert(
std::is_base_of<Node, T>::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

View File

@@ -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 <algorithm>
#include <cstddef>
#include <iostream>
#include <yoga/algorithm/FlexDirection.h>
#include <yoga/debug/AssertFatal.h>
#include <yoga/debug/Log.h>
#include <yoga/node/Node.h>
#include <yoga/numeric/Comparison.h>
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<ptrdiff_t>(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<int>(isDirty) == isDirty_) {
return;
}
isDirty_ = isDirty;
if (isDirty && (dirtiedFunc_ != nullptr)) {
dirtiedFunc_(this);
}
}
void Node::setChildren(const std::vector<Node*>& 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<ptrdiff_t>(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

View File

@@ -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 <cstdint>
#include <cstdio>
#include <vector>
#include <yoga/Yoga.h>
#include <yoga/node/LayoutableChildren.h>
#include <yoga/config/Config.h>
#include <yoga/enums/Dimension.h>
#include <yoga/enums/Direction.h>
#include <yoga/enums/Edge.h>
#include <yoga/enums/Errata.h>
#include <yoga/enums/MeasureMode.h>
#include <yoga/enums/NodeType.h>
#include <yoga/enums/PhysicalEdge.h>
#include <yoga/node/LayoutResults.h>
#include <yoga/style/Style.h>
// 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>;
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<Node*>& 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<size_t>(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<Node*>& 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();
private:
// Used to allow resetting the node
Node& operator=(Node&&) noexcept = default;
float relativePosition(
FlexDirection axis,
Direction direction,
float axisSize) const;
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>() = 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<Node*> children_;
const Config* config_;
std::array<Style::SizeLength, 2> processedDimensions_{
{StyleSizeLength::undefined(), StyleSizeLength::undefined()}};
};
inline Node* resolveRef(const YGNodeRef ref) {
return static_cast<Node*>(ref);
}
inline const Node* resolveRef(const YGNodeConstRef ref) {
return static_cast<const Node*>(ref);
}
} // namespace facebook::yoga

View File

@@ -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 <algorithm>
#include <array>
#include <cmath>
#include <concepts>
#include <yoga/Yoga.h>
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<decltype(value)>::infinity() ||
value == -std::numeric_limits<decltype(value)>::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 <std::size_t Size, typename ElementT>
bool inexactEquals(
const std::array<ElementT, Size>& val1,
const std::array<ElementT, Size>& 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

View File

@@ -0,0 +1,93 @@
/*
* 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/numeric/Comparison.h>
#include <limits>
namespace facebook::yoga {
struct FloatOptional {
private:
float value_ = std::numeric_limits<float>::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, FloatOptional rhs) {
return !(lhs == rhs);
}
constexpr bool operator==(FloatOptional lhs, float rhs) {
return lhs == FloatOptional{rhs};
}
constexpr bool operator!=(FloatOptional lhs, float rhs) {
return !(lhs == rhs);
}
constexpr bool operator==(float lhs, FloatOptional rhs) {
return rhs == lhs;
}
constexpr bool operator!=(float lhs, FloatOptional rhs) {
return !(lhs == rhs);
}
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

View File

@@ -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 <array>
#include <bitset>
#include <cassert>
#include <cstdint>
#include <memory>
#include <vector>
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 <size_t BufferSize>
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<SmallValueBuffer::Overflow>();
}
overflow_->buffer_.push_back(value);
overflow_->wideElements_.push_back(false);
return index;
}
uint16_t push(uint64_t value) {
const auto lsb = static_cast<uint32_t>(value & 0xFFFFFFFF);
const auto msb = static_cast<uint32_t>(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<uint32_t>(value & 0xFFFFFFFF);
const auto msb = static_cast<uint32_t>(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<uint64_t>(msb) << 32) | lsb;
}
SmallValueBuffer& operator=(const SmallValueBuffer& other) {
count_ = other.count_;
buffer_ = other.buffer_;
wideElements_ = other.wideElements_;
overflow_ = other.overflow_ ? std::make_unique<Overflow>(*other.overflow_)
: nullptr;
return *this;
}
SmallValueBuffer& operator=(SmallValueBuffer&& other) noexcept = default;
private:
struct Overflow {
std::vector<uint32_t> buffer_;
std::vector<bool> wideElements_;
};
uint16_t count_{0};
std::array<uint32_t, BufferSize> buffer_{};
std::bitset<BufferSize> wideElements_;
std::unique_ptr<Overflow> overflow_;
};
} // namespace facebook::yoga

View File

@@ -0,0 +1,759 @@
/*
* 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 <array>
#include <cstdint>
#include <type_traits>
#include <yoga/Yoga.h>
#include <yoga/algorithm/FlexDirection.h>
#include <yoga/enums/Align.h>
#include <yoga/enums/BoxSizing.h>
#include <yoga/enums/Dimension.h>
#include <yoga/enums/Direction.h>
#include <yoga/enums/Display.h>
#include <yoga/enums/Edge.h>
#include <yoga/enums/FlexDirection.h>
#include <yoga/enums/Gutter.h>
#include <yoga/enums/Justify.h>
#include <yoga/enums/Overflow.h>
#include <yoga/enums/PhysicalEdge.h>
#include <yoga/enums/PositionType.h>
#include <yoga/enums/Unit.h>
#include <yoga/enums/Wrap.h>
#include <yoga/numeric/FloatOptional.h>
#include <yoga/style/StyleLength.h>
#include <yoga/style/StyleSizeLength.h>
#include <yoga/style/StyleValuePool.h>
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;
}
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);
}
FloatOptional resolvedMinDimension(
Direction direction,
Dimension axis,
float referenceLength,
float ownerWidth) const {
FloatOptional value = minDimension(axis).resolve(referenceLength);
if (boxSizing() == BoxSizing::BorderBox) {
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 {
FloatOptional value = maxDimension(axis).resolve(referenceLength);
if (boxSizing() == BoxSizing::BorderBox) {
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 computePosition(flexStartEdge(axis), direction)
.resolve(axisSize)
.unwrapOrDefault(0.0f);
}
float computeInlineStartPosition(
FlexDirection axis,
Direction direction,
float axisSize) const {
return computePosition(inlineStartEdge(axis, direction), direction)
.resolve(axisSize)
.unwrapOrDefault(0.0f);
}
float computeFlexEndPosition(
FlexDirection axis,
Direction direction,
float axisSize) const {
return computePosition(flexEndEdge(axis), direction)
.resolve(axisSize)
.unwrapOrDefault(0.0f);
}
float computeInlineEndPosition(
FlexDirection axis,
Direction direction,
float axisSize) const {
return computePosition(inlineEndEdge(axis, direction), direction)
.resolve(axisSize)
.unwrapOrDefault(0.0f);
}
float computeFlexStartMargin(
FlexDirection axis,
Direction direction,
float widthSize) const {
return computeMargin(flexStartEdge(axis), direction)
.resolve(widthSize)
.unwrapOrDefault(0.0f);
}
float computeInlineStartMargin(
FlexDirection axis,
Direction direction,
float widthSize) const {
return computeMargin(inlineStartEdge(axis, direction), direction)
.resolve(widthSize)
.unwrapOrDefault(0.0f);
}
float computeFlexEndMargin(
FlexDirection axis,
Direction direction,
float widthSize) const {
return computeMargin(flexEndEdge(axis), direction)
.resolve(widthSize)
.unwrapOrDefault(0.0f);
}
float computeInlineEndMargin(
FlexDirection axis,
Direction direction,
float widthSize) const {
return computeMargin(inlineEndEdge(axis, direction), direction)
.resolve(widthSize)
.unwrapOrDefault(0.0f);
}
float computeFlexStartBorder(FlexDirection axis, Direction direction) const {
return maxOrDefined(
computeBorder(flexStartEdge(axis), direction).resolve(0.0f).unwrap(),
0.0f);
}
float computeInlineStartBorder(FlexDirection axis, Direction direction)
const {
return maxOrDefined(
computeBorder(inlineStartEdge(axis, direction), direction)
.resolve(0.0f)
.unwrap(),
0.0f);
}
float computeFlexEndBorder(FlexDirection axis, Direction direction) const {
return maxOrDefined(
computeBorder(flexEndEdge(axis), direction).resolve(0.0f).unwrap(),
0.0f);
}
float computeInlineEndBorder(FlexDirection axis, Direction direction) const {
return maxOrDefined(
computeBorder(inlineEndEdge(axis, direction), direction)
.resolve(0.0f)
.unwrap(),
0.0f);
}
float computeFlexStartPadding(
FlexDirection axis,
Direction direction,
float widthSize) const {
return maxOrDefined(
computePadding(flexStartEdge(axis), direction)
.resolve(widthSize)
.unwrap(),
0.0f);
}
float computeInlineStartPadding(
FlexDirection axis,
Direction direction,
float widthSize) const {
return maxOrDefined(
computePadding(inlineStartEdge(axis, direction), direction)
.resolve(widthSize)
.unwrap(),
0.0f);
}
float computeFlexEndPadding(
FlexDirection axis,
Direction direction,
float widthSize) const {
return maxOrDefined(
computePadding(flexEndEdge(axis), direction)
.resolve(widthSize)
.unwrap(),
0.0f);
}
float computeInlineEndPadding(
FlexDirection axis,
Direction direction,
float widthSize) const {
return maxOrDefined(
computePadding(inlineEndEdge(axis, direction), direction)
.resolve(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(gap.resolve(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 operator==(const Style& other) const {
return direction_ == other.direction_ &&
flexDirection_ == other.flexDirection_ &&
justifyContent_ == other.justifyContent_ &&
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_) &&
lengthsEqual(dimensions_, pool_, other.dimensions_, other.pool_) &&
lengthsEqual(
minDimensions_, pool_, other.minDimensions_, other.pool_) &&
lengthsEqual(
maxDimensions_, pool_, other.maxDimensions_, other.pool_) &&
numbersEqual(aspectRatio_, pool_, other.aspectRatio_, other.pool_);
}
bool operator!=(const Style& other) const {
return !(*this == other);
}
private:
using Dimensions = std::array<StyleValueHandle, ordinalCount<Dimension>()>;
using Edges = std::array<StyleValueHandle, ordinalCount<Edge>()>;
using Gutters = std::array<StyleValueHandle, ordinalCount<Gutter>()>;
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 <size_t N>
static inline bool lengthsEqual(
const std::array<StyleValueHandle, N>& lhs,
const StyleValuePool& lhsPool,
const std::array<StyleValueHandle, N>& 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);
});
}
Style::Length computeColumnGap() const {
if (gap_[yoga::to_underlying(Gutter::Column)].isDefined()) {
return pool_.getLength(gap_[yoga::to_underlying(Gutter::Column)]);
} else {
return pool_.getLength(gap_[yoga::to_underlying(Gutter::All)]);
}
}
Style::Length computeRowGap() const {
if (gap_[yoga::to_underlying(Gutter::Row)].isDefined()) {
return pool_.getLength(gap_[yoga::to_underlying(Gutter::Row)]);
} else {
return pool_.getLength(gap_[yoga::to_underlying(Gutter::All)]);
}
}
Style::Length computeLeftEdge(const Edges& edges, Direction layoutDirection)
const {
if (layoutDirection == Direction::LTR &&
edges[yoga::to_underlying(Edge::Start)].isDefined()) {
return pool_.getLength(edges[yoga::to_underlying(Edge::Start)]);
} else if (
layoutDirection == Direction::RTL &&
edges[yoga::to_underlying(Edge::End)].isDefined()) {
return pool_.getLength(edges[yoga::to_underlying(Edge::End)]);
} else if (edges[yoga::to_underlying(Edge::Left)].isDefined()) {
return pool_.getLength(edges[yoga::to_underlying(Edge::Left)]);
} else if (edges[yoga::to_underlying(Edge::Horizontal)].isDefined()) {
return pool_.getLength(edges[yoga::to_underlying(Edge::Horizontal)]);
} else {
return pool_.getLength(edges[yoga::to_underlying(Edge::All)]);
}
}
Style::Length computeTopEdge(const Edges& edges) const {
if (edges[yoga::to_underlying(Edge::Top)].isDefined()) {
return pool_.getLength(edges[yoga::to_underlying(Edge::Top)]);
} else if (edges[yoga::to_underlying(Edge::Vertical)].isDefined()) {
return pool_.getLength(edges[yoga::to_underlying(Edge::Vertical)]);
} else {
return pool_.getLength(edges[yoga::to_underlying(Edge::All)]);
}
}
Style::Length computeRightEdge(const Edges& edges, Direction layoutDirection)
const {
if (layoutDirection == Direction::LTR &&
edges[yoga::to_underlying(Edge::End)].isDefined()) {
return pool_.getLength(edges[yoga::to_underlying(Edge::End)]);
} else if (
layoutDirection == Direction::RTL &&
edges[yoga::to_underlying(Edge::Start)].isDefined()) {
return pool_.getLength(edges[yoga::to_underlying(Edge::Start)]);
} else if (edges[yoga::to_underlying(Edge::Right)].isDefined()) {
return pool_.getLength(edges[yoga::to_underlying(Edge::Right)]);
} else if (edges[yoga::to_underlying(Edge::Horizontal)].isDefined()) {
return pool_.getLength(edges[yoga::to_underlying(Edge::Horizontal)]);
} else {
return pool_.getLength(edges[yoga::to_underlying(Edge::All)]);
}
}
Style::Length computeBottomEdge(const Edges& edges) const {
if (edges[yoga::to_underlying(Edge::Bottom)].isDefined()) {
return pool_.getLength(edges[yoga::to_underlying(Edge::Bottom)]);
} else if (edges[yoga::to_underlying(Edge::Vertical)].isDefined()) {
return pool_.getLength(edges[yoga::to_underlying(Edge::Vertical)]);
} else {
return pool_.getLength(edges[yoga::to_underlying(Edge::All)]);
}
}
Style::Length 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_);
}
fatalWithMessage("Invalid physical edge");
}
Style::Length 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_);
}
fatalWithMessage("Invalid physical edge");
}
Style::Length 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_);
}
fatalWithMessage("Invalid physical edge");
}
Style::Length 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_);
}
fatalWithMessage("Invalid physical edge");
}
Direction direction_ : bitCount<Direction>() = Direction::Inherit;
FlexDirection flexDirection_
: bitCount<FlexDirection>() = FlexDirection::Column;
Justify justifyContent_ : bitCount<Justify>() = Justify::FlexStart;
Align alignContent_ : bitCount<Align>() = Align::FlexStart;
Align alignItems_ : bitCount<Align>() = Align::Stretch;
Align alignSelf_ : bitCount<Align>() = Align::Auto;
PositionType positionType_
: bitCount<PositionType>() = PositionType::Relative;
Wrap flexWrap_ : bitCount<Wrap>() = Wrap::NoWrap;
Overflow overflow_ : bitCount<Overflow>() = Overflow::Visible;
Display display_ : bitCount<Display>() = Display::Flex;
BoxSizing boxSizing_ : bitCount<BoxSizing>() = 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_{};
StyleValuePool pool_;
};
} // namespace facebook::yoga

View File

@@ -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 <yoga/enums/Unit.h>
#include <yoga/numeric/FloatOptional.h>
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 <length-percentage> value:
* a. <length> value (e.g. 10px)
* b. <percentage> value of a reference <length>
*
* 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) {
switch (unit_) {
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

View File

@@ -0,0 +1,139 @@
/*
* 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/enums/Unit.h>
#include <yoga/numeric/FloatOptional.h>
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 <length-percentage> value:
* a. <length> value (e.g. 10px)
* b. <percentage> value of a reference <length>
*
* 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 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) {
switch (unit_) {
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

View File

@@ -0,0 +1,106 @@
/*
* 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 <cstdint>
#include <yoga/Yoga.h>
#include <yoga/numeric/FloatOptional.h>
#include <yoga/style/SmallValueBuffer.h>
#include <yoga/style/StyleLength.h>
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;
}
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<uint16_t>(keyword);
}
constexpr Type type() const {
return static_cast<Type>(repr_ & kHandleTypeMask);
}
constexpr void setType(Type handleType) {
repr_ &= (~kHandleTypeMask);
repr_ |= static_cast<uint8_t>(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

View File

@@ -0,0 +1,184 @@
/*
* 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 <cassert>
#include <cstdint>
#include <yoga/numeric/FloatOptional.h>
#include <yoga/style/SmallValueBuffer.h>
#include <yoga/style/StyleLength.h>
#include <yoga/style/StyleSizeLength.h>
#include <yoga/style/StyleValueHandle.h>
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<float>(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<float>(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<float>(buffer_.get32(handle.value()))
: unpackInlineInteger(handle.value());
return FloatOptional{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<uint32_t>(value));
handle.setValue(newIndex);
} else if (isIntegerPackable(value)) {
handle.setValue(packInlineInteger(value));
} else {
auto newIndex = buffer_.push(std::bit_cast<uint32_t>(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<uint32_t>(keyword));
handle.setValue(newIndex);
} else {
handle.setValue(static_cast<uint16_t>(keyword));
}
}
static constexpr bool isIntegerPackable(float f) {
constexpr uint16_t kMaxInlineAbsValue = (1 << 11) - 1;
auto i = static_cast<int32_t>(f);
return static_cast<float>(i) == f && i >= -kMaxInlineAbsValue &&
i <= +kMaxInlineAbsValue;
}
static constexpr uint16_t packInlineInteger(float value) {
uint16_t isNegative = value < 0 ? 1 : 0;
return static_cast<uint16_t>(
(isNegative << 11) |
(static_cast<int32_t>(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<float>(
(value & kValueMagnitudeMask) * (isNegative ? -1 : 1));
}
SmallValueBuffer<4> buffer_;
};
} // namespace facebook::yoga