diff --git a/Tests/LibWeb/Layout/expected/grid/row-span-2.txt b/Tests/LibWeb/Layout/expected/grid/row-span-2.txt new file mode 100644 index 0000000000..fd01a6aba5 --- /dev/null +++ b/Tests/LibWeb/Layout/expected/grid/row-span-2.txt @@ -0,0 +1,105 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer at (0,0) content-size 800x600 [BFC] children: not-inline + BlockContainer at (8,8) content-size 784x193.375 children: not-inline + Box 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 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 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 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> diff --git a/Tests/LibWeb/Layout/input/grid/row-span-2.html b/Tests/LibWeb/Layout/input/grid/row-span-2.html new file mode 100644 index 0000000000..1ebede8ea1 --- /dev/null +++ b/Tests/LibWeb/Layout/input/grid/row-span-2.html @@ -0,0 +1,53 @@ + +
+
+In a sollicitudin augue. Sed ante augue, rhoncus nec porttitor id, +lacinia et nibh. Pellentesque diam libero, ultrices eget eleifend at, +consequat ut orci. +
+
+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. +
+
+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. +
+
\ No newline at end of file diff --git a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp index bc02b99832..913f231a5e 100644 --- a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp @@ -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 track’s 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& 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 item’s size contribution to find the item’s + // 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 track’s 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 track’s item-incurred increase is larger than the track’s planned increase + // set the track’s 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 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 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 track’s 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 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 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 track’s 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 diff --git a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h index 04d542917f..dc8742d572 100644 --- a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h @@ -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& 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);