Use CSS terminology for sizing rules (#1460)

Summary:
X-link: https://github.com/facebook/react-native/pull/41390

Pull Request resolved: https://github.com/facebook/yoga/pull/1460

Yoga passes `MeasureMode`/`YGMeasureMode` to express constraints in how a box should be measured, given definite or indefinite available space.

This is modeled after Android [MeasureSpec](https://developer.android.com/reference/android/view/View.MeasureSpec), with a table above `calculateLayoutImpl()` explaining the CSS terms they map to. This can be confusing when flipping between the spec, and code.

This switches internal usages to the CSS terms, but leaves around `YGMeasureMode` since it is the public API passed to measure functions.

Reviewed By: joevilches

Differential Revision: D51068417

fbshipit-source-id: 0a76266a4e7e0cc39996164607229c3c41de2818
This commit is contained in:
Nick Gerleman
2023-11-25 20:41:22 -08:00
committed by Facebook GitHub Bot
parent aca02406ef
commit a121483e81
9 changed files with 351 additions and 268 deletions

View File

@@ -351,13 +351,13 @@ bool YGNodeCanUseCachedMeasurement(
float marginColumn,
YGConfigRef config) {
return yoga::canUseCachedMeasurement(
scopedEnum(widthMode),
sizingMode(scopedEnum(widthMode)),
availableWidth,
scopedEnum(heightMode),
sizingMode(scopedEnum(heightMode)),
availableHeight,
scopedEnum(lastWidthMode),
sizingMode(scopedEnum(lastWidthMode)),
lastAvailableWidth,
scopedEnum(lastHeightMode),
sizingMode(scopedEnum(lastHeightMode)),
lastAvailableHeight,
lastComputedWidth,
lastComputedHeight,

View File

@@ -186,6 +186,19 @@ typedef struct YGSize {
} YGSize;
/**
* Returns the computed dimensions of the node, following the contraints 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 contraints.
*/
typedef YGSize (*YGMeasureFunc)(

View File

@@ -12,51 +12,52 @@
namespace facebook::yoga {
static inline bool sizeIsExactAndMatchesOldMeasuredSize(
MeasureMode sizeMode,
SizingMode sizeMode,
float size,
float lastComputedSize) {
return sizeMode == MeasureMode::Exactly &&
return sizeMode == SizingMode::StretchFit &&
yoga::inexactEquals(size, lastComputedSize);
}
static inline bool oldSizeIsUnspecifiedAndStillFits(
MeasureMode sizeMode,
static inline bool oldSizeIsMaxContentAndStillFits(
SizingMode sizeMode,
float size,
MeasureMode lastSizeMode,
SizingMode lastSizeMode,
float lastComputedSize) {
return sizeMode == MeasureMode::AtMost &&
lastSizeMode == MeasureMode::Undefined &&
return sizeMode == SizingMode::FitContent &&
lastSizeMode == SizingMode::MaxContent &&
(size >= lastComputedSize || yoga::inexactEquals(size, lastComputedSize));
}
static inline bool newMeasureSizeIsStricterAndStillValid(
MeasureMode sizeMode,
static inline bool newSizeIsStricterAndStillValid(
SizingMode sizeMode,
float size,
MeasureMode lastSizeMode,
SizingMode lastSizeMode,
float lastSize,
float lastComputedSize) {
return lastSizeMode == MeasureMode::AtMost &&
sizeMode == MeasureMode::AtMost && !std::isnan(lastSize) &&
!std::isnan(size) && !std::isnan(lastComputedSize) && lastSize > size &&
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 MeasureMode widthMode,
const SizingMode widthMode,
const float availableWidth,
const MeasureMode heightMode,
const SizingMode heightMode,
const float availableHeight,
const MeasureMode lastWidthMode,
const SizingMode lastWidthMode,
const float lastAvailableWidth,
const MeasureMode lastHeightMode,
const SizingMode lastHeightMode,
const float lastAvailableHeight,
const float lastComputedWidth,
const float lastComputedHeight,
const float marginRow,
const float marginColumn,
const yoga::Config* const config) {
if ((!std::isnan(lastComputedHeight) && lastComputedHeight < 0) ||
(!std::isnan(lastComputedWidth) && lastComputedWidth < 0)) {
if ((yoga::isDefined(lastComputedHeight) && lastComputedHeight < 0) ||
((yoga::isDefined(lastComputedWidth)) && lastComputedWidth < 0)) {
return false;
}
@@ -87,33 +88,32 @@ bool canUseCachedMeasurement(
hasSameWidthSpec ||
sizeIsExactAndMatchesOldMeasuredSize(
widthMode, availableWidth - marginRow, lastComputedWidth) ||
oldSizeIsUnspecifiedAndStillFits(
oldSizeIsMaxContentAndStillFits(
widthMode,
availableWidth - marginRow,
lastWidthMode,
lastComputedWidth) ||
newMeasureSizeIsStricterAndStillValid(
newSizeIsStricterAndStillValid(
widthMode,
availableWidth - marginRow,
lastWidthMode,
lastAvailableWidth,
lastComputedWidth);
const bool heightIsCompatible =
hasSameHeightSpec ||
const bool heightIsCompatible = hasSameHeightSpec ||
sizeIsExactAndMatchesOldMeasuredSize(
heightMode, availableHeight - marginColumn, lastComputedHeight) ||
oldSizeIsUnspecifiedAndStillFits(
heightMode,
availableHeight - marginColumn,
lastHeightMode,
lastComputedHeight) ||
newMeasureSizeIsStricterAndStillValid(
heightMode,
availableHeight - marginColumn,
lastHeightMode,
lastAvailableHeight,
lastComputedHeight);
heightMode,
availableHeight - marginColumn,
lastComputedHeight) ||
oldSizeIsMaxContentAndStillFits(heightMode,
availableHeight - marginColumn,
lastHeightMode,
lastComputedHeight) ||
newSizeIsStricterAndStillValid(heightMode,
availableHeight - marginColumn,
lastHeightMode,
lastAvailableHeight,
lastComputedHeight);
return widthIsCompatible && heightIsCompatible;
}

View File

@@ -7,19 +7,19 @@
#pragma once
#include <yoga/algorithm/SizingMode.h>
#include <yoga/config/Config.h>
#include <yoga/enums/MeasureMode.h>
namespace facebook::yoga {
bool canUseCachedMeasurement(
MeasureMode widthMode,
SizingMode widthMode,
float availableWidth,
MeasureMode heightMode,
SizingMode heightMode,
float availableHeight,
MeasureMode lastWidthMode,
SizingMode lastWidthMode,
float lastAvailableWidth,
MeasureMode lastHeightMode,
SizingMode lastHeightMode,
float lastAvailableHeight,
float lastComputedWidth,
float lastComputedHeight,

File diff suppressed because it is too large Load Diff

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

@@ -7,8 +7,10 @@
#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 {

View File

@@ -8,11 +8,12 @@
#pragma once
#include <yoga/Yoga.h>
#include <yoga/config/Config.h>
#include <yoga/node/Node.h>
namespace facebook::yoga {
class Node;
class Config;
[[noreturn]] void fatalWithMessage(const char* message);
void assertFatal(bool condition, const char* message);

View File

@@ -11,7 +11,7 @@
#include <yoga/Yoga.h>
#include <yoga/enums/MeasureMode.h>
#include <yoga/algorithm/SizingMode.h>
#include <yoga/numeric/Comparison.h>
namespace facebook::yoga {
@@ -19,15 +19,15 @@ namespace facebook::yoga {
struct CachedMeasurement {
float availableWidth{-1};
float availableHeight{-1};
MeasureMode widthMeasureMode{MeasureMode::Undefined};
MeasureMode heightMeasureMode{MeasureMode::Undefined};
SizingMode widthSizingMode{SizingMode::MaxContent};
SizingMode heightSizingMode{SizingMode::MaxContent};
float computedWidth{-1};
float computedHeight{-1};
bool operator==(CachedMeasurement measurement) const {
bool isEqual = widthMeasureMode == measurement.widthMeasureMode &&
heightMeasureMode == measurement.heightMeasureMode;
bool isEqual = widthSizingMode == measurement.widthSizingMode &&
heightSizingMode == measurement.heightSizingMode;
if (!yoga::isUndefined(availableWidth) ||
!yoga::isUndefined(measurement.availableWidth)) {