Skip to content

Commit

Permalink
Fork out Masonry into a separate layout function
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=251459

Reviewed by Matt Woodrow.

I have been finding a significant amount of special cases are getting added to
the grid code to handle masonry. This has been getting rather messy to maintain,
and splitting these two would allow easier changes to the layout to support masonry without
worrying about breaking grid.

* Source/WebCore/rendering/GridMasonryLayout.cpp:
(WebCore::GridMasonryLayout::collectMasonryItems):
* Source/WebCore/rendering/GridTrackSizingAlgorithm.cpp:
(WebCore::GridTrack::cachedTrackSize const):
* Source/WebCore/rendering/GridTrackSizingAlgorithm.h:
(WebCore::GridTrack::cachedTrackSize const): Deleted.
* Source/WebCore/rendering/RenderGrid.cpp:
(WebCore::RenderGrid::layoutBlock):
(WebCore::RenderGrid::layoutGrid):
(WebCore::RenderGrid::layoutMasonry):
* Source/WebCore/rendering/RenderGrid.h:

Canonical link: https://commits.webkit.org/259662@main
  • Loading branch information
stwrt committed Feb 1, 2023
1 parent 664eceb commit 48fe5d1
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 11 deletions.
1 change: 0 additions & 1 deletion Source/WebCore/rendering/GridMasonryLayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ void GridMasonryLayout::collectMasonryItems()
else
m_itemsWithIndefiniteGridAxisPosition.append(child);
}

}
}

Expand Down
6 changes: 6 additions & 0 deletions Source/WebCore/rendering/GridTrackSizingAlgorithm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ void GridTrack::setGrowthLimitCap(std::optional<LayoutUnit> growthLimitCap)
m_growthLimitCap = growthLimitCap;
}

const GridTrackSize& GridTrack::cachedTrackSize() const
{
RELEASE_ASSERT(m_cachedTrackSize);
return *m_cachedTrackSize;
}

void GridTrack::setCachedTrackSize(const GridTrackSize& cachedTrackSize)
{
m_cachedTrackSize = cachedTrackSize;
Expand Down
6 changes: 1 addition & 5 deletions Source/WebCore/rendering/GridTrackSizingAlgorithm.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,7 @@ class GridTrack {
void setGrowthLimitCap(std::optional<LayoutUnit>);
std::optional<LayoutUnit> growthLimitCap() const { return m_growthLimitCap; }

const GridTrackSize& cachedTrackSize() const
{
RELEASE_ASSERT(m_cachedTrackSize);
return *m_cachedTrackSize;
}
const GridTrackSize& cachedTrackSize() const;
void setCachedTrackSize(const GridTrackSize&);

private:
Expand Down
141 changes: 136 additions & 5 deletions Source/WebCore/rendering/RenderGrid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,138 @@ void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit)
if (!relayoutChildren && simplifiedLayout())
return;

// The layoutBlock was handling the layout of both the grid and masonry implementations.
// This caused a huge amount of branching code to handle masonry specific cases. Splitting up the code
// to layout will simplify both implementations.
if (!isMasonry())
layoutGrid(relayoutChildren);
else
layoutMasonry(relayoutChildren);
}

void RenderGrid::layoutGrid(bool relayoutChildren)
{
LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
{
LayoutStateMaintainer statePusher(*this, locationOffset(), isTransformed() || hasReflection() || style().isFlippedBlocksWritingMode());

preparePaginationBeforeBlockLayout(relayoutChildren);
beginUpdateScrollInfoAfterLayoutTransaction();

LayoutSize previousSize = size();

// FIXME: We should use RenderBlock::hasDefiniteLogicalHeight() only but it does not work for positioned stuff.
// FIXME: Consider caching the hasDefiniteLogicalHeight value throughout the layout.
// FIXME: We might need to cache the hasDefiniteLogicalHeight if the call of RenderBlock::hasDefiniteLogicalHeight() causes a relevant performance regression.
bool hasDefiniteLogicalHeight = RenderBlock::hasDefiniteLogicalHeight() || hasOverridingLogicalHeight() || computeContentLogicalHeight(MainOrPreferredSize, style().logicalHeight(), std::nullopt);

auto aspectRatioBlockSizeDependentGridItems = computeAspectRatioDependentAndBaselineItems();

resetLogicalHeightBeforeLayoutIfNeeded();

// Fieldsets need to find their legend and position it inside the border of the object.
// The legend then gets skipped during normal layout. The same is true for ruby text.
// It doesn't get included in the normal layout process but is instead skipped.
layoutExcludedChildren(relayoutChildren);

updateLogicalWidth();

LayoutUnit availableSpaceForColumns = availableLogicalWidth();
placeItemsOnGrid(availableSpaceForColumns);

m_trackSizingAlgorithm.setAvailableSpace(ForColumns, availableSpaceForColumns);
performGridItemsPreLayout(m_trackSizingAlgorithm);

// 1- First, the track sizing algorithm is used to resolve the sizes of the grid columns. At this point the
// logical width is always definite as the above call to updateLogicalWidth() properly resolves intrinsic
// sizes. We cannot do the same for heights though because many code paths inside updateLogicalHeight() require
// a previous call to setLogicalHeight() to resolve heights properly (like for positioned items for example).
computeTrackSizesForDefiniteSize(ForColumns, availableSpaceForColumns);

// 1.5- Compute Content Distribution offsets for column tracks
computeContentPositionAndDistributionOffset(ForColumns, m_trackSizingAlgorithm.freeSpace(ForColumns).value(), nonCollapsedTracks(ForColumns));

// 2- Next, the track sizing algorithm resolves the sizes of the grid rows,
// using the grid column sizes calculated in the previous step.
bool shouldRecomputeHeight = false;
if (!hasDefiniteLogicalHeight) {
computeTrackSizesForIndefiniteSize(m_trackSizingAlgorithm, ForRows);
if (shouldApplySizeContainment())
shouldRecomputeHeight = true;
} else
computeTrackSizesForDefiniteSize(ForRows, availableLogicalHeight(ExcludeMarginBorderPadding));

LayoutUnit trackBasedLogicalHeight = borderAndPaddingLogicalHeight() + scrollbarLogicalHeight();
if (auto size = explicitIntrinsicInnerLogicalSize(ForRows))
trackBasedLogicalHeight += size.value();
else
trackBasedLogicalHeight += m_trackSizingAlgorithm.computeTrackBasedSize();

if (shouldRecomputeHeight)
computeTrackSizesForDefiniteSize(ForRows, trackBasedLogicalHeight);

setLogicalHeight(trackBasedLogicalHeight);

updateLogicalHeight();

// Once grid's indefinite height is resolved, we can compute the
// available free space for Content Alignment.
if (!hasDefiniteLogicalHeight)
m_trackSizingAlgorithm.setFreeSpace(ForRows, logicalHeight() - trackBasedLogicalHeight);

// 2.5- Compute Content Distribution offsets for rows tracks
computeContentPositionAndDistributionOffset(ForRows, m_trackSizingAlgorithm.freeSpace(ForRows).value(), nonCollapsedTracks(ForRows));

if (!aspectRatioBlockSizeDependentGridItems.isEmpty()) {
updateGridAreaForAspectRatioItems(aspectRatioBlockSizeDependentGridItems);
updateLogicalWidth();
}

// 3- If the min-content contribution of any grid items have changed based on the row
// sizes calculated in step 2, steps 1 and 2 are repeated with the new min-content
// contribution (once only).
repeatTracksSizingIfNeeded(availableSpaceForColumns, contentLogicalHeight());

// Grid container should have the minimum height of a line if it's editable. That does not affect track sizing though.
if (hasLineIfEmpty()) {
LayoutUnit minHeightForEmptyLine = borderAndPaddingLogicalHeight()
+ lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)
+ scrollbarLogicalHeight();
setLogicalHeight(std::max(logicalHeight(), minHeightForEmptyLine));
}

layoutGridItems();

endAndCommitUpdateScrollInfoAfterLayoutTransaction();

if (size() != previousSize)
relayoutChildren = true;

m_outOfFlowItemColumn.clear();
m_outOfFlowItemRow.clear();

layoutPositionedObjects(relayoutChildren || isDocumentElementRenderer());
m_trackSizingAlgorithm.reset();

computeOverflow(layoutOverflowLogicalBottom(*this));
}

updateLayerTransform();

// Update our scroll information if we're overflow:auto/scroll/hidden now that we know if
// we overflow or not.
updateScrollInfoAfterLayout();

repainter.repaintAfterLayout();

clearNeedsLayout();

m_trackSizingAlgorithm.clearBaselineItemsCache();
m_baselineItemsCached = false;
}

void RenderGrid::layoutMasonry(bool relayoutChildren)
{
LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
{
LayoutStateMaintainer statePusher(*this, locationOffset(), isTransformed() || hasReflection() || style().isFlippedBlocksWritingMode());
Expand Down Expand Up @@ -293,9 +425,9 @@ void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit)
m_trackSizingAlgorithm.setAvailableSpace(ForColumns, availableSpaceForColumns);
performGridItemsPreLayout(m_trackSizingAlgorithm);

// 1- First, the track sizing algorithm is used to resolve the sizes of the grid columns. At this point the
// logical width is always definite as the above call to updateLogicalWidth() properly resolves intrinsic
// sizes. We cannot do the same for heights though because many code paths inside updateLogicalHeight() require
// 1- First, the track sizing algorithm is used to resolve the sizes of the grid columns. At this point the
// logical width is always definite as the above call to updateLogicalWidth() properly resolves intrinsic
// sizes. We cannot do the same for heights though because many code paths inside updateLogicalHeight() require
// a previous call to setLogicalHeight() to resolve heights properly (like for positioned items for example).
computeTrackSizesForDefiniteSize(ForColumns, availableSpaceForColumns);

Expand Down Expand Up @@ -330,7 +462,7 @@ void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit)
else {
if (areMasonryRows())
trackBasedLogicalHeight += m_masonryLayout.gridContentSize() + m_masonryLayout.gridGap();
else
else
trackBasedLogicalHeight += m_trackSizingAlgorithm.computeTrackBasedSize();
}
if (shouldRecomputeHeight)
Expand All @@ -353,7 +485,6 @@ void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit)
updateLogicalWidth();
}


// 3- If the min-content contribution of any grid items have changed based on the row
// sizes calculated in step 2, steps 1 and 2 are repeated with the new min-content
// contribution (once only).
Expand Down
7 changes: 7 additions & 0 deletions Source/WebCore/rendering/RenderGrid.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ class RenderGrid final : public RenderBlock {

StyleContentAlignmentData contentAlignment(GridTrackSizingDirection) const;

// These functions handle the actual implementation of layoutBlock based on if
// the grid is a standard grid or a masonry one. While masonry is an extension of grid,
// keeping the logic in the same function was leading to a messy amount of if statements being added to handle
// specific masonry cases.
void layoutGrid(bool);
void layoutMasonry(bool);

// Computes the span relative to this RenderGrid, even if the RenderBox is a child
// of a descendant subgrid.
GridSpan gridSpanForChild(const RenderBox&, GridTrackSizingDirection) const;
Expand Down

0 comments on commit 48fe5d1

Please sign in to comment.