1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (c) Meta Platforms, Inc. and affiliates.
3//
4// SPDX-License-Identifier: MIT
5
6#include "YGNode.h"
7#include <algorithm>
8#include <iostream>
9#include "Utils.h"
10
11using namespace facebook;
12using facebook::yoga::detail::CompactValue;
13
14YGNode::YGNode(const YGConfigRef config) : config_{config} {
15 YGAssert(
16 condition: config != nullptr, message: "Attempting to construct YGNode with null config");
17
18 flags_.hasNewLayout = true;
19 if (config->useWebDefaults()) {
20 useWebDefaults();
21 }
22}
23
24YGNode::YGNode(YGNode&& node) {
25 context_ = node.context_;
26 flags_ = node.flags_;
27 measure_ = node.measure_;
28 baseline_ = node.baseline_;
29 print_ = node.print_;
30 dirtied_ = node.dirtied_;
31 style_ = node.style_;
32 layout_ = node.layout_;
33 lineIndex_ = node.lineIndex_;
34 owner_ = node.owner_;
35 children_ = std::move(node.children_);
36 config_ = node.config_;
37 resolvedDimensions_ = node.resolvedDimensions_;
38 for (auto c : children_) {
39 c->setOwner(this);
40 }
41}
42
43void YGNode::print(void* printContext) {
44 if (print_.noContext != nullptr) {
45 if (flags_.printUsesContext) {
46 print_.withContext(this, printContext);
47 } else {
48 print_.noContext(this);
49 }
50 }
51}
52
53CompactValue YGNode::computeEdgeValueForRow(
54 const YGStyle::Edges& edges,
55 YGEdge rowEdge,
56 YGEdge edge,
57 CompactValue defaultValue) {
58 if (!edges[rowEdge].isUndefined()) {
59 return edges[rowEdge];
60 } else if (!edges[edge].isUndefined()) {
61 return edges[edge];
62 } else if (!edges[YGEdgeHorizontal].isUndefined()) {
63 return edges[YGEdgeHorizontal];
64 } else if (!edges[YGEdgeAll].isUndefined()) {
65 return edges[YGEdgeAll];
66 } else {
67 return defaultValue;
68 }
69}
70
71CompactValue YGNode::computeEdgeValueForColumn(
72 const YGStyle::Edges& edges,
73 YGEdge edge,
74 CompactValue defaultValue) {
75 if (!edges[edge].isUndefined()) {
76 return edges[edge];
77 } else if (!edges[YGEdgeVertical].isUndefined()) {
78 return edges[YGEdgeVertical];
79 } else if (!edges[YGEdgeAll].isUndefined()) {
80 return edges[YGEdgeAll];
81 } else {
82 return defaultValue;
83 }
84}
85
86CompactValue YGNode::computeRowGap(
87 const YGStyle::Gutters& gutters,
88 CompactValue defaultValue) {
89 if (!gutters[YGGutterRow].isUndefined()) {
90 return gutters[YGGutterRow];
91 } else if (!gutters[YGGutterAll].isUndefined()) {
92 return gutters[YGGutterAll];
93 } else {
94 return defaultValue;
95 }
96}
97
98CompactValue YGNode::computeColumnGap(
99 const YGStyle::Gutters& gutters,
100 CompactValue defaultValue) {
101 if (!gutters[YGGutterColumn].isUndefined()) {
102 return gutters[YGGutterColumn];
103 } else if (!gutters[YGGutterAll].isUndefined()) {
104 return gutters[YGGutterAll];
105 } else {
106 return defaultValue;
107 }
108}
109
110YGFloatOptional YGNode::getLeadingPosition(
111 const YGFlexDirection axis,
112 const float axisSize) const {
113 auto leadingPosition = YGFlexDirectionIsRow(flexDirection: axis)
114 ? computeEdgeValueForRow(
115 edges: style_.position(),
116 rowEdge: YGEdgeStart,
117 edge: leading[axis],
118 defaultValue: CompactValue::ofZero())
119 : computeEdgeValueForColumn(
120 edges: style_.position(), edge: leading[axis], defaultValue: CompactValue::ofZero());
121 return YGResolveValue(value: leadingPosition, ownerSize: axisSize);
122}
123
124YGFloatOptional YGNode::getTrailingPosition(
125 const YGFlexDirection axis,
126 const float axisSize) const {
127 auto trailingPosition = YGFlexDirectionIsRow(flexDirection: axis)
128 ? computeEdgeValueForRow(
129 edges: style_.position(),
130 rowEdge: YGEdgeEnd,
131 edge: trailing[axis],
132 defaultValue: CompactValue::ofZero())
133 : computeEdgeValueForColumn(
134 edges: style_.position(), edge: trailing[axis], defaultValue: CompactValue::ofZero());
135 return YGResolveValue(value: trailingPosition, ownerSize: axisSize);
136}
137
138bool YGNode::isLeadingPositionDefined(const YGFlexDirection axis) const {
139 auto leadingPosition = YGFlexDirectionIsRow(flexDirection: axis)
140 ? computeEdgeValueForRow(
141 edges: style_.position(),
142 rowEdge: YGEdgeStart,
143 edge: leading[axis],
144 defaultValue: CompactValue::ofUndefined())
145 : computeEdgeValueForColumn(
146 edges: style_.position(), edge: leading[axis], defaultValue: CompactValue::ofUndefined());
147 return !leadingPosition.isUndefined();
148}
149
150bool YGNode::isTrailingPosDefined(const YGFlexDirection axis) const {
151 auto trailingPosition = YGFlexDirectionIsRow(flexDirection: axis)
152 ? computeEdgeValueForRow(
153 edges: style_.position(),
154 rowEdge: YGEdgeEnd,
155 edge: trailing[axis],
156 defaultValue: CompactValue::ofUndefined())
157 : computeEdgeValueForColumn(
158 edges: style_.position(), edge: trailing[axis], defaultValue: CompactValue::ofUndefined());
159 return !trailingPosition.isUndefined();
160}
161
162YGFloatOptional YGNode::getLeadingMargin(
163 const YGFlexDirection axis,
164 const float widthSize) const {
165 auto leadingMargin = YGFlexDirectionIsRow(flexDirection: axis)
166 ? computeEdgeValueForRow(
167 edges: style_.margin(), rowEdge: YGEdgeStart, edge: leading[axis], defaultValue: CompactValue::ofZero())
168 : computeEdgeValueForColumn(
169 edges: style_.margin(), edge: leading[axis], defaultValue: CompactValue::ofZero());
170 return YGResolveValueMargin(value: leadingMargin, ownerSize: widthSize);
171}
172
173YGFloatOptional YGNode::getTrailingMargin(
174 const YGFlexDirection axis,
175 const float widthSize) const {
176 auto trailingMargin = YGFlexDirectionIsRow(flexDirection: axis)
177 ? computeEdgeValueForRow(
178 edges: style_.margin(), rowEdge: YGEdgeEnd, edge: trailing[axis], defaultValue: CompactValue::ofZero())
179 : computeEdgeValueForColumn(
180 edges: style_.margin(), edge: trailing[axis], defaultValue: CompactValue::ofZero());
181 return YGResolveValueMargin(value: trailingMargin, ownerSize: widthSize);
182}
183
184YGFloatOptional YGNode::getMarginForAxis(
185 const YGFlexDirection axis,
186 const float widthSize) const {
187 return getLeadingMargin(axis, widthSize) + getTrailingMargin(axis, widthSize);
188}
189
190YGFloatOptional YGNode::getGapForAxis(
191 const YGFlexDirection axis,
192 const float widthSize) const {
193 auto gap = YGFlexDirectionIsRow(flexDirection: axis)
194 ? computeColumnGap(gutters: style_.gap(), defaultValue: CompactValue::ofZero())
195 : computeRowGap(gutters: style_.gap(), defaultValue: CompactValue::ofZero());
196 return YGResolveValue(value: gap, ownerSize: widthSize);
197}
198
199YGSize YGNode::measure(
200 float width,
201 YGMeasureMode widthMode,
202 float height,
203 YGMeasureMode heightMode,
204 void* layoutContext) {
205 return flags_.measureUsesContext
206 ? measure_.withContext(
207 this, width, widthMode, height, heightMode, layoutContext)
208 : measure_.noContext(this, width, widthMode, height, heightMode);
209}
210
211float YGNode::baseline(float width, float height, void* layoutContext) {
212 return flags_.baselineUsesContext
213 ? baseline_.withContext(this, width, height, layoutContext)
214 : baseline_.noContext(this, width, height);
215}
216
217// Setters
218
219void YGNode::setMeasureFunc(decltype(YGNode::measure_) measureFunc) {
220 if (measureFunc.noContext == nullptr) {
221 // TODO: t18095186 Move nodeType to opt-in function and mark appropriate
222 // places in Litho
223 setNodeType(YGNodeTypeDefault);
224 } else {
225 YGAssertWithNode(
226 node: this,
227 condition: children_.size() == 0,
228 message: "Cannot set measure function: Nodes with measure functions cannot have "
229 "children.");
230 // TODO: t18095186 Move nodeType to opt-in function and mark appropriate
231 // places in Litho
232 setNodeType(YGNodeTypeText);
233 }
234
235 measure_ = measureFunc;
236}
237
238void YGNode::setMeasureFunc(YGMeasureFunc measureFunc) {
239 flags_.measureUsesContext = false;
240 decltype(YGNode::measure_) m;
241 m.noContext = measureFunc;
242 setMeasureFunc(m);
243}
244
245YOGA_EXPORT void YGNode::setMeasureFunc(MeasureWithContextFn measureFunc) {
246 flags_.measureUsesContext = true;
247 decltype(YGNode::measure_) m;
248 m.withContext = measureFunc;
249 setMeasureFunc(m);
250}
251
252void YGNode::replaceChild(YGNodeRef child, uint32_t index) {
253 children_[index] = child;
254}
255
256void YGNode::replaceChild(YGNodeRef oldChild, YGNodeRef newChild) {
257 std::replace(first: children_.begin(), last: children_.end(), old_value: oldChild, new_value: newChild);
258}
259
260void YGNode::insertChild(YGNodeRef child, uint32_t index) {
261 children_.insert(position: children_.begin() + index, x: child);
262}
263
264void YGNode::setConfig(YGConfigRef config) {
265 YGAssert(condition: config != nullptr, message: "Attempting to set a null config on a YGNode");
266 YGAssertWithConfig(
267 config,
268 condition: config->useWebDefaults() == config_->useWebDefaults(),
269 message: "UseWebDefaults may not be changed after constructing a YGNode");
270
271 if (yoga::configUpdateInvalidatesLayout(a: config_, b: config)) {
272 markDirtyAndPropagate();
273 }
274
275 config_ = config;
276}
277
278void YGNode::setDirty(bool isDirty) {
279 if (isDirty == flags_.isDirty) {
280 return;
281 }
282 flags_.isDirty = isDirty;
283 if (isDirty && dirtied_) {
284 dirtied_(this);
285 }
286}
287
288bool YGNode::removeChild(YGNodeRef child) {
289 std::vector<YGNodeRef>::iterator p =
290 std::find(first: children_.begin(), last: children_.end(), val: child);
291 if (p != children_.end()) {
292 children_.erase(position: p);
293 return true;
294 }
295 return false;
296}
297
298void YGNode::removeChild(uint32_t index) {
299 children_.erase(position: children_.begin() + index);
300}
301
302void YGNode::setLayoutDirection(YGDirection direction) {
303 layout_.setDirection(direction);
304}
305
306void YGNode::setLayoutMargin(float margin, int index) {
307 layout_.margin[index] = margin;
308}
309
310void YGNode::setLayoutBorder(float border, int index) {
311 layout_.border[index] = border;
312}
313
314void YGNode::setLayoutPadding(float padding, int index) {
315 layout_.padding[index] = padding;
316}
317
318void YGNode::setLayoutLastOwnerDirection(YGDirection direction) {
319 layout_.lastOwnerDirection = direction;
320}
321
322void YGNode::setLayoutComputedFlexBasis(
323 const YGFloatOptional computedFlexBasis) {
324 layout_.computedFlexBasis = computedFlexBasis;
325}
326
327void YGNode::setLayoutPosition(float position, int index) {
328 layout_.position[index] = position;
329}
330
331void YGNode::setLayoutComputedFlexBasisGeneration(
332 uint32_t computedFlexBasisGeneration) {
333 layout_.computedFlexBasisGeneration = computedFlexBasisGeneration;
334}
335
336void YGNode::setLayoutMeasuredDimension(float measuredDimension, int index) {
337 layout_.measuredDimensions[index] = measuredDimension;
338}
339
340void YGNode::setLayoutHadOverflow(bool hadOverflow) {
341 layout_.setHadOverflow(hadOverflow);
342}
343
344void YGNode::setLayoutDimension(float dimension, int index) {
345 layout_.dimensions[index] = dimension;
346}
347
348// If both left and right are defined, then use left. Otherwise return +left or
349// -right depending on which is defined.
350YGFloatOptional YGNode::relativePosition(
351 const YGFlexDirection axis,
352 const float axisSize) const {
353 if (isLeadingPositionDefined(axis)) {
354 return getLeadingPosition(axis, axisSize);
355 }
356
357 YGFloatOptional trailingPosition = getTrailingPosition(axis, axisSize);
358 if (!trailingPosition.isUndefined()) {
359 trailingPosition = YGFloatOptional{-1 * trailingPosition.unwrap()};
360 }
361 return trailingPosition;
362}
363
364void YGNode::setPosition(
365 const YGDirection direction,
366 const float mainSize,
367 const float crossSize,
368 const float ownerWidth) {
369 /* Root nodes should be always layouted as LTR, so we don't return negative
370 * values. */
371 const YGDirection directionRespectingRoot =
372 owner_ != nullptr ? direction : YGDirectionLTR;
373 const YGFlexDirection mainAxis =
374 YGResolveFlexDirection(flexDirection: style_.flexDirection(), direction: directionRespectingRoot);
375 const YGFlexDirection crossAxis =
376 YGFlexDirectionCross(flexDirection: mainAxis, direction: directionRespectingRoot);
377
378 // Here we should check for `YGPositionTypeStatic` and in this case zero inset
379 // properties (left, right, top, bottom, begin, end).
380 // https://www.w3.org/TR/css-position-3/#valdef-position-static
381 const YGFloatOptional relativePositionMain =
382 relativePosition(axis: mainAxis, axisSize: mainSize);
383 const YGFloatOptional relativePositionCross =
384 relativePosition(axis: crossAxis, axisSize: crossSize);
385
386 setLayoutPosition(
387 position: (getLeadingMargin(axis: mainAxis, widthSize: ownerWidth) + relativePositionMain).unwrap(),
388 index: leading[mainAxis]);
389 setLayoutPosition(
390 position: (getTrailingMargin(axis: mainAxis, widthSize: ownerWidth) + relativePositionMain).unwrap(),
391 index: trailing[mainAxis]);
392 setLayoutPosition(
393 position: (getLeadingMargin(axis: crossAxis, widthSize: ownerWidth) + relativePositionCross)
394 .unwrap(),
395 index: leading[crossAxis]);
396 setLayoutPosition(
397 position: (getTrailingMargin(axis: crossAxis, widthSize: ownerWidth) + relativePositionCross)
398 .unwrap(),
399 index: trailing[crossAxis]);
400}
401
402YGValue YGNode::marginLeadingValue(const YGFlexDirection axis) const {
403 if (YGFlexDirectionIsRow(flexDirection: axis) &&
404 !style_.margin()[YGEdgeStart].isUndefined()) {
405 return style_.margin()[YGEdgeStart];
406 } else {
407 return style_.margin()[leading[axis]];
408 }
409}
410
411YGValue YGNode::marginTrailingValue(const YGFlexDirection axis) const {
412 if (YGFlexDirectionIsRow(flexDirection: axis) && !style_.margin()[YGEdgeEnd].isUndefined()) {
413 return style_.margin()[YGEdgeEnd];
414 } else {
415 return style_.margin()[trailing[axis]];
416 }
417}
418
419YGValue YGNode::resolveFlexBasisPtr() const {
420 YGValue flexBasis = style_.flexBasis();
421 if (flexBasis.unit != YGUnitAuto && flexBasis.unit != YGUnitUndefined) {
422 return flexBasis;
423 }
424 if (!style_.flex().isUndefined() && style_.flex().unwrap() > 0.0f) {
425 return config_->useWebDefaults() ? YGValueAuto : YGValueZero;
426 }
427 return YGValueAuto;
428}
429
430void YGNode::resolveDimension() {
431 using namespace yoga;
432 const YGStyle& style = getStyle();
433 for (auto dim : {YGDimensionWidth, YGDimensionHeight}) {
434 if (!style.maxDimensions()[dim].isUndefined() &&
435 YGValueEqual(a: style.maxDimensions()[dim], b: style.minDimensions()[dim])) {
436 resolvedDimensions_[dim] = style.maxDimensions()[dim];
437 } else {
438 resolvedDimensions_[dim] = style.dimensions()[dim];
439 }
440 }
441}
442
443YGDirection YGNode::resolveDirection(const YGDirection ownerDirection) {
444 if (style_.direction() == YGDirectionInherit) {
445 return ownerDirection > YGDirectionInherit ? ownerDirection
446 : YGDirectionLTR;
447 } else {
448 return style_.direction();
449 }
450}
451
452YOGA_EXPORT void YGNode::clearChildren() {
453 children_.clear();
454 children_.shrink_to_fit();
455}
456
457// Other Methods
458
459void YGNode::cloneChildrenIfNeeded(void* cloneContext) {
460 iterChildrenAfterCloningIfNeeded(callback: [](YGNodeRef, void*) {}, cloneContext);
461}
462
463void YGNode::markDirtyAndPropagate() {
464 if (!flags_.isDirty) {
465 setDirty(true);
466 setLayoutComputedFlexBasis(YGFloatOptional());
467 if (owner_) {
468 owner_->markDirtyAndPropagate();
469 }
470 }
471}
472
473void YGNode::markDirtyAndPropagateDownwards() {
474 flags_.isDirty = true;
475 for_each(first: children_.begin(), last: children_.end(), f: [](YGNodeRef childNode) {
476 childNode->markDirtyAndPropagateDownwards();
477 });
478}
479
480float YGNode::resolveFlexGrow() const {
481 // Root nodes flexGrow should always be 0
482 if (owner_ == nullptr) {
483 return 0.0;
484 }
485 if (!style_.flexGrow().isUndefined()) {
486 return style_.flexGrow().unwrap();
487 }
488 if (!style_.flex().isUndefined() && style_.flex().unwrap() > 0.0f) {
489 return style_.flex().unwrap();
490 }
491 return kDefaultFlexGrow;
492}
493
494float YGNode::resolveFlexShrink() const {
495 if (owner_ == nullptr) {
496 return 0.0;
497 }
498 if (!style_.flexShrink().isUndefined()) {
499 return style_.flexShrink().unwrap();
500 }
501 if (!config_->useWebDefaults() && !style_.flex().isUndefined() &&
502 style_.flex().unwrap() < 0.0f) {
503 return -style_.flex().unwrap();
504 }
505 return config_->useWebDefaults() ? kWebDefaultFlexShrink : kDefaultFlexShrink;
506}
507
508bool YGNode::isNodeFlexible() {
509 return (
510 (style_.positionType() != YGPositionTypeAbsolute) &&
511 (resolveFlexGrow() != 0 || resolveFlexShrink() != 0));
512}
513
514float YGNode::getLeadingBorder(const YGFlexDirection axis) const {
515 YGValue leadingBorder = YGFlexDirectionIsRow(flexDirection: axis)
516 ? computeEdgeValueForRow(
517 edges: style_.border(), rowEdge: YGEdgeStart, edge: leading[axis], defaultValue: CompactValue::ofZero())
518 : computeEdgeValueForColumn(
519 edges: style_.border(), edge: leading[axis], defaultValue: CompactValue::ofZero());
520 return fmaxf(x: leadingBorder.value, y: 0.0f);
521}
522
523float YGNode::getTrailingBorder(const YGFlexDirection axis) const {
524 YGValue trailingBorder = YGFlexDirectionIsRow(flexDirection: axis)
525 ? computeEdgeValueForRow(
526 edges: style_.border(), rowEdge: YGEdgeEnd, edge: trailing[axis], defaultValue: CompactValue::ofZero())
527 : computeEdgeValueForColumn(
528 edges: style_.border(), edge: trailing[axis], defaultValue: CompactValue::ofZero());
529 return fmaxf(x: trailingBorder.value, y: 0.0f);
530}
531
532YGFloatOptional YGNode::getLeadingPadding(
533 const YGFlexDirection axis,
534 const float widthSize) const {
535 auto leadingPadding = YGFlexDirectionIsRow(flexDirection: axis)
536 ? computeEdgeValueForRow(
537 edges: style_.padding(),
538 rowEdge: YGEdgeStart,
539 edge: leading[axis],
540 defaultValue: CompactValue::ofZero())
541 : computeEdgeValueForColumn(
542 edges: style_.padding(), edge: leading[axis], defaultValue: CompactValue::ofZero());
543 return YGFloatOptionalMax(
544 op1: YGResolveValue(value: leadingPadding, ownerSize: widthSize), op2: YGFloatOptional(0.0f));
545}
546
547YGFloatOptional YGNode::getTrailingPadding(
548 const YGFlexDirection axis,
549 const float widthSize) const {
550 auto trailingPadding = YGFlexDirectionIsRow(flexDirection: axis)
551 ? computeEdgeValueForRow(
552 edges: style_.padding(), rowEdge: YGEdgeEnd, edge: trailing[axis], defaultValue: CompactValue::ofZero())
553 : computeEdgeValueForColumn(
554 edges: style_.padding(), edge: trailing[axis], defaultValue: CompactValue::ofZero());
555 return YGFloatOptionalMax(
556 op1: YGResolveValue(value: trailingPadding, ownerSize: widthSize), op2: YGFloatOptional(0.0f));
557}
558
559YGFloatOptional YGNode::getLeadingPaddingAndBorder(
560 const YGFlexDirection axis,
561 const float widthSize) const {
562 return getLeadingPadding(axis, widthSize) +
563 YGFloatOptional(getLeadingBorder(axis));
564}
565
566YGFloatOptional YGNode::getTrailingPaddingAndBorder(
567 const YGFlexDirection axis,
568 const float widthSize) const {
569 return getTrailingPadding(axis, widthSize) +
570 YGFloatOptional(getTrailingBorder(axis));
571}
572
573void YGNode::reset() {
574 YGAssertWithNode(
575 node: this,
576 condition: children_.size() == 0,
577 message: "Cannot reset a node which still has children attached");
578 YGAssertWithNode(
579 node: this, condition: owner_ == nullptr, message: "Cannot reset a node still attached to a owner");
580
581 *this = YGNode{getConfig()};
582}
583

source code of qtdeclarative/src/3rdparty/yoga/YGNode.cpp