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

LibWeb: Add PositionValue class to represent CSS <position>s

This class represents a <position> and handles resolving it to a
Gfx::FloatPoint relative to some rectangle.

It can handle all forms of <position>:

- Two presets:
  left center
- A preset + a length percentage:
  10% bottom
- Or relative to some edges:
  right 20% bottom 30px
This commit is contained in:
MacDue 2022-10-29 13:11:00 +01:00 committed by Linus Groh
parent 067759c0e9
commit e568c93404
2 changed files with 141 additions and 0 deletions

View file

@ -1853,6 +1853,108 @@ void LinearGradientStyleValue::paint(PaintContext& context, Gfx::IntRect const&
Painting::paint_linear_gradient(context, dest_rect, m_resolved->data);
}
Gfx::FloatPoint PositionValue::resolved(Layout::Node const& node, Gfx::FloatRect const& rect) const
{
// Note: A preset + a none default x/y_relative_to is impossible in the syntax (and makes little sense)
float x = horizontal_position.visit(
[&](HorizontalPreset preset) {
return rect.width() * [&] {
switch (preset) {
case HorizontalPreset::Left:
return 0.0f;
case HorizontalPreset::Center:
return 0.5f;
case HorizontalPreset::Right:
return 1.0f;
default:
VERIFY_NOT_REACHED();
}
}();
},
[&](LengthPercentage length_percentage) {
return length_percentage.resolved(node, Length::make_px(rect.width())).to_px(node);
});
float y = vertical_position.visit(
[&](VerticalPreset preset) {
return rect.height() * [&] {
switch (preset) {
case VerticalPreset::Top:
return 0.0f;
case VerticalPreset::Center:
return 0.5f;
case VerticalPreset::Bottom:
return 1.0f;
default:
VERIFY_NOT_REACHED();
}
}();
},
[&](LengthPercentage length_percentage) {
return length_percentage.resolved(node, Length::make_px(rect.height())).to_px(node);
});
if (x_relative_to == HorizontalEdge::Right)
x = rect.width() - x;
if (y_relative_to == VerticalEdge::Bottom)
y = rect.height() - y;
return Gfx::FloatPoint { rect.x() + x, rect.y() + y };
}
void PositionValue::serialize(StringBuilder& builder) const
{
// Note: This means our serialization with simplify any with explicit edges that are just `top left`.
bool has_relative_edges = x_relative_to == HorizontalEdge::Right || y_relative_to == VerticalEdge::Bottom;
if (has_relative_edges)
builder.append(x_relative_to == HorizontalEdge::Left ? "left "sv : "right "sv);
horizontal_position.visit(
[&](HorizontalPreset preset) {
builder.append([&] {
switch (preset) {
case HorizontalPreset::Left:
return "left"sv;
case HorizontalPreset::Center:
return "center"sv;
case HorizontalPreset::Right:
return "right"sv;
default:
VERIFY_NOT_REACHED();
}
}());
},
[&](LengthPercentage length_percentage) {
builder.append(length_percentage.to_string());
});
builder.append(' ');
if (has_relative_edges)
builder.append(y_relative_to == VerticalEdge::Top ? "top "sv : "bottom "sv);
vertical_position.visit(
[&](VerticalPreset preset) {
builder.append([&] {
switch (preset) {
case VerticalPreset::Top:
return "top"sv;
case VerticalPreset::Center:
return "center"sv;
case VerticalPreset::Bottom:
return "bottom"sv;
default:
VERIFY_NOT_REACHED();
}
}());
},
[&](LengthPercentage length_percentage) {
builder.append(length_percentage.to_string());
});
}
bool PositionValue::operator==(PositionValue const& other) const
{
return (
x_relative_to == other.x_relative_to
&& y_relative_to == other.y_relative_to
&& variant_equals(horizontal_position, other.horizontal_position)
&& variant_equals(vertical_position, other.vertical_position));
}
String ConicGradientStyleValue::to_string() const
{
StringBuilder builder;