/src/yoga/yoga/node/Node.h
Line | Count | Source |
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 | | #pragma once |
9 | | |
10 | | #include <cstdint> |
11 | | #include <cstdio> |
12 | | #include <vector> |
13 | | |
14 | | #include <yoga/Yoga.h> |
15 | | #include <yoga/node/LayoutableChildren.h> |
16 | | |
17 | | #include <yoga/config/Config.h> |
18 | | #include <yoga/enums/Dimension.h> |
19 | | #include <yoga/enums/Direction.h> |
20 | | #include <yoga/enums/Edge.h> |
21 | | #include <yoga/enums/Errata.h> |
22 | | #include <yoga/enums/MeasureMode.h> |
23 | | #include <yoga/enums/NodeType.h> |
24 | | #include <yoga/enums/PhysicalEdge.h> |
25 | | #include <yoga/node/LayoutResults.h> |
26 | | #include <yoga/style/Style.h> |
27 | | |
28 | | // Tag struct used to form the opaque YGNodeRef for the public C API |
29 | | struct YGNode {}; |
30 | | |
31 | | namespace facebook::yoga { |
32 | | |
33 | | class YG_EXPORT Node : public ::YGNode { |
34 | | public: |
35 | | using LayoutableChildren = yoga::LayoutableChildren<Node>; |
36 | | Node(); |
37 | | explicit Node(const Config* config); |
38 | | |
39 | | Node(Node&& node) noexcept; |
40 | | |
41 | | // Does not expose true value semantics, as children are not cloned eagerly. |
42 | | // Should we remove this? |
43 | 0 | Node(const Node& node) = default; |
44 | | |
45 | | // assignment means potential leaks of existing children, or alternatively |
46 | | // freeing unowned memory, double free, or freeing stack memory. |
47 | | Node& operator=(const Node&) = delete; |
48 | | |
49 | | // Getters |
50 | 0 | void* getContext() const { |
51 | 0 | return context_; |
52 | 0 | } |
53 | | |
54 | 0 | bool alwaysFormsContainingBlock() const { |
55 | 0 | return alwaysFormsContainingBlock_; |
56 | 0 | } |
57 | | |
58 | 0 | bool getHasNewLayout() const { |
59 | 0 | return hasNewLayout_; |
60 | 0 | } |
61 | | |
62 | 1.66M | NodeType getNodeType() const { |
63 | 1.66M | return nodeType_; |
64 | 1.66M | } |
65 | | |
66 | 48.8M | bool hasMeasureFunc() const noexcept { |
67 | 48.8M | return measureFunc_ != nullptr; |
68 | 48.8M | } |
69 | | |
70 | | YGSize measure( |
71 | | float availableWidth, |
72 | | MeasureMode widthMode, |
73 | | float availableHeight, |
74 | | MeasureMode heightMode); |
75 | | |
76 | 0 | bool hasMinContentMeasureFunc() const noexcept { |
77 | 0 | return minContentMeasureFunc_ != nullptr; |
78 | 0 | } |
79 | | |
80 | | YGSize measureMinContent( |
81 | | float availableWidth, |
82 | | MeasureMode widthMode, |
83 | | float availableHeight, |
84 | | MeasureMode heightMode); |
85 | | |
86 | 0 | FloatOptional getMinContentWidth() const noexcept { |
87 | 0 | return minContentWidth_; |
88 | 0 | } |
89 | | |
90 | 0 | FloatOptional getMinContentHeight() const noexcept { |
91 | 0 | return minContentHeight_; |
92 | 0 | } |
93 | | |
94 | 0 | bool hasBaselineFunc() const noexcept { |
95 | 0 | return baselineFunc_ != nullptr; |
96 | 0 | } |
97 | | |
98 | | float baseline(float width, float height) const; |
99 | | |
100 | | float dimensionWithMargin(FlexDirection axis, float widthSize); |
101 | | |
102 | | bool isLayoutDimensionDefined(FlexDirection axis); |
103 | | |
104 | | /** |
105 | | * Whether the node has a "definite length" along the given axis. |
106 | | * https://www.w3.org/TR/css-sizing-3/#definite |
107 | | */ |
108 | 64.5M | inline bool hasDefiniteLength(Dimension dimension, float ownerSize) { |
109 | 64.5M | auto usedValue = getProcessedDimension(dimension).resolve(ownerSize); |
110 | 64.5M | return usedValue.isDefined() && usedValue.unwrap() >= 0.0f; |
111 | 64.5M | } |
112 | | |
113 | 12.5M | bool hasErrata(Errata errata) const { |
114 | 12.5M | return config_->hasErrata(errata); |
115 | 12.5M | } |
116 | | |
117 | 19.4M | bool hasContentsChildren() const { |
118 | 19.4M | return contentsChildrenCount_ != 0; |
119 | 19.4M | } |
120 | | |
121 | 1.66M | YGDirtiedFunc getDirtiedFunc() const { |
122 | 1.66M | return dirtiedFunc_; |
123 | 1.66M | } |
124 | | |
125 | | // For Performance reasons passing as reference. |
126 | 686M | Style& style() { |
127 | 686M | return style_; |
128 | 686M | } |
129 | | |
130 | 679M | const Style& style() const { |
131 | 679M | return style_; |
132 | 679M | } |
133 | | |
134 | | // For Performance reasons passing as reference. |
135 | 220M | LayoutResults& getLayout() { |
136 | 220M | return layout_; |
137 | 220M | } |
138 | | |
139 | 1.01M | const LayoutResults& getLayout() const { |
140 | 1.01M | return layout_; |
141 | 1.01M | } |
142 | | |
143 | 0 | size_t getLineIndex() const { |
144 | 0 | return lineIndex_; |
145 | 0 | } |
146 | | |
147 | 0 | bool isReferenceBaseline() const { |
148 | 0 | return isReferenceBaseline_; |
149 | 0 | } |
150 | | |
151 | | // returns the Node that owns this Node. An owner is used to identify |
152 | | // the YogaTree that a Node belongs to. This method will return the parent |
153 | | // of the Node when a Node only belongs to one YogaTree or nullptr when |
154 | | // the Node is shared between two or more YogaTrees. |
155 | 23.9M | Node* getOwner() const { |
156 | 23.9M | return owner_; |
157 | 23.9M | } |
158 | | |
159 | 1.66M | const std::vector<Node*>& getChildren() const { |
160 | 1.66M | return children_; |
161 | 1.66M | } |
162 | | |
163 | 103M | Node* getChild(size_t index) const { |
164 | 103M | return children_.at(index); |
165 | 103M | } |
166 | | |
167 | 80.5M | size_t getChildCount() const { |
168 | 80.5M | return children_.size(); |
169 | 80.5M | } |
170 | | |
171 | 32.5M | LayoutableChildren getLayoutChildren() const { |
172 | 32.5M | return LayoutableChildren(this); |
173 | 32.5M | } |
174 | | |
175 | 19.4M | size_t getLayoutChildCount() const { |
176 | 19.4M | if (contentsChildrenCount_ == 0) { |
177 | 19.4M | return children_.size(); |
178 | 19.4M | } else { |
179 | 0 | size_t count = 0; |
180 | 0 | for (auto iter = getLayoutChildren().begin(); |
181 | 0 | iter != getLayoutChildren().end(); |
182 | 0 | iter++) { |
183 | 0 | count++; |
184 | 0 | } |
185 | 0 | return count; |
186 | 0 | } |
187 | 19.4M | } |
188 | | |
189 | 83.9M | const Config* getConfig() const { |
190 | 83.9M | return config_; |
191 | 83.9M | } |
192 | | |
193 | 27.6M | bool isDirty() const { |
194 | 27.6M | return isDirty_; |
195 | 27.6M | } |
196 | | |
197 | 68.0M | Style::SizeLength getProcessedDimension(Dimension dimension) const { |
198 | 68.0M | return processedDimensions_[static_cast<size_t>(dimension)]; |
199 | 68.0M | } |
200 | | |
201 | | FloatOptional getResolvedDimension( |
202 | | Direction direction, |
203 | | Dimension dimension, |
204 | | float referenceLength, |
205 | 2.58M | float ownerWidth) const { |
206 | 2.58M | FloatOptional value = |
207 | 2.58M | getProcessedDimension(dimension).resolve(referenceLength); |
208 | 2.58M | if (style_.boxSizing() == BoxSizing::BorderBox) { |
209 | 2.58M | return value; |
210 | 2.58M | } |
211 | | |
212 | 0 | FloatOptional dimensionPaddingAndBorder = |
213 | 0 | FloatOptional{style_.computePaddingAndBorderForDimension( |
214 | 0 | direction, dimension, ownerWidth)}; |
215 | |
|
216 | 0 | return value + |
217 | 0 | (dimensionPaddingAndBorder.isDefined() ? dimensionPaddingAndBorder |
218 | 0 | : FloatOptional{0.0}); |
219 | 2.58M | } |
220 | | |
221 | | // Setters |
222 | | |
223 | 0 | void setContext(void* context) { |
224 | 0 | context_ = context; |
225 | 0 | } |
226 | | |
227 | 0 | void setAlwaysFormsContainingBlock(bool alwaysFormsContainingBlock) { |
228 | 0 | alwaysFormsContainingBlock_ = alwaysFormsContainingBlock; |
229 | 0 | } |
230 | | |
231 | 1.66M | void setHasNewLayout(bool hasNewLayout) { |
232 | 1.66M | hasNewLayout_ = hasNewLayout; |
233 | 1.66M | } |
234 | | |
235 | 0 | void setNodeType(NodeType nodeType) { |
236 | 0 | nodeType_ = nodeType; |
237 | 0 | } |
238 | | |
239 | | void setMeasureFunc(YGMeasureFunc measureFunc); |
240 | | |
241 | 0 | void setMinContentMeasureFunc(YGMinContentMeasureFunc minContentMeasureFunc) { |
242 | 0 | minContentMeasureFunc_ = minContentMeasureFunc; |
243 | 0 | } |
244 | | |
245 | 0 | void setMinContentWidth(FloatOptional minContentWidth) noexcept { |
246 | 0 | minContentWidth_ = minContentWidth; |
247 | 0 | } |
248 | | |
249 | 0 | void setMinContentHeight(FloatOptional minContentHeight) noexcept { |
250 | 0 | minContentHeight_ = minContentHeight; |
251 | 0 | } |
252 | | |
253 | 0 | void setBaselineFunc(YGBaselineFunc baseLineFunc) { |
254 | 0 | baselineFunc_ = baseLineFunc; |
255 | 0 | } |
256 | | |
257 | 3.33M | void setDirtiedFunc(YGDirtiedFunc dirtiedFunc) { |
258 | 3.33M | dirtiedFunc_ = dirtiedFunc; |
259 | 3.33M | } |
260 | | |
261 | 0 | void setStyle(const Style& style) { |
262 | 0 | style_ = style; |
263 | 0 | } |
264 | | |
265 | 1.66M | void setLayout(const LayoutResults& layout) { |
266 | 1.66M | layout_ = layout; |
267 | 1.66M | } |
268 | | |
269 | 15.5M | void setLineIndex(size_t lineIndex) { |
270 | 15.5M | lineIndex_ = lineIndex; |
271 | 15.5M | } |
272 | | |
273 | 0 | void setIsReferenceBaseline(bool isReferenceBaseline) { |
274 | 0 | isReferenceBaseline_ = isReferenceBaseline; |
275 | 0 | } |
276 | | |
277 | 3.33M | void setOwner(Node* owner) { |
278 | 3.33M | owner_ = owner; |
279 | 3.33M | } |
280 | | |
281 | | // TODO: rvalue override for setChildren |
282 | | |
283 | | void setConfig(Config* config); |
284 | | |
285 | | void setDirty(bool isDirty); |
286 | | void setChildren(const std::vector<Node*>& children); |
287 | | void setLayoutLastOwnerDirection(Direction direction); |
288 | | void setLayoutComputedFlexBasis(FloatOptional computedFlexBasis); |
289 | | void setLayoutComputedFlexBasisGeneration( |
290 | | uint32_t computedFlexBasisGeneration); |
291 | | void setLayoutMeasuredDimension(float measuredDimension, Dimension dimension); |
292 | | void setLayoutHadOverflow(bool hadOverflow); |
293 | | void setLayoutDimension(float lengthValue, Dimension dimension); |
294 | | void setLayoutDirection(Direction direction); |
295 | | void setLayoutMargin(float margin, PhysicalEdge edge); |
296 | | void setLayoutBorder(float border, PhysicalEdge edge); |
297 | | void setLayoutPadding(float padding, PhysicalEdge edge); |
298 | | void setLayoutPosition(float position, PhysicalEdge edge); |
299 | | void setPosition(Direction direction, float ownerWidth, float ownerHeight); |
300 | | |
301 | | // Other methods |
302 | | Style::SizeLength processFlexBasis() const; |
303 | | FloatOptional resolveFlexBasis( |
304 | | Direction direction, |
305 | | FlexDirection flexDirection, |
306 | | float referenceLength, |
307 | | float ownerWidth) const; |
308 | | void processDimensions(); |
309 | | Direction resolveDirection(Direction ownerDirection); |
310 | | void clearChildren(); |
311 | | /// Replaces the occurrences of oldChild with newChild |
312 | | void replaceChild(Node* oldChild, Node* newChild); |
313 | | void replaceChild(Node* child, size_t index); |
314 | | void insertChild(Node* child, size_t index); |
315 | | /// Removes the first occurrence of child |
316 | | bool removeChild(Node* child); |
317 | | void removeChild(size_t index); |
318 | | |
319 | | void cloneChildrenIfNeeded(); |
320 | | void cloneContentsChildrenIfNeeded(); |
321 | | void markDirtyAndPropagate(); |
322 | | float resolveFlexGrow() const; |
323 | | float resolveFlexShrink() const; |
324 | | bool isNodeFlexible(); |
325 | | void reset(); |
326 | | |
327 | | float relativePosition( |
328 | | FlexDirection axis, |
329 | | Direction direction, |
330 | | float axisSize) const; |
331 | | |
332 | | private: |
333 | | // Used to allow resetting the node |
334 | 0 | Node& operator=(Node&&) noexcept = default; |
335 | | |
336 | 0 | void useWebDefaults() { |
337 | 0 | style_.setFlexDirection(FlexDirection::Row); |
338 | 0 | style_.setAlignContent(Align::Stretch); |
339 | 0 | } |
340 | | |
341 | | bool hasNewLayout_ : 1 = true; |
342 | | bool isReferenceBaseline_ : 1 = false; |
343 | | bool isDirty_ : 1 = true; |
344 | | bool alwaysFormsContainingBlock_ : 1 = false; |
345 | | NodeType nodeType_ : bitCount<NodeType>() = NodeType::Default; |
346 | | void* context_ = nullptr; |
347 | | YGMeasureFunc measureFunc_ = nullptr; |
348 | | YGMinContentMeasureFunc minContentMeasureFunc_ = nullptr; |
349 | | FloatOptional minContentWidth_{}; |
350 | | FloatOptional minContentHeight_{}; |
351 | | YGBaselineFunc baselineFunc_ = nullptr; |
352 | | YGDirtiedFunc dirtiedFunc_ = nullptr; |
353 | | Style style_; |
354 | | LayoutResults layout_; |
355 | | size_t lineIndex_ = 0; |
356 | | size_t contentsChildrenCount_ = 0; |
357 | | Node* owner_ = nullptr; |
358 | | std::vector<Node*> children_; |
359 | | const Config* config_; |
360 | | std::array<Style::SizeLength, 2> processedDimensions_{ |
361 | | {StyleSizeLength::undefined(), StyleSizeLength::undefined()}}; |
362 | | }; |
363 | | |
364 | 25.8M | inline Node* resolveRef(const YGNodeRef ref) { |
365 | 25.8M | return static_cast<Node*>(ref); |
366 | 25.8M | } |
367 | | |
368 | 0 | inline const Node* resolveRef(const YGNodeConstRef ref) { |
369 | 0 | return static_cast<const Node*>(ref); |
370 | 0 | } |
371 | | |
372 | | } // namespace facebook::yoga |