1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 18:57:35 +00:00

LibWeb+Base: Re-implement grid track span

Implement span correctly when indicated in the grid-column-start,
grid-row-start, etc. CSS properties. Previously it had been implemented
as if span was something that went alongside the position property, but
actually it seems like if you do 'span 3' in the grid-column-start
property, for example, this means it literally spans 3 blocks, and the
3 has nothing to do with position.
This commit is contained in:
martinfalisse 2022-09-17 18:09:35 +02:00 committed by Andreas Kling
parent 86ce1b64f0
commit 236795e931
5 changed files with 224 additions and 117 deletions

View file

@ -40,26 +40,6 @@
<div class="grid-item">1</div>
</div>
<!-- Using grid-row and grid-column -->
<p>Should render a full-width 4x4 grid with:
<ul>
<li>one large column on the left</li>
<li>one large column on the right, being split in half vertically, with the number 2 in the top half, and numbers 3, 4, 5, and 6 in the bottom</li>
</ul>
<div
class="grid-container"
style="
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-rows: 50px 50px 50px 50px;
">
<div class="grid-item" style="grid-row: 1/-1; grid-column: 1/3;">1</div>
<div class="grid-item" style="grid-row: 1/3; grid-column: 3/-1;">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
</div>
<!-- Different column sizes -->
<p>Should render a 2x2 grid with columns 50px and 50%</p>
<div
@ -84,3 +64,23 @@
grid-column: 2 / 2;
grid-row: 2 / 2">4</div>
</div>
<!-- Using grid-row and grid-column -->
<p>Should render a full-width 4x4 grid with:
<ul>
<li>one large column on the left</li>
<li>one large column on the right, being split in half vertically, with the number 2 in the top half, and numbers 3, 4, 5, and 6 in the bottom</li>
</ul>
<div
class="grid-container"
style="
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
grid-template-rows: 25px 25px 25px 25px 25px 25px 25px 25px;
">
<div class="grid-item" style="grid-row: 1 / -1; grid-column: span 4;">1</div>
<div class="grid-item" style="grid-row: 1 / 5; grid-column: 5 / -1;">2</div>
<div class="grid-item" style="grid-column: span 2; grid-row: span 2;">3</div>
<div class="grid-item" style="grid-column: span 2 / -1; grid-row: span 2;">4</div>
<div class="grid-item" style="grid-column: span 2; grid-row: 7 / span 100;">5</div>
<div class="grid-item" style="grid-column: 7 / span 2; grid-row: span 2 / -1;">6</div>
</div>

View file

@ -9,28 +9,23 @@
namespace Web::CSS {
GridTrackPlacement::GridTrackPlacement(int position, bool has_span)
: m_position(position)
, m_has_span(has_span)
{
}
GridTrackPlacement::GridTrackPlacement(int position)
: m_position(position)
GridTrackPlacement::GridTrackPlacement(int span_or_position, bool has_span)
: m_type(has_span ? Type::Span : Type::Position)
, m_value(span_or_position)
{
}
GridTrackPlacement::GridTrackPlacement()
: m_is_auto(true)
: m_type(Type::Auto)
{
}
String GridTrackPlacement::to_string() const
{
StringBuilder builder;
if (m_has_span)
if (is_span())
builder.append("span "sv);
builder.append(String::number(m_position));
builder.append(String::number(m_value));
return builder.to_string();
}

View file

@ -12,38 +12,34 @@ namespace Web::CSS {
class GridTrackPlacement {
public:
GridTrackPlacement(int, bool);
GridTrackPlacement(int);
enum class Type {
Span,
Position,
Auto
};
GridTrackPlacement(int, bool = false);
GridTrackPlacement();
static GridTrackPlacement make_auto() { return GridTrackPlacement(); };
void set_position(int position)
{
m_is_auto = false;
m_position = position;
}
int position() const { return m_position; }
bool is_span() const { return m_type == Type::Span; }
bool is_position() const { return m_type == Type::Position; }
bool is_auto() const { return m_type == Type::Auto; }
bool is_auto_positioned() const { return m_type == Type::Auto || m_type == Type::Span; }
void set_has_span(bool has_span)
{
VERIFY(!m_is_auto);
m_has_span = has_span;
}
bool has_span() const { return m_has_span; }
bool is_auto() const { return m_is_auto; }
int raw_value() const { return m_value; }
Type type() const { return m_type; }
String to_string() const;
bool operator==(GridTrackPlacement const& other) const
{
return m_position == other.position() && m_has_span == other.has_span();
return m_type == other.type() && m_value == other.raw_value();
}
private:
bool m_is_auto { false };
int m_position { 0 };
bool m_has_span { false };
Type m_type;
int m_value { 0 };
};
}

View file

@ -5457,20 +5457,14 @@ RefPtr<StyleValue> Parser::parse_grid_track_placement(Vector<ComponentValue> con
return {};
}
auto first_grid_track_placement = CSS::GridTrackPlacement();
auto has_span = false;
if (current_token.to_string() == "span"sv) {
has_span = true;
tokens.skip_whitespace();
current_token = tokens.next_token().token();
}
if (current_token.is(Token::Type::Number) && current_token.number().is_integer()) {
first_grid_track_placement.set_position(static_cast<int>(current_token.number_value()));
first_grid_track_placement.set_has_span(has_span);
}
if (!tokens.has_next_token())
return GridTrackPlacementStyleValue::create(first_grid_track_placement);
if (current_token.is(Token::Type::Number) && current_token.number().is_integer() && !tokens.has_next_token())
return GridTrackPlacementStyleValue::create(CSS::GridTrackPlacement(static_cast<int>(current_token.number_value()), has_span));
return {};
}
@ -5488,18 +5482,15 @@ RefPtr<StyleValue> Parser::parse_grid_track_placement_shorthand_value(Vector<Com
}
auto calculate_grid_track_placement = [](auto& current_token, auto& tokens) -> CSS::GridTrackPlacement {
auto grid_track_placement = CSS::GridTrackPlacement();
auto has_span = false;
if (current_token.to_string() == "span"sv) {
has_span = true;
tokens.skip_whitespace();
current_token = tokens.next_token().token();
}
if (current_token.is(Token::Type::Number) && current_token.number().is_integer()) {
grid_track_placement.set_position(static_cast<int>(current_token.number_value()));
grid_track_placement.set_has_span(has_span);
}
return grid_track_placement;
if (current_token.is(Token::Type::Number) && current_token.number().is_integer())
return CSS::GridTrackPlacement(static_cast<int>(current_token.number_value()), has_span);
return CSS::GridTrackPlacement();
};
auto first_grid_track_placement = calculate_grid_track_placement(current_token, tokens);

View file

@ -107,18 +107,29 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
// 1. Position anything that's not auto-positioned.
for (size_t i = 0; i < boxes_to_place.size(); i++) {
auto const& child_box = boxes_to_place[i];
if (child_box.computed_values().grid_row_start().is_auto()
|| child_box.computed_values().grid_row_end().is_auto()
|| child_box.computed_values().grid_column_start().is_auto()
|| child_box.computed_values().grid_column_end().is_auto())
if ((child_box.computed_values().grid_row_start().is_auto_positioned() && child_box.computed_values().grid_row_end().is_auto_positioned())
|| (child_box.computed_values().grid_column_start().is_auto_positioned() && child_box.computed_values().grid_column_end().is_auto_positioned()))
continue;
int row_start = child_box.computed_values().grid_row_start().position();
int row_end = child_box.computed_values().grid_row_end().position();
int column_start = child_box.computed_values().grid_column_start().position();
int column_end = child_box.computed_values().grid_column_end().position();
int row_span = 1;
int column_span = 1;
int row_start = child_box.computed_values().grid_row_start().raw_value();
int row_end = child_box.computed_values().grid_row_end().raw_value();
int column_start = child_box.computed_values().grid_column_start().raw_value();
int column_end = child_box.computed_values().grid_column_end().raw_value();
// https://drafts.csswg.org/css-grid/#line-placement
// 8.3. Line-based Placement: the grid-row-start, grid-column-start, grid-row-end, and grid-column-end properties
// https://drafts.csswg.org/css-grid/#grid-placement-slot
// FIXME: <custom-ident>
// First attempt to match the grid areas edge to a named grid area: if there is a grid line whose
// line name is <custom-ident>-start (for grid-*-start) / <custom-ident>-end (for grid-*-end),
// contributes the first such line to the grid items placement.
// Note: Named grid areas automatically generate implicitly-assigned line names of this form, so
// specifying grid-row-start: foo will choose the start edge of that named grid area (unless another
// line named foo-start was explicitly specified before it).
// Otherwise, treat this as if the integer 1 had been specified along with the <custom-ident>.
// https://drafts.csswg.org/css-grid/#grid-placement-int
// [ <integer [−∞,1]> | <integer [1,∞]> ] && <custom-ident>?
@ -128,33 +139,68 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
row_end = static_cast<int>(occupation_grid.size()) + row_end + 2;
if (column_end < 0)
column_end = static_cast<int>(occupation_grid[0].size()) + column_end + 2;
// FIXME: If a name is given as a <custom-ident>, only lines with that name are counted. If not enough
// If a name is given as a <custom-ident>, 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.
// FIXME: An <integer> value of zero makes the declaration invalid.
// An <integer> value of zero makes the declaration invalid.
// https://drafts.csswg.org/css-grid/#grid-placement-span-int
// span && [ <integer [1,∞]> || <custom-ident> ]
// Contributes a grid span to the grid items placement such that the corresponding edge of the grid
// items 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.
int row_span = 1;
int column_span = 1;
if (child_box.computed_values().grid_row_start().is_position() && child_box.computed_values().grid_row_end().is_span())
row_span = child_box.computed_values().grid_row_end().raw_value();
if (child_box.computed_values().grid_column_start().is_position() && child_box.computed_values().grid_column_end().is_span())
column_span = child_box.computed_values().grid_column_end().raw_value();
if (child_box.computed_values().grid_row_end().is_position() && child_box.computed_values().grid_row_start().is_span()) {
row_span = child_box.computed_values().grid_row_start().raw_value();
row_start = row_end - row_span;
}
if (child_box.computed_values().grid_column_end().is_position() && child_box.computed_values().grid_column_start().is_span()) {
column_span = child_box.computed_values().grid_column_start().raw_value();
column_start = column_end - column_span;
}
// If a name is given as a <custom-ident>, 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 items placement, indicating auto-placement or a
// default span of one. (See §8 Placing Grid Items, above.)
// 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 (row_start > row_end) {
auto temp = row_end;
row_end = row_start;
row_start = temp;
if (child_box.computed_values().grid_row_start().is_position() && child_box.computed_values().grid_row_end().is_position()) {
if (row_start > row_end)
swap(row_start, row_end);
if (row_start != row_end)
row_span = row_end - row_start;
}
if (column_start > column_end) {
auto temp = column_end;
column_end = column_start;
column_start = temp;
if (child_box.computed_values().grid_column_start().is_position() && child_box.computed_values().grid_column_end().is_position()) {
if (column_start > column_end)
swap(column_start, column_end);
if (column_start != column_end)
column_span = column_end - column_start;
}
if (row_start != row_end)
row_span = row_end - row_start;
if (column_start != column_end)
column_span = column_end - column_start;
// FIXME: If the placement contains two spans, remove the one contributed by the end grid-placement
// If the placement contains two spans, remove the one contributed by the end grid-placement
// property.
if (child_box.computed_values().grid_row_start().is_span() && child_box.computed_values().grid_row_end().is_span())
row_span = child_box.computed_values().grid_row_start().raw_value();
if (child_box.computed_values().grid_column_start().is_span() && child_box.computed_values().grid_column_end().is_span())
column_span = child_box.computed_values().grid_column_start().raw_value();
// FIXME: If the placement contains only a span for a named line, replace it with a span of 1.
@ -173,13 +219,26 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
// FIXME: Do "dense" packing
for (size_t i = 0; i < boxes_to_place.size(); i++) {
auto const& child_box = boxes_to_place[i];
if (child_box.computed_values().grid_row_start().is_auto()
|| child_box.computed_values().grid_row_end().is_auto())
if (child_box.computed_values().grid_row_start().is_auto_positioned() && child_box.computed_values().grid_row_end().is_auto_positioned())
continue;
int row_start = child_box.computed_values().grid_row_start().position();
int row_end = child_box.computed_values().grid_row_end().position();
int row_span = 1;
int row_start = child_box.computed_values().grid_row_start().raw_value();
int row_end = child_box.computed_values().grid_row_end().raw_value();
// https://drafts.csswg.org/css-grid/#line-placement
// 8.3. Line-based Placement: the grid-row-start, grid-column-start, grid-row-end, and grid-column-end properties
// https://drafts.csswg.org/css-grid/#grid-placement-slot
// FIXME: <custom-ident>
// First attempt to match the grid areas edge to a named grid area: if there is a grid line whose
// line name is <custom-ident>-start (for grid-*-start) / <custom-ident>-end (for grid-*-end),
// contributes the first such line to the grid items placement.
// Note: Named grid areas automatically generate implicitly-assigned line names of this form, so
// specifying grid-row-start: foo will choose the start edge of that named grid area (unless another
// line named foo-start was explicitly specified before it).
// Otherwise, treat this as if the integer 1 had been specified along with the <custom-ident>.
// https://drafts.csswg.org/css-grid/#grid-placement-int
// [ <integer [−∞,1]> | <integer [1,∞]> ] && <custom-ident>?
@ -187,26 +246,53 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
// instead counts in reverse, starting from the end edge of the explicit grid.
if (row_end < 0)
row_end = static_cast<int>(occupation_grid.size()) + row_end + 2;
// FIXME: If a name is given as a <custom-ident>, only lines with that name are counted. If not enough
// If a name is given as a <custom-ident>, 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.
// FIXME: An <integer> value of zero makes the declaration invalid.
// An <integer> value of zero makes the declaration invalid.
// https://drafts.csswg.org/css-grid/#grid-placement-span-int
// span && [ <integer [1,∞]> || <custom-ident> ]
// Contributes a grid span to the grid items placement such that the corresponding edge of the grid
// items 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.
int row_span = 1;
if (child_box.computed_values().grid_row_start().is_position() && child_box.computed_values().grid_row_end().is_span())
row_span = child_box.computed_values().grid_row_end().raw_value();
if (child_box.computed_values().grid_row_end().is_position() && child_box.computed_values().grid_row_start().is_span()) {
row_span = child_box.computed_values().grid_row_start().raw_value();
row_start = row_end - row_span;
}
// If a name is given as a <custom-ident>, 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 items placement, indicating auto-placement or a
// default span of one. (See §8 Placing Grid Items, above.)
// 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 (row_start > row_end) {
auto temp = row_end;
row_end = row_start;
row_start = temp;
if (child_box.computed_values().grid_row_start().is_position() && child_box.computed_values().grid_row_end().is_position()) {
if (row_start > row_end)
swap(row_start, row_end);
if (row_start != row_end)
row_span = row_end - row_start;
}
if (row_start != row_end)
row_span = row_end - row_start;
// FIXME: If the placement contains two spans, remove the one contributed by the end grid-placement
// If the placement contains two spans, remove the one contributed by the end grid-placement
// property.
if (child_box.computed_values().grid_row_start().is_span() && child_box.computed_values().grid_row_end().is_span())
row_span = child_box.computed_values().grid_row_start().raw_value();
// FIXME: If the placement contains only a span for a named line, replace it with a span of 1.
@ -263,10 +349,24 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
// FIXME: no distinction made. See #4.2
// 4.1.1. If the item has a definite column position:
if (!child_box.computed_values().grid_column_start().is_auto()) {
int column_start = child_box.computed_values().grid_column_start().position();
int column_end = child_box.computed_values().grid_column_end().position();
int column_span = 1;
if (!(child_box.computed_values().grid_column_start().is_auto_positioned() && child_box.computed_values().grid_column_end().is_auto_positioned())) {
int column_start = child_box.computed_values().grid_column_start().raw_value();
int column_end = child_box.computed_values().grid_column_end().raw_value();
// https://drafts.csswg.org/css-grid/#line-placement
// 8.3. Line-based Placement: the grid-row-start, grid-column-start, grid-row-end, and grid-column-end properties
// https://drafts.csswg.org/css-grid/#grid-placement-slot
// FIXME: <custom-ident>
// First attempt to match the grid areas edge to a named grid area: if there is a grid line whose
// line name is <custom-ident>-start (for grid-*-start) / <custom-ident>-end (for grid-*-end),
// contributes the first such line to the grid items placement.
// Note: Named grid areas automatically generate implicitly-assigned line names of this form, so
// specifying grid-row-start: foo will choose the start edge of that named grid area (unless another
// line named foo-start was explicitly specified before it).
// Otherwise, treat this as if the integer 1 had been specified along with the <custom-ident>.
// https://drafts.csswg.org/css-grid/#grid-placement-int
// [ <integer [−∞,1]> | <integer [1,∞]> ] && <custom-ident>?
@ -274,28 +374,53 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
// instead counts in reverse, starting from the end edge of the explicit grid.
if (column_end < 0)
column_end = static_cast<int>(occupation_grid[0].size()) + column_end + 2;
// FIXME: If a name is given as a <custom-ident>, only lines with that name are counted. If not enough
// If a name is given as a <custom-ident>, 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.
// FIXME: An <integer> value of zero makes the declaration invalid.
// An <integer> value of zero makes the declaration invalid.
// https://drafts.csswg.org/css-grid/#grid-placement-span-int
// span && [ <integer [1,∞]> || <custom-ident> ]
// Contributes a grid span to the grid items placement such that the corresponding edge of the grid
// items 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.
int column_span = 1;
if (child_box.computed_values().grid_column_start().is_position() && child_box.computed_values().grid_column_end().is_span())
column_span = child_box.computed_values().grid_column_end().raw_value();
if (child_box.computed_values().grid_column_end().is_position() && child_box.computed_values().grid_column_start().is_span()) {
column_span = child_box.computed_values().grid_column_start().raw_value();
column_start = column_end - column_span;
}
// If a name is given as a <custom-ident>, 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 items placement, indicating auto-placement or a
// default span of one. (See §8 Placing Grid Items, above.)
// 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 (!child_box.computed_values().grid_column_end().is_auto()) {
if (column_start > column_end) {
auto temp = column_end;
column_end = column_start;
column_start = temp;
}
if (child_box.computed_values().grid_column_start().is_position() && child_box.computed_values().grid_column_end().is_position()) {
if (column_start > column_end)
swap(column_start, column_end);
if (column_start != column_end)
column_span = column_end - column_start;
}
// FIXME: If the placement contains two spans, remove the one contributed by the end grid-placement
// If the placement contains two spans, remove the one contributed by the end grid-placement
// property.
if (child_box.computed_values().grid_column_start().is_span() && child_box.computed_values().grid_column_end().is_span())
column_span = child_box.computed_values().grid_column_start().raw_value();
// FIXME: If the placement contains only a span for a named line, replace it with a span of 1.