| 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 <algorithm> |
| 11 | #include <array> |
| 12 | #include <cstdint> |
| 13 | #include <type_traits> |
| 14 | |
| 15 | #include <yoga/Yoga.h> |
| 16 | |
| 17 | #include "CompactValue.h" |
| 18 | #include "YGFloatOptional.h" |
| 19 | #include "Yoga-internal.h" |
| 20 | #include "BitUtils.h" |
| 21 | |
| 22 | QT_YOGA_NAMESPACE_BEGIN |
| 23 | |
| 24 | class YOGA_EXPORT YGStyle { |
| 25 | template <typename Enum> |
| 26 | using Values = |
| 27 | facebook::yoga::detail::Values<facebook::yoga::enums::count<Enum>()>; |
| 28 | using CompactValue = facebook::yoga::detail::CompactValue; |
| 29 | |
| 30 | public: |
| 31 | using Dimensions = Values<YGDimension>; |
| 32 | using Edges = Values<YGEdge>; |
| 33 | using Gutters = Values<YGGutter>; |
| 34 | |
| 35 | template <typename T> |
| 36 | struct BitfieldRef { |
| 37 | YGStyle& style; |
| 38 | size_t offset; |
| 39 | operator T() const { |
| 40 | return facebook::yoga::detail::getEnumData<T>(style.flags, offset); |
| 41 | } |
| 42 | BitfieldRef<T>& operator=(T x) { |
| 43 | facebook::yoga::detail::setEnumData<T>(style.flags, offset, x); |
| 44 | return *this; |
| 45 | } |
| 46 | }; |
| 47 | |
| 48 | template <typename T, T YGStyle::*Prop> |
| 49 | struct Ref { |
| 50 | YGStyle& style; |
| 51 | operator T() const { return style.*Prop; } |
| 52 | Ref<T, Prop>& operator=(T value) { |
| 53 | style.*Prop = value; |
| 54 | return *this; |
| 55 | } |
| 56 | }; |
| 57 | |
| 58 | template <typename Idx, Values<Idx> YGStyle::*Prop> |
| 59 | struct IdxRef { |
| 60 | struct Ref { |
| 61 | YGStyle& style; |
| 62 | Idx idx; |
| 63 | operator CompactValue() const { return (style.*Prop)[idx]; } |
| 64 | operator YGValue() const { return (style.*Prop)[idx]; } |
| 65 | Ref& operator=(CompactValue value) { |
| 66 | (style.*Prop)[idx] = value; |
| 67 | return *this; |
| 68 | } |
| 69 | }; |
| 70 | |
| 71 | YGStyle& style; |
| 72 | IdxRef<Idx, Prop>& operator=(const Values<Idx>& values) { |
| 73 | style.*Prop = values; |
| 74 | return *this; |
| 75 | } |
| 76 | operator const Values<Idx>&() const { return style.*Prop; } |
| 77 | Ref operator[](Idx idx) { return {style, idx}; } |
| 78 | CompactValue operator[](Idx idx) const { return (style.*Prop)[idx]; } |
| 79 | }; |
| 80 | |
| 81 | YGStyle() { |
| 82 | alignContent() = YGAlignFlexStart; |
| 83 | alignItems() = YGAlignStretch; |
| 84 | } |
| 85 | ~YGStyle() = default; |
| 86 | |
| 87 | private: |
| 88 | static constexpr size_t directionOffset = 0; |
| 89 | static constexpr size_t flexdirectionOffset = |
| 90 | directionOffset + facebook::yoga::detail::bitWidthFn<YGDirection>(); |
| 91 | static constexpr size_t justifyContentOffset = flexdirectionOffset + |
| 92 | facebook::yoga::detail::bitWidthFn<YGFlexDirection>(); |
| 93 | static constexpr size_t alignContentOffset = |
| 94 | justifyContentOffset + facebook::yoga::detail::bitWidthFn<YGJustify>(); |
| 95 | static constexpr size_t alignItemsOffset = |
| 96 | alignContentOffset + facebook::yoga::detail::bitWidthFn<YGAlign>(); |
| 97 | static constexpr size_t alignSelfOffset = |
| 98 | alignItemsOffset + facebook::yoga::detail::bitWidthFn<YGAlign>(); |
| 99 | static constexpr size_t positionTypeOffset = |
| 100 | alignSelfOffset + facebook::yoga::detail::bitWidthFn<YGAlign>(); |
| 101 | static constexpr size_t flexWrapOffset = |
| 102 | positionTypeOffset + facebook::yoga::detail::bitWidthFn<YGPositionType>(); |
| 103 | static constexpr size_t overflowOffset = |
| 104 | flexWrapOffset + facebook::yoga::detail::bitWidthFn<YGWrap>(); |
| 105 | static constexpr size_t displayOffset = |
| 106 | overflowOffset + facebook::yoga::detail::bitWidthFn<YGOverflow>(); |
| 107 | |
| 108 | uint32_t flags = 0; |
| 109 | |
| 110 | YGFloatOptional flex_ = {}; |
| 111 | YGFloatOptional flexGrow_ = {}; |
| 112 | YGFloatOptional flexShrink_ = {}; |
| 113 | CompactValue flexBasis_ = CompactValue::ofAuto(); |
| 114 | Edges margin_ = {}; |
| 115 | Edges position_ = {}; |
| 116 | Edges padding_ = {}; |
| 117 | Edges border_ = {}; |
| 118 | Gutters gap_ = {}; |
| 119 | Dimensions dimensions_{CompactValue::ofAuto()}; |
| 120 | Dimensions minDimensions_ = {}; |
| 121 | Dimensions maxDimensions_ = {}; |
| 122 | // Yoga specific properties, not compatible with flexbox specification |
| 123 | YGFloatOptional aspectRatio_ = {}; |
| 124 | |
| 125 | public: |
| 126 | // for library users needing a type |
| 127 | using ValueRepr = std::remove_reference<decltype(margin_[0])>::type; |
| 128 | |
| 129 | YGDirection direction() const { |
| 130 | return facebook::yoga::detail::getEnumData<YGDirection>( |
| 131 | flags, index: directionOffset); |
| 132 | } |
| 133 | BitfieldRef<YGDirection> direction() { return {.style: *this, .offset: directionOffset}; } |
| 134 | |
| 135 | YGFlexDirection flexDirection() const { |
| 136 | return facebook::yoga::detail::getEnumData<YGFlexDirection>( |
| 137 | flags, index: flexdirectionOffset); |
| 138 | } |
| 139 | BitfieldRef<YGFlexDirection> flexDirection() { |
| 140 | return {.style: *this, .offset: flexdirectionOffset}; |
| 141 | } |
| 142 | |
| 143 | YGJustify justifyContent() const { |
| 144 | return facebook::yoga::detail::getEnumData<YGJustify>( |
| 145 | flags, index: justifyContentOffset); |
| 146 | } |
| 147 | BitfieldRef<YGJustify> justifyContent() { |
| 148 | return {.style: *this, .offset: justifyContentOffset}; |
| 149 | } |
| 150 | |
| 151 | YGAlign alignContent() const { |
| 152 | return facebook::yoga::detail::getEnumData<YGAlign>( |
| 153 | flags, index: alignContentOffset); |
| 154 | } |
| 155 | BitfieldRef<YGAlign> alignContent() { return {.style: *this, .offset: alignContentOffset}; } |
| 156 | |
| 157 | YGAlign alignItems() const { |
| 158 | return facebook::yoga::detail::getEnumData<YGAlign>( |
| 159 | flags, index: alignItemsOffset); |
| 160 | } |
| 161 | BitfieldRef<YGAlign> alignItems() { return {.style: *this, .offset: alignItemsOffset}; } |
| 162 | |
| 163 | YGAlign alignSelf() const { |
| 164 | return facebook::yoga::detail::getEnumData<YGAlign>(flags, index: alignSelfOffset); |
| 165 | } |
| 166 | BitfieldRef<YGAlign> alignSelf() { return {.style: *this, .offset: alignSelfOffset}; } |
| 167 | |
| 168 | YGPositionType positionType() const { |
| 169 | return facebook::yoga::detail::getEnumData<YGPositionType>( |
| 170 | flags, index: positionTypeOffset); |
| 171 | } |
| 172 | BitfieldRef<YGPositionType> positionType() { |
| 173 | return {.style: *this, .offset: positionTypeOffset}; |
| 174 | } |
| 175 | |
| 176 | YGWrap flexWrap() const { |
| 177 | return facebook::yoga::detail::getEnumData<YGWrap>(flags, index: flexWrapOffset); |
| 178 | } |
| 179 | BitfieldRef<YGWrap> flexWrap() { return {.style: *this, .offset: flexWrapOffset}; } |
| 180 | |
| 181 | YGOverflow overflow() const { |
| 182 | return facebook::yoga::detail::getEnumData<YGOverflow>( |
| 183 | flags, index: overflowOffset); |
| 184 | } |
| 185 | BitfieldRef<YGOverflow> overflow() { return {.style: *this, .offset: overflowOffset}; } |
| 186 | |
| 187 | YGDisplay display() const { |
| 188 | return facebook::yoga::detail::getEnumData<YGDisplay>(flags, index: displayOffset); |
| 189 | } |
| 190 | BitfieldRef<YGDisplay> display() { return {.style: *this, .offset: displayOffset}; } |
| 191 | |
| 192 | YGFloatOptional flex() const { return flex_; } |
| 193 | Ref<YGFloatOptional, &YGStyle::flex_> flex() { return {.style: *this}; } |
| 194 | |
| 195 | YGFloatOptional flexGrow() const { return flexGrow_; } |
| 196 | Ref<YGFloatOptional, &YGStyle::flexGrow_> flexGrow() { return {.style: *this}; } |
| 197 | |
| 198 | YGFloatOptional flexShrink() const { return flexShrink_; } |
| 199 | Ref<YGFloatOptional, &YGStyle::flexShrink_> flexShrink() { return {.style: *this}; } |
| 200 | |
| 201 | CompactValue flexBasis() const { return flexBasis_; } |
| 202 | Ref<CompactValue, &YGStyle::flexBasis_> flexBasis() { return {.style: *this}; } |
| 203 | |
| 204 | const Edges& margin() const { return margin_; } |
| 205 | IdxRef<YGEdge, &YGStyle::margin_> margin() { return {.style: *this}; } |
| 206 | |
| 207 | const Edges& position() const { return position_; } |
| 208 | IdxRef<YGEdge, &YGStyle::position_> position() { return {.style: *this}; } |
| 209 | |
| 210 | const Edges& padding() const { return padding_; } |
| 211 | IdxRef<YGEdge, &YGStyle::padding_> padding() { return {.style: *this}; } |
| 212 | |
| 213 | const Edges& border() const { return border_; } |
| 214 | IdxRef<YGEdge, &YGStyle::border_> border() { return {.style: *this}; } |
| 215 | |
| 216 | const Gutters& gap() const { return gap_; } |
| 217 | IdxRef<YGGutter, &YGStyle::gap_> gap() { return {.style: *this}; } |
| 218 | |
| 219 | const Dimensions& dimensions() const { return dimensions_; } |
| 220 | IdxRef<YGDimension, &YGStyle::dimensions_> dimensions() { return {.style: *this}; } |
| 221 | |
| 222 | const Dimensions& minDimensions() const { return minDimensions_; } |
| 223 | IdxRef<YGDimension, &YGStyle::minDimensions_> minDimensions() { |
| 224 | return {.style: *this}; |
| 225 | } |
| 226 | |
| 227 | const Dimensions& maxDimensions() const { return maxDimensions_; } |
| 228 | IdxRef<YGDimension, &YGStyle::maxDimensions_> maxDimensions() { |
| 229 | return {.style: *this}; |
| 230 | } |
| 231 | |
| 232 | // Yoga specific properties, not compatible with flexbox specification |
| 233 | YGFloatOptional aspectRatio() const { return aspectRatio_; } |
| 234 | Ref<YGFloatOptional, &YGStyle::aspectRatio_> aspectRatio() { return {.style: *this}; } |
| 235 | }; |
| 236 | |
| 237 | YOGA_EXPORT bool operator==(const YGStyle& lhs, const YGStyle& rhs); |
| 238 | YOGA_EXPORT inline bool operator!=(const YGStyle& lhs, const YGStyle& rhs) { |
| 239 | return !(lhs == rhs); |
| 240 | } |
| 241 | |
| 242 | QT_YOGA_NAMESPACE_END |
| 243 | |