react-native-windows 0.73.6 → 0.73.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2755 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ #include <algorithm>
9
+ #include <atomic>
10
+ #include <cfloat>
11
+ #include <cmath>
12
+ #include <cstring>
13
+
14
+ #include <yoga/Yoga.h>
15
+
16
+ #include <yoga/algorithm/Align.h>
17
+ #include <yoga/algorithm/Baseline.h>
18
+ #include <yoga/algorithm/BoundAxis.h>
19
+ #include <yoga/algorithm/Cache.h>
20
+ #include <yoga/algorithm/CalculateLayout.h>
21
+ #include <yoga/algorithm/FlexDirection.h>
22
+ #include <yoga/algorithm/FlexLine.h>
23
+ #include <yoga/algorithm/PixelGrid.h>
24
+ #include <yoga/algorithm/ResolveValue.h>
25
+ #include <yoga/debug/AssertFatal.h>
26
+ #include <yoga/debug/Log.h>
27
+ #include <yoga/debug/NodeToString.h>
28
+ #include <yoga/event/event.h>
29
+ #include <yoga/node/Node.h>
30
+ #include <yoga/numeric/Comparison.h>
31
+ #include <yoga/numeric/FloatOptional.h>
32
+
33
+ namespace facebook::yoga {
34
+
35
+ std::atomic<uint32_t> gCurrentGenerationCount(0);
36
+
37
+ bool calculateLayoutInternal(
38
+ yoga::Node* const node,
39
+ const float availableWidth,
40
+ const float availableHeight,
41
+ const Direction ownerDirection,
42
+ const MeasureMode widthMeasureMode,
43
+ const MeasureMode heightMeasureMode,
44
+ const float ownerWidth,
45
+ const float ownerHeight,
46
+ const bool performLayout,
47
+ const LayoutPassReason reason,
48
+ LayoutData& layoutMarkerData,
49
+ const uint32_t depth,
50
+ const uint32_t generationCount);
51
+
52
+ static inline float dimensionWithMargin(
53
+ const yoga::Node* const node,
54
+ const FlexDirection axis,
55
+ const float widthSize) {
56
+ return node->getLayout().measuredDimension(dimension(axis)) +
57
+ (node->getLeadingMargin(axis, widthSize) +
58
+ node->getTrailingMargin(axis, widthSize))
59
+ .unwrap();
60
+ }
61
+
62
+ static inline bool styleDefinesDimension(
63
+ const yoga::Node* const node,
64
+ const FlexDirection axis,
65
+ const float ownerSize) {
66
+ bool isUndefined =
67
+ yoga::isUndefined(node->getResolvedDimension(dimension(axis)).value);
68
+
69
+ auto resolvedDimension = node->getResolvedDimension(dimension(axis));
70
+ return !(
71
+ resolvedDimension.unit == YGUnitAuto ||
72
+ resolvedDimension.unit == YGUnitUndefined ||
73
+ (resolvedDimension.unit == YGUnitPoint && !isUndefined &&
74
+ resolvedDimension.value < 0.0f) ||
75
+ (resolvedDimension.unit == YGUnitPercent && !isUndefined &&
76
+ (resolvedDimension.value < 0.0f || yoga::isUndefined(ownerSize))));
77
+ }
78
+
79
+ static inline bool isLayoutDimensionDefined(
80
+ const yoga::Node* const node,
81
+ const FlexDirection axis) {
82
+ const float value = node->getLayout().measuredDimension(dimension(axis));
83
+ return !yoga::isUndefined(value) && value >= 0.0f;
84
+ }
85
+
86
+ static void setChildTrailingPosition(
87
+ const yoga::Node* const node,
88
+ yoga::Node* const child,
89
+ const FlexDirection axis) {
90
+ const float size = child->getLayout().measuredDimension(dimension(axis));
91
+ child->setLayoutPosition(
92
+ node->getLayout().measuredDimension(dimension(axis)) - size -
93
+ child->getLayout().position[leadingEdge(axis)],
94
+ trailingEdge(axis));
95
+ }
96
+
97
+ static void constrainMaxSizeForMode(
98
+ const yoga::Node* const node,
99
+ const enum FlexDirection axis,
100
+ const float ownerAxisSize,
101
+ const float ownerWidth,
102
+ MeasureMode* mode,
103
+ float* size) {
104
+ const FloatOptional maxSize =
105
+ yoga::resolveValue(
106
+ node->getStyle().maxDimension(dimension(axis)), ownerAxisSize) +
107
+ FloatOptional(node->getMarginForAxis(axis, ownerWidth));
108
+ switch (*mode) {
109
+ case MeasureMode::Exactly:
110
+ case MeasureMode::AtMost:
111
+ *size = (maxSize.isUndefined() || *size < maxSize.unwrap())
112
+ ? *size
113
+ : maxSize.unwrap();
114
+ break;
115
+ case MeasureMode::Undefined:
116
+ if (!maxSize.isUndefined()) {
117
+ *mode = MeasureMode::AtMost;
118
+ *size = maxSize.unwrap();
119
+ }
120
+ break;
121
+ }
122
+ }
123
+
124
+ static void computeFlexBasisForChild(
125
+ const yoga::Node* const node,
126
+ yoga::Node* const child,
127
+ const float width,
128
+ const MeasureMode widthMode,
129
+ const float height,
130
+ const float ownerWidth,
131
+ const float ownerHeight,
132
+ const MeasureMode heightMode,
133
+ const Direction direction,
134
+ LayoutData& layoutMarkerData,
135
+ const uint32_t depth,
136
+ const uint32_t generationCount) {
137
+ const FlexDirection mainAxis =
138
+ resolveDirection(node->getStyle().flexDirection(), direction);
139
+ const bool isMainAxisRow = isRow(mainAxis);
140
+ const float mainAxisSize = isMainAxisRow ? width : height;
141
+ const float mainAxisownerSize = isMainAxisRow ? ownerWidth : ownerHeight;
142
+
143
+ float childWidth;
144
+ float childHeight;
145
+ MeasureMode childWidthMeasureMode;
146
+ MeasureMode childHeightMeasureMode;
147
+
148
+ const FloatOptional resolvedFlexBasis =
149
+ yoga::resolveValue(child->resolveFlexBasisPtr(), mainAxisownerSize);
150
+ const bool isRowStyleDimDefined =
151
+ styleDefinesDimension(child, FlexDirection::Row, ownerWidth);
152
+ const bool isColumnStyleDimDefined =
153
+ styleDefinesDimension(child, FlexDirection::Column, ownerHeight);
154
+
155
+ if (!resolvedFlexBasis.isUndefined() && !yoga::isUndefined(mainAxisSize)) {
156
+ if (child->getLayout().computedFlexBasis.isUndefined() ||
157
+ (child->getConfig()->isExperimentalFeatureEnabled(
158
+ ExperimentalFeature::WebFlexBasis) &&
159
+ child->getLayout().computedFlexBasisGeneration != generationCount)) {
160
+ const FloatOptional paddingAndBorder =
161
+ FloatOptional(paddingAndBorderForAxis(child, mainAxis, ownerWidth));
162
+ child->setLayoutComputedFlexBasis(
163
+ yoga::maxOrDefined(resolvedFlexBasis, paddingAndBorder));
164
+ }
165
+ } else if (isMainAxisRow && isRowStyleDimDefined) {
166
+ // The width is definite, so use that as the flex basis.
167
+ const FloatOptional paddingAndBorder = FloatOptional(
168
+ paddingAndBorderForAxis(child, FlexDirection::Row, ownerWidth));
169
+
170
+ child->setLayoutComputedFlexBasis(yoga::maxOrDefined(
171
+ yoga::resolveValue(
172
+ child->getResolvedDimension(YGDimensionWidth), ownerWidth),
173
+ paddingAndBorder));
174
+ } else if (!isMainAxisRow && isColumnStyleDimDefined) {
175
+ // The height is definite, so use that as the flex basis.
176
+ const FloatOptional paddingAndBorder = FloatOptional(
177
+ paddingAndBorderForAxis(child, FlexDirection::Column, ownerWidth));
178
+ child->setLayoutComputedFlexBasis(yoga::maxOrDefined(
179
+ yoga::resolveValue(
180
+ child->getResolvedDimension(YGDimensionHeight), ownerHeight),
181
+ paddingAndBorder));
182
+ } else {
183
+ // Compute the flex basis and hypothetical main size (i.e. the clamped flex
184
+ // basis).
185
+ childWidth = YGUndefined;
186
+ childHeight = YGUndefined;
187
+ childWidthMeasureMode = MeasureMode::Undefined;
188
+ childHeightMeasureMode = MeasureMode::Undefined;
189
+
190
+ auto marginRow =
191
+ child->getMarginForAxis(FlexDirection::Row, ownerWidth).unwrap();
192
+ auto marginColumn =
193
+ child->getMarginForAxis(FlexDirection::Column, ownerWidth).unwrap();
194
+
195
+ if (isRowStyleDimDefined) {
196
+ childWidth =
197
+ yoga::resolveValue(
198
+ child->getResolvedDimension(YGDimensionWidth), ownerWidth)
199
+ .unwrap() +
200
+ marginRow;
201
+ childWidthMeasureMode = MeasureMode::Exactly;
202
+ }
203
+ if (isColumnStyleDimDefined) {
204
+ childHeight =
205
+ yoga::resolveValue(
206
+ child->getResolvedDimension(YGDimensionHeight), ownerHeight)
207
+ .unwrap() +
208
+ marginColumn;
209
+ childHeightMeasureMode = MeasureMode::Exactly;
210
+ }
211
+
212
+ // The W3C spec doesn't say anything about the 'overflow' property, but all
213
+ // major browsers appear to implement the following logic.
214
+ if ((!isMainAxisRow && node->getStyle().overflow() == Overflow::Scroll) ||
215
+ node->getStyle().overflow() != Overflow::Scroll) {
216
+ if (yoga::isUndefined(childWidth) && !yoga::isUndefined(width)) {
217
+ childWidth = width;
218
+ childWidthMeasureMode = MeasureMode::AtMost;
219
+ }
220
+ }
221
+
222
+ if ((isMainAxisRow && node->getStyle().overflow() == Overflow::Scroll) ||
223
+ node->getStyle().overflow() != Overflow::Scroll) {
224
+ if (yoga::isUndefined(childHeight) && !yoga::isUndefined(height)) {
225
+ childHeight = height;
226
+ childHeightMeasureMode = MeasureMode::AtMost;
227
+ }
228
+ }
229
+
230
+ const auto& childStyle = child->getStyle();
231
+ if (!childStyle.aspectRatio().isUndefined()) {
232
+ if (!isMainAxisRow && childWidthMeasureMode == MeasureMode::Exactly) {
233
+ childHeight = marginColumn +
234
+ (childWidth - marginRow) / childStyle.aspectRatio().unwrap();
235
+ childHeightMeasureMode = MeasureMode::Exactly;
236
+ } else if (
237
+ isMainAxisRow && childHeightMeasureMode == MeasureMode::Exactly) {
238
+ childWidth = marginRow +
239
+ (childHeight - marginColumn) * childStyle.aspectRatio().unwrap();
240
+ childWidthMeasureMode = MeasureMode::Exactly;
241
+ }
242
+ }
243
+
244
+ // If child has no defined size in the cross axis and is set to stretch, set
245
+ // the cross axis to be measured exactly with the available inner width
246
+
247
+ const bool hasExactWidth =
248
+ !yoga::isUndefined(width) && widthMode == MeasureMode::Exactly;
249
+ const bool childWidthStretch =
250
+ resolveChildAlignment(node, child) == Align::Stretch &&
251
+ childWidthMeasureMode != MeasureMode::Exactly;
252
+ if (!isMainAxisRow && !isRowStyleDimDefined && hasExactWidth &&
253
+ childWidthStretch) {
254
+ childWidth = width;
255
+ childWidthMeasureMode = MeasureMode::Exactly;
256
+ if (!childStyle.aspectRatio().isUndefined()) {
257
+ childHeight =
258
+ (childWidth - marginRow) / childStyle.aspectRatio().unwrap();
259
+ childHeightMeasureMode = MeasureMode::Exactly;
260
+ }
261
+ }
262
+
263
+ const bool hasExactHeight =
264
+ !yoga::isUndefined(height) && heightMode == MeasureMode::Exactly;
265
+ const bool childHeightStretch =
266
+ resolveChildAlignment(node, child) == Align::Stretch &&
267
+ childHeightMeasureMode != MeasureMode::Exactly;
268
+ if (isMainAxisRow && !isColumnStyleDimDefined && hasExactHeight &&
269
+ childHeightStretch) {
270
+ childHeight = height;
271
+ childHeightMeasureMode = MeasureMode::Exactly;
272
+
273
+ if (!childStyle.aspectRatio().isUndefined()) {
274
+ childWidth =
275
+ (childHeight - marginColumn) * childStyle.aspectRatio().unwrap();
276
+ childWidthMeasureMode = MeasureMode::Exactly;
277
+ }
278
+ }
279
+
280
+ constrainMaxSizeForMode(
281
+ child,
282
+ FlexDirection::Row,
283
+ ownerWidth,
284
+ ownerWidth,
285
+ &childWidthMeasureMode,
286
+ &childWidth);
287
+ constrainMaxSizeForMode(
288
+ child,
289
+ FlexDirection::Column,
290
+ ownerHeight,
291
+ ownerWidth,
292
+ &childHeightMeasureMode,
293
+ &childHeight);
294
+
295
+ // Measure the child
296
+ calculateLayoutInternal(
297
+ child,
298
+ childWidth,
299
+ childHeight,
300
+ direction,
301
+ childWidthMeasureMode,
302
+ childHeightMeasureMode,
303
+ ownerWidth,
304
+ ownerHeight,
305
+ false,
306
+ LayoutPassReason::kMeasureChild,
307
+ layoutMarkerData,
308
+ depth,
309
+ generationCount);
310
+
311
+ child->setLayoutComputedFlexBasis(FloatOptional(yoga::maxOrDefined(
312
+ child->getLayout().measuredDimension(dimension(mainAxis)),
313
+ paddingAndBorderForAxis(child, mainAxis, ownerWidth))));
314
+ }
315
+ child->setLayoutComputedFlexBasisGeneration(generationCount);
316
+ }
317
+
318
+ static void layoutAbsoluteChild(
319
+ const yoga::Node* const node,
320
+ yoga::Node* const child,
321
+ const float width,
322
+ const MeasureMode widthMode,
323
+ const float height,
324
+ const Direction direction,
325
+ LayoutData& layoutMarkerData,
326
+ const uint32_t depth,
327
+ const uint32_t generationCount) {
328
+ const FlexDirection mainAxis =
329
+ resolveDirection(node->getStyle().flexDirection(), direction);
330
+ const FlexDirection crossAxis = resolveCrossDirection(mainAxis, direction);
331
+ const bool isMainAxisRow = isRow(mainAxis);
332
+
333
+ float childWidth = YGUndefined;
334
+ float childHeight = YGUndefined;
335
+ MeasureMode childWidthMeasureMode = MeasureMode::Undefined;
336
+ MeasureMode childHeightMeasureMode = MeasureMode::Undefined;
337
+
338
+ auto marginRow = child->getMarginForAxis(FlexDirection::Row, width).unwrap();
339
+ auto marginColumn =
340
+ child->getMarginForAxis(FlexDirection::Column, width).unwrap();
341
+
342
+ if (styleDefinesDimension(child, FlexDirection::Row, width)) {
343
+ childWidth =
344
+ yoga::resolveValue(child->getResolvedDimension(YGDimensionWidth), width)
345
+ .unwrap() +
346
+ marginRow;
347
+ } else {
348
+ // If the child doesn't have a specified width, compute the width based on
349
+ // the left/right offsets if they're defined.
350
+ if (child->isLeadingPositionDefined(FlexDirection::Row) &&
351
+ child->isTrailingPosDefined(FlexDirection::Row)) {
352
+ childWidth = node->getLayout().measuredDimension(YGDimensionWidth) -
353
+ (node->getLeadingBorder(FlexDirection::Row) +
354
+ node->getTrailingBorder(FlexDirection::Row)) -
355
+ (child->getLeadingPosition(FlexDirection::Row, width) +
356
+ child->getTrailingPosition(FlexDirection::Row, width))
357
+ .unwrap();
358
+ childWidth =
359
+ boundAxis(child, FlexDirection::Row, childWidth, width, width);
360
+ }
361
+ }
362
+
363
+ if (styleDefinesDimension(child, FlexDirection::Column, height)) {
364
+ childHeight = yoga::resolveValue(
365
+ child->getResolvedDimension(YGDimensionHeight), height)
366
+ .unwrap() +
367
+ marginColumn;
368
+ } else {
369
+ // If the child doesn't have a specified height, compute the height based on
370
+ // the top/bottom offsets if they're defined.
371
+ if (child->isLeadingPositionDefined(FlexDirection::Column) &&
372
+ child->isTrailingPosDefined(FlexDirection::Column)) {
373
+ childHeight = node->getLayout().measuredDimension(YGDimensionHeight) -
374
+ (node->getLeadingBorder(FlexDirection::Column) +
375
+ node->getTrailingBorder(FlexDirection::Column)) -
376
+ (child->getLeadingPosition(FlexDirection::Column, height) +
377
+ child->getTrailingPosition(FlexDirection::Column, height))
378
+ .unwrap();
379
+ childHeight =
380
+ boundAxis(child, FlexDirection::Column, childHeight, height, width);
381
+ }
382
+ }
383
+
384
+ // Exactly one dimension needs to be defined for us to be able to do aspect
385
+ // ratio calculation. One dimension being the anchor and the other being
386
+ // flexible.
387
+ const auto& childStyle = child->getStyle();
388
+ if (yoga::isUndefined(childWidth) ^ yoga::isUndefined(childHeight)) {
389
+ if (!childStyle.aspectRatio().isUndefined()) {
390
+ if (yoga::isUndefined(childWidth)) {
391
+ childWidth = marginRow +
392
+ (childHeight - marginColumn) * childStyle.aspectRatio().unwrap();
393
+ } else if (yoga::isUndefined(childHeight)) {
394
+ childHeight = marginColumn +
395
+ (childWidth - marginRow) / childStyle.aspectRatio().unwrap();
396
+ }
397
+ }
398
+ }
399
+
400
+ // If we're still missing one or the other dimension, measure the content.
401
+ if (yoga::isUndefined(childWidth) || yoga::isUndefined(childHeight)) {
402
+ childWidthMeasureMode = yoga::isUndefined(childWidth)
403
+ ? MeasureMode::Undefined
404
+ : MeasureMode::Exactly;
405
+ childHeightMeasureMode = yoga::isUndefined(childHeight)
406
+ ? MeasureMode::Undefined
407
+ : MeasureMode::Exactly;
408
+
409
+ // If the size of the owner is defined then try to constrain the absolute
410
+ // child to that size as well. This allows text within the absolute child to
411
+ // wrap to the size of its owner. This is the same behavior as many browsers
412
+ // implement.
413
+ if (!isMainAxisRow && yoga::isUndefined(childWidth) &&
414
+ widthMode != MeasureMode::Undefined && !yoga::isUndefined(width) &&
415
+ width > 0) {
416
+ childWidth = width;
417
+ childWidthMeasureMode = MeasureMode::AtMost;
418
+ }
419
+
420
+ calculateLayoutInternal(
421
+ child,
422
+ childWidth,
423
+ childHeight,
424
+ direction,
425
+ childWidthMeasureMode,
426
+ childHeightMeasureMode,
427
+ childWidth,
428
+ childHeight,
429
+ false,
430
+ LayoutPassReason::kAbsMeasureChild,
431
+ layoutMarkerData,
432
+ depth,
433
+ generationCount);
434
+ childWidth = child->getLayout().measuredDimension(YGDimensionWidth) +
435
+ child->getMarginForAxis(FlexDirection::Row, width).unwrap();
436
+ childHeight = child->getLayout().measuredDimension(YGDimensionHeight) +
437
+ child->getMarginForAxis(FlexDirection::Column, width).unwrap();
438
+ }
439
+
440
+ calculateLayoutInternal(
441
+ child,
442
+ childWidth,
443
+ childHeight,
444
+ direction,
445
+ MeasureMode::Exactly,
446
+ MeasureMode::Exactly,
447
+ childWidth,
448
+ childHeight,
449
+ true,
450
+ LayoutPassReason::kAbsLayout,
451
+ layoutMarkerData,
452
+ depth,
453
+ generationCount);
454
+
455
+ if (child->isTrailingPosDefined(mainAxis) &&
456
+ !child->isLeadingPositionDefined(mainAxis)) {
457
+ child->setLayoutPosition(
458
+ node->getLayout().measuredDimension(dimension(mainAxis)) -
459
+ child->getLayout().measuredDimension(dimension(mainAxis)) -
460
+ node->getTrailingBorder(mainAxis) -
461
+ child->getTrailingMargin(mainAxis, isMainAxisRow ? width : height)
462
+ .unwrap() -
463
+ child->getTrailingPosition(mainAxis, isMainAxisRow ? width : height)
464
+ .unwrap(),
465
+ leadingEdge(mainAxis));
466
+ } else if (
467
+ !child->isLeadingPositionDefined(mainAxis) &&
468
+ node->getStyle().justifyContent() == Justify::Center) {
469
+ child->setLayoutPosition(
470
+ (node->getLayout().measuredDimension(dimension(mainAxis)) -
471
+ child->getLayout().measuredDimension(dimension(mainAxis))) /
472
+ 2.0f,
473
+ leadingEdge(mainAxis));
474
+ } else if (
475
+ !child->isLeadingPositionDefined(mainAxis) &&
476
+ node->getStyle().justifyContent() == Justify::FlexEnd) {
477
+ child->setLayoutPosition(
478
+ (node->getLayout().measuredDimension(dimension(mainAxis)) -
479
+ child->getLayout().measuredDimension(dimension(mainAxis))),
480
+ leadingEdge(mainAxis));
481
+ } else if (
482
+ node->getConfig()->isExperimentalFeatureEnabled(
483
+ ExperimentalFeature::AbsolutePercentageAgainstPaddingEdge) &&
484
+ child->isLeadingPositionDefined(mainAxis)) {
485
+ child->setLayoutPosition(
486
+ child->getLeadingPosition(
487
+ mainAxis,
488
+ node->getLayout().measuredDimension(dimension(mainAxis)))
489
+ .unwrap() +
490
+ node->getLeadingBorder(mainAxis) +
491
+ child
492
+ ->getLeadingMargin(
493
+ mainAxis,
494
+ node->getLayout().measuredDimension(dimension(mainAxis)))
495
+ .unwrap(),
496
+ leadingEdge(mainAxis));
497
+ }
498
+
499
+ if (child->isTrailingPosDefined(crossAxis) &&
500
+ !child->isLeadingPositionDefined(crossAxis)) {
501
+ child->setLayoutPosition(
502
+ node->getLayout().measuredDimension(dimension(crossAxis)) -
503
+ child->getLayout().measuredDimension(dimension(crossAxis)) -
504
+ node->getTrailingBorder(crossAxis) -
505
+ child->getTrailingMargin(crossAxis, isMainAxisRow ? height : width)
506
+ .unwrap() -
507
+ child
508
+ ->getTrailingPosition(crossAxis, isMainAxisRow ? height : width)
509
+ .unwrap(),
510
+ leadingEdge(crossAxis));
511
+
512
+ } else if (
513
+ !child->isLeadingPositionDefined(crossAxis) &&
514
+ resolveChildAlignment(node, child) == Align::Center) {
515
+ child->setLayoutPosition(
516
+ (node->getLayout().measuredDimension(dimension(crossAxis)) -
517
+ child->getLayout().measuredDimension(dimension(crossAxis))) /
518
+ 2.0f,
519
+ leadingEdge(crossAxis));
520
+ } else if (
521
+ !child->isLeadingPositionDefined(crossAxis) &&
522
+ ((resolveChildAlignment(node, child) == Align::FlexEnd) ^
523
+ (node->getStyle().flexWrap() == Wrap::WrapReverse))) {
524
+ child->setLayoutPosition(
525
+ (node->getLayout().measuredDimension(dimension(crossAxis)) -
526
+ child->getLayout().measuredDimension(dimension(crossAxis))),
527
+ leadingEdge(crossAxis));
528
+ } else if (
529
+ node->getConfig()->isExperimentalFeatureEnabled(
530
+ ExperimentalFeature::AbsolutePercentageAgainstPaddingEdge) &&
531
+ child->isLeadingPositionDefined(crossAxis)) {
532
+ child->setLayoutPosition(
533
+ child->getLeadingPosition(
534
+ crossAxis,
535
+ node->getLayout().measuredDimension(dimension(crossAxis)))
536
+ .unwrap() +
537
+ node->getLeadingBorder(crossAxis) +
538
+ child
539
+ ->getLeadingMargin(
540
+ crossAxis,
541
+ node->getLayout().measuredDimension(dimension(crossAxis)))
542
+ .unwrap(),
543
+ leadingEdge(crossAxis));
544
+ }
545
+ }
546
+
547
+ static void measureNodeWithMeasureFunc(
548
+ yoga::Node* const node,
549
+ float availableWidth,
550
+ float availableHeight,
551
+ const MeasureMode widthMeasureMode,
552
+ const MeasureMode heightMeasureMode,
553
+ const float ownerWidth,
554
+ const float ownerHeight,
555
+ LayoutData& layoutMarkerData,
556
+ const LayoutPassReason reason) {
557
+ yoga::assertFatalWithNode(
558
+ node,
559
+ node->hasMeasureFunc(),
560
+ "Expected node to have custom measure function");
561
+
562
+ if (widthMeasureMode == MeasureMode::Undefined) {
563
+ availableWidth = YGUndefined;
564
+ }
565
+ if (heightMeasureMode == MeasureMode::Undefined) {
566
+ availableHeight = YGUndefined;
567
+ }
568
+
569
+ const auto& padding = node->getLayout().padding;
570
+ const auto& border = node->getLayout().border;
571
+ const float paddingAndBorderAxisRow = padding[YGEdgeLeft] +
572
+ padding[YGEdgeRight] + border[YGEdgeLeft] + border[YGEdgeRight];
573
+ const float paddingAndBorderAxisColumn = padding[YGEdgeTop] +
574
+ padding[YGEdgeBottom] + border[YGEdgeTop] + border[YGEdgeBottom];
575
+
576
+ // We want to make sure we don't call measure with negative size
577
+ const float innerWidth = yoga::isUndefined(availableWidth)
578
+ ? availableWidth
579
+ : yoga::maxOrDefined(0, availableWidth - paddingAndBorderAxisRow);
580
+ const float innerHeight = yoga::isUndefined(availableHeight)
581
+ ? availableHeight
582
+ : yoga::maxOrDefined(0, availableHeight - paddingAndBorderAxisColumn);
583
+
584
+ if (widthMeasureMode == MeasureMode::Exactly &&
585
+ heightMeasureMode == MeasureMode::Exactly &&
586
+ !YGConfigIsExperimentalFeatureEnabled( // [Win] YGExperimentalFeatureCallMeasureCallbackOnAllNodes check added for NetUI
587
+ node->getConfig(), YGExperimentalFeatureCallMeasureCallbackOnAllNodes)) {
588
+ // Don't bother sizing the text if both dimensions are already defined.
589
+ node->setLayoutMeasuredDimension(
590
+ boundAxis(
591
+ node, FlexDirection::Row, availableWidth, ownerWidth, ownerWidth),
592
+ YGDimensionWidth);
593
+ node->setLayoutMeasuredDimension(
594
+ boundAxis(
595
+ node,
596
+ FlexDirection::Column,
597
+ availableHeight,
598
+ ownerHeight,
599
+ ownerWidth),
600
+ YGDimensionHeight);
601
+ } else {
602
+ Event::publish<Event::MeasureCallbackStart>(node);
603
+
604
+ // Measure the text under the current constraints.
605
+ const YGSize measuredSize = node->measure(
606
+ innerWidth, widthMeasureMode, innerHeight, heightMeasureMode);
607
+
608
+ layoutMarkerData.measureCallbacks += 1;
609
+ layoutMarkerData.measureCallbackReasonsCount[static_cast<size_t>(reason)] +=
610
+ 1;
611
+
612
+ Event::publish<Event::MeasureCallbackEnd>(
613
+ node,
614
+ {innerWidth,
615
+ unscopedEnum(widthMeasureMode),
616
+ innerHeight,
617
+ unscopedEnum(heightMeasureMode),
618
+ measuredSize.width,
619
+ measuredSize.height,
620
+ reason});
621
+
622
+ node->setLayoutMeasuredDimension(
623
+ boundAxis(
624
+ node,
625
+ FlexDirection::Row,
626
+ (widthMeasureMode == MeasureMode::Undefined ||
627
+ widthMeasureMode == MeasureMode::AtMost)
628
+ ? measuredSize.width + paddingAndBorderAxisRow
629
+ : availableWidth,
630
+ ownerWidth,
631
+ ownerWidth),
632
+ YGDimensionWidth);
633
+
634
+ node->setLayoutMeasuredDimension(
635
+ boundAxis(
636
+ node,
637
+ FlexDirection::Column,
638
+ (heightMeasureMode == MeasureMode::Undefined ||
639
+ heightMeasureMode == MeasureMode::AtMost)
640
+ ? measuredSize.height + paddingAndBorderAxisColumn
641
+ : availableHeight,
642
+ ownerHeight,
643
+ ownerWidth),
644
+ YGDimensionHeight);
645
+ }
646
+ }
647
+
648
+ // For nodes with no children, use the available values if they were provided,
649
+ // or the minimum size as indicated by the padding and border sizes.
650
+ static void measureNodeWithoutChildren(
651
+ yoga::Node* const node,
652
+ const float availableWidth,
653
+ const float availableHeight,
654
+ const MeasureMode widthMeasureMode,
655
+ const MeasureMode heightMeasureMode,
656
+ const float ownerWidth,
657
+ const float ownerHeight) {
658
+ const auto& padding = node->getLayout().padding;
659
+ const auto& border = node->getLayout().border;
660
+
661
+ float width = availableWidth;
662
+ if (widthMeasureMode == MeasureMode::Undefined ||
663
+ widthMeasureMode == MeasureMode::AtMost) {
664
+ width = padding[YGEdgeLeft] + padding[YGEdgeRight] + border[YGEdgeLeft] +
665
+ border[YGEdgeRight];
666
+ }
667
+ node->setLayoutMeasuredDimension(
668
+ boundAxis(node, FlexDirection::Row, width, ownerWidth, ownerWidth),
669
+ YGDimensionWidth);
670
+
671
+ float height = availableHeight;
672
+ if (heightMeasureMode == MeasureMode::Undefined ||
673
+ heightMeasureMode == MeasureMode::AtMost) {
674
+ height = padding[YGEdgeTop] + padding[YGEdgeBottom] + border[YGEdgeTop] +
675
+ border[YGEdgeBottom];
676
+ }
677
+ node->setLayoutMeasuredDimension(
678
+ boundAxis(node, FlexDirection::Column, height, ownerHeight, ownerWidth),
679
+ YGDimensionHeight);
680
+ }
681
+
682
+ static bool measureNodeWithFixedSize(
683
+ yoga::Node* const node,
684
+ const float availableWidth,
685
+ const float availableHeight,
686
+ const MeasureMode widthMeasureMode,
687
+ const MeasureMode heightMeasureMode,
688
+ const float ownerWidth,
689
+ const float ownerHeight) {
690
+ if ((!yoga::isUndefined(availableWidth) &&
691
+ widthMeasureMode == MeasureMode::AtMost && availableWidth <= 0.0f) ||
692
+ (!yoga::isUndefined(availableHeight) &&
693
+ heightMeasureMode == MeasureMode::AtMost && availableHeight <= 0.0f) ||
694
+ (widthMeasureMode == MeasureMode::Exactly &&
695
+ heightMeasureMode == MeasureMode::Exactly)) {
696
+ node->setLayoutMeasuredDimension(
697
+ boundAxis(
698
+ node,
699
+ FlexDirection::Row,
700
+ yoga::isUndefined(availableWidth) ||
701
+ (widthMeasureMode == MeasureMode::AtMost &&
702
+ availableWidth < 0.0f)
703
+ ? 0.0f
704
+ : availableWidth,
705
+ ownerWidth,
706
+ ownerWidth),
707
+ YGDimensionWidth);
708
+
709
+ node->setLayoutMeasuredDimension(
710
+ boundAxis(
711
+ node,
712
+ FlexDirection::Column,
713
+ yoga::isUndefined(availableHeight) ||
714
+ (heightMeasureMode == MeasureMode::AtMost &&
715
+ availableHeight < 0.0f)
716
+ ? 0.0f
717
+ : availableHeight,
718
+ ownerHeight,
719
+ ownerWidth),
720
+ YGDimensionHeight);
721
+ return true;
722
+ }
723
+
724
+ return false;
725
+ }
726
+
727
+ static void zeroOutLayoutRecursively(yoga::Node* const node) {
728
+ node->getLayout() = {};
729
+ node->setLayoutDimension(0, YGDimensionWidth);
730
+ node->setLayoutDimension(0, YGDimensionHeight);
731
+ node->setHasNewLayout(true);
732
+
733
+ node->cloneChildrenIfNeeded();
734
+ for (const auto child : node->getChildren()) {
735
+ zeroOutLayoutRecursively(child);
736
+ }
737
+ }
738
+
739
+ static float calculateAvailableInnerDimension(
740
+ const yoga::Node* const node,
741
+ const YGDimension dimension,
742
+ const float availableDim,
743
+ const float paddingAndBorder,
744
+ const float ownerDim) {
745
+ float availableInnerDim = availableDim - paddingAndBorder;
746
+ // Max dimension overrides predefined dimension value; Min dimension in turn
747
+ // overrides both of the above
748
+ if (!yoga::isUndefined(availableInnerDim)) {
749
+ // We want to make sure our available height does not violate min and max
750
+ // constraints
751
+ const FloatOptional minDimensionOptional =
752
+ yoga::resolveValue(node->getStyle().minDimension(dimension), ownerDim);
753
+ const float minInnerDim = minDimensionOptional.isUndefined()
754
+ ? 0.0f
755
+ : minDimensionOptional.unwrap() - paddingAndBorder;
756
+
757
+ const FloatOptional maxDimensionOptional =
758
+ yoga::resolveValue(node->getStyle().maxDimension(dimension), ownerDim);
759
+
760
+ const float maxInnerDim = maxDimensionOptional.isUndefined()
761
+ ? FLT_MAX
762
+ : maxDimensionOptional.unwrap() - paddingAndBorder;
763
+ availableInnerDim = yoga::maxOrDefined(
764
+ yoga::minOrDefined(availableInnerDim, maxInnerDim), minInnerDim);
765
+ }
766
+
767
+ return availableInnerDim;
768
+ }
769
+
770
+ static float computeFlexBasisForChildren(
771
+ yoga::Node* const node,
772
+ const float availableInnerWidth,
773
+ const float availableInnerHeight,
774
+ MeasureMode widthMeasureMode,
775
+ MeasureMode heightMeasureMode,
776
+ Direction direction,
777
+ FlexDirection mainAxis,
778
+ bool performLayout,
779
+ LayoutData& layoutMarkerData,
780
+ const uint32_t depth,
781
+ const uint32_t generationCount) {
782
+ float totalOuterFlexBasis = 0.0f;
783
+ YGNodeRef singleFlexChild = nullptr;
784
+ const auto& children = node->getChildren();
785
+ MeasureMode measureModeMainDim =
786
+ isRow(mainAxis) ? widthMeasureMode : heightMeasureMode;
787
+ // If there is only one child with flexGrow + flexShrink it means we can set
788
+ // the computedFlexBasis to 0 instead of measuring and shrinking / flexing the
789
+ // child to exactly match the remaining space
790
+ if (measureModeMainDim == MeasureMode::Exactly) {
791
+ for (auto child : children) {
792
+ if (child->isNodeFlexible()) {
793
+ if (singleFlexChild != nullptr ||
794
+ yoga::inexactEquals(child->resolveFlexGrow(), 0.0f) ||
795
+ yoga::inexactEquals(child->resolveFlexShrink(), 0.0f)) {
796
+ // There is already a flexible child, or this flexible child doesn't
797
+ // have flexGrow and flexShrink, abort
798
+ singleFlexChild = nullptr;
799
+ break;
800
+ } else {
801
+ singleFlexChild = child;
802
+ }
803
+ }
804
+ }
805
+ }
806
+
807
+ for (auto child : children) {
808
+ child->resolveDimension();
809
+ if (child->getStyle().display() == Display::None) {
810
+ zeroOutLayoutRecursively(child);
811
+ child->setHasNewLayout(true);
812
+ child->setDirty(false);
813
+ continue;
814
+ }
815
+ if (performLayout) {
816
+ // Set the initial position (relative to the owner).
817
+ const Direction childDirection = child->resolveDirection(direction);
818
+ const float mainDim =
819
+ isRow(mainAxis) ? availableInnerWidth : availableInnerHeight;
820
+ const float crossDim =
821
+ isRow(mainAxis) ? availableInnerHeight : availableInnerWidth;
822
+ child->setPosition(
823
+ childDirection, mainDim, crossDim, availableInnerWidth);
824
+ }
825
+
826
+ if (child->getStyle().positionType() == PositionType::Absolute) {
827
+ continue;
828
+ }
829
+ if (child == singleFlexChild) {
830
+ child->setLayoutComputedFlexBasisGeneration(generationCount);
831
+ child->setLayoutComputedFlexBasis(FloatOptional(0));
832
+ } else {
833
+ computeFlexBasisForChild(
834
+ node,
835
+ child,
836
+ availableInnerWidth,
837
+ widthMeasureMode,
838
+ availableInnerHeight,
839
+ availableInnerWidth,
840
+ availableInnerHeight,
841
+ heightMeasureMode,
842
+ direction,
843
+ layoutMarkerData,
844
+ depth,
845
+ generationCount);
846
+ }
847
+
848
+ totalOuterFlexBasis +=
849
+ (child->getLayout().computedFlexBasis +
850
+ child->getMarginForAxis(mainAxis, availableInnerWidth))
851
+ .unwrap();
852
+ }
853
+
854
+ return totalOuterFlexBasis;
855
+ }
856
+
857
+ // It distributes the free space to the flexible items and ensures that the size
858
+ // of the flex items abide the min and max constraints. At the end of this
859
+ // function the child nodes would have proper size. Prior using this function
860
+ // please ensure that distributeFreeSpaceFirstPass is called.
861
+ static float distributeFreeSpaceSecondPass(
862
+ FlexLine& flexLine,
863
+ yoga::Node* const node,
864
+ const FlexDirection mainAxis,
865
+ const FlexDirection crossAxis,
866
+ const float mainAxisownerSize,
867
+ const float availableInnerMainDim,
868
+ const float availableInnerCrossDim,
869
+ const float availableInnerWidth,
870
+ const float availableInnerHeight,
871
+ const bool mainAxisOverflows,
872
+ const MeasureMode measureModeCrossDim,
873
+ const bool performLayout,
874
+ LayoutData& layoutMarkerData,
875
+ const uint32_t depth,
876
+ const uint32_t generationCount) {
877
+ float childFlexBasis = 0;
878
+ float flexShrinkScaledFactor = 0;
879
+ float flexGrowFactor = 0;
880
+ float deltaFreeSpace = 0;
881
+ const bool isMainAxisRow = isRow(mainAxis);
882
+ const bool isNodeFlexWrap = node->getStyle().flexWrap() != Wrap::NoWrap;
883
+
884
+ for (auto currentLineChild : flexLine.itemsInFlow) {
885
+ childFlexBasis = boundAxisWithinMinAndMax(
886
+ currentLineChild,
887
+ mainAxis,
888
+ currentLineChild->getLayout().computedFlexBasis,
889
+ mainAxisownerSize)
890
+ .unwrap();
891
+ float updatedMainSize = childFlexBasis;
892
+
893
+ if (!yoga::isUndefined(flexLine.layout.remainingFreeSpace) &&
894
+ flexLine.layout.remainingFreeSpace < 0) {
895
+ flexShrinkScaledFactor =
896
+ -currentLineChild->resolveFlexShrink() * childFlexBasis;
897
+ // Is this child able to shrink?
898
+ if (flexShrinkScaledFactor != 0) {
899
+ float childSize;
900
+
901
+ if (!yoga::isUndefined(flexLine.layout.totalFlexShrinkScaledFactors) &&
902
+ flexLine.layout.totalFlexShrinkScaledFactors == 0) {
903
+ childSize = childFlexBasis + flexShrinkScaledFactor;
904
+ } else {
905
+ childSize = childFlexBasis +
906
+ (flexLine.layout.remainingFreeSpace /
907
+ flexLine.layout.totalFlexShrinkScaledFactors) *
908
+ flexShrinkScaledFactor;
909
+ }
910
+
911
+ updatedMainSize = boundAxis(
912
+ currentLineChild,
913
+ mainAxis,
914
+ childSize,
915
+ availableInnerMainDim,
916
+ availableInnerWidth);
917
+ }
918
+ } else if (
919
+ !yoga::isUndefined(flexLine.layout.remainingFreeSpace) &&
920
+ flexLine.layout.remainingFreeSpace > 0) {
921
+ flexGrowFactor = currentLineChild->resolveFlexGrow();
922
+
923
+ // Is this child able to grow?
924
+ if (!std::isnan(flexGrowFactor) && flexGrowFactor != 0) {
925
+ updatedMainSize = boundAxis(
926
+ currentLineChild,
927
+ mainAxis,
928
+ childFlexBasis +
929
+ flexLine.layout.remainingFreeSpace /
930
+ flexLine.layout.totalFlexGrowFactors * flexGrowFactor,
931
+ availableInnerMainDim,
932
+ availableInnerWidth);
933
+ }
934
+ }
935
+
936
+ deltaFreeSpace += updatedMainSize - childFlexBasis;
937
+
938
+ const float marginMain =
939
+ currentLineChild->getMarginForAxis(mainAxis, availableInnerWidth)
940
+ .unwrap();
941
+ const float marginCross =
942
+ currentLineChild->getMarginForAxis(crossAxis, availableInnerWidth)
943
+ .unwrap();
944
+
945
+ float childCrossSize;
946
+ float childMainSize = updatedMainSize + marginMain;
947
+ MeasureMode childCrossMeasureMode;
948
+ MeasureMode childMainMeasureMode = MeasureMode::Exactly;
949
+
950
+ const auto& childStyle = currentLineChild->getStyle();
951
+ if (!childStyle.aspectRatio().isUndefined()) {
952
+ childCrossSize = isMainAxisRow
953
+ ? (childMainSize - marginMain) / childStyle.aspectRatio().unwrap()
954
+ : (childMainSize - marginMain) * childStyle.aspectRatio().unwrap();
955
+ childCrossMeasureMode = MeasureMode::Exactly;
956
+
957
+ childCrossSize += marginCross;
958
+ } else if (
959
+ !std::isnan(availableInnerCrossDim) &&
960
+ !styleDefinesDimension(
961
+ currentLineChild, crossAxis, availableInnerCrossDim) &&
962
+ measureModeCrossDim == MeasureMode::Exactly &&
963
+ !(isNodeFlexWrap && mainAxisOverflows) &&
964
+ resolveChildAlignment(node, currentLineChild) == Align::Stretch &&
965
+ currentLineChild->marginLeadingValue(crossAxis).unit != YGUnitAuto &&
966
+ currentLineChild->marginTrailingValue(crossAxis).unit != YGUnitAuto) {
967
+ childCrossSize = availableInnerCrossDim;
968
+ childCrossMeasureMode = MeasureMode::Exactly;
969
+ } else if (!styleDefinesDimension(
970
+ currentLineChild, crossAxis, availableInnerCrossDim)) {
971
+ childCrossSize = availableInnerCrossDim;
972
+ childCrossMeasureMode = yoga::isUndefined(childCrossSize)
973
+ ? MeasureMode::Undefined
974
+ : MeasureMode::AtMost;
975
+ } else {
976
+ childCrossSize =
977
+ yoga::resolveValue(
978
+ currentLineChild->getResolvedDimension(dimension(crossAxis)),
979
+ availableInnerCrossDim)
980
+ .unwrap() +
981
+ marginCross;
982
+ const bool isLoosePercentageMeasurement =
983
+ currentLineChild->getResolvedDimension(dimension(crossAxis)).unit ==
984
+ YGUnitPercent &&
985
+ measureModeCrossDim != MeasureMode::Exactly;
986
+ childCrossMeasureMode =
987
+ yoga::isUndefined(childCrossSize) || isLoosePercentageMeasurement
988
+ ? MeasureMode::Undefined
989
+ : MeasureMode::Exactly;
990
+ }
991
+
992
+ constrainMaxSizeForMode(
993
+ currentLineChild,
994
+ mainAxis,
995
+ availableInnerMainDim,
996
+ availableInnerWidth,
997
+ &childMainMeasureMode,
998
+ &childMainSize);
999
+ constrainMaxSizeForMode(
1000
+ currentLineChild,
1001
+ crossAxis,
1002
+ availableInnerCrossDim,
1003
+ availableInnerWidth,
1004
+ &childCrossMeasureMode,
1005
+ &childCrossSize);
1006
+
1007
+ const bool requiresStretchLayout =
1008
+ !styleDefinesDimension(
1009
+ currentLineChild, crossAxis, availableInnerCrossDim) &&
1010
+ resolveChildAlignment(node, currentLineChild) == Align::Stretch &&
1011
+ currentLineChild->marginLeadingValue(crossAxis).unit != YGUnitAuto &&
1012
+ currentLineChild->marginTrailingValue(crossAxis).unit != YGUnitAuto;
1013
+
1014
+ const float childWidth = isMainAxisRow ? childMainSize : childCrossSize;
1015
+ const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize;
1016
+
1017
+ const MeasureMode childWidthMeasureMode =
1018
+ isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode;
1019
+ const MeasureMode childHeightMeasureMode =
1020
+ !isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode;
1021
+
1022
+ const bool isLayoutPass = performLayout && !requiresStretchLayout;
1023
+ // Recursively call the layout algorithm for this child with the updated
1024
+ // main size.
1025
+ calculateLayoutInternal(
1026
+ currentLineChild,
1027
+ childWidth,
1028
+ childHeight,
1029
+ node->getLayout().direction(),
1030
+ childWidthMeasureMode,
1031
+ childHeightMeasureMode,
1032
+ availableInnerWidth,
1033
+ availableInnerHeight,
1034
+ isLayoutPass,
1035
+ isLayoutPass ? LayoutPassReason::kFlexLayout
1036
+ : LayoutPassReason::kFlexMeasure,
1037
+ layoutMarkerData,
1038
+ depth,
1039
+ generationCount);
1040
+ node->setLayoutHadOverflow(
1041
+ node->getLayout().hadOverflow() ||
1042
+ currentLineChild->getLayout().hadOverflow());
1043
+ }
1044
+ return deltaFreeSpace;
1045
+ }
1046
+
1047
+ // It distributes the free space to the flexible items.For those flexible items
1048
+ // whose min and max constraints are triggered, those flex item's clamped size
1049
+ // is removed from the remaingfreespace.
1050
+ static void distributeFreeSpaceFirstPass(
1051
+ FlexLine& flexLine,
1052
+ const FlexDirection mainAxis,
1053
+ const float mainAxisownerSize,
1054
+ const float availableInnerMainDim,
1055
+ const float availableInnerWidth) {
1056
+ float flexShrinkScaledFactor = 0;
1057
+ float flexGrowFactor = 0;
1058
+ float baseMainSize = 0;
1059
+ float boundMainSize = 0;
1060
+ float deltaFreeSpace = 0;
1061
+
1062
+ for (auto currentLineChild : flexLine.itemsInFlow) {
1063
+ float childFlexBasis = boundAxisWithinMinAndMax(
1064
+ currentLineChild,
1065
+ mainAxis,
1066
+ currentLineChild->getLayout().computedFlexBasis,
1067
+ mainAxisownerSize)
1068
+ .unwrap();
1069
+
1070
+ if (flexLine.layout.remainingFreeSpace < 0) {
1071
+ flexShrinkScaledFactor =
1072
+ -currentLineChild->resolveFlexShrink() * childFlexBasis;
1073
+
1074
+ // Is this child able to shrink?
1075
+ if (!yoga::isUndefined(flexShrinkScaledFactor) &&
1076
+ flexShrinkScaledFactor != 0) {
1077
+ baseMainSize = childFlexBasis +
1078
+ flexLine.layout.remainingFreeSpace /
1079
+ flexLine.layout.totalFlexShrinkScaledFactors *
1080
+ flexShrinkScaledFactor;
1081
+ boundMainSize = boundAxis(
1082
+ currentLineChild,
1083
+ mainAxis,
1084
+ baseMainSize,
1085
+ availableInnerMainDim,
1086
+ availableInnerWidth);
1087
+ if (!yoga::isUndefined(baseMainSize) &&
1088
+ !yoga::isUndefined(boundMainSize) &&
1089
+ baseMainSize != boundMainSize) {
1090
+ // By excluding this item's size and flex factor from remaining, this
1091
+ // item's min/max constraints should also trigger in the second pass
1092
+ // resulting in the item's size calculation being identical in the
1093
+ // first and second passes.
1094
+ deltaFreeSpace += boundMainSize - childFlexBasis;
1095
+ flexLine.layout.totalFlexShrinkScaledFactors -=
1096
+ (-currentLineChild->resolveFlexShrink() *
1097
+ currentLineChild->getLayout().computedFlexBasis.unwrap());
1098
+ }
1099
+ }
1100
+ } else if (
1101
+ !yoga::isUndefined(flexLine.layout.remainingFreeSpace) &&
1102
+ flexLine.layout.remainingFreeSpace > 0) {
1103
+ flexGrowFactor = currentLineChild->resolveFlexGrow();
1104
+
1105
+ // Is this child able to grow?
1106
+ if (!yoga::isUndefined(flexGrowFactor) && flexGrowFactor != 0) {
1107
+ baseMainSize = childFlexBasis +
1108
+ flexLine.layout.remainingFreeSpace /
1109
+ flexLine.layout.totalFlexGrowFactors * flexGrowFactor;
1110
+ boundMainSize = boundAxis(
1111
+ currentLineChild,
1112
+ mainAxis,
1113
+ baseMainSize,
1114
+ availableInnerMainDim,
1115
+ availableInnerWidth);
1116
+
1117
+ if (!yoga::isUndefined(baseMainSize) &&
1118
+ !yoga::isUndefined(boundMainSize) &&
1119
+ baseMainSize != boundMainSize) {
1120
+ // By excluding this item's size and flex factor from remaining, this
1121
+ // item's min/max constraints should also trigger in the second pass
1122
+ // resulting in the item's size calculation being identical in the
1123
+ // first and second passes.
1124
+ deltaFreeSpace += boundMainSize - childFlexBasis;
1125
+ flexLine.layout.totalFlexGrowFactors -= flexGrowFactor;
1126
+ }
1127
+ }
1128
+ }
1129
+ }
1130
+ flexLine.layout.remainingFreeSpace -= deltaFreeSpace;
1131
+ }
1132
+
1133
+ // Do two passes over the flex items to figure out how to distribute the
1134
+ // remaining space.
1135
+ //
1136
+ // The first pass finds the items whose min/max constraints trigger, freezes
1137
+ // them at those sizes, and excludes those sizes from the remaining space.
1138
+ //
1139
+ // The second pass sets the size of each flexible item. It distributes the
1140
+ // remaining space amongst the items whose min/max constraints didn't trigger in
1141
+ // the first pass. For the other items, it sets their sizes by forcing their
1142
+ // min/max constraints to trigger again.
1143
+ //
1144
+ // This two pass approach for resolving min/max constraints deviates from the
1145
+ // spec. The spec
1146
+ // (https://www.w3.org/TR/CSS-flexbox-1/#resolve-flexible-lengths) describes a
1147
+ // process that needs to be repeated a variable number of times. The algorithm
1148
+ // implemented here won't handle all cases but it was simpler to implement and
1149
+ // it mitigates performance concerns because we know exactly how many passes
1150
+ // it'll do.
1151
+ //
1152
+ // At the end of this function the child nodes would have the proper size
1153
+ // assigned to them.
1154
+ //
1155
+ static void resolveFlexibleLength(
1156
+ yoga::Node* const node,
1157
+ FlexLine& flexLine,
1158
+ const FlexDirection mainAxis,
1159
+ const FlexDirection crossAxis,
1160
+ const float mainAxisownerSize,
1161
+ const float availableInnerMainDim,
1162
+ const float availableInnerCrossDim,
1163
+ const float availableInnerWidth,
1164
+ const float availableInnerHeight,
1165
+ const bool mainAxisOverflows,
1166
+ const MeasureMode measureModeCrossDim,
1167
+ const bool performLayout,
1168
+ LayoutData& layoutMarkerData,
1169
+ const uint32_t depth,
1170
+ const uint32_t generationCount) {
1171
+ const float originalFreeSpace = flexLine.layout.remainingFreeSpace;
1172
+ // First pass: detect the flex items whose min/max constraints trigger
1173
+ distributeFreeSpaceFirstPass(
1174
+ flexLine,
1175
+ mainAxis,
1176
+ mainAxisownerSize,
1177
+ availableInnerMainDim,
1178
+ availableInnerWidth);
1179
+
1180
+ // Second pass: resolve the sizes of the flexible items
1181
+ const float distributedFreeSpace = distributeFreeSpaceSecondPass(
1182
+ flexLine,
1183
+ node,
1184
+ mainAxis,
1185
+ crossAxis,
1186
+ mainAxisownerSize,
1187
+ availableInnerMainDim,
1188
+ availableInnerCrossDim,
1189
+ availableInnerWidth,
1190
+ availableInnerHeight,
1191
+ mainAxisOverflows,
1192
+ measureModeCrossDim,
1193
+ performLayout,
1194
+ layoutMarkerData,
1195
+ depth,
1196
+ generationCount);
1197
+
1198
+ flexLine.layout.remainingFreeSpace = originalFreeSpace - distributedFreeSpace;
1199
+ }
1200
+
1201
+ static void justifyMainAxis(
1202
+ yoga::Node* const node,
1203
+ FlexLine& flexLine,
1204
+ const size_t startOfLineIndex,
1205
+ const FlexDirection mainAxis,
1206
+ const FlexDirection crossAxis,
1207
+ const MeasureMode measureModeMainDim,
1208
+ const MeasureMode measureModeCrossDim,
1209
+ const float mainAxisownerSize,
1210
+ const float ownerWidth,
1211
+ const float availableInnerMainDim,
1212
+ const float availableInnerCrossDim,
1213
+ const float availableInnerWidth,
1214
+ const bool performLayout) {
1215
+ const auto& style = node->getStyle();
1216
+ const float leadingPaddingAndBorderMain =
1217
+ node->getLeadingPaddingAndBorder(mainAxis, ownerWidth).unwrap();
1218
+ const float trailingPaddingAndBorderMain =
1219
+ node->getTrailingPaddingAndBorder(mainAxis, ownerWidth).unwrap();
1220
+ const float gap = node->getGapForAxis(mainAxis, ownerWidth).unwrap();
1221
+ // If we are using "at most" rules in the main axis, make sure that
1222
+ // remainingFreeSpace is 0 when min main dimension is not given
1223
+ if (measureModeMainDim == MeasureMode::AtMost &&
1224
+ flexLine.layout.remainingFreeSpace > 0) {
1225
+ if (!style.minDimension(dimension(mainAxis)).isUndefined() &&
1226
+ !yoga::resolveValue(
1227
+ style.minDimension(dimension(mainAxis)), mainAxisownerSize)
1228
+ .isUndefined()) {
1229
+ // This condition makes sure that if the size of main dimension(after
1230
+ // considering child nodes main dim, leading and trailing padding etc)
1231
+ // falls below min dimension, then the remainingFreeSpace is reassigned
1232
+ // considering the min dimension
1233
+
1234
+ // `minAvailableMainDim` denotes minimum available space in which child
1235
+ // can be laid out, it will exclude space consumed by padding and border.
1236
+ const float minAvailableMainDim =
1237
+ yoga::resolveValue(
1238
+ style.minDimension(dimension(mainAxis)), mainAxisownerSize)
1239
+ .unwrap() -
1240
+ leadingPaddingAndBorderMain - trailingPaddingAndBorderMain;
1241
+ const float occupiedSpaceByChildNodes =
1242
+ availableInnerMainDim - flexLine.layout.remainingFreeSpace;
1243
+ flexLine.layout.remainingFreeSpace = yoga::maxOrDefined(
1244
+ 0, minAvailableMainDim - occupiedSpaceByChildNodes);
1245
+ } else {
1246
+ flexLine.layout.remainingFreeSpace = 0;
1247
+ }
1248
+ }
1249
+
1250
+ int numberOfAutoMarginsOnCurrentLine = 0;
1251
+ for (size_t i = startOfLineIndex; i < flexLine.endOfLineIndex; i++) {
1252
+ auto child = node->getChild(i);
1253
+ if (child->getStyle().positionType() != PositionType::Absolute) {
1254
+ if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) {
1255
+ numberOfAutoMarginsOnCurrentLine++;
1256
+ }
1257
+ if (child->marginTrailingValue(mainAxis).unit == YGUnitAuto) {
1258
+ numberOfAutoMarginsOnCurrentLine++;
1259
+ }
1260
+ }
1261
+ }
1262
+
1263
+ // In order to position the elements in the main axis, we have two controls.
1264
+ // The space between the beginning and the first element and the space between
1265
+ // each two elements.
1266
+ float leadingMainDim = 0;
1267
+ float betweenMainDim = gap;
1268
+ const Justify justifyContent = node->getStyle().justifyContent();
1269
+
1270
+ if (numberOfAutoMarginsOnCurrentLine == 0) {
1271
+ switch (justifyContent) {
1272
+ case Justify::Center:
1273
+ leadingMainDim = flexLine.layout.remainingFreeSpace / 2;
1274
+ break;
1275
+ case Justify::FlexEnd:
1276
+ leadingMainDim = flexLine.layout.remainingFreeSpace;
1277
+ break;
1278
+ case Justify::SpaceBetween:
1279
+ if (flexLine.itemsInFlow.size() > 1) {
1280
+ betweenMainDim +=
1281
+ yoga::maxOrDefined(flexLine.layout.remainingFreeSpace, 0) /
1282
+ static_cast<float>(flexLine.itemsInFlow.size() - 1);
1283
+ }
1284
+ break;
1285
+ case Justify::SpaceEvenly:
1286
+ // Space is distributed evenly across all elements
1287
+ leadingMainDim = flexLine.layout.remainingFreeSpace /
1288
+ static_cast<float>(flexLine.itemsInFlow.size() + 1);
1289
+ betweenMainDim += leadingMainDim;
1290
+ break;
1291
+ case Justify::SpaceAround:
1292
+ // Space on the edges is half of the space between elements
1293
+ leadingMainDim = 0.5f * flexLine.layout.remainingFreeSpace /
1294
+ static_cast<float>(flexLine.itemsInFlow.size());
1295
+ betweenMainDim += leadingMainDim * 2;
1296
+ break;
1297
+ case Justify::FlexStart:
1298
+ break;
1299
+ }
1300
+ }
1301
+
1302
+ flexLine.layout.mainDim = leadingPaddingAndBorderMain + leadingMainDim;
1303
+ flexLine.layout.crossDim = 0;
1304
+
1305
+ float maxAscentForCurrentLine = 0;
1306
+ float maxDescentForCurrentLine = 0;
1307
+ bool isNodeBaselineLayout = isBaselineLayout(node);
1308
+ for (size_t i = startOfLineIndex; i < flexLine.endOfLineIndex; i++) {
1309
+ const auto child = node->getChild(i);
1310
+ const Style& childStyle = child->getStyle();
1311
+ const LayoutResults& childLayout = child->getLayout();
1312
+ if (childStyle.display() == Display::None) {
1313
+ continue;
1314
+ }
1315
+ if (childStyle.positionType() == PositionType::Absolute &&
1316
+ child->isLeadingPositionDefined(mainAxis)) {
1317
+ if (performLayout) {
1318
+ // In case the child is position absolute and has left/top being
1319
+ // defined, we override the position to whatever the user said (and
1320
+ // margin/border).
1321
+ child->setLayoutPosition(
1322
+ child->getLeadingPosition(mainAxis, availableInnerMainDim)
1323
+ .unwrap() +
1324
+ node->getLeadingBorder(mainAxis) +
1325
+ child->getLeadingMargin(mainAxis, availableInnerWidth).unwrap(),
1326
+ leadingEdge(mainAxis));
1327
+ }
1328
+ } else {
1329
+ // Now that we placed the element, we need to update the variables.
1330
+ // We need to do that only for relative elements. Absolute elements do not
1331
+ // take part in that phase.
1332
+ if (childStyle.positionType() != PositionType::Absolute) {
1333
+ if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) {
1334
+ flexLine.layout.mainDim += flexLine.layout.remainingFreeSpace /
1335
+ static_cast<float>(numberOfAutoMarginsOnCurrentLine);
1336
+ }
1337
+
1338
+ if (performLayout) {
1339
+ child->setLayoutPosition(
1340
+ childLayout.position[leadingEdge(mainAxis)] +
1341
+ flexLine.layout.mainDim,
1342
+ leadingEdge(mainAxis));
1343
+ }
1344
+
1345
+ if (child != flexLine.itemsInFlow.back()) {
1346
+ flexLine.layout.mainDim += betweenMainDim;
1347
+ }
1348
+
1349
+ if (child->marginTrailingValue(mainAxis).unit == YGUnitAuto) {
1350
+ flexLine.layout.mainDim += flexLine.layout.remainingFreeSpace /
1351
+ static_cast<float>(numberOfAutoMarginsOnCurrentLine);
1352
+ }
1353
+ bool canSkipFlex =
1354
+ !performLayout && measureModeCrossDim == MeasureMode::Exactly;
1355
+ if (canSkipFlex) {
1356
+ // If we skipped the flex step, then we can't rely on the measuredDims
1357
+ // because they weren't computed. This means we can't call
1358
+ // dimensionWithMargin.
1359
+ flexLine.layout.mainDim +=
1360
+ child->getMarginForAxis(mainAxis, availableInnerWidth).unwrap() +
1361
+ childLayout.computedFlexBasis.unwrap();
1362
+ flexLine.layout.crossDim = availableInnerCrossDim;
1363
+ } else {
1364
+ // The main dimension is the sum of all the elements dimension plus
1365
+ // the spacing.
1366
+ flexLine.layout.mainDim +=
1367
+ dimensionWithMargin(child, mainAxis, availableInnerWidth);
1368
+
1369
+ if (isNodeBaselineLayout) {
1370
+ // If the child is baseline aligned then the cross dimension is
1371
+ // calculated by adding maxAscent and maxDescent from the baseline.
1372
+ const float ascent = calculateBaseline(child) +
1373
+ child
1374
+ ->getLeadingMargin(
1375
+ FlexDirection::Column, availableInnerWidth)
1376
+ .unwrap();
1377
+ const float descent =
1378
+ child->getLayout().measuredDimension(YGDimensionHeight) +
1379
+ child
1380
+ ->getMarginForAxis(
1381
+ FlexDirection::Column, availableInnerWidth)
1382
+ .unwrap() -
1383
+ ascent;
1384
+
1385
+ maxAscentForCurrentLine =
1386
+ yoga::maxOrDefined(maxAscentForCurrentLine, ascent);
1387
+ maxDescentForCurrentLine =
1388
+ yoga::maxOrDefined(maxDescentForCurrentLine, descent);
1389
+ } else {
1390
+ // The cross dimension is the max of the elements dimension since
1391
+ // there can only be one element in that cross dimension in the case
1392
+ // when the items are not baseline aligned
1393
+ flexLine.layout.crossDim = yoga::maxOrDefined(
1394
+ flexLine.layout.crossDim,
1395
+ dimensionWithMargin(child, crossAxis, availableInnerWidth));
1396
+ }
1397
+ }
1398
+ } else if (performLayout) {
1399
+ child->setLayoutPosition(
1400
+ childLayout.position[leadingEdge(mainAxis)] +
1401
+ node->getLeadingBorder(mainAxis) + leadingMainDim,
1402
+ leadingEdge(mainAxis));
1403
+ }
1404
+ }
1405
+ }
1406
+ flexLine.layout.mainDim += trailingPaddingAndBorderMain;
1407
+
1408
+ if (isNodeBaselineLayout) {
1409
+ flexLine.layout.crossDim =
1410
+ maxAscentForCurrentLine + maxDescentForCurrentLine;
1411
+ }
1412
+ }
1413
+
1414
+ //
1415
+ // This is the main routine that implements a subset of the flexbox layout
1416
+ // algorithm described in the W3C CSS documentation:
1417
+ // https://www.w3.org/TR/CSS3-flexbox/.
1418
+ //
1419
+ // Limitations of this algorithm, compared to the full standard:
1420
+ // * Display property is always assumed to be 'flex' except for Text nodes,
1421
+ // which are assumed to be 'inline-flex'.
1422
+ // * The 'zIndex' property (or any form of z ordering) is not supported. Nodes
1423
+ // are stacked in document order.
1424
+ // * The 'order' property is not supported. The order of flex items is always
1425
+ // defined by document order.
1426
+ // * The 'visibility' property is always assumed to be 'visible'. Values of
1427
+ // 'collapse' and 'hidden' are not supported.
1428
+ // * There is no support for forced breaks.
1429
+ // * It does not support vertical inline directions (top-to-bottom or
1430
+ // bottom-to-top text).
1431
+ //
1432
+ // Deviations from standard:
1433
+ // * Section 4.5 of the spec indicates that all flex items have a default
1434
+ // minimum main size. For text blocks, for example, this is the width of the
1435
+ // widest word. Calculating the minimum width is expensive, so we forego it
1436
+ // and assume a default minimum main size of 0.
1437
+ // * Min/Max sizes in the main axis are not honored when resolving flexible
1438
+ // lengths.
1439
+ // * The spec indicates that the default value for 'flexDirection' is 'row',
1440
+ // but the algorithm below assumes a default of 'column'.
1441
+ //
1442
+ // Input parameters:
1443
+ // - node: current node to be sized and laid out
1444
+ // - availableWidth & availableHeight: available size to be used for sizing
1445
+ // the node or YGUndefined if the size is not available; interpretation
1446
+ // depends on layout flags
1447
+ // - ownerDirection: the inline (text) direction within the owner
1448
+ // (left-to-right or right-to-left)
1449
+ // - widthMeasureMode: indicates the sizing rules for the width (see below
1450
+ // for explanation)
1451
+ // - heightMeasureMode: indicates the sizing rules for the height (see below
1452
+ // for explanation)
1453
+ // - performLayout: specifies whether the caller is interested in just the
1454
+ // dimensions of the node or it requires the entire node and its subtree to
1455
+ // be laid out (with final positions)
1456
+ //
1457
+ // Details:
1458
+ // This routine is called recursively to lay out subtrees of flexbox
1459
+ // elements. It uses the information in node.style, which is treated as a
1460
+ // read-only input. It is responsible for setting the layout.direction and
1461
+ // layout.measuredDimensions fields for the input node as well as the
1462
+ // layout.position and layout.lineIndex fields for its child nodes. The
1463
+ // layout.measuredDimensions field includes any border or padding for the
1464
+ // node but does not include margins.
1465
+ //
1466
+ // The spec describes four different layout modes: "fill available", "max
1467
+ // content", "min content", and "fit content". Of these, we don't use "min
1468
+ // content" because we don't support default minimum main sizes (see above
1469
+ // for details). Each of our measure modes maps to a layout mode from the
1470
+ // spec (https://www.w3.org/TR/CSS3-sizing/#terms):
1471
+ // - MeasureMode::Undefined: max content
1472
+ // - MeasureMode::Exactly: fill available
1473
+ // - MeasureMode::AtMost: fit content
1474
+ //
1475
+ // When calling calculateLayoutImpl and calculateLayoutInternal, if the
1476
+ // caller passes an available size of undefined then it must also pass a
1477
+ // measure mode of MeasureMode::Undefined in that dimension.
1478
+ //
1479
+ static void calculateLayoutImpl(
1480
+ yoga::Node* const node,
1481
+ const float availableWidth,
1482
+ const float availableHeight,
1483
+ const Direction ownerDirection,
1484
+ const MeasureMode widthMeasureMode,
1485
+ const MeasureMode heightMeasureMode,
1486
+ const float ownerWidth,
1487
+ const float ownerHeight,
1488
+ const bool performLayout,
1489
+ LayoutData& layoutMarkerData,
1490
+ const uint32_t depth,
1491
+ const uint32_t generationCount,
1492
+ const LayoutPassReason reason) {
1493
+ yoga::assertFatalWithNode(
1494
+ node,
1495
+ yoga::isUndefined(availableWidth)
1496
+ ? widthMeasureMode == MeasureMode::Undefined
1497
+ : true,
1498
+ "availableWidth is indefinite so widthMeasureMode must be "
1499
+ "MeasureMode::Undefined");
1500
+ yoga::assertFatalWithNode(
1501
+ node,
1502
+ yoga::isUndefined(availableHeight)
1503
+ ? heightMeasureMode == MeasureMode::Undefined
1504
+ : true,
1505
+ "availableHeight is indefinite so heightMeasureMode must be "
1506
+ "MeasureMode::Undefined");
1507
+
1508
+ (performLayout ? layoutMarkerData.layouts : layoutMarkerData.measures) += 1;
1509
+
1510
+ // Set the resolved resolution in the node's layout.
1511
+ const Direction direction = node->resolveDirection(ownerDirection);
1512
+ node->setLayoutDirection(direction);
1513
+
1514
+ const FlexDirection flexRowDirection =
1515
+ resolveDirection(FlexDirection::Row, direction);
1516
+ const FlexDirection flexColumnDirection =
1517
+ resolveDirection(FlexDirection::Column, direction);
1518
+
1519
+ const YGEdge startEdge =
1520
+ direction == Direction::LTR ? YGEdgeLeft : YGEdgeRight;
1521
+ const YGEdge endEdge = direction == Direction::LTR ? YGEdgeRight : YGEdgeLeft;
1522
+
1523
+ const float marginRowLeading =
1524
+ node->getLeadingMargin(flexRowDirection, ownerWidth).unwrap();
1525
+ node->setLayoutMargin(marginRowLeading, startEdge);
1526
+ const float marginRowTrailing =
1527
+ node->getTrailingMargin(flexRowDirection, ownerWidth).unwrap();
1528
+ node->setLayoutMargin(marginRowTrailing, endEdge);
1529
+ const float marginColumnLeading =
1530
+ node->getLeadingMargin(flexColumnDirection, ownerWidth).unwrap();
1531
+ node->setLayoutMargin(marginColumnLeading, YGEdgeTop);
1532
+ const float marginColumnTrailing =
1533
+ node->getTrailingMargin(flexColumnDirection, ownerWidth).unwrap();
1534
+ node->setLayoutMargin(marginColumnTrailing, YGEdgeBottom);
1535
+
1536
+ const float marginAxisRow = marginRowLeading + marginRowTrailing;
1537
+ const float marginAxisColumn = marginColumnLeading + marginColumnTrailing;
1538
+
1539
+ node->setLayoutBorder(node->getLeadingBorder(flexRowDirection), startEdge);
1540
+ node->setLayoutBorder(node->getTrailingBorder(flexRowDirection), endEdge);
1541
+ node->setLayoutBorder(node->getLeadingBorder(flexColumnDirection), YGEdgeTop);
1542
+ node->setLayoutBorder(
1543
+ node->getTrailingBorder(flexColumnDirection), YGEdgeBottom);
1544
+
1545
+ node->setLayoutPadding(
1546
+ node->getLeadingPadding(flexRowDirection, ownerWidth).unwrap(),
1547
+ startEdge);
1548
+ node->setLayoutPadding(
1549
+ node->getTrailingPadding(flexRowDirection, ownerWidth).unwrap(), endEdge);
1550
+ node->setLayoutPadding(
1551
+ node->getLeadingPadding(flexColumnDirection, ownerWidth).unwrap(),
1552
+ YGEdgeTop);
1553
+ node->setLayoutPadding(
1554
+ node->getTrailingPadding(flexColumnDirection, ownerWidth).unwrap(),
1555
+ YGEdgeBottom);
1556
+
1557
+ if (node->hasMeasureFunc()) {
1558
+ measureNodeWithMeasureFunc(
1559
+ node,
1560
+ availableWidth - marginAxisRow,
1561
+ availableHeight - marginAxisColumn,
1562
+ widthMeasureMode,
1563
+ heightMeasureMode,
1564
+ ownerWidth,
1565
+ ownerHeight,
1566
+ layoutMarkerData,
1567
+ reason);
1568
+ return;
1569
+ }
1570
+
1571
+ const auto childCount = node->getChildCount();
1572
+ if (childCount == 0) {
1573
+ measureNodeWithoutChildren(
1574
+ node,
1575
+ availableWidth - marginAxisRow,
1576
+ availableHeight - marginAxisColumn,
1577
+ widthMeasureMode,
1578
+ heightMeasureMode,
1579
+ ownerWidth,
1580
+ ownerHeight);
1581
+ return;
1582
+ }
1583
+
1584
+ // If we're not being asked to perform a full layout we can skip the algorithm
1585
+ // if we already know the size
1586
+ if (!performLayout &&
1587
+ measureNodeWithFixedSize(
1588
+ node,
1589
+ availableWidth - marginAxisRow,
1590
+ availableHeight - marginAxisColumn,
1591
+ widthMeasureMode,
1592
+ heightMeasureMode,
1593
+ ownerWidth,
1594
+ ownerHeight)) {
1595
+ return;
1596
+ }
1597
+
1598
+ // At this point we know we're going to perform work. Ensure that each child
1599
+ // has a mutable copy.
1600
+ node->cloneChildrenIfNeeded();
1601
+ // Reset layout flags, as they could have changed.
1602
+ node->setLayoutHadOverflow(false);
1603
+
1604
+ // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM
1605
+ const FlexDirection mainAxis =
1606
+ resolveDirection(node->getStyle().flexDirection(), direction);
1607
+ const FlexDirection crossAxis = resolveCrossDirection(mainAxis, direction);
1608
+ const bool isMainAxisRow = isRow(mainAxis);
1609
+ const bool isNodeFlexWrap = node->getStyle().flexWrap() != Wrap::NoWrap;
1610
+
1611
+ const float mainAxisownerSize = isMainAxisRow ? ownerWidth : ownerHeight;
1612
+ const float crossAxisownerSize = isMainAxisRow ? ownerHeight : ownerWidth;
1613
+
1614
+ const float paddingAndBorderAxisMain =
1615
+ paddingAndBorderForAxis(node, mainAxis, ownerWidth);
1616
+ const float leadingPaddingAndBorderCross =
1617
+ node->getLeadingPaddingAndBorder(crossAxis, ownerWidth).unwrap();
1618
+ const float trailingPaddingAndBorderCross =
1619
+ node->getTrailingPaddingAndBorder(crossAxis, ownerWidth).unwrap();
1620
+ const float paddingAndBorderAxisCross =
1621
+ leadingPaddingAndBorderCross + trailingPaddingAndBorderCross;
1622
+
1623
+ MeasureMode measureModeMainDim =
1624
+ isMainAxisRow ? widthMeasureMode : heightMeasureMode;
1625
+ MeasureMode measureModeCrossDim =
1626
+ isMainAxisRow ? heightMeasureMode : widthMeasureMode;
1627
+
1628
+ const float paddingAndBorderAxisRow =
1629
+ isMainAxisRow ? paddingAndBorderAxisMain : paddingAndBorderAxisCross;
1630
+ const float paddingAndBorderAxisColumn =
1631
+ isMainAxisRow ? paddingAndBorderAxisCross : paddingAndBorderAxisMain;
1632
+
1633
+ // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS
1634
+
1635
+ float availableInnerWidth = calculateAvailableInnerDimension(
1636
+ node,
1637
+ YGDimensionWidth,
1638
+ availableWidth - marginAxisRow,
1639
+ paddingAndBorderAxisRow,
1640
+ ownerWidth);
1641
+ float availableInnerHeight = calculateAvailableInnerDimension(
1642
+ node,
1643
+ YGDimensionHeight,
1644
+ availableHeight - marginAxisColumn,
1645
+ paddingAndBorderAxisColumn,
1646
+ ownerHeight);
1647
+
1648
+ float availableInnerMainDim =
1649
+ isMainAxisRow ? availableInnerWidth : availableInnerHeight;
1650
+ const float availableInnerCrossDim =
1651
+ isMainAxisRow ? availableInnerHeight : availableInnerWidth;
1652
+
1653
+ // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM
1654
+
1655
+ // Computed basis + margins + gap
1656
+ float totalMainDim = 0;
1657
+ totalMainDim += computeFlexBasisForChildren(
1658
+ node,
1659
+ availableInnerWidth,
1660
+ availableInnerHeight,
1661
+ widthMeasureMode,
1662
+ heightMeasureMode,
1663
+ direction,
1664
+ mainAxis,
1665
+ performLayout,
1666
+ layoutMarkerData,
1667
+ depth,
1668
+ generationCount);
1669
+
1670
+ if (childCount > 1) {
1671
+ totalMainDim +=
1672
+ node->getGapForAxis(mainAxis, availableInnerCrossDim).unwrap() *
1673
+ static_cast<float>(childCount - 1);
1674
+ }
1675
+
1676
+ const bool mainAxisOverflows =
1677
+ (measureModeMainDim != MeasureMode::Undefined) &&
1678
+ totalMainDim > availableInnerMainDim;
1679
+
1680
+ if (isNodeFlexWrap && mainAxisOverflows &&
1681
+ measureModeMainDim == MeasureMode::AtMost) {
1682
+ measureModeMainDim = MeasureMode::Exactly;
1683
+ }
1684
+ // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES
1685
+
1686
+ // Indexes of children that represent the first and last items in the line.
1687
+ size_t startOfLineIndex = 0;
1688
+ size_t endOfLineIndex = 0;
1689
+
1690
+ // Number of lines.
1691
+ size_t lineCount = 0;
1692
+
1693
+ // Accumulated cross dimensions of all lines so far.
1694
+ float totalLineCrossDim = 0;
1695
+
1696
+ const float crossAxisGap =
1697
+ node->getGapForAxis(crossAxis, availableInnerCrossDim).unwrap();
1698
+
1699
+ // Max main dimension of all the lines.
1700
+ float maxLineMainDim = 0;
1701
+ for (; endOfLineIndex < childCount;
1702
+ lineCount++, startOfLineIndex = endOfLineIndex) {
1703
+ auto flexLine = calculateFlexLine(
1704
+ node,
1705
+ ownerDirection,
1706
+ mainAxisownerSize,
1707
+ availableInnerWidth,
1708
+ availableInnerMainDim,
1709
+ startOfLineIndex,
1710
+ lineCount);
1711
+
1712
+ endOfLineIndex = flexLine.endOfLineIndex;
1713
+
1714
+ // If we don't need to measure the cross axis, we can skip the entire flex
1715
+ // step.
1716
+ const bool canSkipFlex =
1717
+ !performLayout && measureModeCrossDim == MeasureMode::Exactly;
1718
+
1719
+ // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS
1720
+ // Calculate the remaining available space that needs to be allocated. If
1721
+ // the main dimension size isn't known, it is computed based on the line
1722
+ // length, so there's no more space left to distribute.
1723
+
1724
+ bool sizeBasedOnContent = false;
1725
+ // If we don't measure with exact main dimension we want to ensure we don't
1726
+ // violate min and max
1727
+ if (measureModeMainDim != MeasureMode::Exactly) {
1728
+ const auto& style = node->getStyle();
1729
+ const float minInnerWidth =
1730
+ yoga::resolveValue(style.minDimension(YGDimensionWidth), ownerWidth)
1731
+ .unwrap() -
1732
+ paddingAndBorderAxisRow;
1733
+ const float maxInnerWidth =
1734
+ yoga::resolveValue(style.maxDimension(YGDimensionWidth), ownerWidth)
1735
+ .unwrap() -
1736
+ paddingAndBorderAxisRow;
1737
+ const float minInnerHeight =
1738
+ yoga::resolveValue(style.minDimension(YGDimensionHeight), ownerHeight)
1739
+ .unwrap() -
1740
+ paddingAndBorderAxisColumn;
1741
+ const float maxInnerHeight =
1742
+ yoga::resolveValue(style.maxDimension(YGDimensionHeight), ownerHeight)
1743
+ .unwrap() -
1744
+ paddingAndBorderAxisColumn;
1745
+
1746
+ const float minInnerMainDim =
1747
+ isMainAxisRow ? minInnerWidth : minInnerHeight;
1748
+ const float maxInnerMainDim =
1749
+ isMainAxisRow ? maxInnerWidth : maxInnerHeight;
1750
+
1751
+ if (!yoga::isUndefined(minInnerMainDim) &&
1752
+ flexLine.sizeConsumed < minInnerMainDim) {
1753
+ availableInnerMainDim = minInnerMainDim;
1754
+ } else if (
1755
+ !yoga::isUndefined(maxInnerMainDim) &&
1756
+ flexLine.sizeConsumed > maxInnerMainDim) {
1757
+ availableInnerMainDim = maxInnerMainDim;
1758
+ } else {
1759
+ bool useLegacyStretchBehaviour =
1760
+ node->hasErrata(Errata::StretchFlexBasis);
1761
+
1762
+ if (!useLegacyStretchBehaviour &&
1763
+ ((!yoga::isUndefined(flexLine.layout.totalFlexGrowFactors) &&
1764
+ flexLine.layout.totalFlexGrowFactors == 0) ||
1765
+ (!yoga::isUndefined(node->resolveFlexGrow()) &&
1766
+ node->resolveFlexGrow() == 0))) {
1767
+ // If we don't have any children to flex or we can't flex the node
1768
+ // itself, space we've used is all space we need. Root node also
1769
+ // should be shrunk to minimum
1770
+ availableInnerMainDim = flexLine.sizeConsumed;
1771
+ }
1772
+
1773
+ sizeBasedOnContent = !useLegacyStretchBehaviour;
1774
+ }
1775
+ }
1776
+
1777
+ if (!sizeBasedOnContent && !yoga::isUndefined(availableInnerMainDim)) {
1778
+ flexLine.layout.remainingFreeSpace =
1779
+ availableInnerMainDim - flexLine.sizeConsumed;
1780
+ } else if (flexLine.sizeConsumed < 0) {
1781
+ // availableInnerMainDim is indefinite which means the node is being sized
1782
+ // based on its content. sizeConsumed is negative which means
1783
+ // the node will allocate 0 points for its content. Consequently,
1784
+ // remainingFreeSpace is 0 - sizeConsumed.
1785
+ flexLine.layout.remainingFreeSpace = -flexLine.sizeConsumed;
1786
+ }
1787
+
1788
+ if (!canSkipFlex) {
1789
+ resolveFlexibleLength(
1790
+ node,
1791
+ flexLine,
1792
+ mainAxis,
1793
+ crossAxis,
1794
+ mainAxisownerSize,
1795
+ availableInnerMainDim,
1796
+ availableInnerCrossDim,
1797
+ availableInnerWidth,
1798
+ availableInnerHeight,
1799
+ mainAxisOverflows,
1800
+ measureModeCrossDim,
1801
+ performLayout,
1802
+ layoutMarkerData,
1803
+ depth,
1804
+ generationCount);
1805
+ }
1806
+
1807
+ node->setLayoutHadOverflow(
1808
+ node->getLayout().hadOverflow() |
1809
+ (flexLine.layout.remainingFreeSpace < 0));
1810
+
1811
+ // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION
1812
+
1813
+ // At this point, all the children have their dimensions set in the main
1814
+ // axis. Their dimensions are also set in the cross axis with the exception
1815
+ // of items that are aligned "stretch". We need to compute these stretch
1816
+ // values and set the final positions.
1817
+
1818
+ justifyMainAxis(
1819
+ node,
1820
+ flexLine,
1821
+ startOfLineIndex,
1822
+ mainAxis,
1823
+ crossAxis,
1824
+ measureModeMainDim,
1825
+ measureModeCrossDim,
1826
+ mainAxisownerSize,
1827
+ ownerWidth,
1828
+ availableInnerMainDim,
1829
+ availableInnerCrossDim,
1830
+ availableInnerWidth,
1831
+ performLayout);
1832
+
1833
+ float containerCrossAxis = availableInnerCrossDim;
1834
+ if (measureModeCrossDim == MeasureMode::Undefined ||
1835
+ measureModeCrossDim == MeasureMode::AtMost) {
1836
+ // Compute the cross axis from the max cross dimension of the children.
1837
+ containerCrossAxis =
1838
+ boundAxis(
1839
+ node,
1840
+ crossAxis,
1841
+ flexLine.layout.crossDim + paddingAndBorderAxisCross,
1842
+ crossAxisownerSize,
1843
+ ownerWidth) -
1844
+ paddingAndBorderAxisCross;
1845
+ }
1846
+
1847
+ // If there's no flex wrap, the cross dimension is defined by the container.
1848
+ if (!isNodeFlexWrap && measureModeCrossDim == MeasureMode::Exactly) {
1849
+ flexLine.layout.crossDim = availableInnerCrossDim;
1850
+ }
1851
+
1852
+ // Clamp to the min/max size specified on the container.
1853
+ flexLine.layout.crossDim =
1854
+ boundAxis(
1855
+ node,
1856
+ crossAxis,
1857
+ flexLine.layout.crossDim + paddingAndBorderAxisCross,
1858
+ crossAxisownerSize,
1859
+ ownerWidth) -
1860
+ paddingAndBorderAxisCross;
1861
+
1862
+ // STEP 7: CROSS-AXIS ALIGNMENT
1863
+ // We can skip child alignment if we're just measuring the container.
1864
+ if (performLayout) {
1865
+ for (size_t i = startOfLineIndex; i < endOfLineIndex; i++) {
1866
+ const auto child = node->getChild(i);
1867
+ if (child->getStyle().display() == Display::None) {
1868
+ continue;
1869
+ }
1870
+ if (child->getStyle().positionType() == PositionType::Absolute) {
1871
+ // If the child is absolutely positioned and has a
1872
+ // top/left/bottom/right set, override all the previously computed
1873
+ // positions to set it correctly.
1874
+ const bool isChildLeadingPosDefined =
1875
+ child->isLeadingPositionDefined(crossAxis);
1876
+ if (isChildLeadingPosDefined) {
1877
+ child->setLayoutPosition(
1878
+ child->getLeadingPosition(crossAxis, availableInnerCrossDim)
1879
+ .unwrap() +
1880
+ node->getLeadingBorder(crossAxis) +
1881
+ child->getLeadingMargin(crossAxis, availableInnerWidth)
1882
+ .unwrap(),
1883
+ leadingEdge(crossAxis));
1884
+ }
1885
+ // If leading position is not defined or calculations result in Nan,
1886
+ // default to border + margin
1887
+ if (!isChildLeadingPosDefined ||
1888
+ yoga::isUndefined(
1889
+ child->getLayout().position[leadingEdge(crossAxis)])) {
1890
+ child->setLayoutPosition(
1891
+ node->getLeadingBorder(crossAxis) +
1892
+ child->getLeadingMargin(crossAxis, availableInnerWidth)
1893
+ .unwrap(),
1894
+ leadingEdge(crossAxis));
1895
+ }
1896
+ } else {
1897
+ float leadingCrossDim = leadingPaddingAndBorderCross;
1898
+
1899
+ // For a relative children, we're either using alignItems (owner) or
1900
+ // alignSelf (child) in order to determine the position in the cross
1901
+ // axis
1902
+ const Align alignItem = resolveChildAlignment(node, child);
1903
+
1904
+ // If the child uses align stretch, we need to lay it out one more
1905
+ // time, this time forcing the cross-axis size to be the computed
1906
+ // cross size for the current line.
1907
+ if (alignItem == Align::Stretch &&
1908
+ child->marginLeadingValue(crossAxis).unit != YGUnitAuto &&
1909
+ child->marginTrailingValue(crossAxis).unit != YGUnitAuto) {
1910
+ // If the child defines a definite size for its cross axis, there's
1911
+ // no need to stretch.
1912
+ if (!styleDefinesDimension(
1913
+ child, crossAxis, availableInnerCrossDim)) {
1914
+ float childMainSize =
1915
+ child->getLayout().measuredDimension(dimension(mainAxis));
1916
+ const auto& childStyle = child->getStyle();
1917
+ float childCrossSize = !childStyle.aspectRatio().isUndefined()
1918
+ ? child->getMarginForAxis(crossAxis, availableInnerWidth)
1919
+ .unwrap() +
1920
+ (isMainAxisRow
1921
+ ? childMainSize / childStyle.aspectRatio().unwrap()
1922
+ : childMainSize * childStyle.aspectRatio().unwrap())
1923
+ : flexLine.layout.crossDim;
1924
+
1925
+ childMainSize +=
1926
+ child->getMarginForAxis(mainAxis, availableInnerWidth)
1927
+ .unwrap();
1928
+
1929
+ MeasureMode childMainMeasureMode = MeasureMode::Exactly;
1930
+ MeasureMode childCrossMeasureMode = MeasureMode::Exactly;
1931
+ constrainMaxSizeForMode(
1932
+ child,
1933
+ mainAxis,
1934
+ availableInnerMainDim,
1935
+ availableInnerWidth,
1936
+ &childMainMeasureMode,
1937
+ &childMainSize);
1938
+ constrainMaxSizeForMode(
1939
+ child,
1940
+ crossAxis,
1941
+ availableInnerCrossDim,
1942
+ availableInnerWidth,
1943
+ &childCrossMeasureMode,
1944
+ &childCrossSize);
1945
+
1946
+ const float childWidth =
1947
+ isMainAxisRow ? childMainSize : childCrossSize;
1948
+ const float childHeight =
1949
+ !isMainAxisRow ? childMainSize : childCrossSize;
1950
+
1951
+ auto alignContent = node->getStyle().alignContent();
1952
+ auto crossAxisDoesNotGrow =
1953
+ alignContent != Align::Stretch && isNodeFlexWrap;
1954
+ const MeasureMode childWidthMeasureMode =
1955
+ yoga::isUndefined(childWidth) ||
1956
+ (!isMainAxisRow && crossAxisDoesNotGrow)
1957
+ ? MeasureMode::Undefined
1958
+ : MeasureMode::Exactly;
1959
+ const MeasureMode childHeightMeasureMode =
1960
+ yoga::isUndefined(childHeight) ||
1961
+ (isMainAxisRow && crossAxisDoesNotGrow)
1962
+ ? MeasureMode::Undefined
1963
+ : MeasureMode::Exactly;
1964
+
1965
+ calculateLayoutInternal(
1966
+ child,
1967
+ childWidth,
1968
+ childHeight,
1969
+ direction,
1970
+ childWidthMeasureMode,
1971
+ childHeightMeasureMode,
1972
+ availableInnerWidth,
1973
+ availableInnerHeight,
1974
+ true,
1975
+ LayoutPassReason::kStretch,
1976
+ layoutMarkerData,
1977
+ depth,
1978
+ generationCount);
1979
+ }
1980
+ } else {
1981
+ const float remainingCrossDim = containerCrossAxis -
1982
+ dimensionWithMargin(child, crossAxis, availableInnerWidth);
1983
+
1984
+ if (child->marginLeadingValue(crossAxis).unit == YGUnitAuto &&
1985
+ child->marginTrailingValue(crossAxis).unit == YGUnitAuto) {
1986
+ leadingCrossDim +=
1987
+ yoga::maxOrDefined(0.0f, remainingCrossDim / 2);
1988
+ } else if (
1989
+ child->marginTrailingValue(crossAxis).unit == YGUnitAuto) {
1990
+ // No-Op
1991
+ } else if (
1992
+ child->marginLeadingValue(crossAxis).unit == YGUnitAuto) {
1993
+ leadingCrossDim += yoga::maxOrDefined(0.0f, remainingCrossDim);
1994
+ } else if (alignItem == Align::FlexStart) {
1995
+ // No-Op
1996
+ } else if (alignItem == Align::Center) {
1997
+ leadingCrossDim += remainingCrossDim / 2;
1998
+ } else {
1999
+ leadingCrossDim += remainingCrossDim;
2000
+ }
2001
+ }
2002
+ // And we apply the position
2003
+ child->setLayoutPosition(
2004
+ child->getLayout().position[leadingEdge(crossAxis)] +
2005
+ totalLineCrossDim + leadingCrossDim,
2006
+ leadingEdge(crossAxis));
2007
+ }
2008
+ }
2009
+ }
2010
+
2011
+ const float appliedCrossGap = lineCount != 0 ? crossAxisGap : 0.0f;
2012
+ totalLineCrossDim += flexLine.layout.crossDim + appliedCrossGap;
2013
+ maxLineMainDim =
2014
+ yoga::maxOrDefined(maxLineMainDim, flexLine.layout.mainDim);
2015
+ }
2016
+
2017
+ // STEP 8: MULTI-LINE CONTENT ALIGNMENT
2018
+ // currentLead stores the size of the cross dim
2019
+ if (performLayout && (isNodeFlexWrap || isBaselineLayout(node))) {
2020
+ float crossDimLead = 0;
2021
+ float currentLead = leadingPaddingAndBorderCross;
2022
+ if (!yoga::isUndefined(availableInnerCrossDim)) {
2023
+ const float remainingAlignContentDim =
2024
+ availableInnerCrossDim - totalLineCrossDim;
2025
+ switch (node->getStyle().alignContent()) {
2026
+ case Align::FlexEnd:
2027
+ currentLead += remainingAlignContentDim;
2028
+ break;
2029
+ case Align::Center:
2030
+ currentLead += remainingAlignContentDim / 2;
2031
+ break;
2032
+ case Align::Stretch:
2033
+ if (availableInnerCrossDim > totalLineCrossDim) {
2034
+ crossDimLead =
2035
+ remainingAlignContentDim / static_cast<float>(lineCount);
2036
+ }
2037
+ break;
2038
+ case Align::SpaceAround:
2039
+ if (availableInnerCrossDim > totalLineCrossDim) {
2040
+ currentLead +=
2041
+ remainingAlignContentDim / (2 * static_cast<float>(lineCount));
2042
+ if (lineCount > 1) {
2043
+ crossDimLead =
2044
+ remainingAlignContentDim / static_cast<float>(lineCount);
2045
+ }
2046
+ } else {
2047
+ currentLead += remainingAlignContentDim / 2;
2048
+ }
2049
+ break;
2050
+ case Align::SpaceBetween:
2051
+ if (availableInnerCrossDim > totalLineCrossDim && lineCount > 1) {
2052
+ crossDimLead =
2053
+ remainingAlignContentDim / static_cast<float>(lineCount - 1);
2054
+ }
2055
+ break;
2056
+ case Align::Auto:
2057
+ case Align::FlexStart:
2058
+ case Align::Baseline:
2059
+ break;
2060
+ }
2061
+ }
2062
+ size_t endIndex = 0;
2063
+ for (size_t i = 0; i < lineCount; i++) {
2064
+ const size_t startIndex = endIndex;
2065
+ size_t ii;
2066
+
2067
+ // compute the line's height and find the endIndex
2068
+ float lineHeight = 0;
2069
+ float maxAscentForCurrentLine = 0;
2070
+ float maxDescentForCurrentLine = 0;
2071
+ for (ii = startIndex; ii < childCount; ii++) {
2072
+ const auto child = node->getChild(ii);
2073
+ if (child->getStyle().display() == Display::None) {
2074
+ continue;
2075
+ }
2076
+ if (child->getStyle().positionType() != PositionType::Absolute) {
2077
+ if (child->getLineIndex() != i) {
2078
+ break;
2079
+ }
2080
+ if (isLayoutDimensionDefined(child, crossAxis)) {
2081
+ lineHeight = yoga::maxOrDefined(
2082
+ lineHeight,
2083
+ child->getLayout().measuredDimension(dimension(crossAxis)) +
2084
+ child->getMarginForAxis(crossAxis, availableInnerWidth)
2085
+ .unwrap());
2086
+ }
2087
+ if (resolveChildAlignment(node, child) == Align::Baseline) {
2088
+ const float ascent = calculateBaseline(child) +
2089
+ child
2090
+ ->getLeadingMargin(
2091
+ FlexDirection::Column, availableInnerWidth)
2092
+ .unwrap();
2093
+ const float descent =
2094
+ child->getLayout().measuredDimension(YGDimensionHeight) +
2095
+ child
2096
+ ->getMarginForAxis(
2097
+ FlexDirection::Column, availableInnerWidth)
2098
+ .unwrap() -
2099
+ ascent;
2100
+ maxAscentForCurrentLine =
2101
+ yoga::maxOrDefined(maxAscentForCurrentLine, ascent);
2102
+ maxDescentForCurrentLine =
2103
+ yoga::maxOrDefined(maxDescentForCurrentLine, descent);
2104
+ lineHeight = yoga::maxOrDefined(
2105
+ lineHeight, maxAscentForCurrentLine + maxDescentForCurrentLine);
2106
+ }
2107
+ }
2108
+ }
2109
+ endIndex = ii;
2110
+ lineHeight += crossDimLead;
2111
+ currentLead += i != 0 ? crossAxisGap : 0;
2112
+
2113
+ if (performLayout) {
2114
+ for (ii = startIndex; ii < endIndex; ii++) {
2115
+ const auto child = node->getChild(ii);
2116
+ if (child->getStyle().display() == Display::None) {
2117
+ continue;
2118
+ }
2119
+ if (child->getStyle().positionType() != PositionType::Absolute) {
2120
+ switch (resolveChildAlignment(node, child)) {
2121
+ case Align::FlexStart: {
2122
+ child->setLayoutPosition(
2123
+ currentLead +
2124
+ child->getLeadingMargin(crossAxis, availableInnerWidth)
2125
+ .unwrap(),
2126
+ leadingEdge(crossAxis));
2127
+ break;
2128
+ }
2129
+ case Align::FlexEnd: {
2130
+ child->setLayoutPosition(
2131
+ currentLead + lineHeight -
2132
+ child->getTrailingMargin(crossAxis, availableInnerWidth)
2133
+ .unwrap() -
2134
+ child->getLayout().measuredDimension(
2135
+ dimension(crossAxis)),
2136
+ leadingEdge(crossAxis));
2137
+ break;
2138
+ }
2139
+ case Align::Center: {
2140
+ float childHeight =
2141
+ child->getLayout().measuredDimension(dimension(crossAxis));
2142
+
2143
+ child->setLayoutPosition(
2144
+ currentLead + (lineHeight - childHeight) / 2,
2145
+ leadingEdge(crossAxis));
2146
+ break;
2147
+ }
2148
+ case Align::Stretch: {
2149
+ child->setLayoutPosition(
2150
+ currentLead +
2151
+ child->getLeadingMargin(crossAxis, availableInnerWidth)
2152
+ .unwrap(),
2153
+ leadingEdge(crossAxis));
2154
+
2155
+ // Remeasure child with the line height as it as been only
2156
+ // measured with the owners height yet.
2157
+ if (!styleDefinesDimension(
2158
+ child, crossAxis, availableInnerCrossDim)) {
2159
+ const float childWidth = isMainAxisRow
2160
+ ? (child->getLayout().measuredDimension(
2161
+ YGDimensionWidth) +
2162
+ child->getMarginForAxis(mainAxis, availableInnerWidth)
2163
+ .unwrap())
2164
+ : lineHeight;
2165
+
2166
+ const float childHeight = !isMainAxisRow
2167
+ ? (child->getLayout().measuredDimension(
2168
+ YGDimensionHeight) +
2169
+ child->getMarginForAxis(crossAxis, availableInnerWidth)
2170
+ .unwrap())
2171
+ : lineHeight;
2172
+
2173
+ if (!(yoga::inexactEquals(
2174
+ childWidth,
2175
+ child->getLayout().measuredDimension(
2176
+ YGDimensionWidth)) &&
2177
+ yoga::inexactEquals(
2178
+ childHeight,
2179
+ child->getLayout().measuredDimension(
2180
+ YGDimensionHeight)))) {
2181
+ calculateLayoutInternal(
2182
+ child,
2183
+ childWidth,
2184
+ childHeight,
2185
+ direction,
2186
+ MeasureMode::Exactly,
2187
+ MeasureMode::Exactly,
2188
+ availableInnerWidth,
2189
+ availableInnerHeight,
2190
+ true,
2191
+ LayoutPassReason::kMultilineStretch,
2192
+ layoutMarkerData,
2193
+ depth,
2194
+ generationCount);
2195
+ }
2196
+ }
2197
+ break;
2198
+ }
2199
+ case Align::Baseline: {
2200
+ child->setLayoutPosition(
2201
+ currentLead + maxAscentForCurrentLine -
2202
+ calculateBaseline(child) +
2203
+ child
2204
+ ->getLeadingPosition(
2205
+ FlexDirection::Column, availableInnerCrossDim)
2206
+ .unwrap(),
2207
+ YGEdgeTop);
2208
+
2209
+ break;
2210
+ }
2211
+ case Align::Auto:
2212
+ case Align::SpaceBetween:
2213
+ case Align::SpaceAround:
2214
+ break;
2215
+ }
2216
+ }
2217
+ }
2218
+ }
2219
+ currentLead += lineHeight;
2220
+ }
2221
+ }
2222
+
2223
+ // STEP 9: COMPUTING FINAL DIMENSIONS
2224
+
2225
+ node->setLayoutMeasuredDimension(
2226
+ boundAxis(
2227
+ node,
2228
+ FlexDirection::Row,
2229
+ availableWidth - marginAxisRow,
2230
+ ownerWidth,
2231
+ ownerWidth),
2232
+ YGDimensionWidth);
2233
+
2234
+ node->setLayoutMeasuredDimension(
2235
+ boundAxis(
2236
+ node,
2237
+ FlexDirection::Column,
2238
+ availableHeight - marginAxisColumn,
2239
+ ownerHeight,
2240
+ ownerWidth),
2241
+ YGDimensionHeight);
2242
+
2243
+ // If the user didn't specify a width or height for the node, set the
2244
+ // dimensions based on the children.
2245
+ if (measureModeMainDim == MeasureMode::Undefined ||
2246
+ (node->getStyle().overflow() != Overflow::Scroll &&
2247
+ measureModeMainDim == MeasureMode::AtMost)) {
2248
+ // Clamp the size to the min/max size, if specified, and make sure it
2249
+ // doesn't go below the padding and border amount.
2250
+ node->setLayoutMeasuredDimension(
2251
+ boundAxis(
2252
+ node, mainAxis, maxLineMainDim, mainAxisownerSize, ownerWidth),
2253
+ dimension(mainAxis));
2254
+
2255
+ } else if (
2256
+ measureModeMainDim == MeasureMode::AtMost &&
2257
+ node->getStyle().overflow() == Overflow::Scroll) {
2258
+ node->setLayoutMeasuredDimension(
2259
+ yoga::maxOrDefined(
2260
+ yoga::minOrDefined(
2261
+ availableInnerMainDim + paddingAndBorderAxisMain,
2262
+ boundAxisWithinMinAndMax(
2263
+ node,
2264
+ mainAxis,
2265
+ FloatOptional{maxLineMainDim},
2266
+ mainAxisownerSize)
2267
+ .unwrap()),
2268
+ paddingAndBorderAxisMain),
2269
+ dimension(mainAxis));
2270
+ }
2271
+
2272
+ if (measureModeCrossDim == MeasureMode::Undefined ||
2273
+ (node->getStyle().overflow() != Overflow::Scroll &&
2274
+ measureModeCrossDim == MeasureMode::AtMost)) {
2275
+ // Clamp the size to the min/max size, if specified, and make sure it
2276
+ // doesn't go below the padding and border amount.
2277
+ node->setLayoutMeasuredDimension(
2278
+ boundAxis(
2279
+ node,
2280
+ crossAxis,
2281
+ totalLineCrossDim + paddingAndBorderAxisCross,
2282
+ crossAxisownerSize,
2283
+ ownerWidth),
2284
+ dimension(crossAxis));
2285
+
2286
+ } else if (
2287
+ measureModeCrossDim == MeasureMode::AtMost &&
2288
+ node->getStyle().overflow() == Overflow::Scroll) {
2289
+ node->setLayoutMeasuredDimension(
2290
+ yoga::maxOrDefined(
2291
+ yoga::minOrDefined(
2292
+ availableInnerCrossDim + paddingAndBorderAxisCross,
2293
+ boundAxisWithinMinAndMax(
2294
+ node,
2295
+ crossAxis,
2296
+ FloatOptional{
2297
+ totalLineCrossDim + paddingAndBorderAxisCross},
2298
+ crossAxisownerSize)
2299
+ .unwrap()),
2300
+ paddingAndBorderAxisCross),
2301
+ dimension(crossAxis));
2302
+ }
2303
+
2304
+ // As we only wrapped in normal direction yet, we need to reverse the
2305
+ // positions on wrap-reverse.
2306
+ if (performLayout && node->getStyle().flexWrap() == Wrap::WrapReverse) {
2307
+ for (size_t i = 0; i < childCount; i++) {
2308
+ const auto child = node->getChild(i);
2309
+ if (child->getStyle().positionType() != PositionType::Absolute) {
2310
+ child->setLayoutPosition(
2311
+ node->getLayout().measuredDimension(dimension(crossAxis)) -
2312
+ child->getLayout().position[leadingEdge(crossAxis)] -
2313
+ child->getLayout().measuredDimension(dimension(crossAxis)),
2314
+ leadingEdge(crossAxis));
2315
+ }
2316
+ }
2317
+ }
2318
+
2319
+ if (performLayout) {
2320
+ // STEP 10: SIZING AND POSITIONING ABSOLUTE CHILDREN
2321
+ for (auto child : node->getChildren()) {
2322
+ if (child->getStyle().display() == Display::None ||
2323
+ child->getStyle().positionType() != PositionType::Absolute) {
2324
+ continue;
2325
+ }
2326
+ const bool absolutePercentageAgainstPaddingEdge =
2327
+ node->getConfig()->isExperimentalFeatureEnabled(
2328
+ ExperimentalFeature::AbsolutePercentageAgainstPaddingEdge);
2329
+
2330
+ layoutAbsoluteChild(
2331
+ node,
2332
+ child,
2333
+ absolutePercentageAgainstPaddingEdge
2334
+ ? node->getLayout().measuredDimension(YGDimensionWidth)
2335
+ : availableInnerWidth,
2336
+ isMainAxisRow ? measureModeMainDim : measureModeCrossDim,
2337
+ absolutePercentageAgainstPaddingEdge
2338
+ ? node->getLayout().measuredDimension(YGDimensionHeight)
2339
+ : availableInnerHeight,
2340
+ direction,
2341
+ layoutMarkerData,
2342
+ depth,
2343
+ generationCount);
2344
+ }
2345
+
2346
+ // STEP 11: SETTING TRAILING POSITIONS FOR CHILDREN
2347
+ const bool needsMainTrailingPos = mainAxis == FlexDirection::RowReverse ||
2348
+ mainAxis == FlexDirection::ColumnReverse;
2349
+ const bool needsCrossTrailingPos = crossAxis == FlexDirection::RowReverse ||
2350
+ crossAxis == FlexDirection::ColumnReverse;
2351
+
2352
+ // Set trailing position if necessary.
2353
+ if (needsMainTrailingPos || needsCrossTrailingPos) {
2354
+ for (size_t i = 0; i < childCount; i++) {
2355
+ const auto child = node->getChild(i);
2356
+ if (child->getStyle().display() == Display::None) {
2357
+ continue;
2358
+ }
2359
+ if (needsMainTrailingPos) {
2360
+ setChildTrailingPosition(node, child, mainAxis);
2361
+ }
2362
+
2363
+ if (needsCrossTrailingPos) {
2364
+ setChildTrailingPosition(node, child, crossAxis);
2365
+ }
2366
+ }
2367
+ }
2368
+ }
2369
+ }
2370
+
2371
+ bool gPrintChanges = false;
2372
+ bool gPrintSkips = false;
2373
+
2374
+ static const char* spacer =
2375
+ " ";
2376
+
2377
+ static const char* spacerWithLength(const unsigned long level) {
2378
+ const size_t spacerLen = strlen(spacer);
2379
+ if (level > spacerLen) {
2380
+ return &spacer[0];
2381
+ } else {
2382
+ return &spacer[spacerLen - level];
2383
+ }
2384
+ }
2385
+
2386
+ static const char* measureModeName(
2387
+ const MeasureMode mode,
2388
+ const bool performLayout) {
2389
+ switch (mode) {
2390
+ case MeasureMode::Undefined:
2391
+ return performLayout ? "LAY_UNDEFINED" : "UNDEFINED";
2392
+ case MeasureMode::Exactly:
2393
+ return performLayout ? "LAY_EXACTLY" : "EXACTLY";
2394
+ case MeasureMode::AtMost:
2395
+ return performLayout ? "LAY_AT_MOST" : "AT_MOST";
2396
+ }
2397
+ return "";
2398
+ }
2399
+
2400
+ //
2401
+ // This is a wrapper around the calculateLayoutImpl function. It determines
2402
+ // whether the layout request is redundant and can be skipped.
2403
+ //
2404
+ // Parameters:
2405
+ // Input parameters are the same as calculateLayoutImpl (see above)
2406
+ // Return parameter is true if layout was performed, false if skipped
2407
+ //
2408
+ bool calculateLayoutInternal(
2409
+ yoga::Node* const node,
2410
+ const float availableWidth,
2411
+ const float availableHeight,
2412
+ const Direction ownerDirection,
2413
+ const MeasureMode widthMeasureMode,
2414
+ const MeasureMode heightMeasureMode,
2415
+ const float ownerWidth,
2416
+ const float ownerHeight,
2417
+ const bool performLayout,
2418
+ const LayoutPassReason reason,
2419
+ LayoutData& layoutMarkerData,
2420
+ uint32_t depth,
2421
+ const uint32_t generationCount) {
2422
+ LayoutResults* layout = &node->getLayout();
2423
+
2424
+ depth++;
2425
+
2426
+ const bool needToVisitNode =
2427
+ (node->isDirty() && layout->generationCount != generationCount) ||
2428
+ layout->lastOwnerDirection != ownerDirection;
2429
+
2430
+ if (needToVisitNode) {
2431
+ // Invalidate the cached results.
2432
+ layout->nextCachedMeasurementsIndex = 0;
2433
+ layout->cachedLayout.availableWidth = -1;
2434
+ layout->cachedLayout.availableHeight = -1;
2435
+ layout->cachedLayout.widthMeasureMode = MeasureMode::Undefined;
2436
+ layout->cachedLayout.heightMeasureMode = MeasureMode::Undefined;
2437
+ layout->cachedLayout.computedWidth = -1;
2438
+ layout->cachedLayout.computedHeight = -1;
2439
+ }
2440
+
2441
+ CachedMeasurement* cachedResults = nullptr;
2442
+
2443
+ // Determine whether the results are already cached. We maintain a separate
2444
+ // cache for layouts and measurements. A layout operation modifies the
2445
+ // positions and dimensions for nodes in the subtree. The algorithm assumes
2446
+ // that each node gets laid out a maximum of one time per tree layout, but
2447
+ // multiple measurements may be required to resolve all of the flex
2448
+ // dimensions. We handle nodes with measure functions specially here because
2449
+ // they are the most expensive to measure, so it's worth avoiding redundant
2450
+ // measurements if at all possible.
2451
+ if (node->hasMeasureFunc()) {
2452
+ const float marginAxisRow =
2453
+ node->getMarginForAxis(FlexDirection::Row, ownerWidth).unwrap();
2454
+ const float marginAxisColumn =
2455
+ node->getMarginForAxis(FlexDirection::Column, ownerWidth).unwrap();
2456
+
2457
+ // First, try to use the layout cache.
2458
+ if (canUseCachedMeasurement(
2459
+ widthMeasureMode,
2460
+ availableWidth,
2461
+ heightMeasureMode,
2462
+ availableHeight,
2463
+ layout->cachedLayout.widthMeasureMode,
2464
+ layout->cachedLayout.availableWidth,
2465
+ layout->cachedLayout.heightMeasureMode,
2466
+ layout->cachedLayout.availableHeight,
2467
+ layout->cachedLayout.computedWidth,
2468
+ layout->cachedLayout.computedHeight,
2469
+ marginAxisRow,
2470
+ marginAxisColumn,
2471
+ node->getConfig())) {
2472
+ cachedResults = &layout->cachedLayout;
2473
+ } else {
2474
+ // Try to use the measurement cache.
2475
+ for (size_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) {
2476
+ if (canUseCachedMeasurement(
2477
+ widthMeasureMode,
2478
+ availableWidth,
2479
+ heightMeasureMode,
2480
+ availableHeight,
2481
+ layout->cachedMeasurements[i].widthMeasureMode,
2482
+ layout->cachedMeasurements[i].availableWidth,
2483
+ layout->cachedMeasurements[i].heightMeasureMode,
2484
+ layout->cachedMeasurements[i].availableHeight,
2485
+ layout->cachedMeasurements[i].computedWidth,
2486
+ layout->cachedMeasurements[i].computedHeight,
2487
+ marginAxisRow,
2488
+ marginAxisColumn,
2489
+ node->getConfig())) {
2490
+ cachedResults = &layout->cachedMeasurements[i];
2491
+ break;
2492
+ }
2493
+ }
2494
+ }
2495
+ } else if (performLayout) {
2496
+ if (yoga::inexactEquals(
2497
+ layout->cachedLayout.availableWidth, availableWidth) &&
2498
+ yoga::inexactEquals(
2499
+ layout->cachedLayout.availableHeight, availableHeight) &&
2500
+ layout->cachedLayout.widthMeasureMode == widthMeasureMode &&
2501
+ layout->cachedLayout.heightMeasureMode == heightMeasureMode) {
2502
+ cachedResults = &layout->cachedLayout;
2503
+ }
2504
+ } else {
2505
+ for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) {
2506
+ if (yoga::inexactEquals(
2507
+ layout->cachedMeasurements[i].availableWidth, availableWidth) &&
2508
+ yoga::inexactEquals(
2509
+ layout->cachedMeasurements[i].availableHeight, availableHeight) &&
2510
+ layout->cachedMeasurements[i].widthMeasureMode == widthMeasureMode &&
2511
+ layout->cachedMeasurements[i].heightMeasureMode ==
2512
+ heightMeasureMode) {
2513
+ cachedResults = &layout->cachedMeasurements[i];
2514
+ break;
2515
+ }
2516
+ }
2517
+ }
2518
+
2519
+ if (!needToVisitNode && cachedResults != nullptr) {
2520
+ layout->setMeasuredDimension(
2521
+ YGDimensionWidth, cachedResults->computedWidth);
2522
+ layout->setMeasuredDimension(
2523
+ YGDimensionHeight, cachedResults->computedHeight);
2524
+
2525
+ (performLayout ? layoutMarkerData.cachedLayouts
2526
+ : layoutMarkerData.cachedMeasures) += 1;
2527
+
2528
+ if (gPrintChanges && gPrintSkips) {
2529
+ yoga::log(
2530
+ node,
2531
+ LogLevel::Verbose,
2532
+ "%s%d.{[skipped] ",
2533
+ spacerWithLength(depth),
2534
+ depth);
2535
+ node->print();
2536
+ yoga::log(
2537
+ node,
2538
+ LogLevel::Verbose,
2539
+ "wm: %s, hm: %s, aw: %f ah: %f => d: (%f, %f) %s\n",
2540
+ measureModeName(widthMeasureMode, performLayout),
2541
+ measureModeName(heightMeasureMode, performLayout),
2542
+ availableWidth,
2543
+ availableHeight,
2544
+ cachedResults->computedWidth,
2545
+ cachedResults->computedHeight,
2546
+ LayoutPassReasonToString(reason));
2547
+ }
2548
+ } else {
2549
+ if (gPrintChanges) {
2550
+ yoga::log(
2551
+ node,
2552
+ LogLevel::Verbose,
2553
+ "%s%d.{%s",
2554
+ spacerWithLength(depth),
2555
+ depth,
2556
+ needToVisitNode ? "*" : "");
2557
+ node->print();
2558
+ yoga::log(
2559
+ node,
2560
+ LogLevel::Verbose,
2561
+ "wm: %s, hm: %s, aw: %f ah: %f %s\n",
2562
+ measureModeName(widthMeasureMode, performLayout),
2563
+ measureModeName(heightMeasureMode, performLayout),
2564
+ availableWidth,
2565
+ availableHeight,
2566
+ LayoutPassReasonToString(reason));
2567
+ }
2568
+
2569
+ calculateLayoutImpl(
2570
+ node,
2571
+ availableWidth,
2572
+ availableHeight,
2573
+ ownerDirection,
2574
+ widthMeasureMode,
2575
+ heightMeasureMode,
2576
+ ownerWidth,
2577
+ ownerHeight,
2578
+ performLayout,
2579
+ layoutMarkerData,
2580
+ depth,
2581
+ generationCount,
2582
+ reason);
2583
+
2584
+ if (gPrintChanges) {
2585
+ yoga::log(
2586
+ node,
2587
+ LogLevel::Verbose,
2588
+ "%s%d.}%s",
2589
+ spacerWithLength(depth),
2590
+ depth,
2591
+ needToVisitNode ? "*" : "");
2592
+ node->print();
2593
+ yoga::log(
2594
+ node,
2595
+ LogLevel::Verbose,
2596
+ "wm: %s, hm: %s, d: (%f, %f) %s\n",
2597
+ measureModeName(widthMeasureMode, performLayout),
2598
+ measureModeName(heightMeasureMode, performLayout),
2599
+ layout->measuredDimension(YGDimensionWidth),
2600
+ layout->measuredDimension(YGDimensionHeight),
2601
+ LayoutPassReasonToString(reason));
2602
+ }
2603
+
2604
+ layout->lastOwnerDirection = ownerDirection;
2605
+
2606
+ if (cachedResults == nullptr) {
2607
+ layoutMarkerData.maxMeasureCache = std::max(
2608
+ layoutMarkerData.maxMeasureCache,
2609
+ layout->nextCachedMeasurementsIndex + 1u);
2610
+
2611
+ if (layout->nextCachedMeasurementsIndex ==
2612
+ LayoutResults::MaxCachedMeasurements) {
2613
+ if (gPrintChanges) {
2614
+ yoga::log(node, LogLevel::Verbose, "Out of cache entries!\n");
2615
+ }
2616
+ layout->nextCachedMeasurementsIndex = 0;
2617
+ }
2618
+
2619
+ CachedMeasurement* newCacheEntry;
2620
+ if (performLayout) {
2621
+ // Use the single layout cache entry.
2622
+ newCacheEntry = &layout->cachedLayout;
2623
+ } else {
2624
+ // Allocate a new measurement cache entry.
2625
+ newCacheEntry =
2626
+ &layout->cachedMeasurements[layout->nextCachedMeasurementsIndex];
2627
+ layout->nextCachedMeasurementsIndex++;
2628
+ }
2629
+
2630
+ newCacheEntry->availableWidth = availableWidth;
2631
+ newCacheEntry->availableHeight = availableHeight;
2632
+ newCacheEntry->widthMeasureMode = widthMeasureMode;
2633
+ newCacheEntry->heightMeasureMode = heightMeasureMode;
2634
+ newCacheEntry->computedWidth =
2635
+ layout->measuredDimension(YGDimensionWidth);
2636
+ newCacheEntry->computedHeight =
2637
+ layout->measuredDimension(YGDimensionHeight);
2638
+ }
2639
+ }
2640
+
2641
+ if (performLayout) {
2642
+ node->setLayoutDimension(
2643
+ node->getLayout().measuredDimension(YGDimensionWidth),
2644
+ YGDimensionWidth);
2645
+ node->setLayoutDimension(
2646
+ node->getLayout().measuredDimension(YGDimensionHeight),
2647
+ YGDimensionHeight);
2648
+
2649
+ node->setHasNewLayout(true);
2650
+ node->setDirty(false);
2651
+ }
2652
+
2653
+ layout->generationCount = generationCount;
2654
+
2655
+ LayoutType layoutType;
2656
+ if (performLayout) {
2657
+ layoutType = !needToVisitNode && cachedResults == &layout->cachedLayout
2658
+ ? LayoutType::kCachedLayout
2659
+ : LayoutType::kLayout;
2660
+ } else {
2661
+ layoutType = cachedResults != nullptr ? LayoutType::kCachedMeasure
2662
+ : LayoutType::kMeasure;
2663
+ }
2664
+ Event::publish<Event::NodeLayout>(node, {layoutType});
2665
+
2666
+ return (needToVisitNode || cachedResults == nullptr);
2667
+ }
2668
+
2669
+ void calculateLayout(
2670
+ yoga::Node* const node,
2671
+ const float ownerWidth,
2672
+ const float ownerHeight,
2673
+ const Direction ownerDirection) {
2674
+ Event::publish<Event::LayoutPassStart>(node);
2675
+ LayoutData markerData = {};
2676
+
2677
+ // Increment the generation count. This will force the recursive routine to
2678
+ // visit all dirty nodes at least once. Subsequent visits will be skipped if
2679
+ // the input parameters don't change.
2680
+ gCurrentGenerationCount.fetch_add(1, std::memory_order_relaxed);
2681
+ node->resolveDimension();
2682
+ float width = YGUndefined;
2683
+ MeasureMode widthMeasureMode = MeasureMode::Undefined;
2684
+ const auto& style = node->getStyle();
2685
+ if (styleDefinesDimension(node, FlexDirection::Row, ownerWidth)) {
2686
+ width = (yoga::resolveValue(
2687
+ node->getResolvedDimension(dimension(FlexDirection::Row)),
2688
+ ownerWidth) +
2689
+ node->getMarginForAxis(FlexDirection::Row, ownerWidth))
2690
+ .unwrap();
2691
+ widthMeasureMode = MeasureMode::Exactly;
2692
+ } else if (!yoga::resolveValue(
2693
+ style.maxDimension(YGDimensionWidth), ownerWidth)
2694
+ .isUndefined()) {
2695
+ width = yoga::resolveValue(style.maxDimension(YGDimensionWidth), ownerWidth)
2696
+ .unwrap();
2697
+ widthMeasureMode = MeasureMode::AtMost;
2698
+ } else {
2699
+ width = ownerWidth;
2700
+ widthMeasureMode = yoga::isUndefined(width) ? MeasureMode::Undefined
2701
+ : MeasureMode::Exactly;
2702
+ }
2703
+
2704
+ float height = YGUndefined;
2705
+ MeasureMode heightMeasureMode = MeasureMode::Undefined;
2706
+ if (styleDefinesDimension(node, FlexDirection::Column, ownerHeight)) {
2707
+ height = (yoga::resolveValue(
2708
+ node->getResolvedDimension(dimension(FlexDirection::Column)),
2709
+ ownerHeight) +
2710
+ node->getMarginForAxis(FlexDirection::Column, ownerWidth))
2711
+ .unwrap();
2712
+ heightMeasureMode = MeasureMode::Exactly;
2713
+ } else if (!yoga::resolveValue(
2714
+ style.maxDimension(YGDimensionHeight), ownerHeight)
2715
+ .isUndefined()) {
2716
+ height =
2717
+ yoga::resolveValue(style.maxDimension(YGDimensionHeight), ownerHeight)
2718
+ .unwrap();
2719
+ heightMeasureMode = MeasureMode::AtMost;
2720
+ } else {
2721
+ height = ownerHeight;
2722
+ heightMeasureMode = yoga::isUndefined(height) ? MeasureMode::Undefined
2723
+ : MeasureMode::Exactly;
2724
+ }
2725
+ if (calculateLayoutInternal(
2726
+ node,
2727
+ width,
2728
+ height,
2729
+ ownerDirection,
2730
+ widthMeasureMode,
2731
+ heightMeasureMode,
2732
+ ownerWidth,
2733
+ ownerHeight,
2734
+ true,
2735
+ LayoutPassReason::kInitial,
2736
+ markerData,
2737
+ 0, // tree root
2738
+ gCurrentGenerationCount.load(std::memory_order_relaxed))) {
2739
+ node->setPosition(
2740
+ node->getLayout().direction(), ownerWidth, ownerHeight, ownerWidth);
2741
+ roundLayoutResultsToPixelGrid(node, 0.0f, 0.0f);
2742
+
2743
+ #ifdef DEBUG
2744
+ if (node->getConfig()->shouldPrintTree()) {
2745
+ yoga::print(
2746
+ node,
2747
+ PrintOptions::Layout | PrintOptions::Children | PrintOptions::Style);
2748
+ }
2749
+ #endif
2750
+ }
2751
+
2752
+ Event::publish<Event::LayoutPassEnd>(node, {&markerData});
2753
+ }
2754
+
2755
+ } // namespace facebook::yoga