diff --git a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp index 5d1b8b942a..f7236ad651 100644 --- a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp @@ -139,6 +139,80 @@ int GridFormattingContext::count_of_repeated_auto_fill_or_fit_tracks(GridDimensi // floor be 1px. } +GridFormattingContext::PlacementPosition GridFormattingContext::resolve_grid_position(Box const& child_box, GridDimension const dimension) +{ + auto const& computed_values = child_box.computed_values(); + auto const& placement_start = dimension == GridDimension::Row ? computed_values.grid_row_start() : computed_values.grid_column_start(); + auto const& placement_end = dimension == GridDimension::Row ? computed_values.grid_row_end() : computed_values.grid_column_end(); + + PlacementPosition result; + + if (placement_start.has_line_number() && placement_start.line_number() > 0) + result.start = placement_start.line_number() - 1; + else if (placement_start.has_line_number()) { + auto explicit_line_count = dimension == GridDimension::Row ? m_explicit_rows_line_count : m_explicit_columns_line_count; + result.start = explicit_line_count + placement_start.line_number(); + } + if (placement_end.has_line_number()) + result.end = placement_end.line_number() - 1; + + if (result.end < 0) { + if (dimension == GridDimension::Row) + result.end = m_occupation_grid.row_count() + result.end + 2; + else + result.end = m_occupation_grid.column_count() + result.end + 2; + } + + if (placement_start.has_line_number() && placement_end.is_span()) + result.span = placement_end.span(); + if (placement_end.has_line_number() && placement_start.is_span()) { + result.span = placement_start.span(); + result.start = result.end - result.span; + // FIXME: Remove me once have implemented spans overflowing into negative indexes, e.g., grid-row: span 2 / 1 + if (result.start < 0) + result.start = 0; + } + + if (placement_end.has_identifier()) { + if (auto maybe_grid_area = m_grid_areas.get(placement_end.identifier()); maybe_grid_area.has_value()) { + result.end = dimension == GridDimension::Row ? maybe_grid_area->row_end : maybe_grid_area->column_end; + } else if (auto line_name_index = get_line_index_by_line_name(dimension, placement_end.identifier()); line_name_index.has_value()) { + result.end = line_name_index.value(); + } else { + result.end = 1; + } + result.start = result.end - 1; + } + + if (placement_start.has_identifier()) { + if (auto maybe_grid_area = m_grid_areas.get(placement_start.identifier()); maybe_grid_area.has_value()) { + result.start = dimension == GridDimension::Row ? maybe_grid_area->row_start : maybe_grid_area->column_start; + } else if (auto line_name_index = get_line_index_by_line_name(dimension, placement_start.identifier()); line_name_index.has_value()) { + result.start = line_name_index.value(); + } else { + result.start = 0; + } + } + + if (placement_start.is_positioned() && placement_end.is_positioned()) { + if (result.start > result.end) + swap(result.start, result.end); + if (result.start != result.end) + result.span = result.end - result.start; + } + + // FIXME: Have yet to find the spec for this. + if (!placement_start.is_positioned() && placement_end.is_positioned() && result.end == 0) + result.start = 0; + + // If the placement contains two spans, remove the one contributed by the end grid-placement + // property. + if (placement_start.is_span() && placement_end.is_span()) + result.span = placement_start.span(); + + return result; +} + void GridFormattingContext::place_item_with_row_and_column_position(Box const& child_box) { auto const& grid_row_start = child_box.computed_values().grid_row_start(); @@ -292,112 +366,14 @@ void GridFormattingContext::place_item_with_row_and_column_position(Box const& c void GridFormattingContext::place_item_with_row_position(Box const& child_box) { - auto const& grid_row_start = child_box.computed_values().grid_row_start(); - auto const& grid_row_end = child_box.computed_values().grid_row_end(); + auto placement_position = resolve_grid_position(child_box, GridDimension::Row); + auto row_start = placement_position.start; + size_t row_span = placement_position.span; + auto const& grid_column_start = child_box.computed_values().grid_column_start(); - - int row_start = 0, row_end = 0; - - if (grid_row_start.has_line_number()) - row_start = grid_row_start.line_number() - 1; - if (grid_row_end.has_line_number()) - row_end = grid_row_end.line_number() - 1; - - // https://www.w3.org/TR/css-grid-2/#line-placement - // 8.3. Line-based Placement: the grid-row-start, grid-column-start, grid-row-end, and grid-column-end properties - - // https://www.w3.org/TR/css-grid-2/#grid-placement-slot - // First attempt to match the grid area’s edge to a named grid area: if there is a grid line whose - // line name is -start (for grid-*-start) / -end (for grid-*-end), - // contributes the first such line to the grid item’s placement. - - // Otherwise, treat this as if the integer 1 had been specified along with the . - - // https://www.w3.org/TR/css-grid-2/#grid-placement-int - // Contributes the Nth grid line to the grid item’s placement. If a negative integer is given, it - // instead counts in reverse, starting from the end edge of the explicit grid. - if (row_end < 0) - row_end = m_occupation_grid.row_count() + row_end + 2; - - // If a name is given as a , only lines with that name are counted. If not enough - // lines with that name exist, all implicit grid lines are assumed to have that name for the purpose - // of finding this position. - - // https://www.w3.org/TR/css-grid-2/#grid-placement-span-int - // Contributes a grid span to the grid item’s placement such that the corresponding edge of the grid - // item’s grid area is N lines from its opposite edge in the corresponding direction. For example, - // grid-column-end: span 2 indicates the second grid line in the endward direction from the - // grid-column-start line. - size_t row_span = 1; - if (grid_row_start.has_line_number() && grid_row_end.is_span()) - row_span = grid_row_end.span(); - if (grid_row_end.has_line_number() && grid_row_start.is_span()) { - row_span = grid_row_start.span(); - row_start = row_end - row_span; - // FIXME: Remove me once have implemented spans overflowing into negative indexes, e.g., grid-row: span 2 / 1 - if (row_start < 0) - row_start = 0; - } - - // If a name is given as a , only lines with that name are counted. If not enough - // lines with that name exist, all implicit grid lines on the side of the explicit grid - // corresponding to the search direction are assumed to have that name for the purpose of counting - // this span. - - // https://drafts.csswg.org/css-grid/#grid-placement-auto - // auto - // The property contributes nothing to the grid item’s placement, indicating auto-placement or a - // default span of one. (See § 8 Placing Grid Items, above.) - - // https://www.w3.org/TR/css-grid-2/#common-uses-named-lines - // 8.1.3. Named Lines and Spans - // Instead of counting lines by number, lines can be referenced by their line name: - if (grid_row_end.has_identifier()) { - if (auto maybe_grid_area = m_grid_areas.get(grid_row_end.identifier()); maybe_grid_area.has_value()) - row_end = maybe_grid_area->row_end; - else if (auto line_name_index = get_line_index_by_line_name(GridDimension::Row, grid_row_end.identifier()); line_name_index.has_value()) - row_end = line_name_index.value() - 1; - else - row_end = 1; - row_start = row_end - 1; - } - - if (grid_row_start.has_identifier()) { - if (auto maybe_grid_area = m_grid_areas.get(grid_row_start.identifier()); maybe_grid_area.has_value()) - row_start = maybe_grid_area->row_start; - else if (auto line_name_index = get_line_index_by_line_name(GridDimension::Row, grid_row_start.identifier()); line_name_index.has_value()) - row_start = line_name_index.value(); - else - row_start = 0; - } - - // If there are multiple lines of the same name, they effectively establish a named set of grid - // lines, which can be exclusively indexed by filtering the placement by name: - - // https://drafts.csswg.org/css-grid/#grid-placement-errors - // 8.3.1. Grid Placement Conflict Handling - // If the placement for a grid item contains two lines, and the start line is further end-ward than - // the end line, swap the two lines. If the start line is equal to the end line, remove the end - // line. - if (grid_row_start.is_positioned() && grid_row_end.is_positioned()) { - if (row_start > row_end) - swap(row_start, row_end); - if (row_start != row_end) - row_span = row_end - row_start; - } - // FIXME: Have yet to find the spec for this. - if (!grid_row_start.is_positioned() && grid_row_end.is_positioned() && row_end == 0) - row_start = 0; - - // If the placement contains two spans, remove the one contributed by the end grid-placement - // property. - if (grid_row_start.is_span() && grid_row_end.is_span()) - row_span = grid_row_start.span(); - - // FIXME: If the placement contains only a span for a named line, replace it with a span of 1. - int column_start = 0; size_t column_span = grid_column_start.is_span() ? grid_column_start.span() : 1; + bool found_available_column = false; for (size_t column_index = column_start; column_index < m_occupation_grid.column_count(); column_index++) { if (!m_occupation_grid.is_occupied(column_index, row_start)) { @@ -421,116 +397,12 @@ void GridFormattingContext::place_item_with_row_position(Box const& child_box) void GridFormattingContext::place_item_with_column_position(Box const& child_box, int& auto_placement_cursor_x, int& auto_placement_cursor_y) { + auto placement_position = resolve_grid_position(child_box, GridDimension::Column); + auto column_start = placement_position.start; + size_t column_span = placement_position.span; + auto const& grid_row_start = child_box.computed_values().grid_row_start(); - auto const& grid_column_start = child_box.computed_values().grid_column_start(); - auto const& grid_column_end = child_box.computed_values().grid_column_end(); - - int column_start = 0; - if (grid_column_start.has_line_number() && grid_column_start.line_number() > 0) { - column_start = grid_column_start.line_number() - 1; - } else if (grid_column_start.has_line_number()) { - // NOTE: Negative indexes count from the end side of the explicit grid - column_start = m_explicit_columns_line_count + grid_column_start.line_number(); - } - - int column_end = 0; - if (grid_column_end.has_line_number()) - column_end = grid_column_end.line_number() - 1; - - // https://www.w3.org/TR/css-grid-2/#line-placement - // 8.3. Line-based Placement: the grid-row-start, grid-column-start, grid-row-end, and grid-column-end properties - - // https://www.w3.org/TR/css-grid-2/#grid-placement-slot - // First attempt to match the grid area’s edge to a named grid area: if there is a grid line whose - // line name is -start (for grid-*-start) / -end (for grid-*-end), - // contributes the first such line to the grid item’s placement. - - // Otherwise, treat this as if the integer 1 had been specified along with the . - - // https://www.w3.org/TR/css-grid-2/#grid-placement-int - // Contributes the Nth grid line to the grid item’s placement. If a negative integer is given, it - // instead counts in reverse, starting from the end edge of the explicit grid. - if (column_end < 0) - column_end = m_occupation_grid.column_count() + column_end + 2; - - // If a name is given as a , only lines with that name are counted. If not enough - // lines with that name exist, all implicit grid lines are assumed to have that name for the purpose - // of finding this position. - - // https://www.w3.org/TR/css-grid-2/#grid-placement-span-int - // Contributes a grid span to the grid item’s placement such that the corresponding edge of the grid - // item’s grid area is N lines from its opposite edge in the corresponding direction. For example, - // grid-column-end: span 2 indicates the second grid line in the endward direction from the - // grid-column-start line. - size_t column_span = 1; size_t row_span = grid_row_start.is_span() ? grid_row_start.span() : 1; - if (grid_column_start.has_line_number() && grid_column_end.is_span()) - column_span = grid_column_end.span(); - if (grid_column_end.has_line_number() && grid_column_start.is_span()) { - column_span = grid_column_start.span(); - column_start = column_end - column_span; - // FIXME: Remove me once have implemented spans overflowing into negative indexes, e.g., grid-column: span 2 / 1 - if (column_start < 0) - column_start = 0; - } - // FIXME: Have yet to find the spec for this. - if (!grid_column_start.is_positioned() && grid_column_end.is_positioned() && column_end == 0) - column_start = 0; - - // If a name is given as a , only lines with that name are counted. If not enough - // lines with that name exist, all implicit grid lines on the side of the explicit grid - // corresponding to the search direction are assumed to have that name for the purpose of counting - // this span. - - // https://drafts.csswg.org/css-grid/#grid-placement-auto - // auto - // The property contributes nothing to the grid item’s placement, indicating auto-placement or a - // default span of one. (See § 8 Placing Grid Items, above.) - - // https://www.w3.org/TR/css-grid-2/#common-uses-named-lines - // 8.1.3. Named Lines and Spans - // Instead of counting lines by number, lines can be referenced by their line name: - if (grid_column_end.has_identifier()) { - if (auto maybe_grid_area = m_grid_areas.get(grid_column_end.identifier()); maybe_grid_area.has_value()) - column_end = maybe_grid_area->column_end; - else if (auto line_name_index = get_line_index_by_line_name(GridDimension::Column, grid_column_end.identifier()); line_name_index.has_value()) { - column_end = line_name_index.value(); - } else - column_end = 1; - column_start = column_end - 1; - } - - if (grid_column_start.has_identifier()) { - if (auto maybe_grid_area = m_grid_areas.get(grid_column_start.identifier()); maybe_grid_area.has_value()) - column_start = maybe_grid_area->column_start; - else if (auto line_name_index = get_line_index_by_line_name(GridDimension::Column, grid_column_start.identifier()); line_name_index.has_value()) { - column_start = line_name_index.value(); - } else { - column_start = 0; - } - } - - // If there are multiple lines of the same name, they effectively establish a named set of grid - // lines, which can be exclusively indexed by filtering the placement by name: - - // https://drafts.csswg.org/css-grid/#grid-placement-errors - // 8.3.1. Grid Placement Conflict Handling - // If the placement for a grid item contains two lines, and the start line is further end-ward than - // the end line, swap the two lines. If the start line is equal to the end line, remove the end - // line. - if (grid_column_start.is_positioned() && grid_column_end.is_positioned()) { - if (column_start > column_end) - swap(column_start, column_end); - if (column_start != column_end) - column_span = column_end - column_start; - } - - // If the placement contains two spans, remove the one contributed by the end grid-placement - // property. - if (grid_column_start.is_span() && grid_column_end.is_span()) - column_span = grid_column_start.span(); - - // FIXME: If the placement contains only a span for a named line, replace it with a span of 1. // 4.1.1.1. Set the column position of the cursor to the grid item's column-start line. If this is // less than the previous column position of the cursor, increment the row position by 1. diff --git a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h index 298b9b53ce..67c15a1789 100644 --- a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h @@ -255,6 +255,13 @@ private: void build_grid_areas(); + struct PlacementPosition { + int start { 0 }; + int end { 0 }; + size_t span { 1 }; + }; + PlacementPosition resolve_grid_position(Box const& child_box, GridDimension const dimension); + void place_grid_items(); void place_item_with_row_and_column_position(Box const& child_box); void place_item_with_row_position(Box const& child_box);