Implement gap/row-gap/column-gap (within the C ABI)
Summary: This extracts the core changes from https://github.com/facebook/yoga/pull/1116, to support gap/row-gap/column-gap, mostly identical, apart from the rename of gaps -> gutters. The core functionality in this PR looks to be well tested from the fixtures added. I am not an expert in the internals of Yoga, but I am seeing everything that I would expect to. The space for the gap is accounted for in line-breaking, and the accumulated gaps limit the available line-length, before sizing flexible children, so items are sized correctly as to accommodate the gap. Then the gap is used for spacing during main axis and cross-axis justification. Changelog: [Genral][Added] - Implement gap/row-gap/column-gap (within the yoga C ABI) Reviewed By: javache Differential Revision: D39922410 fbshipit-source-id: 5850f22032169028bd8383b49dd240b335c11d3d
This commit is contained in:
committed by
Facebook GitHub Bot
parent
05dd228317
commit
582533dbc6
@@ -83,6 +83,30 @@ CompactValue YGNode::computeEdgeValueForColumn(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CompactValue YGNode::computeRowGap(
|
||||||
|
const YGStyle::Gutters& gutters,
|
||||||
|
CompactValue defaultValue) {
|
||||||
|
if (!gutters[YGGutterRow].isUndefined()) {
|
||||||
|
return gutters[YGGutterRow];
|
||||||
|
} else if (!gutters[YGGutterAll].isUndefined()) {
|
||||||
|
return gutters[YGGutterAll];
|
||||||
|
} else {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CompactValue YGNode::computeColumnGap(
|
||||||
|
const YGStyle::Gutters& gutters,
|
||||||
|
CompactValue defaultValue) {
|
||||||
|
if (!gutters[YGGutterColumn].isUndefined()) {
|
||||||
|
return gutters[YGGutterColumn];
|
||||||
|
} else if (!gutters[YGGutterAll].isUndefined()) {
|
||||||
|
return gutters[YGGutterAll];
|
||||||
|
} else {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
YGFloatOptional YGNode::getLeadingPosition(
|
YGFloatOptional YGNode::getLeadingPosition(
|
||||||
const YGFlexDirection axis,
|
const YGFlexDirection axis,
|
||||||
const float axisSize) const {
|
const float axisSize) const {
|
||||||
@@ -163,6 +187,15 @@ YGFloatOptional YGNode::getMarginForAxis(
|
|||||||
return getLeadingMargin(axis, widthSize) + getTrailingMargin(axis, widthSize);
|
return getLeadingMargin(axis, widthSize) + getTrailingMargin(axis, widthSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
YGFloatOptional YGNode::getGapForAxis(
|
||||||
|
const YGFlexDirection axis,
|
||||||
|
const float widthSize) const {
|
||||||
|
auto gap = YGFlexDirectionIsRow(axis)
|
||||||
|
? computeColumnGap(style_.gap(), CompactValue::ofZero())
|
||||||
|
: computeRowGap(style_.gap(), CompactValue::ofZero());
|
||||||
|
return YGResolveValue(gap, widthSize);
|
||||||
|
}
|
||||||
|
|
||||||
YGSize YGNode::measure(
|
YGSize YGNode::measure(
|
||||||
float width,
|
float width,
|
||||||
YGMeasureMode widthMode,
|
YGMeasureMode widthMode,
|
||||||
|
@@ -204,6 +204,14 @@ public:
|
|||||||
YGEdge edge,
|
YGEdge edge,
|
||||||
CompactValue defaultValue);
|
CompactValue defaultValue);
|
||||||
|
|
||||||
|
static CompactValue computeRowGap(
|
||||||
|
const YGStyle::Gutters& gutters,
|
||||||
|
CompactValue defaultValue);
|
||||||
|
|
||||||
|
static CompactValue computeColumnGap(
|
||||||
|
const YGStyle::Gutters& gutters,
|
||||||
|
CompactValue defaultValue);
|
||||||
|
|
||||||
// Methods related to positions, margin, padding and border
|
// Methods related to positions, margin, padding and border
|
||||||
YGFloatOptional getLeadingPosition(
|
YGFloatOptional getLeadingPosition(
|
||||||
const YGFlexDirection axis,
|
const YGFlexDirection axis,
|
||||||
@@ -236,6 +244,9 @@ public:
|
|||||||
YGFloatOptional getMarginForAxis(
|
YGFloatOptional getMarginForAxis(
|
||||||
const YGFlexDirection axis,
|
const YGFlexDirection axis,
|
||||||
const float widthSize) const;
|
const float widthSize) const;
|
||||||
|
YGFloatOptional getGapForAxis(
|
||||||
|
const YGFlexDirection axis,
|
||||||
|
const float widthSize) const;
|
||||||
// Setters
|
// Setters
|
||||||
|
|
||||||
void setContext(void* context) { context_ = context; }
|
void setContext(void* context) { context_ = context; }
|
||||||
|
@@ -184,6 +184,20 @@ void YGNodeToString(
|
|||||||
appendEdges(str, "padding", style.padding());
|
appendEdges(str, "padding", style.padding());
|
||||||
appendEdges(str, "border", style.border());
|
appendEdges(str, "border", style.border());
|
||||||
|
|
||||||
|
if (YGNode::computeColumnGap(
|
||||||
|
style.gap(), detail::CompactValue::ofUndefined()) !=
|
||||||
|
YGNode::computeColumnGap(
|
||||||
|
YGNode().getStyle().gap(), detail::CompactValue::ofUndefined())) {
|
||||||
|
appendNumberIfNotUndefined(
|
||||||
|
str, "column-gap", style.gap()[YGGutterColumn]);
|
||||||
|
}
|
||||||
|
if (YGNode::computeRowGap(
|
||||||
|
style.gap(), detail::CompactValue::ofUndefined()) !=
|
||||||
|
YGNode::computeRowGap(
|
||||||
|
YGNode().getStyle().gap(), detail::CompactValue::ofUndefined())) {
|
||||||
|
appendNumberIfNotUndefined(str, "row-gap", style.gap()[YGGutterRow]);
|
||||||
|
}
|
||||||
|
|
||||||
appendNumberIfNotAuto(str, "width", style.dimensions()[YGDimensionWidth]);
|
appendNumberIfNotAuto(str, "width", style.dimensions()[YGDimensionWidth]);
|
||||||
appendNumberIfNotAuto(str, "height", style.dimensions()[YGDimensionHeight]);
|
appendNumberIfNotAuto(str, "height", style.dimensions()[YGDimensionHeight]);
|
||||||
appendNumberIfNotAuto(
|
appendNumberIfNotAuto(
|
||||||
|
@@ -22,7 +22,7 @@ bool operator==(const YGStyle& lhs, const YGStyle& rhs) {
|
|||||||
YGValueEqual(lhs.flexBasis(), rhs.flexBasis()) &&
|
YGValueEqual(lhs.flexBasis(), rhs.flexBasis()) &&
|
||||||
lhs.margin() == rhs.margin() && lhs.position() == rhs.position() &&
|
lhs.margin() == rhs.margin() && lhs.position() == rhs.position() &&
|
||||||
lhs.padding() == rhs.padding() && lhs.border() == rhs.border() &&
|
lhs.padding() == rhs.padding() && lhs.border() == rhs.border() &&
|
||||||
lhs.dimensions() == rhs.dimensions() &&
|
lhs.gap() == rhs.gap() && lhs.dimensions() == rhs.dimensions() &&
|
||||||
lhs.minDimensions() == rhs.minDimensions() &&
|
lhs.minDimensions() == rhs.minDimensions() &&
|
||||||
lhs.maxDimensions() == rhs.maxDimensions();
|
lhs.maxDimensions() == rhs.maxDimensions();
|
||||||
|
|
||||||
|
@@ -29,6 +29,7 @@ class YOGA_EXPORT YGStyle {
|
|||||||
public:
|
public:
|
||||||
using Dimensions = Values<YGDimension>;
|
using Dimensions = Values<YGDimension>;
|
||||||
using Edges = Values<YGEdge>;
|
using Edges = Values<YGEdge>;
|
||||||
|
using Gutters = Values<YGGutter>;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct BitfieldRef {
|
struct BitfieldRef {
|
||||||
@@ -113,6 +114,7 @@ private:
|
|||||||
Edges position_ = {};
|
Edges position_ = {};
|
||||||
Edges padding_ = {};
|
Edges padding_ = {};
|
||||||
Edges border_ = {};
|
Edges border_ = {};
|
||||||
|
Gutters gap_ = {};
|
||||||
Dimensions dimensions_{CompactValue::ofAuto()};
|
Dimensions dimensions_{CompactValue::ofAuto()};
|
||||||
Dimensions minDimensions_ = {};
|
Dimensions minDimensions_ = {};
|
||||||
Dimensions maxDimensions_ = {};
|
Dimensions maxDimensions_ = {};
|
||||||
@@ -210,6 +212,9 @@ public:
|
|||||||
const Edges& border() const { return border_; }
|
const Edges& border() const { return border_; }
|
||||||
IdxRef<YGEdge, &YGStyle::border_> border() { return {*this}; }
|
IdxRef<YGEdge, &YGStyle::border_> border() { return {*this}; }
|
||||||
|
|
||||||
|
const Gutters& gap() const { return gap_; }
|
||||||
|
IdxRef<YGGutter, &YGStyle::gap_> gap() { return {*this}; }
|
||||||
|
|
||||||
const Dimensions& dimensions() const { return dimensions_; }
|
const Dimensions& dimensions() const { return dimensions_; }
|
||||||
IdxRef<YGDimension, &YGStyle::dimensions_> dimensions() { return {*this}; }
|
IdxRef<YGDimension, &YGStyle::dimensions_> dimensions() { return {*this}; }
|
||||||
|
|
||||||
|
@@ -797,6 +797,27 @@ YOGA_EXPORT float YGNodeStyleGetBorder(
|
|||||||
return static_cast<YGValue>(border).value;
|
return static_cast<YGValue>(border).value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
YOGA_EXPORT void YGNodeStyleSetGap(
|
||||||
|
const YGNodeRef node,
|
||||||
|
const YGGutter gutter,
|
||||||
|
const float gapLength) {
|
||||||
|
auto length = detail::CompactValue::ofMaybe<YGUnitPoint>(gapLength);
|
||||||
|
updateIndexedStyleProp<MSVC_HINT(gap)>(node, &YGStyle::gap, gutter, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
YOGA_EXPORT float YGNodeStyleGetGap(
|
||||||
|
const YGNodeConstRef node,
|
||||||
|
const YGGutter gutter) {
|
||||||
|
auto gapLength = node->getStyle().gap()[gutter];
|
||||||
|
if (gapLength.isUndefined() || gapLength.isAuto()) {
|
||||||
|
// TODO(T26792433): Rather than returning YGUndefined, change the api to
|
||||||
|
// return YGFloatOptional.
|
||||||
|
return YGUndefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<YGValue>(gapLength).value;
|
||||||
|
}
|
||||||
|
|
||||||
// Yoga specific properties, not compatible with flexbox specification
|
// Yoga specific properties, not compatible with flexbox specification
|
||||||
|
|
||||||
// TODO(T26792433): Change the API to accept YGFloatOptional.
|
// TODO(T26792433): Change the API to accept YGFloatOptional.
|
||||||
@@ -1972,6 +1993,7 @@ static YGCollectFlexItemsRowValues YGCalculateCollectFlexItemsRowValues(
|
|||||||
const YGFlexDirection mainAxis = YGResolveFlexDirection(
|
const YGFlexDirection mainAxis = YGResolveFlexDirection(
|
||||||
node->getStyle().flexDirection(), node->resolveDirection(ownerDirection));
|
node->getStyle().flexDirection(), node->resolveDirection(ownerDirection));
|
||||||
const bool isNodeFlexWrap = node->getStyle().flexWrap() != YGWrapNoWrap;
|
const bool isNodeFlexWrap = node->getStyle().flexWrap() != YGWrapNoWrap;
|
||||||
|
const float gap = node->getGapForAxis(mainAxis, availableInnerWidth).unwrap();
|
||||||
|
|
||||||
// Add items to the current line until it's full or we run out of items.
|
// Add items to the current line until it's full or we run out of items.
|
||||||
uint32_t endOfLineIndex = startOfLineIndex;
|
uint32_t endOfLineIndex = startOfLineIndex;
|
||||||
@@ -1981,9 +2003,13 @@ static YGCollectFlexItemsRowValues YGCalculateCollectFlexItemsRowValues(
|
|||||||
child->getStyle().positionType() == YGPositionTypeAbsolute) {
|
child->getStyle().positionType() == YGPositionTypeAbsolute) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool isFirstElementInLine = (endOfLineIndex - startOfLineIndex) == 0;
|
||||||
|
|
||||||
child->setLineIndex(lineCount);
|
child->setLineIndex(lineCount);
|
||||||
const float childMarginMainAxis =
|
const float childMarginMainAxis =
|
||||||
child->getMarginForAxis(mainAxis, availableInnerWidth).unwrap();
|
child->getMarginForAxis(mainAxis, availableInnerWidth).unwrap();
|
||||||
|
const float childLeadingGapMainAxis = isFirstElementInLine ? 0.0f : gap;
|
||||||
const float flexBasisWithMinAndMaxConstraints =
|
const float flexBasisWithMinAndMaxConstraints =
|
||||||
YGNodeBoundAxisWithinMinAndMax(
|
YGNodeBoundAxisWithinMinAndMax(
|
||||||
child,
|
child,
|
||||||
@@ -1996,16 +2022,19 @@ static YGCollectFlexItemsRowValues YGCalculateCollectFlexItemsRowValues(
|
|||||||
// size, we've hit the end of the current line. Break out of the loop and
|
// size, we've hit the end of the current line. Break out of the loop and
|
||||||
// lay out the current line.
|
// lay out the current line.
|
||||||
if (sizeConsumedOnCurrentLineIncludingMinConstraint +
|
if (sizeConsumedOnCurrentLineIncludingMinConstraint +
|
||||||
flexBasisWithMinAndMaxConstraints + childMarginMainAxis >
|
flexBasisWithMinAndMaxConstraints + childMarginMainAxis +
|
||||||
|
childLeadingGapMainAxis >
|
||||||
availableInnerMainDim &&
|
availableInnerMainDim &&
|
||||||
isNodeFlexWrap && flexAlgoRowMeasurement.itemsOnLine > 0) {
|
isNodeFlexWrap && flexAlgoRowMeasurement.itemsOnLine > 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
sizeConsumedOnCurrentLineIncludingMinConstraint +=
|
sizeConsumedOnCurrentLineIncludingMinConstraint +=
|
||||||
flexBasisWithMinAndMaxConstraints + childMarginMainAxis;
|
flexBasisWithMinAndMaxConstraints + childMarginMainAxis +
|
||||||
|
childLeadingGapMainAxis;
|
||||||
flexAlgoRowMeasurement.sizeConsumedOnCurrentLine +=
|
flexAlgoRowMeasurement.sizeConsumedOnCurrentLine +=
|
||||||
flexBasisWithMinAndMaxConstraints + childMarginMainAxis;
|
flexBasisWithMinAndMaxConstraints + childMarginMainAxis +
|
||||||
|
childLeadingGapMainAxis;
|
||||||
flexAlgoRowMeasurement.itemsOnLine++;
|
flexAlgoRowMeasurement.itemsOnLine++;
|
||||||
|
|
||||||
if (child->isNodeFlexible()) {
|
if (child->isNodeFlexible()) {
|
||||||
@@ -2415,6 +2444,7 @@ static void YGJustifyMainAxis(
|
|||||||
node->getLeadingPaddingAndBorder(mainAxis, ownerWidth).unwrap();
|
node->getLeadingPaddingAndBorder(mainAxis, ownerWidth).unwrap();
|
||||||
const float trailingPaddingAndBorderMain =
|
const float trailingPaddingAndBorderMain =
|
||||||
node->getTrailingPaddingAndBorder(mainAxis, ownerWidth).unwrap();
|
node->getTrailingPaddingAndBorder(mainAxis, ownerWidth).unwrap();
|
||||||
|
const float gap = node->getGapForAxis(mainAxis, ownerWidth).unwrap();
|
||||||
// If we are using "at most" rules in the main axis, make sure that
|
// If we are using "at most" rules in the main axis, make sure that
|
||||||
// remainingFreeSpace is 0 when min main dimension is not given
|
// remainingFreeSpace is 0 when min main dimension is not given
|
||||||
if (measureModeMainDim == YGMeasureModeAtMost &&
|
if (measureModeMainDim == YGMeasureModeAtMost &&
|
||||||
@@ -2462,7 +2492,7 @@ static void YGJustifyMainAxis(
|
|||||||
// The space between the beginning and the first element and the space between
|
// The space between the beginning and the first element and the space between
|
||||||
// each two elements.
|
// each two elements.
|
||||||
float leadingMainDim = 0;
|
float leadingMainDim = 0;
|
||||||
float betweenMainDim = 0;
|
float betweenMainDim = gap;
|
||||||
const YGJustify justifyContent = node->getStyle().justifyContent();
|
const YGJustify justifyContent = node->getStyle().justifyContent();
|
||||||
|
|
||||||
if (numberOfAutoMarginsOnCurrentLine == 0) {
|
if (numberOfAutoMarginsOnCurrentLine == 0) {
|
||||||
@@ -2475,24 +2505,22 @@ static void YGJustifyMainAxis(
|
|||||||
break;
|
break;
|
||||||
case YGJustifySpaceBetween:
|
case YGJustifySpaceBetween:
|
||||||
if (collectedFlexItemsValues.itemsOnLine > 1) {
|
if (collectedFlexItemsValues.itemsOnLine > 1) {
|
||||||
betweenMainDim =
|
betweenMainDim +=
|
||||||
YGFloatMax(collectedFlexItemsValues.remainingFreeSpace, 0) /
|
YGFloatMax(collectedFlexItemsValues.remainingFreeSpace, 0) /
|
||||||
(collectedFlexItemsValues.itemsOnLine - 1);
|
(collectedFlexItemsValues.itemsOnLine - 1);
|
||||||
} else {
|
|
||||||
betweenMainDim = 0;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case YGJustifySpaceEvenly:
|
case YGJustifySpaceEvenly:
|
||||||
// Space is distributed evenly across all elements
|
// Space is distributed evenly across all elements
|
||||||
betweenMainDim = collectedFlexItemsValues.remainingFreeSpace /
|
leadingMainDim = collectedFlexItemsValues.remainingFreeSpace /
|
||||||
(collectedFlexItemsValues.itemsOnLine + 1);
|
(collectedFlexItemsValues.itemsOnLine + 1);
|
||||||
leadingMainDim = betweenMainDim;
|
betweenMainDim += leadingMainDim;
|
||||||
break;
|
break;
|
||||||
case YGJustifySpaceAround:
|
case YGJustifySpaceAround:
|
||||||
// Space on the edges is half of the space between elements
|
// Space on the edges is half of the space between elements
|
||||||
betweenMainDim = collectedFlexItemsValues.remainingFreeSpace /
|
leadingMainDim = 0.5f * collectedFlexItemsValues.remainingFreeSpace /
|
||||||
collectedFlexItemsValues.itemsOnLine;
|
collectedFlexItemsValues.itemsOnLine;
|
||||||
leadingMainDim = betweenMainDim / 2;
|
betweenMainDim += leadingMainDim * 2;
|
||||||
break;
|
break;
|
||||||
case YGJustifyFlexStart:
|
case YGJustifyFlexStart:
|
||||||
break;
|
break;
|
||||||
@@ -2890,6 +2918,9 @@ static void YGNodelayoutImpl(
|
|||||||
// Accumulated cross dimensions of all lines so far.
|
// Accumulated cross dimensions of all lines so far.
|
||||||
float totalLineCrossDim = 0;
|
float totalLineCrossDim = 0;
|
||||||
|
|
||||||
|
const float crossAxisGap =
|
||||||
|
node->getGapForAxis(crossAxis, availableInnerCrossDim).unwrap();
|
||||||
|
|
||||||
// Max main dimension of all the lines.
|
// Max main dimension of all the lines.
|
||||||
float maxLineMainDim = 0;
|
float maxLineMainDim = 0;
|
||||||
YGCollectFlexItemsRowValues collectedFlexItemsValues;
|
YGCollectFlexItemsRowValues collectedFlexItemsValues;
|
||||||
@@ -3209,7 +3240,8 @@ static void YGNodelayoutImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
totalLineCrossDim += collectedFlexItemsValues.crossDim;
|
const float appliedCrossGap = lineCount != 0 ? crossAxisGap : 0.0f;
|
||||||
|
totalLineCrossDim += collectedFlexItemsValues.crossDim + appliedCrossGap;
|
||||||
maxLineMainDim =
|
maxLineMainDim =
|
||||||
YGFloatMax(maxLineMainDim, collectedFlexItemsValues.mainDim);
|
YGFloatMax(maxLineMainDim, collectedFlexItemsValues.mainDim);
|
||||||
}
|
}
|
||||||
@@ -3304,6 +3336,7 @@ static void YGNodelayoutImpl(
|
|||||||
}
|
}
|
||||||
endIndex = ii;
|
endIndex = ii;
|
||||||
lineHeight += crossDimLead;
|
lineHeight += crossDimLead;
|
||||||
|
currentLead += i != 0 ? crossAxisGap : 0;
|
||||||
|
|
||||||
if (performLayout) {
|
if (performLayout) {
|
||||||
for (ii = startIndex; ii < endIndex; ii++) {
|
for (ii = startIndex; ii < endIndex; ii++) {
|
||||||
|
@@ -232,6 +232,12 @@ WIN_EXPORT YGValue YGNodeStyleGetPadding(YGNodeConstRef node, YGEdge edge);
|
|||||||
WIN_EXPORT void YGNodeStyleSetBorder(YGNodeRef node, YGEdge edge, float border);
|
WIN_EXPORT void YGNodeStyleSetBorder(YGNodeRef node, YGEdge edge, float border);
|
||||||
WIN_EXPORT float YGNodeStyleGetBorder(YGNodeConstRef node, YGEdge edge);
|
WIN_EXPORT float YGNodeStyleGetBorder(YGNodeConstRef node, YGEdge edge);
|
||||||
|
|
||||||
|
WIN_EXPORT void YGNodeStyleSetGap(
|
||||||
|
YGNodeRef node,
|
||||||
|
YGGutter gutter,
|
||||||
|
float gapLength);
|
||||||
|
WIN_EXPORT float YGNodeStyleGetGap(YGNodeConstRef node, YGGutter gutter);
|
||||||
|
|
||||||
WIN_EXPORT void YGNodeStyleSetWidth(YGNodeRef node, float width);
|
WIN_EXPORT void YGNodeStyleSetWidth(YGNodeRef node, float width);
|
||||||
WIN_EXPORT void YGNodeStyleSetWidthPercent(YGNodeRef node, float width);
|
WIN_EXPORT void YGNodeStyleSetWidthPercent(YGNodeRef node, float width);
|
||||||
WIN_EXPORT void YGNodeStyleSetWidthAuto(YGNodeRef node);
|
WIN_EXPORT void YGNodeStyleSetWidthAuto(YGNodeRef node);
|
||||||
|
Reference in New Issue
Block a user