Skip to content
Permalink
Browse files
Add support for flex-shrink
https://bugs.webkit.org/show_bug.cgi?id=240957

Reviewed by Antti Koivisto.

This patch implements a simple "let's shrink the flex items when available space is limited" logic.

* Source/WebCore/layout/formattingContexts/flex/FlexFormattingContext.cpp:
(WebCore::Layout::FlexFormattingContext::computeLogicalWidthForShrinkingFlexItems):
(WebCore::Layout::FlexFormattingContext::computeLogicalWidthForStretchingFlexItems):
(WebCore::Layout::FlexFormattingContext::computeLogicalWidthForFlexItems):
* Source/WebCore/layout/formattingContexts/flex/FlexFormattingContext.h:

Canonical link: https://commits.webkit.org/251027@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@294904 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
alanbujtas committed May 26, 2022
1 parent 1bebfc4 commit 30c38fd6c36184a210d24b43cc38481ae968e4c6
Showing 2 changed files with 91 additions and 13 deletions.
@@ -204,41 +204,97 @@ void FlexFormattingContext::setFlexItemsGeometry(const LogicalFlexItems& logical
}
}

void FlexFormattingContext::computeLogicalWidthForFlexItems(LogicalFlexItems& logicalFlexItemList, const ConstraintsForFlexContent& flexConstraints)
void FlexFormattingContext::computeLogicalWidthForShrinkingFlexItems(LogicalFlexItems& logicalFlexItemList, LayoutUnit availableSpace)
{
auto& formattingState = this->formattingState();

auto flexDirection = root().style().flexDirection();
auto flexDirectionIsInlineAxis = flexDirection == FlexDirection::Row || flexDirection == FlexDirection::RowReverse;
auto availableSpace = std::optional<LayoutUnit> { flexDirectionIsInlineAxis ? std::make_optional(flexConstraints.horizontal().logicalWidth) : flexConstraints.availableVerticalSpace() };
auto totalShrink = 0.f;
auto totalFlexibleSpace = LayoutUnit { };
auto flexShrinkBase = 0.f;
Vector<size_t> shrinkingItems;

auto computeTotalShrinkAndOverflowingSpace = [&] {
// Collect flex items with non-zero flex-shrink value. flex-shrink: 0 flex items
// don't participate in content flexing.
for (size_t index = 0; index < logicalFlexItemList.size(); ++index) {
auto& logicalFlexItem = logicalFlexItemList[index];
if (auto flexShrink = logicalFlexItem.layoutBox->style().flexShrink()) {
shrinkingItems.append(index);
totalShrink += flexShrink;
totalFlexibleSpace += logicalFlexItem.rect.width();
} else
availableSpace -= logicalFlexItem.rect.width();
}
if (totalShrink)
flexShrinkBase = (totalFlexibleSpace - availableSpace) / totalShrink;
};
computeTotalShrinkAndOverflowingSpace();

auto adjustShrinkBase = [&] {
// Now that we know how much each flex item needs to be shrunk, let's check
// if they hit their minimum content width (i.e. whether they can be sized that small).
for (auto flexItemIndex : shrinkingItems) {
auto& flexItem = logicalFlexItemList[flexItemIndex];

auto flexShrink = flexItem.layoutBox->style().flexShrink();
auto flexedSize = flexItem.rect.width() - (flexShrink * flexShrinkBase);
auto minimumSize = formattingState.intrinsicWidthConstraintsForBox(*flexItem.layoutBox)->minimum;
if (minimumSize >= flexedSize) {
totalShrink -= flexShrink;
totalFlexibleSpace -= flexItem.rect.width();
availableSpace -= minimumSize;
}
}
flexShrinkBase = totalShrink ? (totalFlexibleSpace - availableSpace) / totalShrink : 0.f;
};
adjustShrinkBase();

auto computeLogicalWidth = [&] {
// Adjust the total grow width by the overflow value (shrink) except when min content with disagrees.
for (auto flexItemIndex : shrinkingItems) {
auto& flexItem = logicalFlexItemList[flexItemIndex];

auto flexShrink = flexItem.layoutBox->style().flexShrink();
auto flexedSize = LayoutUnit { flexItem.rect.width() - (flexShrink * flexShrinkBase) };
auto minimumSize = formattingState.intrinsicWidthConstraintsForBox(*flexItem.layoutBox)->minimum;
flexItem.rect.setWidth(std::max(minimumSize, flexedSize));
}
};
computeLogicalWidth();
}

void FlexFormattingContext::computeLogicalWidthForStretchingFlexItems(LogicalFlexItems& logicalFlexItemList, LayoutUnit availableSpace)
{
auto& formattingState = this->formattingState();

auto totalGrowth = 0.f;
auto totalFlexibleSpace = *availableSpace;
auto flexGrowBase = 0.f;
Vector<size_t> flexingItems;
Vector<size_t> stretchingItems;

auto computeTotalGrowthAndFlexibleSpace = [&] {
// Collect flex items with non-zero flex-grow value. flex-grow: 0 (initial) flex items
// don't participate in available space distribution.
for (size_t index = 0; index < logicalFlexItemList.size(); ++index) {
auto& logicalFlexItem = logicalFlexItemList[index];
if (auto flexGrow = logicalFlexItem.layoutBox->style().flexGrow()) {
flexingItems.append(index);
stretchingItems.append(index);
totalGrowth += flexGrow;
} else
totalFlexibleSpace -= logicalFlexItem.rect.width();
availableSpace -= logicalFlexItem.rect.width();
}
if (totalGrowth)
flexGrowBase = totalFlexibleSpace / totalGrowth;
flexGrowBase = availableSpace / totalGrowth;
};
computeTotalGrowthAndFlexibleSpace();
if (!totalGrowth)
return;

auto totalLogicalWidth = [&] {
// This is where we compute how much space the flexing boxes take up if we just
// let them flex by their flex-grow value. Note that we can't size them below their minimum content width.
// Such flex items are removed from the final overflow distribution.
auto accumulatedWidth = LayoutUnit { };
for (auto flexItemIndex : flexingItems) {
for (auto flexItemIndex : stretchingItems) {
auto& flexItem = logicalFlexItemList[flexItemIndex];

auto flexGrow = flexItem.layoutBox->style().flexGrow();
@@ -251,13 +307,13 @@ void FlexFormattingContext::computeLogicalWidthForFlexItems(LogicalFlexItems& lo
accumulatedWidth += flexedSize;
}
return accumulatedWidth;
}();
auto overflowWidth = totalLogicalWidth - totalFlexibleSpace;
};
auto overflowWidth = totalLogicalWidth() - availableSpace;
ASSERT(overflowWidth >= 0);

auto computeLogicalWidth = [&] {
// Adjust the total grow width by the overflow value (shrink) except when min content with disagrees.
for (auto flexItemIndex : flexingItems) {
for (auto flexItemIndex : stretchingItems) {
auto& flexItem = logicalFlexItemList[flexItemIndex];

auto flexGrow = flexItem.layoutBox->style().flexGrow();
@@ -274,6 +330,26 @@ void FlexFormattingContext::computeLogicalWidthForFlexItems(LogicalFlexItems& lo
computeLogicalWidth();
}

void FlexFormattingContext::computeLogicalWidthForFlexItems(LogicalFlexItems& logicalFlexItemList, const ConstraintsForFlexContent& flexConstraints)
{
auto flexDirection = root().style().flexDirection();
auto flexDirectionIsInlineAxis = flexDirection == FlexDirection::Row || flexDirection == FlexDirection::RowReverse;
auto availableSpace = std::optional<LayoutUnit> { flexDirectionIsInlineAxis ? std::make_optional(flexConstraints.horizontal().logicalWidth) : flexConstraints.availableVerticalSpace() };
auto contentLogicalWidth = [&] {
auto logicalWidth = LayoutUnit { };
for (auto& logicalFlexItem : logicalFlexItemList)
logicalWidth += logicalFlexItem.rect.width();
return logicalWidth;
};

if (!availableSpace)
ASSERT_NOT_IMPLEMENTED_YET();
else if (*availableSpace > contentLogicalWidth())
computeLogicalWidthForStretchingFlexItems(logicalFlexItemList, *availableSpace);
else
computeLogicalWidthForShrinkingFlexItems(logicalFlexItemList, *availableSpace);
}

void FlexFormattingContext::layoutInFlowContentForIntegration(const ConstraintsForInFlowContent& constraints)
{
auto logicalFlexItemList = convertFlexItemsToLogicalSpace();
@@ -66,6 +66,8 @@ class FlexFormattingContext final : public FormattingContext {
LogicalFlexItems convertFlexItemsToLogicalSpace();
void setFlexItemsGeometry(const LogicalFlexItems&, const ConstraintsForFlexContent&);
void computeLogicalWidthForFlexItems(LogicalFlexItems&, const ConstraintsForFlexContent&);
void computeLogicalWidthForStretchingFlexItems(LogicalFlexItems&, LayoutUnit availableSpace);
void computeLogicalWidthForShrinkingFlexItems(LogicalFlexItems&, LayoutUnit availableSpace);

const FlexFormattingState& formattingState() const { return downcast<FlexFormattingState>(FormattingContext::formattingState()); }
FlexFormattingState& formattingState() { return downcast<FlexFormattingState>(FormattingContext::formattingState()); }

0 comments on commit 30c38fd

Please sign in to comment.