| 1 | /* |
| 2 | * Copyright (C) 2016 The Qt Company Ltd. |
| 3 | * Copyright (c) Meta Platforms, Inc. and affiliates. |
| 4 | * |
| 5 | * SPDX-License-Identifier: MIT |
| 6 | */ |
| 7 | |
| 8 | #pragma once |
| 9 | |
| 10 | #include <cstdint> |
| 11 | #include <stdio.h> |
| 12 | #include "CompactValue.h" |
| 13 | #include "YGConfig.h" |
| 14 | #include "YGLayout.h" |
| 15 | #include "YGStyle.h" |
| 16 | #include "Yoga-internal.h" |
| 17 | |
| 18 | YGConfigRef YGConfigGetDefault(); |
| 19 | |
| 20 | #pragma pack(push) |
| 21 | #pragma pack(1) |
| 22 | struct YGNodeFlags { |
| 23 | bool hasNewLayout : 1; |
| 24 | bool isReferenceBaseline : 1; |
| 25 | bool isDirty : 1; |
| 26 | uint8_t nodeType : 1; |
| 27 | bool measureUsesContext : 1; |
| 28 | bool baselineUsesContext : 1; |
| 29 | bool printUsesContext : 1; |
| 30 | }; |
| 31 | #pragma pack(pop) |
| 32 | |
| 33 | QT_YOGA_NAMESPACE_BEGIN |
| 34 | |
| 35 | struct YOGA_EXPORT YGNode { |
| 36 | using MeasureWithContextFn = |
| 37 | YGSize (*)(YGNode*, float, YGMeasureMode, float, YGMeasureMode, void*); |
| 38 | using BaselineWithContextFn = float (*)(YGNode*, float, float, void*); |
| 39 | using PrintWithContextFn = void (*)(YGNode*, void*); |
| 40 | |
| 41 | private: |
| 42 | void* context_ = nullptr; |
| 43 | YGNodeFlags flags_ = {}; |
| 44 | union { |
| 45 | YGMeasureFunc noContext; |
| 46 | MeasureWithContextFn withContext; |
| 47 | } measure_ = {.noContext: nullptr}; |
| 48 | union { |
| 49 | YGBaselineFunc noContext; |
| 50 | BaselineWithContextFn withContext; |
| 51 | } baseline_ = {.noContext: nullptr}; |
| 52 | union { |
| 53 | YGPrintFunc noContext; |
| 54 | PrintWithContextFn withContext; |
| 55 | } print_ = {.noContext: nullptr}; |
| 56 | YGDirtiedFunc dirtied_ = nullptr; |
| 57 | YGStyle style_ = {}; |
| 58 | YGLayout layout_ = {}; |
| 59 | uint32_t lineIndex_ = 0; |
| 60 | YGNodeRef owner_ = nullptr; |
| 61 | YGVector children_ = {}; |
| 62 | YGConfigRef config_; |
| 63 | std::array<YGValue, 2> resolvedDimensions_ = { |
| 64 | ._M_elems: {YGValueUndefined, YGValueUndefined}}; |
| 65 | |
| 66 | YGFloatOptional relativePosition( |
| 67 | const YGFlexDirection axis, |
| 68 | const float axisSize) const; |
| 69 | |
| 70 | void setMeasureFunc(decltype(measure_)); |
| 71 | void setBaselineFunc(decltype(baseline_)); |
| 72 | |
| 73 | void useWebDefaults() { |
| 74 | style_.flexDirection() = YGFlexDirectionRow; |
| 75 | style_.alignContent() = YGAlignStretch; |
| 76 | } |
| 77 | |
| 78 | // DANGER DANGER DANGER! |
| 79 | // If the node assigned to has children, we'd either have to deallocate |
| 80 | // them (potentially incorrect) or ignore them (danger of leaks). Only ever |
| 81 | // use this after checking that there are no children. |
| 82 | // DO NOT CHANGE THE VISIBILITY OF THIS METHOD! |
| 83 | YGNode& operator=(YGNode&&) = default; |
| 84 | |
| 85 | using CompactValue = facebook::yoga::detail::CompactValue; |
| 86 | |
| 87 | public: |
| 88 | YGNode() : YGNode{YGConfigGetDefault()} { flags_.hasNewLayout = true; } |
| 89 | explicit YGNode(const YGConfigRef config); |
| 90 | ~YGNode() = default; // cleanup of owner/children relationships in YGNodeFree |
| 91 | |
| 92 | YGNode(YGNode&&); |
| 93 | |
| 94 | // Does not expose true value semantics, as children are not cloned eagerly. |
| 95 | // Should we remove this? |
| 96 | YGNode(const YGNode& node) = default; |
| 97 | |
| 98 | // assignment means potential leaks of existing children, or alternatively |
| 99 | // freeing unowned memory, double free, or freeing stack memory. |
| 100 | YGNode& operator=(const YGNode&) = delete; |
| 101 | |
| 102 | // Getters |
| 103 | void* getContext() const { return context_; } |
| 104 | |
| 105 | void print(void*); |
| 106 | |
| 107 | bool getHasNewLayout() const { return flags_.hasNewLayout; } |
| 108 | |
| 109 | YGNodeType getNodeType() const { |
| 110 | return static_cast<YGNodeType>(flags_.nodeType); |
| 111 | } |
| 112 | |
| 113 | bool hasMeasureFunc() const noexcept { return measure_.noContext != nullptr; } |
| 114 | |
| 115 | YGSize measure(float, YGMeasureMode, float, YGMeasureMode, void*); |
| 116 | |
| 117 | bool hasBaselineFunc() const noexcept { |
| 118 | return baseline_.noContext != nullptr; |
| 119 | } |
| 120 | |
| 121 | float baseline(float width, float height, void* layoutContext); |
| 122 | |
| 123 | bool hasErrata(YGErrata errata) const { return config_->hasErrata(errata); } |
| 124 | |
| 125 | YGDirtiedFunc getDirtied() const { return dirtied_; } |
| 126 | |
| 127 | // For Performance reasons passing as reference. |
| 128 | YGStyle& getStyle() { return style_; } |
| 129 | |
| 130 | const YGStyle& getStyle() const { return style_; } |
| 131 | |
| 132 | // For Performance reasons passing as reference. |
| 133 | YGLayout& getLayout() { return layout_; } |
| 134 | |
| 135 | const YGLayout& getLayout() const { return layout_; } |
| 136 | |
| 137 | uint32_t getLineIndex() const { return lineIndex_; } |
| 138 | |
| 139 | bool isReferenceBaseline() { return flags_.isReferenceBaseline; } |
| 140 | |
| 141 | // returns the YGNodeRef that owns this YGNode. An owner is used to identify |
| 142 | // the YogaTree that a YGNode belongs to. This method will return the parent |
| 143 | // of the YGNode when a YGNode only belongs to one YogaTree or nullptr when |
| 144 | // the YGNode is shared between two or more YogaTrees. |
| 145 | YGNodeRef getOwner() const { return owner_; } |
| 146 | |
| 147 | // Deprecated, use getOwner() instead. |
| 148 | YGNodeRef getParent() const { return getOwner(); } |
| 149 | |
| 150 | const YGVector& getChildren() const { return children_; } |
| 151 | |
| 152 | // Applies a callback to all children, after cloning them if they are not |
| 153 | // owned. |
| 154 | template <typename T> |
| 155 | void iterChildrenAfterCloningIfNeeded(T callback, void* cloneContext) { |
| 156 | int i = 0; |
| 157 | for (YGNodeRef& child : children_) { |
| 158 | if (child->getOwner() != this) { |
| 159 | child = config_->cloneNode(node: child, owner: this, childIndex: i, cloneContext); |
| 160 | child->setOwner(this); |
| 161 | } |
| 162 | i += 1; |
| 163 | |
| 164 | callback(child, cloneContext); |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | YGNodeRef getChild(uint32_t index) const { return children_.at(n: index); } |
| 169 | |
| 170 | YGConfigRef getConfig() const { return config_; } |
| 171 | |
| 172 | bool isDirty() const { return flags_.isDirty; } |
| 173 | |
| 174 | std::array<YGValue, 2> getResolvedDimensions() const { |
| 175 | return resolvedDimensions_; |
| 176 | } |
| 177 | |
| 178 | YGValue getResolvedDimension(int index) const { |
| 179 | return resolvedDimensions_[index]; |
| 180 | } |
| 181 | |
| 182 | static CompactValue computeEdgeValueForColumn( |
| 183 | const YGStyle::Edges& edges, |
| 184 | YGEdge edge, |
| 185 | CompactValue defaultValue); |
| 186 | |
| 187 | static CompactValue computeEdgeValueForRow( |
| 188 | const YGStyle::Edges& edges, |
| 189 | YGEdge rowEdge, |
| 190 | YGEdge edge, |
| 191 | CompactValue defaultValue); |
| 192 | |
| 193 | static CompactValue computeRowGap( |
| 194 | const YGStyle::Gutters& gutters, |
| 195 | CompactValue defaultValue); |
| 196 | |
| 197 | static CompactValue computeColumnGap( |
| 198 | const YGStyle::Gutters& gutters, |
| 199 | CompactValue defaultValue); |
| 200 | |
| 201 | // Methods related to positions, margin, padding and border |
| 202 | YGFloatOptional getLeadingPosition( |
| 203 | const YGFlexDirection axis, |
| 204 | const float axisSize) const; |
| 205 | bool isLeadingPositionDefined(const YGFlexDirection axis) const; |
| 206 | bool isTrailingPosDefined(const YGFlexDirection axis) const; |
| 207 | YGFloatOptional getTrailingPosition( |
| 208 | const YGFlexDirection axis, |
| 209 | const float axisSize) const; |
| 210 | YGFloatOptional getLeadingMargin( |
| 211 | const YGFlexDirection axis, |
| 212 | const float widthSize) const; |
| 213 | YGFloatOptional getTrailingMargin( |
| 214 | const YGFlexDirection axis, |
| 215 | const float widthSize) const; |
| 216 | float getLeadingBorder(const YGFlexDirection flexDirection) const; |
| 217 | float getTrailingBorder(const YGFlexDirection flexDirection) const; |
| 218 | YGFloatOptional getLeadingPadding( |
| 219 | const YGFlexDirection axis, |
| 220 | const float widthSize) const; |
| 221 | YGFloatOptional getTrailingPadding( |
| 222 | const YGFlexDirection axis, |
| 223 | const float widthSize) const; |
| 224 | YGFloatOptional getLeadingPaddingAndBorder( |
| 225 | const YGFlexDirection axis, |
| 226 | const float widthSize) const; |
| 227 | YGFloatOptional getTrailingPaddingAndBorder( |
| 228 | const YGFlexDirection axis, |
| 229 | const float widthSize) const; |
| 230 | YGFloatOptional getMarginForAxis( |
| 231 | const YGFlexDirection axis, |
| 232 | const float widthSize) const; |
| 233 | YGFloatOptional getGapForAxis( |
| 234 | const YGFlexDirection axis, |
| 235 | const float widthSize) const; |
| 236 | // Setters |
| 237 | |
| 238 | void setContext(void* context) { context_ = context; } |
| 239 | |
| 240 | void setPrintFunc(YGPrintFunc printFunc) { |
| 241 | print_.noContext = printFunc; |
| 242 | flags_.printUsesContext = false; |
| 243 | } |
| 244 | void setPrintFunc(PrintWithContextFn printFunc) { |
| 245 | print_.withContext = printFunc; |
| 246 | flags_.printUsesContext = true; |
| 247 | } |
| 248 | void setPrintFunc(std::nullptr_t) { setPrintFunc(YGPrintFunc{nullptr}); } |
| 249 | |
| 250 | void setHasNewLayout(bool hasNewLayout) { |
| 251 | flags_.hasNewLayout = hasNewLayout; |
| 252 | } |
| 253 | |
| 254 | void setNodeType(YGNodeType nodeType) { |
| 255 | flags_.nodeType = static_cast<uint8_t>(nodeType); |
| 256 | } |
| 257 | |
| 258 | void setMeasureFunc(YGMeasureFunc measureFunc); |
| 259 | void setMeasureFunc(MeasureWithContextFn); |
| 260 | void setMeasureFunc(std::nullptr_t) { |
| 261 | return setMeasureFunc(YGMeasureFunc{nullptr}); |
| 262 | } |
| 263 | |
| 264 | void setBaselineFunc(YGBaselineFunc baseLineFunc) { |
| 265 | flags_.baselineUsesContext = false; |
| 266 | baseline_.noContext = baseLineFunc; |
| 267 | } |
| 268 | void setBaselineFunc(BaselineWithContextFn baseLineFunc) { |
| 269 | flags_.baselineUsesContext = true; |
| 270 | baseline_.withContext = baseLineFunc; |
| 271 | } |
| 272 | void setBaselineFunc(std::nullptr_t) { |
| 273 | return setBaselineFunc(YGBaselineFunc{nullptr}); |
| 274 | } |
| 275 | |
| 276 | void setDirtiedFunc(YGDirtiedFunc dirtiedFunc) { dirtied_ = dirtiedFunc; } |
| 277 | |
| 278 | void setStyle(const YGStyle& style) { style_ = style; } |
| 279 | |
| 280 | void setLayout(const YGLayout& layout) { layout_ = layout; } |
| 281 | |
| 282 | void setLineIndex(uint32_t lineIndex) { lineIndex_ = lineIndex; } |
| 283 | |
| 284 | void setIsReferenceBaseline(bool isReferenceBaseline) { |
| 285 | flags_.isReferenceBaseline = isReferenceBaseline; |
| 286 | } |
| 287 | |
| 288 | void setOwner(YGNodeRef owner) { owner_ = owner; } |
| 289 | |
| 290 | void setChildren(const YGVector& children) { children_ = children; } |
| 291 | |
| 292 | // TODO: rvalue override for setChildren |
| 293 | |
| 294 | void setConfig(YGConfigRef config); |
| 295 | |
| 296 | void setDirty(bool isDirty); |
| 297 | void setLayoutLastOwnerDirection(YGDirection direction); |
| 298 | void setLayoutComputedFlexBasis(const YGFloatOptional computedFlexBasis); |
| 299 | void setLayoutComputedFlexBasisGeneration( |
| 300 | uint32_t computedFlexBasisGeneration); |
| 301 | void setLayoutMeasuredDimension(float measuredDimension, int index); |
| 302 | void setLayoutHadOverflow(bool hadOverflow); |
| 303 | void setLayoutDimension(float dimension, int index); |
| 304 | void setLayoutDirection(YGDirection direction); |
| 305 | void setLayoutMargin(float margin, int index); |
| 306 | void setLayoutBorder(float border, int index); |
| 307 | void setLayoutPadding(float padding, int index); |
| 308 | void setLayoutPosition(float position, int index); |
| 309 | void setPosition( |
| 310 | const YGDirection direction, |
| 311 | const float mainSize, |
| 312 | const float crossSize, |
| 313 | const float ownerWidth); |
| 314 | void markDirtyAndPropagateDownwards(); |
| 315 | |
| 316 | // Other methods |
| 317 | YGValue marginLeadingValue(const YGFlexDirection axis) const; |
| 318 | YGValue marginTrailingValue(const YGFlexDirection axis) const; |
| 319 | YGValue resolveFlexBasisPtr() const; |
| 320 | void resolveDimension(); |
| 321 | YGDirection resolveDirection(const YGDirection ownerDirection); |
| 322 | void clearChildren(); |
| 323 | /// Replaces the occurrences of oldChild with newChild |
| 324 | void replaceChild(YGNodeRef oldChild, YGNodeRef newChild); |
| 325 | void replaceChild(YGNodeRef child, uint32_t index); |
| 326 | void insertChild(YGNodeRef child, uint32_t index); |
| 327 | /// Removes the first occurrence of child |
| 328 | bool removeChild(YGNodeRef child); |
| 329 | void removeChild(uint32_t index); |
| 330 | |
| 331 | void cloneChildrenIfNeeded(void*); |
| 332 | void markDirtyAndPropagate(); |
| 333 | float resolveFlexGrow() const; |
| 334 | float resolveFlexShrink() const; |
| 335 | bool isNodeFlexible(); |
| 336 | void reset(); |
| 337 | }; |
| 338 | |
| 339 | QT_YOGA_NAMESPACE_END |
| 340 | |