Skip to content

Commit 1373a70

Browse files
NickGerlemanfacebook-github-bot
authored andcommitted
Implement gap/row-gap/column-gap (within the C ABI)
Summary: This extracts the core changes from facebook/yoga#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
1 parent 51af7cd commit 1373a70

File tree

7 files changed

+115
-13
lines changed

7 files changed

+115
-13
lines changed

ReactCommon/yoga/yoga/YGNode.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,30 @@ CompactValue YGNode::computeEdgeValueForColumn(
8383
}
8484
}
8585

86+
CompactValue YGNode::computeRowGap(
87+
const YGStyle::Gutters& gutters,
88+
CompactValue defaultValue) {
89+
if (!gutters[YGGutterRow].isUndefined()) {
90+
return gutters[YGGutterRow];
91+
} else if (!gutters[YGGutterAll].isUndefined()) {
92+
return gutters[YGGutterAll];
93+
} else {
94+
return defaultValue;
95+
}
96+
}
97+
98+
CompactValue YGNode::computeColumnGap(
99+
const YGStyle::Gutters& gutters,
100+
CompactValue defaultValue) {
101+
if (!gutters[YGGutterColumn].isUndefined()) {
102+
return gutters[YGGutterColumn];
103+
} else if (!gutters[YGGutterAll].isUndefined()) {
104+
return gutters[YGGutterAll];
105+
} else {
106+
return defaultValue;
107+
}
108+
}
109+
86110
YGFloatOptional YGNode::getLeadingPosition(
87111
const YGFlexDirection axis,
88112
const float axisSize) const {
@@ -163,6 +187,15 @@ YGFloatOptional YGNode::getMarginForAxis(
163187
return getLeadingMargin(axis, widthSize) + getTrailingMargin(axis, widthSize);
164188
}
165189

190+
YGFloatOptional YGNode::getGapForAxis(
191+
const YGFlexDirection axis,
192+
const float widthSize) const {
193+
auto gap = YGFlexDirectionIsRow(axis)
194+
? computeColumnGap(style_.gap(), CompactValue::ofZero())
195+
: computeRowGap(style_.gap(), CompactValue::ofZero());
196+
return YGResolveValue(gap, widthSize);
197+
}
198+
166199
YGSize YGNode::measure(
167200
float width,
168201
YGMeasureMode widthMode,

ReactCommon/yoga/yoga/YGNode.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,14 @@ struct YOGA_EXPORT YGNode {
204204
YGEdge edge,
205205
CompactValue defaultValue);
206206

207+
static CompactValue computeRowGap(
208+
const YGStyle::Gutters& gutters,
209+
CompactValue defaultValue);
210+
211+
static CompactValue computeColumnGap(
212+
const YGStyle::Gutters& gutters,
213+
CompactValue defaultValue);
214+
207215
// Methods related to positions, margin, padding and border
208216
YGFloatOptional getLeadingPosition(
209217
const YGFlexDirection axis,
@@ -236,6 +244,9 @@ struct YOGA_EXPORT YGNode {
236244
YGFloatOptional getMarginForAxis(
237245
const YGFlexDirection axis,
238246
const float widthSize) const;
247+
YGFloatOptional getGapForAxis(
248+
const YGFlexDirection axis,
249+
const float widthSize) const;
239250
// Setters
240251

241252
void setContext(void* context) { context_ = context; }

ReactCommon/yoga/yoga/YGNodePrint.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,20 @@ void YGNodeToString(
184184
appendEdges(str, "padding", style.padding());
185185
appendEdges(str, "border", style.border());
186186

187+
if (YGNode::computeColumnGap(
188+
style.gap(), detail::CompactValue::ofUndefined()) !=
189+
YGNode::computeColumnGap(
190+
YGNode().getStyle().gap(), detail::CompactValue::ofUndefined())) {
191+
appendNumberIfNotUndefined(
192+
str, "column-gap", style.gap()[YGGutterColumn]);
193+
}
194+
if (YGNode::computeRowGap(
195+
style.gap(), detail::CompactValue::ofUndefined()) !=
196+
YGNode::computeRowGap(
197+
YGNode().getStyle().gap(), detail::CompactValue::ofUndefined())) {
198+
appendNumberIfNotUndefined(str, "row-gap", style.gap()[YGGutterRow]);
199+
}
200+
187201
appendNumberIfNotAuto(str, "width", style.dimensions()[YGDimensionWidth]);
188202
appendNumberIfNotAuto(str, "height", style.dimensions()[YGDimensionHeight]);
189203
appendNumberIfNotAuto(

ReactCommon/yoga/yoga/YGStyle.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ bool operator==(const YGStyle& lhs, const YGStyle& rhs) {
2222
YGValueEqual(lhs.flexBasis(), rhs.flexBasis()) &&
2323
lhs.margin() == rhs.margin() && lhs.position() == rhs.position() &&
2424
lhs.padding() == rhs.padding() && lhs.border() == rhs.border() &&
25-
lhs.dimensions() == rhs.dimensions() &&
25+
lhs.gap() == rhs.gap() && lhs.dimensions() == rhs.dimensions() &&
2626
lhs.minDimensions() == rhs.minDimensions() &&
2727
lhs.maxDimensions() == rhs.maxDimensions();
2828

ReactCommon/yoga/yoga/YGStyle.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class YOGA_EXPORT YGStyle {
2929
public:
3030
using Dimensions = Values<YGDimension>;
3131
using Edges = Values<YGEdge>;
32+
using Gutters = Values<YGGutter>;
3233

3334
template <typename T>
3435
struct BitfieldRef {
@@ -113,6 +114,7 @@ class YOGA_EXPORT YGStyle {
113114
Edges position_ = {};
114115
Edges padding_ = {};
115116
Edges border_ = {};
117+
Gutters gap_ = {};
116118
Dimensions dimensions_{CompactValue::ofAuto()};
117119
Dimensions minDimensions_ = {};
118120
Dimensions maxDimensions_ = {};
@@ -210,6 +212,9 @@ class YOGA_EXPORT YGStyle {
210212
const Edges& border() const { return border_; }
211213
IdxRef<YGEdge, &YGStyle::border_> border() { return {*this}; }
212214

215+
const Gutters& gap() const { return gap_; }
216+
IdxRef<YGGutter, &YGStyle::gap_> gap() { return {*this}; }
217+
213218
const Dimensions& dimensions() const { return dimensions_; }
214219
IdxRef<YGDimension, &YGStyle::dimensions_> dimensions() { return {*this}; }
215220

ReactCommon/yoga/yoga/Yoga.cpp

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,27 @@ YOGA_EXPORT float YGNodeStyleGetBorder(
797797
return static_cast<YGValue>(border).value;
798798
}
799799

800+
YOGA_EXPORT void YGNodeStyleSetGap(
801+
const YGNodeRef node,
802+
const YGGutter gutter,
803+
const float gapLength) {
804+
auto length = detail::CompactValue::ofMaybe<YGUnitPoint>(gapLength);
805+
updateIndexedStyleProp<MSVC_HINT(gap)>(node, &YGStyle::gap, gutter, length);
806+
}
807+
808+
YOGA_EXPORT float YGNodeStyleGetGap(
809+
const YGNodeConstRef node,
810+
const YGGutter gutter) {
811+
auto gapLength = node->getStyle().gap()[gutter];
812+
if (gapLength.isUndefined() || gapLength.isAuto()) {
813+
// TODO(T26792433): Rather than returning YGUndefined, change the api to
814+
// return YGFloatOptional.
815+
return YGUndefined;
816+
}
817+
818+
return static_cast<YGValue>(gapLength).value;
819+
}
820+
800821
// Yoga specific properties, not compatible with flexbox specification
801822

802823
// TODO(T26792433): Change the API to accept YGFloatOptional.
@@ -1972,6 +1993,7 @@ static YGCollectFlexItemsRowValues YGCalculateCollectFlexItemsRowValues(
19721993
const YGFlexDirection mainAxis = YGResolveFlexDirection(
19731994
node->getStyle().flexDirection(), node->resolveDirection(ownerDirection));
19741995
const bool isNodeFlexWrap = node->getStyle().flexWrap() != YGWrapNoWrap;
1996+
const float gap = node->getGapForAxis(mainAxis, availableInnerWidth).unwrap();
19751997

19761998
// Add items to the current line until it's full or we run out of items.
19771999
uint32_t endOfLineIndex = startOfLineIndex;
@@ -1981,9 +2003,13 @@ static YGCollectFlexItemsRowValues YGCalculateCollectFlexItemsRowValues(
19812003
child->getStyle().positionType() == YGPositionTypeAbsolute) {
19822004
continue;
19832005
}
2006+
2007+
const bool isFirstElementInLine = (endOfLineIndex - startOfLineIndex) == 0;
2008+
19842009
child->setLineIndex(lineCount);
19852010
const float childMarginMainAxis =
19862011
child->getMarginForAxis(mainAxis, availableInnerWidth).unwrap();
2012+
const float childLeadingGapMainAxis = isFirstElementInLine ? 0.0f : gap;
19872013
const float flexBasisWithMinAndMaxConstraints =
19882014
YGNodeBoundAxisWithinMinAndMax(
19892015
child,
@@ -1996,16 +2022,19 @@ static YGCollectFlexItemsRowValues YGCalculateCollectFlexItemsRowValues(
19962022
// size, we've hit the end of the current line. Break out of the loop and
19972023
// lay out the current line.
19982024
if (sizeConsumedOnCurrentLineIncludingMinConstraint +
1999-
flexBasisWithMinAndMaxConstraints + childMarginMainAxis >
2025+
flexBasisWithMinAndMaxConstraints + childMarginMainAxis +
2026+
childLeadingGapMainAxis >
20002027
availableInnerMainDim &&
20012028
isNodeFlexWrap && flexAlgoRowMeasurement.itemsOnLine > 0) {
20022029
break;
20032030
}
20042031

20052032
sizeConsumedOnCurrentLineIncludingMinConstraint +=
2006-
flexBasisWithMinAndMaxConstraints + childMarginMainAxis;
2033+
flexBasisWithMinAndMaxConstraints + childMarginMainAxis +
2034+
childLeadingGapMainAxis;
20072035
flexAlgoRowMeasurement.sizeConsumedOnCurrentLine +=
2008-
flexBasisWithMinAndMaxConstraints + childMarginMainAxis;
2036+
flexBasisWithMinAndMaxConstraints + childMarginMainAxis +
2037+
childLeadingGapMainAxis;
20092038
flexAlgoRowMeasurement.itemsOnLine++;
20102039

20112040
if (child->isNodeFlexible()) {
@@ -2415,6 +2444,7 @@ static void YGJustifyMainAxis(
24152444
node->getLeadingPaddingAndBorder(mainAxis, ownerWidth).unwrap();
24162445
const float trailingPaddingAndBorderMain =
24172446
node->getTrailingPaddingAndBorder(mainAxis, ownerWidth).unwrap();
2447+
const float gap = node->getGapForAxis(mainAxis, ownerWidth).unwrap();
24182448
// If we are using "at most" rules in the main axis, make sure that
24192449
// remainingFreeSpace is 0 when min main dimension is not given
24202450
if (measureModeMainDim == YGMeasureModeAtMost &&
@@ -2462,7 +2492,7 @@ static void YGJustifyMainAxis(
24622492
// The space between the beginning and the first element and the space between
24632493
// each two elements.
24642494
float leadingMainDim = 0;
2465-
float betweenMainDim = 0;
2495+
float betweenMainDim = gap;
24662496
const YGJustify justifyContent = node->getStyle().justifyContent();
24672497

24682498
if (numberOfAutoMarginsOnCurrentLine == 0) {
@@ -2475,24 +2505,22 @@ static void YGJustifyMainAxis(
24752505
break;
24762506
case YGJustifySpaceBetween:
24772507
if (collectedFlexItemsValues.itemsOnLine > 1) {
2478-
betweenMainDim =
2508+
betweenMainDim +=
24792509
YGFloatMax(collectedFlexItemsValues.remainingFreeSpace, 0) /
24802510
(collectedFlexItemsValues.itemsOnLine - 1);
2481-
} else {
2482-
betweenMainDim = 0;
24832511
}
24842512
break;
24852513
case YGJustifySpaceEvenly:
24862514
// Space is distributed evenly across all elements
2487-
betweenMainDim = collectedFlexItemsValues.remainingFreeSpace /
2515+
leadingMainDim = collectedFlexItemsValues.remainingFreeSpace /
24882516
(collectedFlexItemsValues.itemsOnLine + 1);
2489-
leadingMainDim = betweenMainDim;
2517+
betweenMainDim += leadingMainDim;
24902518
break;
24912519
case YGJustifySpaceAround:
24922520
// Space on the edges is half of the space between elements
2493-
betweenMainDim = collectedFlexItemsValues.remainingFreeSpace /
2521+
leadingMainDim = 0.5f * collectedFlexItemsValues.remainingFreeSpace /
24942522
collectedFlexItemsValues.itemsOnLine;
2495-
leadingMainDim = betweenMainDim / 2;
2523+
betweenMainDim += leadingMainDim * 2;
24962524
break;
24972525
case YGJustifyFlexStart:
24982526
break;
@@ -2890,6 +2918,9 @@ static void YGNodelayoutImpl(
28902918
// Accumulated cross dimensions of all lines so far.
28912919
float totalLineCrossDim = 0;
28922920

2921+
const float crossAxisGap =
2922+
node->getGapForAxis(crossAxis, availableInnerCrossDim).unwrap();
2923+
28932924
// Max main dimension of all the lines.
28942925
float maxLineMainDim = 0;
28952926
YGCollectFlexItemsRowValues collectedFlexItemsValues;
@@ -3209,7 +3240,8 @@ static void YGNodelayoutImpl(
32093240
}
32103241
}
32113242

3212-
totalLineCrossDim += collectedFlexItemsValues.crossDim;
3243+
const float appliedCrossGap = lineCount != 0 ? crossAxisGap : 0.0f;
3244+
totalLineCrossDim += collectedFlexItemsValues.crossDim + appliedCrossGap;
32133245
maxLineMainDim =
32143246
YGFloatMax(maxLineMainDim, collectedFlexItemsValues.mainDim);
32153247
}
@@ -3304,6 +3336,7 @@ static void YGNodelayoutImpl(
33043336
}
33053337
endIndex = ii;
33063338
lineHeight += crossDimLead;
3339+
currentLead += i != 0 ? crossAxisGap : 0;
33073340

33083341
if (performLayout) {
33093342
for (ii = startIndex; ii < endIndex; ii++) {

ReactCommon/yoga/yoga/Yoga.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,12 @@ WIN_EXPORT YGValue YGNodeStyleGetPadding(YGNodeConstRef node, YGEdge edge);
232232
WIN_EXPORT void YGNodeStyleSetBorder(YGNodeRef node, YGEdge edge, float border);
233233
WIN_EXPORT float YGNodeStyleGetBorder(YGNodeConstRef node, YGEdge edge);
234234

235+
WIN_EXPORT void YGNodeStyleSetGap(
236+
YGNodeRef node,
237+
YGGutter gutter,
238+
float gapLength);
239+
WIN_EXPORT float YGNodeStyleGetGap(YGNodeConstRef node, YGGutter gutter);
240+
235241
WIN_EXPORT void YGNodeStyleSetWidth(YGNodeRef node, float width);
236242
WIN_EXPORT void YGNodeStyleSetWidthPercent(YGNodeRef node, float width);
237243
WIN_EXPORT void YGNodeStyleSetWidthAuto(YGNodeRef node);

0 commit comments

Comments
 (0)