diff --git a/CSSLayout/CSSLayout.c b/CSSLayout/CSSLayout.c index d9d7d65b..75fa825c 100644 --- a/CSSLayout/CSSLayout.c +++ b/CSSLayout/CSSLayout.c @@ -259,7 +259,8 @@ static void _CSSNodeMarkDirty(const CSSNodeRef node) { } void CSSNodeSetMeasureFunc(const CSSNodeRef node, CSSMeasureFunc measureFunc) { - CSS_ASSERT(CSSNodeChildCount(node) == 0, "Cannot set measure function: Nodes with measure functions cannot have children."); + CSS_ASSERT(CSSNodeChildCount(node) == 0, + "Cannot set measure function: Nodes with measure functions cannot have children."); node->measure = measureFunc; } @@ -269,7 +270,8 @@ CSSMeasureFunc CSSNodeGetMeasureFunc(const CSSNodeRef node) { void CSSNodeInsertChild(const CSSNodeRef node, const CSSNodeRef child, const uint32_t index) { CSS_ASSERT(child->parent == NULL, "Child already has a parent, it must be removed first."); - CSS_ASSERT(node->measure == NULL, "Cannot add child: Nodes with measure functions cannot have children."); + CSS_ASSERT(node->measure == NULL, + "Cannot add child: Nodes with measure functions cannot have children."); CSSNodeListInsert(&node->children, child, index); child->parent = node; _CSSNodeMarkDirty(node); @@ -1367,6 +1369,26 @@ static void layoutNodeImpl(const CSSNodeRef node, const float availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight; const float availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth; + // If there is only one child with flexGrow + flexShrink it means we can set the + // computedFlexBasis to 0 instead of measuring and shrinking / flexing the child to exactly + // match the remaining space + CSSNodeRef singleFlexChild = NULL; + if ((isMainAxisRow && widthMeasureMode != CSSMeasureModeUndefined) || + (!isMainAxisRow && heightMeasureMode != CSSMeasureModeUndefined)) { + for (uint32_t i = 0; i < childCount; i++) { + const CSSNodeRef child = CSSNodeGetChild(node, i); + if (singleFlexChild) { + if (isFlex(child)) { + // There is already a flexible child, abort. + singleFlexChild = NULL; + break; + } + } else if (CSSNodeStyleGetFlexGrow(child) > 0 && CSSNodeStyleGetFlexShrink(child) > 0) { + singleFlexChild = child; + } + } + } + // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM for (uint32_t i = 0; i < childCount; i++) { const CSSNodeRef child = CSSNodeListGet(node->children, i); @@ -1391,13 +1413,17 @@ static void layoutNodeImpl(const CSSNodeRef node, currentAbsoluteChild = child; child->nextChild = NULL; } else { - computeChildFlexBasis(node, - child, - availableInnerWidth, - widthMeasureMode, - availableInnerHeight, - heightMeasureMode, - direction); + if (child == singleFlexChild) { + child->layout.computedFlexBasis = 0; + } else { + computeChildFlexBasis(node, + child, + availableInnerWidth, + widthMeasureMode, + availableInnerHeight, + heightMeasureMode, + direction); + } } } @@ -2133,17 +2159,17 @@ static inline bool newMeasureSizeIsStricterAndStillValid(CSSMeasureMode sizeMode } bool CSSNodeCanUseCachedMeasurement(const CSSMeasureMode widthMode, - const float width, - const CSSMeasureMode heightMode, - const float height, - const CSSMeasureMode lastWidthMode, - const float lastWidth, - const CSSMeasureMode lastHeightMode, - const float lastHeight, - const float lastComputedWidth, - const float lastComputedHeight, - const float marginRow, - const float marginColumn) { + const float width, + const CSSMeasureMode heightMode, + const float height, + const CSSMeasureMode lastWidthMode, + const float lastWidth, + const CSSMeasureMode lastHeightMode, + const float lastHeight, + const float lastComputedWidth, + const float lastComputedHeight, + const float marginRow, + const float marginColumn) { if (lastComputedHeight < 0 || lastComputedWidth < 0) { return false; } @@ -2161,19 +2187,16 @@ bool CSSNodeCanUseCachedMeasurement(const CSSMeasureMode widthMode, newMeasureSizeIsStricterAndStillValid( widthMode, width - marginRow, lastWidthMode, lastWidth, lastComputedWidth); - const bool heightIsCompatible = hasSameHeightSpec || - newSizeIsExactAndMatchesOldMeasuredSize(heightMode, - height - marginColumn, - lastComputedHeight) || - oldSizeIsUnspecifiedAndStillFits(heightMode, + const bool heightIsCompatible = + hasSameHeightSpec || newSizeIsExactAndMatchesOldMeasuredSize(heightMode, height - marginColumn, - lastHeightMode, lastComputedHeight) || - newMeasureSizeIsStricterAndStillValid(heightMode, - height - marginColumn, - lastHeightMode, - lastHeight, - lastComputedHeight); + oldSizeIsUnspecifiedAndStillFits(heightMode, + height - marginColumn, + lastHeightMode, + lastComputedHeight) || + newMeasureSizeIsStricterAndStillValid( + heightMode, height - marginColumn, lastHeightMode, lastHeight, lastComputedHeight); return widthIsCompatible && heightIsCompatible; } diff --git a/java/jni/CSSJNI.cpp b/java/jni/CSSJNI.cpp index e1c86237..16869f1a 100644 --- a/java/jni/CSSJNI.cpp +++ b/java/jni/CSSJNI.cpp @@ -40,7 +40,8 @@ static void _jniTransferLayoutOutputsRecursive(CSSNodeRef root) { } static void _jniPrint(CSSNodeRef node) { - auto obj = adopt_local(Environment::current()->NewLocalRef(reinterpret_cast(CSSNodeGetContext(node)))); + auto obj = adopt_local( + Environment::current()->NewLocalRef(reinterpret_cast(CSSNodeGetContext(node)))); cout << obj->toString() << endl; } @@ -49,10 +50,11 @@ static CSSSize _jniMeasureFunc(CSSNodeRef node, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode) { - auto obj = adopt_local(Environment::current()->NewLocalRef(reinterpret_cast(CSSNodeGetContext(node)))); + auto obj = adopt_local( + Environment::current()->NewLocalRef(reinterpret_cast(CSSNodeGetContext(node)))); static auto measureFunc = findClassLocal("com/facebook/csslayout/CSSNode") - ->getMethod("measure"); + ->getMethod("measure"); _jniTransferLayoutDirection(node, obj); const auto measureResult = measureFunc(obj, width, widthMode, height, heightMode); diff --git a/tests/CSSLayoutMeasureCacheTest.cpp b/tests/CSSLayoutMeasureCacheTest.cpp index c0a8e6fa..9378b579 100644 --- a/tests/CSSLayoutMeasureCacheTest.cpp +++ b/tests/CSSLayoutMeasureCacheTest.cpp @@ -17,7 +17,8 @@ static CSSSize _measureMax(CSSNodeRef node, CSSMeasureMode heightMode) { int *measureCount = (int *)CSSNodeGetContext(node); - *measureCount = *measureCount + 1; + (*measureCount)++; + return CSSSize { .width = widthMode == CSSMeasureModeUndefined ? 10 : width, .height = heightMode == CSSMeasureModeUndefined ? 10 : height, diff --git a/tests/CSSLayoutMeasureTest.cpp b/tests/CSSLayoutMeasureTest.cpp index 960f3e50..b238173c 100644 --- a/tests/CSSLayoutMeasureTest.cpp +++ b/tests/CSSLayoutMeasureTest.cpp @@ -10,18 +10,42 @@ #include #include -#if GTEST_HAS_DEATH_TEST static CSSSize _measure(CSSNodeRef node, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode) { + int *measureCount = (int*) CSSNodeGetContext(node); + if (measureCount) { + (*measureCount)++; + } + return CSSSize { - .width = 0, - .height = 0, + .width = 10, + .height = 10, }; } +TEST(CSSLayoutTest, dont_measure_single_grow_shrink_child) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + int measureCount = 0; + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeSetContext(root_child0, &measureCount); + CSSNodeSetMeasureFunc(root_child0, _measure); + CSSNodeStyleSetFlexGrow(root_child0, 1); + CSSNodeStyleSetFlexShrink(root_child0, 1); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, measureCount); +} + +#if GTEST_HAS_DEATH_TEST TEST(CSSLayoutTest, cannot_add_child_to_node_with_measure_func) { const CSSNodeRef root = CSSNodeNew(); CSSNodeSetMeasureFunc(root, _measure);