1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 19:27:44 +00:00

LibWeb: Start implementing sizing for tracks with span > 1 items in GFC

Partially implements:
- Increase sizes to accommodate spanning items crossing content-sized
  tracks
- Increase sizes to accommodate spanning items crossing flexible tracks

from https://www.w3.org/TR/css-grid-2/#algo-content
This commit is contained in:
Aliaksandr Kalenik 2023-05-14 19:16:26 +03:00 committed by Andreas Kling
parent 7a5603052a
commit 87a7299078
4 changed files with 305 additions and 45 deletions

View file

@ -0,0 +1,105 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (0,0) content-size 800x600 [BFC] children: not-inline
BlockContainer <body> at (8,8) content-size 784x193.375 children: not-inline
Box <div.grid-container> at (8,8) content-size 784x193.375 [GFC] children: not-inline
BlockContainer <(anonymous)> at (8,8) content-size 0x0 [BFC] children: inline
TextNode <#text>
BlockContainer <div.grid-item.item-span-one-one> at (401.46875,8) content-size 392x131.296875 [BFC] children: inline
line 0 width: 319.171875, height: 17.46875, bottom: 17.46875, baseline: 13.53125
frag 0 from TextNode start: 1, length: 40, rect: [401.46875,8 319.171875x17.46875]
"In a sollicitudin augue. Sed ante augue,"
line 1 width: 335.125, height: 17.9375, bottom: 35.40625, baseline: 13.53125
frag 0 from TextNode start: 42, length: 42, rect: [401.46875,25 335.125x17.46875]
"rhoncus nec porttitor id, lacinia et nibh."
line 2 width: 378.625, height: 18.40625, bottom: 53.34375, baseline: 13.53125
frag 0 from TextNode start: 85, length: 48, rect: [401.46875,42 378.625x17.46875]
"Pellentesque diam libero, ultrices eget eleifend"
line 3 width: 182.8125, height: 17.875, bottom: 70.28125, baseline: 13.53125
frag 0 from TextNode start: 134, length: 22, rect: [401.46875,60 182.8125x17.46875]
"at, consequat ut orci."
TextNode <#text>
BlockContainer <(anonymous)> at (8,8) content-size 0x0 [BFC] children: inline
TextNode <#text>
BlockContainer <div.grid-item.item-span-one-two> at (401.46875,139.296875) content-size 392x184.109375 [BFC] children: inline
line 0 width: 359.15625, height: 17.46875, bottom: 17.46875, baseline: 13.53125
frag 0 from TextNode start: 1, length: 43, rect: [401.46875,139.296875 359.15625x17.46875]
"Suspendisse potenti. Pellentesque at varius"
line 1 width: 318.5625, height: 17.9375, bottom: 35.40625, baseline: 13.53125
frag 0 from TextNode start: 45, length: 41, rect: [401.46875,156.296875 318.5625x17.46875]
"lacus, sed sollicitudin leo. Pellentesque"
line 2 width: 377.640625, height: 18.40625, bottom: 53.34375, baseline: 13.53125
frag 0 from TextNode start: 87, length: 44, rect: [401.46875,173.296875 377.640625x17.46875]
"malesuada mi eget pellentesque tempor. Donec"
line 3 width: 378.03125, height: 17.875, bottom: 70.28125, baseline: 13.53125
frag 0 from TextNode start: 132, length: 47, rect: [401.46875,191.296875 378.03125x17.46875]
"egestas mauris est, ut lobortis nisi luctus at."
line 4 width: 345.953125, height: 18.34375, bottom: 88.21875, baseline: 13.53125
frag 0 from TextNode start: 180, length: 41, rect: [401.46875,208.296875 345.953125x17.46875]
"Vivamus eleifend, lorem vulputate maximus"
line 5 width: 312.765625, height: 17.8125, bottom: 105.15625, baseline: 13.53125
frag 0 from TextNode start: 222, length: 37, rect: [401.46875,226.296875 312.765625x17.46875]
"porta, nunc metus porttitor nibh, nec"
line 6 width: 242.921875, height: 18.28125, bottom: 123.09375, baseline: 13.53125
frag 0 from TextNode start: 260, length: 31, rect: [401.46875,243.296875 242.921875x17.46875]
"bibendum nulla lectus ut felis."
TextNode <#text>
BlockContainer <(anonymous)> at (8,8) content-size 0x0 [BFC] children: inline
TextNode <#text>
BlockContainer <div.grid-item.item-span-two> at (8,8) content-size 393.46875x315.40625 [BFC] children: inline
line 0 width: 337.6875, height: 17.46875, bottom: 17.46875, baseline: 13.53125
frag 0 from TextNode start: 1, length: 39, rect: [8,8 337.6875x17.46875]
"Lorem ipsum dolor sit amet, consectetur"
line 1 width: 376.34375, height: 17.9375, bottom: 35.40625, baseline: 13.53125
frag 0 from TextNode start: 41, length: 47, rect: [8,25 376.34375x17.46875]
"adipiscing elit. Sed vitae condimentum erat, ac"
line 2 width: 365.84375, height: 18.40625, bottom: 53.34375, baseline: 13.53125
frag 0 from TextNode start: 89, length: 45, rect: [8,42 365.84375x17.46875]
"posuere arcu. Aenean tincidunt mi ligula, vel"
line 3 width: 381.96875, height: 17.875, bottom: 70.28125, baseline: 13.53125
frag 0 from TextNode start: 135, length: 46, rect: [8,60 381.96875x17.46875]
"semper dolor aliquet at. Phasellus scelerisque"
line 4 width: 377.203125, height: 18.34375, bottom: 88.21875, baseline: 13.53125
frag 0 from TextNode start: 182, length: 45, rect: [8,77 377.203125x17.46875]
"dapibus diam sed rhoncus. Proin sed orci leo."
line 5 width: 375.390625, height: 17.8125, bottom: 105.15625, baseline: 13.53125
frag 0 from TextNode start: 228, length: 45, rect: [8,95 375.390625x17.46875]
"Praesent pellentesque mi eu nunc gravida, vel"
line 6 width: 383.53125, height: 18.28125, bottom: 123.09375, baseline: 13.53125
frag 0 from TextNode start: 274, length: 46, rect: [8,112 383.53125x17.46875]
"consectetur nulla malesuada. Sed pellentesque,"
line 7 width: 344.8125, height: 17.75, bottom: 140.03125, baseline: 13.53125
frag 0 from TextNode start: 321, length: 47, rect: [8,130 344.8125x17.46875]
"elit sit amet sollicitudin sollicitudin, lectus"
line 8 width: 374.703125, height: 18.21875, bottom: 157.96875, baseline: 13.53125
frag 0 from TextNode start: 369, length: 46, rect: [8,147 374.703125x17.46875]
"justo facilisis lacus, ac vehicula metus neque"
line 9 width: 384.125, height: 17.6875, bottom: 174.90625, baseline: 13.53125
frag 0 from TextNode start: 416, length: 45, rect: [8,165 384.125x17.46875]
"ac mi. In in augue et massa maximus venenatis"
line 10 width: 373.25, height: 18.15625, bottom: 192.84375, baseline: 13.53125
frag 0 from TextNode start: 462, length: 44, rect: [8,182 373.25x17.46875]
"auctor fermentum dui. Aliquam dictum finibus"
line 11 width: 288.203125, height: 17.625, bottom: 209.78125, baseline: 13.53125
frag 0 from TextNode start: 507, length: 35, rect: [8,200 288.203125x17.46875]
"urna, quis lacinia massa laoreet a."
line 12 width: 316.296875, height: 18.09375, bottom: 227.71875, baseline: 13.53125
frag 0 from TextNode start: 543, length: 36, rect: [8,217 316.296875x17.46875]
"Suspendisse elementum non lectus nec"
line 13 width: 388.78125, height: 17.5625, bottom: 244.65625, baseline: 13.53125
frag 0 from TextNode start: 580, length: 48, rect: [8,235 388.78125x17.46875]
"elementum. Quisque ultricies suscipit porttitor."
line 14 width: 373.828125, height: 18.03125, bottom: 262.59375, baseline: 13.53125
frag 0 from TextNode start: 629, length: 45, rect: [8,252 373.828125x17.46875]
"Sed non urna rutrum, mattis nulla at, feugiat"
line 15 width: 368.75, height: 17.5, bottom: 279.53125, baseline: 13.53125
frag 0 from TextNode start: 675, length: 48, rect: [8,270 368.75x17.46875]
"erat. Duis orci elit, vehicula sed blandit eget,"
line 16 width: 390.625, height: 17.96875, bottom: 297.46875, baseline: 13.53125
frag 0 from TextNode start: 724, length: 46, rect: [8,287 390.625x17.46875]
"auctor in arcu. Ut cursus magna sit amet nulla"
line 17 width: 294.90625, height: 18.4375, bottom: 315.40625, baseline: 13.53125
frag 0 from TextNode start: 771, length: 36, rect: [8,304 294.90625x17.46875]
"cursus, vitae gravida mauris dictum."
TextNode <#text>
BlockContainer <(anonymous)> at (8,8) content-size 0x0 [BFC] children: inline
TextNode <#text>

View file

@ -0,0 +1,53 @@
<style>
.grid-container {
display: grid;
grid-template-columns: auto auto;
grid-template-rows: auto auto;
}
.item-span-one-one {
background-color: lightpink;
grid-row: 1 / span 1;
grid-column: 2;
}
.item-span-one-two {
background-color: lightgreen;
grid-row: 2 / span 1;
grid-column: 2;
}
.item-span-two {
background-color: lightskyblue;
grid-row: 1 / span 2;
grid-column: 1;
}
</style>
<div class="grid-container">
<div class="grid-item item-span-one-one">
In a sollicitudin augue. Sed ante augue, rhoncus nec porttitor id,
lacinia et nibh. Pellentesque diam libero, ultrices eget eleifend at,
consequat ut orci.
</div>
<div class="grid-item item-span-one-two">
Suspendisse potenti. Pellentesque at varius lacus, sed sollicitudin leo.
Pellentesque malesuada mi eget pellentesque tempor. Donec egestas mauris
est, ut lobortis nisi luctus at. Vivamus eleifend, lorem vulputate
maximus porta, nunc metus porttitor nibh, nec bibendum nulla lectus ut
felis.
</div>
<div class="grid-item item-span-two">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vitae
condimentum erat, ac posuere arcu. Aenean tincidunt mi ligula, vel
semper dolor aliquet at. Phasellus scelerisque dapibus diam sed rhoncus.
Proin sed orci leo. Praesent pellentesque mi eu nunc gravida, vel
consectetur nulla malesuada. Sed pellentesque, elit sit amet
sollicitudin sollicitudin, lectus justo facilisis lacus, ac vehicula
metus neque ac mi. In in augue et massa maximus venenatis auctor
fermentum dui. Aliquam dictum finibus urna, quis lacinia massa laoreet
a. Suspendisse elementum non lectus nec elementum. Quisque ultricies
suscipit porttitor. Sed non urna rutrum, mattis nulla at, feugiat erat.
Duis orci elit, vehicula sed blandit eget, auctor in arcu. Ut cursus
magna sit amet nulla cursus, vitae gravida mauris dictum.
</div>
</div>

View file

@ -841,55 +841,18 @@ void GridFormattingContext::resolve_intrinsic_track_sizes(AvailableSpace const&
// 3. Increase sizes to accommodate spanning items crossing content-sized tracks: Next, consider the
// items with a span of 2 that do not span a track with a flexible sizing function.
// FIXME: Content-sized tracks not implemented (min-content, etc.)
// 3.1. For intrinsic minimums: First increase the base size of tracks with an intrinsic min track sizing
// function by distributing extra space as needed to accommodate these items minimum contributions.
// If the grid container is being sized under a min- or max-content constraint, use the items
// limited min-content contributions in place of their minimum contributions here. (For an item
// spanning multiple tracks, the upper limit used to calculate its limited min-/max-content
// contribution is the sum of the fixed max track sizing functions of any tracks it spans, and is
// applied if it only spans such tracks.)
// 3.2. For content-based minimums: Next continue to increase the base size of tracks with a min track
// sizing function of min-content or max-content by distributing extra space as needed to account
// for these items' min-content contributions.
// 3.3. For max-content minimums: Next, if the grid container is being sized under a max-content
// constraint, continue to increase the base size of tracks with a min track sizing function of auto
// or max-content by distributing extra space as needed to account for these items' limited
// max-content contributions.
// In all cases, continue to increase the base size of tracks with a min track sizing function of
// max-content by distributing extra space as needed to account for these items' max-content
// contributions.
// 3.4. If at this point any tracks growth limit is now less than its base size, increase its growth
// limit to match its base size.
// 3.5. For intrinsic maximums: Next increase the growth limit of tracks with an intrinsic max track
// sizing function by distributing extra space as needed to account for these items' min-content
// contributions. Mark any tracks whose growth limit changed from infinite to finite in this step as
// infinitely growable for the next step.
// 3.6. For max-content maximums: Lastly continue to increase the growth limit of tracks with a max track
// sizing function of max-content by distributing extra space as needed to account for these items'
// max-content contributions. However, limit the growth of any fit-content() tracks by their
// fit-content() argument.
// Repeat incrementally for items with greater spans until all items have been considered.
size_t max_item_span = 1;
for (auto& item : m_grid_items)
max_item_span = max(item.span(dimension), max_item_span);
for (size_t span = 2; span <= max_item_span; span++) {
increase_sizes_to_accommodate_spanning_items_crossing_content_sized_tracks(dimension, 2);
}
// FIXME: 4. Increase sizes to accommodate spanning items crossing flexible tracks: Next, repeat the previous
// 4. Increase sizes to accommodate spanning items crossing flexible tracks: Next, repeat the previous
// step instead considering (together, rather than grouped by span size) all items that do span a
// track with a flexible sizing function while
// - distributing space only to flexible tracks (i.e. treating all other tracks as having a fixed
// sizing function)
// - if the sum of the flexible sizing functions of all flexible tracks spanned by the item is greater
// than zero, distributing space to such tracks according to the ratios of their flexible sizing
// functions rather than distributing space equally
increase_sizes_to_accommodate_spanning_items_crossing_flexible_tracks(dimension);
// 5. If any track still has an infinite growth limit (because, for example, it had no items placed in
// it or it is a flexible track), set its growth limit to its base size.
@ -903,6 +866,140 @@ void GridFormattingContext::resolve_intrinsic_track_sizes(AvailableSpace const&
track.has_definite_base_size = true;
}
void GridFormattingContext::distribute_extra_space_across_spanned_tracks(CSSPixels item_size_contribution, Vector<TemporaryTrack&>& spanned_tracks)
{
for (auto& track : spanned_tracks)
track.planned_increase = 0;
// 1. Find the space to distribute:
CSSPixels spanned_tracks_sizes_sum = 0;
for (auto& track : spanned_tracks)
spanned_tracks_sizes_sum += track.base_size;
// Subtract the corresponding size of every spanned track from the items size contribution to find the items
// remaining size contribution.
auto extra_space = max(CSSPixels(0), item_size_contribution - spanned_tracks_sizes_sum);
// 2. Distribute space up to limits:
while (extra_space > 0) {
auto all_frozen = all_of(spanned_tracks, [](auto const& track) { return track.frozen; });
if (all_frozen)
break;
// Find the item-incurred increase for each spanned track with an affected size by: distributing the space
// equally among such tracks, freezing a tracks item-incurred increase as its affected size + item-incurred
// increase reaches its limit
CSSPixels increase_per_track = extra_space / spanned_tracks.size();
for (auto& track : spanned_tracks) {
if (increase_per_track >= track.growth_limit) {
track.frozen = true;
track.item_incurred_increase = track.growth_limit;
extra_space -= track.growth_limit;
} else {
track.item_incurred_increase += increase_per_track;
extra_space -= increase_per_track;
}
}
}
// FIXME: 3. Distribute space beyond limits
// 4. For each affected track, if the tracks item-incurred increase is larger than the tracks planned increase
// set the tracks planned increase to that value.
for (auto& track : spanned_tracks) {
if (track.item_incurred_increase > track.planned_increase)
track.planned_increase = track.item_incurred_increase;
}
}
void GridFormattingContext::increase_sizes_to_accommodate_spanning_items_crossing_content_sized_tracks(GridDimension const dimension, size_t span)
{
auto& tracks = dimension == GridDimension::Column ? m_grid_columns : m_grid_rows;
for (auto& item : m_grid_items) {
auto const item_span = item.span(dimension);
if (item_span != span)
continue;
Vector<TemporaryTrack&> spanned_tracks;
auto item_start_track_index = item.raw_position(dimension);
for (size_t span = 0; span < item_span; span++) {
auto& track = tracks[item_start_track_index + span];
spanned_tracks.append(track);
}
auto item_spans_tracks_with_flexible_sizing_function = any_of(spanned_tracks, [](auto& track) {
return track.min_track_sizing_function.is_flexible_length() || track.max_track_sizing_function.is_flexible_length();
});
if (item_spans_tracks_with_flexible_sizing_function)
continue;
// 1. For intrinsic minimums: First increase the base size of tracks with an intrinsic min track sizing
// function by distributing extra space as needed to accommodate these items minimum contributions.
Vector<TemporaryTrack&> intrinsic_minimum_spanned_tracks;
for (auto& track : spanned_tracks) {
if (track.min_track_sizing_function.is_intrinsic_track_sizing())
intrinsic_minimum_spanned_tracks.append(track);
}
auto item_minimum_contribution = calculate_minimum_contribution(item, dimension);
distribute_extra_space_across_spanned_tracks(item_minimum_contribution, intrinsic_minimum_spanned_tracks);
for (auto& track : spanned_tracks) {
track.base_size += track.planned_increase;
}
// 4. If at this point any tracks growth limit is now less than its base size, increase its growth limit to
// match its base size.
for (auto& track : tracks) {
if (track.growth_limit < track.base_size)
track.growth_limit = track.base_size;
}
}
}
void GridFormattingContext::increase_sizes_to_accommodate_spanning_items_crossing_flexible_tracks(GridDimension const dimension)
{
auto& tracks = dimension == GridDimension::Column ? m_grid_columns : m_grid_rows;
for (auto& item : m_grid_items) {
Vector<TemporaryTrack&> spanned_tracks;
auto item_start_track_index = item.raw_position(dimension);
size_t span = 0;
// FIXME: out of bounds check should not be needed here and currently present only
// because there is some placement bug for tracks with repeat()
while (span < item.span(dimension) && item_start_track_index + span < tracks.size()) {
auto& track = tracks[item_start_track_index + span];
spanned_tracks.append(track);
span++;
}
auto item_spans_tracks_with_flexible_sizing_function = any_of(spanned_tracks, [](auto& track) {
return track.min_track_sizing_function.is_flexible_length() || track.max_track_sizing_function.is_flexible_length();
});
if (!item_spans_tracks_with_flexible_sizing_function)
continue;
// 1. For intrinsic minimums: First increase the base size of tracks with an intrinsic min track sizing
// function by distributing extra space as needed to accommodate these items minimum contributions.
Vector<TemporaryTrack&> spanned_flexible_tracks;
for (auto& track : spanned_tracks) {
if (track.min_track_sizing_function.is_flexible_length())
spanned_flexible_tracks.append(track);
}
auto item_minimum_contribution = calculate_limited_min_content_contribution(item, dimension);
distribute_extra_space_across_spanned_tracks(item_minimum_contribution, spanned_flexible_tracks);
for (auto& track : spanned_tracks) {
track.base_size += track.planned_increase;
}
// 4. If at this point any tracks growth limit is now less than its base size, increase its growth limit to
// match its base size.
for (auto& track : tracks) {
if (track.growth_limit < track.base_size)
track.growth_limit = track.base_size;
}
}
}
void GridFormattingContext::maximize_tracks(AvailableSpace const& available_space, GridDimension const dimension)
{
// https://www.w3.org/TR/css-grid-2/#algo-grow-tracks

View file

@ -99,6 +99,8 @@ private:
CSSPixels growth_limit { 0 };
CSSPixels space_to_distribute { 0 };
CSSPixels planned_increase { 0 };
CSSPixels item_incurred_increase { 0 };
bool frozen { false };
bool is_gap { false };
CSSPixels border_left { 0 };
@ -189,6 +191,9 @@ private:
void initialize_track_sizes(AvailableSpace const&, GridDimension const);
void resolve_intrinsic_track_sizes(AvailableSpace const&, GridDimension const);
void distribute_extra_space_across_spanned_tracks(CSSPixels item_size_contribution, Vector<TemporaryTrack&>& spanned_tracks);
void increase_sizes_to_accommodate_spanning_items_crossing_content_sized_tracks(GridDimension const, size_t span);
void increase_sizes_to_accommodate_spanning_items_crossing_flexible_tracks(GridDimension const);
void maximize_tracks(AvailableSpace const&, GridDimension const);
void expand_flexible_tracks(AvailableSpace const&, GridDimension const);
void stretch_auto_tracks(AvailableSpace const&, GridDimension const);