diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp index 8eb5fc8864..a7186fa628 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp @@ -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; diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h index 77a8bc57f2..b23016d3e2 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.h @@ -96,6 +96,45 @@ struct ColorStopListElement { using LinearColorStopListElement = ColorStopListElement; using AngularColorStopListElement = ColorStopListElement; +// FIXME: Named PositionValue to avoid conflicts with enums, but this represents a +struct PositionValue { + enum class HorizontalPreset { + Left, + Center, + Right + }; + + enum class VerticalPreset { + Top, + Center, + Bottom + }; + + enum class HorizontalEdge { + Left, + Right + }; + + enum class VerticalEdge { + Top, + Bottom + }; + + inline static PositionValue center() + { + return PositionValue { HorizontalPreset::Center, VerticalPreset::Center }; + } + + Variant horizontal_position { HorizontalPreset::Left }; + Variant vertical_position { VerticalPreset::Top }; + HorizontalEdge x_relative_to { HorizontalEdge::Left }; + VerticalEdge y_relative_to { VerticalEdge::Top }; + + Gfx::FloatPoint resolved(Layout::Node const&, Gfx::FloatRect const&) const; + void serialize(StringBuilder&) const; + bool operator==(PositionValue const&) const; +}; + struct EdgeRect { Length top_edge; Length right_edge;