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, float marginColumn,
YGConfigRef config) { YGConfigRef config) {
return yoga::canUseCachedMeasurement( return yoga::canUseCachedMeasurement(
scopedEnum(widthMode), sizingMode(scopedEnum(widthMode)),
availableWidth, availableWidth,
scopedEnum(heightMode), sizingMode(scopedEnum(heightMode)),
availableHeight, availableHeight,
scopedEnum(lastWidthMode), sizingMode(scopedEnum(lastWidthMode)),
lastAvailableWidth, lastAvailableWidth,
scopedEnum(lastHeightMode), sizingMode(scopedEnum(lastHeightMode)),
lastAvailableHeight, lastAvailableHeight,
lastComputedWidth, lastComputedWidth,
lastComputedHeight, lastComputedHeight,

View File

@@ -186,6 +186,19 @@ typedef struct YGSize {
} 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. * @returns the size of the leaf node, measured under the given contraints.
*/ */
typedef YGSize (*YGMeasureFunc)( typedef YGSize (*YGMeasureFunc)(

View File

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

View File

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

View File

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

View File

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