diff --git a/tests/GridStyleTest.cpp b/tests/GridStyleTest.cpp new file mode 100644 index 0000000000..f25676f95e --- /dev/null +++ b/tests/GridStyleTest.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * 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 + +namespace facebook::yoga { + +// GridStyleStorage must cost a node the same as a plain pointer when it is not +// a grid container or item. +static_assert(sizeof(GridStyleStorage) == sizeof(void*)); + +TEST(GridStyle, unset_grid_style_reads_as_the_defaults) { + Style style; + + ASSERT_TRUE(style.gridTemplateColumns().empty()); + ASSERT_TRUE(style.gridAutoRows().empty()); + ASSERT_EQ(style.gridColumnStart(), GridLine::auto_()); + ASSERT_EQ(style.gridRowEnd(), GridLine::auto_()); +} + +TEST(GridStyle, unset_grid_style_equals_grid_style_set_to_the_defaults) { + Style unset; + + Style setToDefaults; + setToDefaults.setGridColumnStart(GridLine::auto_()); + + ASSERT_TRUE(unset == setToDefaults); + ASSERT_TRUE(setToDefaults == unset); +} + +TEST(GridStyle, styles_with_different_grid_values_are_not_equal) { + Style style; + Style withColumnStart; + withColumnStart.setGridColumnStart(GridLine::fromInteger(2)); + + ASSERT_FALSE(style == withColumnStart); + ASSERT_FALSE(withColumnStart == style); +} + +TEST(GridStyle, copies_do_not_share_grid_style) { + Style style; + style.setGridTemplateColumns(GridTrackList{GridTrackSize::length(10.0f)}); + style.setGridRowEnd(GridLine::span(3)); + + Style copy = style; + + ASSERT_TRUE(copy == style); + ASSERT_EQ(copy.gridTemplateColumns()[0], GridTrackSize::length(10.0f)); + ASSERT_EQ(copy.gridRowEnd(), GridLine::span(3)); + + copy.setGridTemplateColumnAt(0, GridTrackSize::length(20.0f)); + + ASSERT_EQ(style.gridTemplateColumns()[0], GridTrackSize::length(10.0f)); + ASSERT_FALSE(copy == style); +} + +TEST(GridStyle, moving_a_style_hands_over_the_grid_style) { + Style style; + style.setGridRowEnd(GridLine::span(3)); + + Style moved = std::move(style); + ASSERT_EQ(moved.gridRowEnd(), GridLine::span(3)); + ASSERT_EQ(style.gridRowEnd(), GridLine::auto_()); + + Style target; + target = std::move(moved); + ASSERT_EQ(target.gridRowEnd(), GridLine::span(3)); + ASSERT_EQ(moved.gridRowEnd(), GridLine::auto_()); +} + +TEST(GridStyle, assigning_an_unset_style_clears_a_set_one) { + Style style; + style.setGridTemplateColumns(GridTrackList{GridTrackSize::length(10.0f)}); + style.setGridRowStart(GridLine::span(2)); + + style = Style{}; + + ASSERT_TRUE(style.gridTemplateColumns().empty()); + ASSERT_EQ(style.gridRowStart(), GridLine::auto_()); +} + +TEST(GridStyle, tracks_can_be_sized_then_filled) { + Style style; + style.resizeGridTemplateColumns(2); + style.setGridTemplateColumnAt(0, GridTrackSize::fr(1.0f)); + style.setGridTemplateColumnAt(1, GridTrackSize::percent(50.0f)); + + ASSERT_EQ(style.gridTemplateColumns().size(), 2u); + ASSERT_EQ(style.gridTemplateColumns()[0], GridTrackSize::fr(1.0f)); + ASSERT_EQ(style.gridTemplateColumns()[1], GridTrackSize::percent(50.0f)); +} + +} // namespace facebook::yoga diff --git a/tests/StyleTest.cpp b/tests/StyleTest.cpp index d44b73d50f..58a9e798c9 100644 --- a/tests/StyleTest.cpp +++ b/tests/StyleTest.cpp @@ -10,6 +10,10 @@ namespace facebook::yoga { +#if defined(__LP64__) || defined(_WIN64) +static_assert(sizeof(Style) <= 160, "Style grew. Was this intended?"); +#endif + TEST(Style, computed_padding_is_floored) { yoga::Style style; style.setPadding(Edge::All, StyleLength::points(-1.0f)); diff --git a/yoga/style/GridStyle.h b/yoga/style/GridStyle.h new file mode 100644 index 0000000000..f1cf9d7b11 --- /dev/null +++ b/yoga/style/GridStyle.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include +#include + +namespace facebook::yoga { + +/** + * The CSS Grid properties of a single node. + */ +struct GridStyle { + // Grid container properties + GridTrackList templateColumns{}; + GridTrackList templateRows{}; + GridTrackList autoColumns{}; + GridTrackList autoRows{}; + + // Grid item properties + GridLine columnStart{}; + GridLine columnEnd{}; + GridLine rowStart{}; + GridLine rowEnd{}; + + bool operator==(const GridStyle& other) const = default; +}; + +/** + * Storage for a GridStyle which stays empty until the first grid property is + * set. + */ +class GridStyleStorage { + public: + GridStyleStorage() = default; + GridStyleStorage(GridStyleStorage&&) noexcept = default; + GridStyleStorage& operator=(GridStyleStorage&&) noexcept = default; + + GridStyleStorage(const GridStyleStorage& other) { + *this = other; + } + + GridStyleStorage& operator=(const GridStyleStorage& other) { + grid_ = other.grid_ == nullptr ? nullptr + : std::make_unique(*other.grid_); + return *this; + } + + const GridStyle& get() const { + return grid_ == nullptr ? defaults() : *grid_; + } + + GridStyle& ensure() { + if (grid_ == nullptr) { + grid_ = std::make_unique(); + } + return *grid_; + } + + bool operator==(const GridStyleStorage& other) const { + return grid_ == other.grid_ || get() == other.get(); + } + + private: + static const GridStyle& defaults() { + static const GridStyle kDefaults{}; + return kDefaults; + } + + std::unique_ptr grid_{}; +}; + +} // namespace facebook::yoga diff --git a/yoga/style/Style.h b/yoga/style/Style.h index a06bd246b4..00af38c692 100644 --- a/yoga/style/Style.h +++ b/yoga/style/Style.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -209,84 +210,84 @@ class YG_EXPORT Style { // Grid Container Properties const GridTrackList& gridTemplateColumns() const { - return gridTemplateColumns_; + return grid_.get().templateColumns; } void setGridTemplateColumns(GridTrackList value) { - gridTemplateColumns_ = std::move(value); + grid_.ensure().templateColumns = std::move(value); } void resizeGridTemplateColumns(size_t count) { - gridTemplateColumns_.resize(count); + grid_.ensure().templateColumns.resize(count); } void setGridTemplateColumnAt(size_t index, GridTrackSize value) { - gridTemplateColumns_[index] = value; + grid_.ensure().templateColumns[index] = value; } const GridTrackList& gridTemplateRows() const { - return gridTemplateRows_; + return grid_.get().templateRows; } void setGridTemplateRows(GridTrackList value) { - gridTemplateRows_ = std::move(value); + grid_.ensure().templateRows = std::move(value); } void resizeGridTemplateRows(size_t count) { - gridTemplateRows_.resize(count); + grid_.ensure().templateRows.resize(count); } void setGridTemplateRowAt(size_t index, GridTrackSize value) { - gridTemplateRows_[index] = value; + grid_.ensure().templateRows[index] = value; } const GridTrackList& gridAutoColumns() const { - return gridAutoColumns_; + return grid_.get().autoColumns; } void setGridAutoColumns(GridTrackList value) { - gridAutoColumns_ = std::move(value); + grid_.ensure().autoColumns = std::move(value); } void resizeGridAutoColumns(size_t count) { - gridAutoColumns_.resize(count); + grid_.ensure().autoColumns.resize(count); } void setGridAutoColumnAt(size_t index, GridTrackSize value) { - gridAutoColumns_[index] = value; + grid_.ensure().autoColumns[index] = value; } const GridTrackList& gridAutoRows() const { - return gridAutoRows_; + return grid_.get().autoRows; } void setGridAutoRows(GridTrackList value) { - gridAutoRows_ = std::move(value); + grid_.ensure().autoRows = std::move(value); } void resizeGridAutoRows(size_t count) { - gridAutoRows_.resize(count); + grid_.ensure().autoRows.resize(count); } void setGridAutoRowAt(size_t index, GridTrackSize value) { - gridAutoRows_[index] = value; + grid_.ensure().autoRows[index] = value; } // Grid Item Properties const GridLine& gridColumnStart() const { - return gridColumnStart_; + return grid_.get().columnStart; } void setGridColumnStart(GridLine value) { - gridColumnStart_ = value; + grid_.ensure().columnStart = value; } const GridLine& gridColumnEnd() const { - return gridColumnEnd_; + return grid_.get().columnEnd; } void setGridColumnEnd(GridLine value) { - gridColumnEnd_ = value; + grid_.ensure().columnEnd = value; } const GridLine& gridRowStart() const { - return gridRowStart_; + return grid_.get().rowStart; } void setGridRowStart(GridLine value) { - gridRowStart_ = value; + grid_.ensure().rowStart = value; } const GridLine& gridRowEnd() const { - return gridRowEnd_; + return grid_.get().rowEnd; } void setGridRowEnd(GridLine value) { - gridRowEnd_ = value; + grid_.ensure().rowEnd = value; } FloatOptional resolvedMinDimension( @@ -667,14 +668,7 @@ class YG_EXPORT Style { sizeLengthsEqual( maxDimensions_, pool_, other.maxDimensions_, other.pool_) && numbersEqual(aspectRatio_, pool_, other.aspectRatio_, other.pool_) && - gridTemplateColumns_ == other.gridTemplateColumns_ && - gridTemplateRows_ == other.gridTemplateRows_ && - gridAutoColumns_ == other.gridAutoColumns_ && - gridAutoRows_ == other.gridAutoRows_ && - gridColumnStart_ == other.gridColumnStart_ && - gridColumnEnd_ == other.gridColumnEnd_ && - gridRowStart_ == other.gridRowStart_ && - gridRowEnd_ == other.gridRowEnd_; + grid_ == other.grid_; } private: @@ -929,15 +923,8 @@ class YG_EXPORT Style { Dimensions maxDimensions_{}; StyleValueHandle aspectRatio_{}; - // Grid properties - GridTrackList gridTemplateColumns_{}; - GridTrackList gridTemplateRows_{}; - GridTrackList gridAutoColumns_{}; - GridTrackList gridAutoRows_{}; - GridLine gridColumnStart_{}; - GridLine gridColumnEnd_{}; - GridLine gridRowStart_{}; - GridLine gridRowEnd_{}; + // Grid properties, allocated only when one of them is set + GridStyleStorage grid_{}; StyleValuePool pool_; };