diff --git a/tests/YGStyleAccessorsTest.cpp b/tests/YGStyleAccessorsTest.cpp index 1123ea6e..5d6e7c7c 100644 --- a/tests/YGStyleAccessorsTest.cpp +++ b/tests/YGStyleAccessorsTest.cpp @@ -4,15 +4,29 @@ * This source code is licensed under the MIT license found in the LICENSE * file in the root directory of this source tree. */ +#include +#include #include +#include #include #include #include +using AssignedProps = + std::remove_reference::type; + +namespace { +constexpr AssignedProps setBits(int from, int n) { + return n > 0 ? (setBits(from, n - 1) | AssignedProps{1ull << (from + n - 1)}) + : 0; +} +} // namespace + #define ACCESSOR_TESTS_1(NAME, X) \ style.NAME() = X; \ - ASSERT_EQ(style.NAME(), X); + ASSERT_EQ(style.NAME(), X); \ + ASSERT_EQ(style.assignedProps(), AssignedProps{1ull << YGStyle::NAME##Bit}); #define ACCESSOR_TESTS_2(NAME, X, ...) \ ACCESSOR_TESTS_1(NAME, X); \ ACCESSOR_TESTS_1(NAME, __VA_ARGS__); @@ -31,11 +45,20 @@ #define INDEX_ACCESSOR_TESTS_1(NAME, IDX, X) \ { \ + auto style = YGStyle{}; \ style.NAME()[IDX] = X; \ ASSERT_EQ(style.NAME()[IDX], X); \ + ASSERT_EQ( \ + style.assignedProps(), \ + AssignedProps{1ull << (YGStyle::NAME##Bit + IDX)}); \ auto asArray = decltype(std::declval().NAME()){X}; \ style.NAME() = asArray; \ ASSERT_EQ(static_cast(style.NAME()), asArray); \ + ASSERT_EQ( \ + style.assignedProps(), \ + AssignedProps{setBits( \ + YGStyle::NAME##Bit, \ + facebook::yoga::enums::count())}); \ } #define INDEX_ACCESSOR_TESTS_2(NAME, IDX, X, Y) \ @@ -69,8 +92,8 @@ #define INDEX_ACCESSOR_TEST(NAME, DEFAULT_VAL, IDX, ...) \ TEST(YGStyle, style_##NAME##_access) { \ - auto style = YGStyle{}; \ - ASSERT_EQ(style.NAME()[IDX], DEFAULT_VAL); \ + ASSERT_EQ(YGStyle{}.NAME()[IDX], DEFAULT_VAL); \ + ASSERT_EQ(YGStyle{}.assignedProps(), 0); \ INDEX_ACCESSOR_TESTS(__VA_ARGS__)(NAME, IDX, __VA_ARGS__) \ } @@ -247,5 +270,29 @@ ACCESSOR_TEST( YGFloatOptional{0.0f}, YGFloatOptional{}); +TEST(YGStyle, set_properties_default_to_0) { + ASSERT_EQ(YGStyle{}.assignedProps(), AssignedProps{0}); +} + +TEST(YGStyle, set_properties_reflects_all_set_properties) { + auto style = YGStyle{}; + + style.direction() = YGDirectionRTL; + style.justifyContent() = YGJustifySpaceAround; + style.flexWrap() = YGWrapWrap; + style.padding()[YGEdgeVertical] = YGValue{1, YGUnitPoint}; + style.minDimensions()[YGDimensionHeight] = YGValue{1, YGUnitPercent}; + style.aspectRatio() = YGFloatOptional{1.23}; + + ASSERT_EQ( + style.assignedProps(), + AssignedProps{1ull << YGStyle::directionBit | + 1ull << YGStyle::justifyContentBit | + 1ull << YGStyle::flexWrapBit | + 1ull << (YGStyle::paddingBit + YGEdgeVertical) | + 1ull << (YGStyle::minDimensionsBit + YGDimensionHeight) | + 1ull << YGStyle::aspectRatioBit}); +} + } // namespace yoga } // namespace facebook diff --git a/yoga/YGStyle.h b/yoga/YGStyle.h index 849fa0e7..d8e805e1 100644 --- a/yoga/YGStyle.h +++ b/yoga/YGStyle.h @@ -7,6 +7,8 @@ #pragma once #include #include +#include +#include #include #include "CompactValue.h" #include "YGEnums.h" @@ -33,21 +35,28 @@ class YGStyle { facebook::yoga::detail::Values()>; using CompactValue = facebook::yoga::detail::CompactValue; + static constexpr uint64_t allBits(int fromBit, int toBit) { + return fromBit < toBit + ? (uint64_t{1} << fromBit) | allBits(fromBit + 1, toBit) + : 0; + } + public: using Dimensions = Values; using Edges = Values; - template + template struct Ref { YGStyle& style; operator T() const { return style.*Prop; } - Ref& operator=(T value) { + Ref& operator=(T value) { style.*Prop = value; + style.assignedProps_.set(PropBit); return *this; } }; - template YGStyle::*Prop> + template YGStyle::*Prop, int PropBit> struct IdxRef { struct Ref { YGStyle& style; @@ -56,13 +65,16 @@ public: operator YGValue() const { return (style.*Prop)[idx]; } Ref& operator=(CompactValue value) { (style.*Prop)[idx] = value; + style.assignedProps_.set(PropBit + idx); return *this; } }; YGStyle& style; - IdxRef& operator=(const Values& values) { + IdxRef& operator=(const Values& values) { style.*Prop = values; + style.assignedProps_ |= + allBits(PropBit, PropBit + facebook::yoga::enums::count()); return *this; } operator const Values&() const { return style.*Prop; } @@ -70,15 +82,16 @@ public: CompactValue operator[](Idx idx) const { return (style.*Prop)[idx]; } }; - template + template struct BitfieldRef { YGStyle& style; T (YGStyle::*get)() const; void (YGStyle::*set)(T); operator T() const { return (style.*get)(); } - BitfieldRef& operator=(T x) { + BitfieldRef& operator=(T x) { (style.*set)(x); + style.assignedProps_.set(PropBit); return *this; } }; @@ -96,7 +109,39 @@ public: display_(YGDisplayFlex) {} ~YGStyle() = default; + static constexpr int directionBit = 0; + static constexpr int flexDirectionBit = directionBit + 1; + static constexpr int justifyContentBit = flexDirectionBit + 1; + static constexpr int alignContentBit = justifyContentBit + 1; + static constexpr int alignItemsBit = alignContentBit + 1; + static constexpr int alignSelfBit = alignItemsBit + 1; + static constexpr int positionTypeBit = alignSelfBit + 1; + static constexpr int flexWrapBit = positionTypeBit + 1; + static constexpr int overflowBit = flexWrapBit + 1; + static constexpr int displayBit = overflowBit + 1; + static constexpr int flexBit = displayBit + 1; + static constexpr int flexGrowBit = flexBit + 1; + static constexpr int flexShrinkBit = flexGrowBit + 1; + static constexpr int flexBasisBit = flexShrinkBit + 1; + static constexpr int marginBit = flexBasisBit + 1; + static constexpr int positionBit = + marginBit + facebook::yoga::enums::count(); + static constexpr int paddingBit = + positionBit + facebook::yoga::enums::count(); + static constexpr int borderBit = + paddingBit + facebook::yoga::enums::count(); + static constexpr int dimensionsBit = + borderBit + facebook::yoga::enums::count(); + static constexpr int maxDimensionsBit = + dimensionsBit + facebook::yoga::enums::count(); + static constexpr int minDimensionsBit = + maxDimensionsBit + facebook::yoga::enums::count(); + static constexpr int aspectRatioBit = + minDimensionsBit + facebook::yoga::enums::count(); + private: + std::bitset assignedProps_; + /* Some platforms don't support enum bitfields, so please use BITFIELD_ENUM_SIZED(BITS_COUNT) */ YGDirection direction_ BITFIELD_ENUM_SIZED(2); @@ -124,85 +169,113 @@ private: YGFloatOptional aspectRatio_ = {}; public: + const decltype(assignedProps_)& assignedProps() const { + return assignedProps_; + } + // for library users needing a type using ValueRepr = std::remove_reference::type; YGDirection direction() const { return direction_; } - BitfieldRef direction() { return BITFIELD_REF(direction); } + BitfieldRef direction() { + return BITFIELD_REF(direction); + } YGFlexDirection flexDirection() const { return flexDirection_; } - BitfieldRef flexDirection() { + BitfieldRef flexDirection() { return BITFIELD_REF(flexDirection); } YGJustify justifyContent() const { return justifyContent_; } - BitfieldRef justifyContent() { + BitfieldRef justifyContent() { return BITFIELD_REF(justifyContent); } YGAlign alignContent() const { return alignContent_; } - BitfieldRef alignContent() { return BITFIELD_REF(alignContent); } + BitfieldRef alignContent() { + return BITFIELD_REF(alignContent); + } YGAlign alignItems() const { return alignItems_; } - BitfieldRef alignItems() { return BITFIELD_REF(alignItems); } + BitfieldRef alignItems() { + return BITFIELD_REF(alignItems); + } YGAlign alignSelf() const { return alignSelf_; } - BitfieldRef alignSelf() { return BITFIELD_REF(alignSelf); } + BitfieldRef alignSelf() { + return BITFIELD_REF(alignSelf); + } YGPositionType positionType() const { return positionType_; } - BitfieldRef positionType() { + BitfieldRef positionType() { return BITFIELD_REF(positionType); } YGWrap flexWrap() const { return flexWrap_; } - BitfieldRef flexWrap() { return BITFIELD_REF(flexWrap); } + BitfieldRef flexWrap() { return BITFIELD_REF(flexWrap); } YGOverflow overflow() const { return overflow_; } - BitfieldRef overflow() { return BITFIELD_REF(overflow); } + BitfieldRef overflow() { + return BITFIELD_REF(overflow); + } YGDisplay display() const { return display_; } - BitfieldRef display() { return BITFIELD_REF(display); } + BitfieldRef display() { return BITFIELD_REF(display); } YGFloatOptional flex() const { return flex_; } - Ref flex() { return {*this}; } + Ref flex() { return {*this}; } YGFloatOptional flexGrow() const { return flexGrow_; } - Ref flexGrow() { return {*this}; } + Ref flexGrow() { + return {*this}; + } YGFloatOptional flexShrink() const { return flexShrink_; } - Ref flexShrink() { return {*this}; } + Ref flexShrink() { + return {*this}; + } CompactValue flexBasis() const { return flexBasis_; } - Ref flexBasis() { return {*this}; } + Ref flexBasis() { + return {*this}; + } const Edges& margin() const { return margin_; } - IdxRef margin() { return {*this}; } + IdxRef margin() { return {*this}; } const Edges& position() const { return position_; } - IdxRef position() { return {*this}; } + IdxRef position() { + return {*this}; + } const Edges& padding() const { return padding_; } - IdxRef padding() { return {*this}; } + IdxRef padding() { return {*this}; } const Edges& border() const { return border_; } - IdxRef border() { return {*this}; } + IdxRef border() { return {*this}; } const Dimensions& dimensions() const { return dimensions_; } - IdxRef dimensions() { return {*this}; } + IdxRef dimensions() { + return {*this}; + } const Dimensions& minDimensions() const { return minDimensions_; } - IdxRef minDimensions() { + IdxRef + minDimensions() { return {*this}; } const Dimensions& maxDimensions() const { return maxDimensions_; } - IdxRef maxDimensions() { + IdxRef + maxDimensions() { return {*this}; } // Yoga specific properties, not compatible with flexbox specification YGFloatOptional aspectRatio() const { return aspectRatio_; } - Ref aspectRatio() { return {*this}; } + Ref aspectRatio() { + return {*this}; + } private: BITFIELD_ACCESSORS(direction)