Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions tests/YGMeasureCacheTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,48 @@ TEST(

ASSERT_EQ(1, measureCount);
}

TEST(YogaTest, keep_cached_measurement_when_owner_resizes_without_percentages) {
YGNodeRef root = YGNodeNew();
YGNodeStyleSetWidth(root, 100);

YGNodeRef root_child0 = YGNodeNew();
int measureCount = 0;
YGNodeSetContext(root_child0, &measureCount);
YGNodeSetMeasureFunc(root_child0, _measureMax);
YGNodeStyleSetWidth(root_child0, 50);
YGNodeInsertChild(root, root_child0, 0);

YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
const int measuresBeforeResize = measureCount;

YGNodeStyleSetWidth(root, 200);
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
ASSERT_EQ(measuresBeforeResize, measureCount);

YGNodeFreeRecursive(root);
}

TEST(YogaTest, remeasure_when_owner_resizes_with_percent_padding) {
YGNodeRef root = YGNodeNew();
YGNodeStyleSetWidth(root, 100);

YGNodeRef root_child0 = YGNodeNew();
int measureCount = 0;
YGNodeSetContext(root_child0, &measureCount);
YGNodeSetMeasureFunc(root_child0, _measureMax);
YGNodeStyleSetWidth(root_child0, 50);
YGNodeStyleSetPaddingPercent(root_child0, YGEdgeTop, 10);
YGNodeInsertChild(root, root_child0, 0);

YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
ASSERT_FLOAT_EQ(10, YGNodeLayoutGetPadding(root_child0, YGEdgeTop));
const int measuresBeforeResize = measureCount;

YGNodeStyleSetWidth(root, 200);
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
ASSERT_FLOAT_EQ(20, YGNodeLayoutGetPadding(root_child0, YGEdgeTop));
ASSERT_GT(measureCount, measuresBeforeResize);

YGNodeFreeRecursive(root);
}
76 changes: 76 additions & 0 deletions tests/YGRelayoutTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,79 @@ TEST(YogaTest, has_new_layout_flag_set_static) {

YGNodeFreeRecursive(root);
}

TEST(YogaTest, percent_padding_follows_owner_size_on_relayout) {
YGConfigRef config = YGConfigNew();

YGNodeRef root = YGNodeNewWithConfig(config);
YGNodeStyleSetWidth(root, 100);

YGNodeRef root_child0 = YGNodeNewWithConfig(config);
YGNodeStyleSetWidth(root_child0, 50);
YGNodeStyleSetPaddingPercent(root_child0, YGEdgeLeft, 50);
YGNodeInsertChild(root, root_child0, 0);

YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
ASSERT_FLOAT_EQ(50, YGNodeLayoutGetPadding(root_child0, YGEdgeLeft));

YGNodeStyleSetWidth(root, 60);
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
ASSERT_FLOAT_EQ(30, YGNodeLayoutGetPadding(root_child0, YGEdgeLeft));

YGNodeFreeRecursive(root);

YGConfigFree(config);
}

TEST(YogaTest, percent_min_width_follows_owner_size_on_relayout) {
YGConfigRef config = YGConfigNew();

YGNodeRef root = YGNodeNewWithConfig(config);
YGNodeStyleSetWidth(root, 100);

YGNodeRef root_child0 = YGNodeNewWithConfig(config);
YGNodeStyleSetWidth(root_child0, 50);
YGNodeStyleSetMinWidthPercent(root_child0, 100);
YGNodeInsertChild(root, root_child0, 0);

YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config);
YGNodeStyleSetHeight(root_child0_child0, 10);
YGNodeInsertChild(root_child0, root_child0_child0, 0);

YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0));
ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0));

YGNodeStyleSetWidth(root, 200);
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0));
ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0_child0));

YGNodeFreeRecursive(root);

YGConfigFree(config);
}

TEST(YogaTest, percent_min_height_follows_owner_size_on_relayout) {
YGConfigRef config = YGConfigNew();

YGNodeRef root = YGNodeNewWithConfig(config);
YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow);
YGNodeStyleSetHeight(root, 100);

YGNodeRef root_child0 = YGNodeNewWithConfig(config);
YGNodeStyleSetHeight(root_child0, 50);
YGNodeStyleSetMinHeightPercent(root_child0, 100);
YGNodeInsertChild(root, root_child0, 0);

YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0));

YGNodeStyleSetHeight(root, 200);
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0));

YGNodeFreeRecursive(root);

YGConfigFree(config);
}
33 changes: 29 additions & 4 deletions yoga/algorithm/CalculateLayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2672,6 +2672,19 @@ static void calculateLayoutImpl(
// Input parameters are the same as calculateLayoutImpl (see above)
// Return parameter is true if layout was performed, false if skipped
//
// A cached result holds only for the owner size it was computed against when
// the node's style resolves a percentage against that size.
static bool isCachedForOwnerSize(
const yoga::Node* const node,
const CachedMeasurement& cachedMeasurement,
const float ownerWidth,
const float ownerHeight) {
return (!node->style().resolvesAgainstOwnerWidth() ||
yoga::inexactEquals(cachedMeasurement.ownerWidth, ownerWidth)) &&
(!node->style().resolvesAgainstOwnerHeight() ||
yoga::inexactEquals(cachedMeasurement.ownerHeight, ownerHeight));
}

bool calculateLayoutInternal(
yoga::Node* const node,
const float availableWidth,
Expand Down Expand Up @@ -2704,6 +2717,8 @@ bool calculateLayoutInternal(
layout->cachedLayout.heightSizingMode = SizingMode::MaxContent;
layout->cachedLayout.computedWidth = -1;
layout->cachedLayout.computedHeight = -1;
layout->cachedLayout.ownerWidth = -1;
layout->cachedLayout.ownerHeight = -1;
}

CachedMeasurement* cachedResults = nullptr;
Expand Down Expand Up @@ -2736,7 +2751,9 @@ bool calculateLayoutInternal(
layout->cachedLayout.computedHeight,
marginAxisRow,
marginAxisColumn,
node->getConfig())) {
node->getConfig()) &&
isCachedForOwnerSize(
node, layout->cachedLayout, ownerWidth, ownerHeight)) {
cachedResults = &layout->cachedLayout;
} else {
// Try to use the measurement cache.
Expand All @@ -2754,7 +2771,9 @@ bool calculateLayoutInternal(
layout->cachedMeasurements[i].computedHeight,
marginAxisRow,
marginAxisColumn,
node->getConfig())) {
node->getConfig()) &&
isCachedForOwnerSize(
node, layout->cachedMeasurements[i], ownerWidth, ownerHeight)) {
cachedResults = &layout->cachedMeasurements[i];
break;
}
Expand All @@ -2766,7 +2785,9 @@ bool calculateLayoutInternal(
yoga::inexactEquals(
layout->cachedLayout.availableHeight, availableHeight) &&
layout->cachedLayout.widthSizingMode == widthSizingMode &&
layout->cachedLayout.heightSizingMode == heightSizingMode) {
layout->cachedLayout.heightSizingMode == heightSizingMode &&
isCachedForOwnerSize(
node, layout->cachedLayout, ownerWidth, ownerHeight)) {
cachedResults = &layout->cachedLayout;
}
} else {
Expand All @@ -2776,7 +2797,9 @@ bool calculateLayoutInternal(
yoga::inexactEquals(
layout->cachedMeasurements[i].availableHeight, availableHeight) &&
layout->cachedMeasurements[i].widthSizingMode == widthSizingMode &&
layout->cachedMeasurements[i].heightSizingMode == heightSizingMode) {
layout->cachedMeasurements[i].heightSizingMode == heightSizingMode &&
isCachedForOwnerSize(
node, layout->cachedMeasurements[i], ownerWidth, ownerHeight)) {
cachedResults = &layout->cachedMeasurements[i];
break;
}
Expand Down Expand Up @@ -2839,6 +2862,8 @@ bool calculateLayoutInternal(
layout->measuredDimension(Dimension::Width);
newCacheEntry->computedHeight =
layout->measuredDimension(Dimension::Height);
newCacheEntry->ownerWidth = ownerWidth;
newCacheEntry->ownerHeight = ownerHeight;
}
}

Expand Down
11 changes: 11 additions & 0 deletions yoga/node/CachedMeasurement.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ struct CachedMeasurement {
float computedWidth{-1};
float computedHeight{-1};

float ownerWidth{-1};
float ownerHeight{-1};

bool operator==(CachedMeasurement measurement) const {
bool isEqual = widthSizingMode == measurement.widthSizingMode &&
heightSizingMode == measurement.heightSizingMode;
Expand All @@ -45,6 +48,14 @@ struct CachedMeasurement {
!yoga::isUndefined(measurement.computedHeight)) {
isEqual = isEqual && computedHeight == measurement.computedHeight;
}
if (!yoga::isUndefined(ownerWidth) ||
!yoga::isUndefined(measurement.ownerWidth)) {
isEqual = isEqual && ownerWidth == measurement.ownerWidth;
}
if (!yoga::isUndefined(ownerHeight) ||
!yoga::isUndefined(measurement.ownerHeight)) {
isEqual = isEqual && ownerHeight == measurement.ownerHeight;
}

return isEqual;
}
Expand Down
26 changes: 26 additions & 0 deletions yoga/style/Style.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#pragma once

#include <algorithm>
#include <array>
#include <cstdint>
#include <type_traits>
Expand Down Expand Up @@ -341,6 +342,22 @@ class YG_EXPORT Style {
: FloatOptional{0.0});
}

// Percentage margins and paddings resolve against the owner's width on both
// axes, and a percentage size or size bound against the owner's size on its
// own axis.
bool resolvesAgainstOwnerWidth() const {
return anyIsPercent(margin_) || anyIsPercent(padding_) ||
dimensions_[yoga::to_underlying(Dimension::Width)].isPercent() ||
minDimensions_[yoga::to_underlying(Dimension::Width)].isPercent() ||
maxDimensions_[yoga::to_underlying(Dimension::Width)].isPercent();
}

bool resolvesAgainstOwnerHeight() const {
return dimensions_[yoga::to_underlying(Dimension::Height)].isPercent() ||
minDimensions_[yoga::to_underlying(Dimension::Height)].isPercent() ||
maxDimensions_[yoga::to_underlying(Dimension::Height)].isPercent();
}

FloatOptional aspectRatio() const {
return pool_.getNumber(aspectRatio_);
}
Expand Down Expand Up @@ -677,6 +694,15 @@ class YG_EXPORT Style {
using Edges = std::array<StyleValueHandle, ordinalCount<Edge>()>;
using Gutters = std::array<StyleValueHandle, ordinalCount<Gutter>()>;

template <size_t N>
static inline bool anyIsPercent(
const std::array<StyleValueHandle, N>& handles) {
return std::any_of(
handles.begin(), handles.end(), [](const StyleValueHandle& handle) {
return handle.isPercent();
});
}

static inline bool numbersEqual(
const StyleValueHandle& lhsHandle,
const StyleValuePool& lhsPool,
Expand Down
Loading