diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp index 63221f75d4..e4fda14f4e 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp @@ -143,6 +143,12 @@ LengthStyleValue const& StyleValue::as_length() const return static_cast(*this); } +LinearGradientStyleValue const& StyleValue::as_linear_gradient() const +{ + VERIFY(is_linear_gradient()); + return static_cast(*this); +} + ListStyleStyleValue const& StyleValue::as_list_style() const { VERIFY(is_list_style()); @@ -1402,6 +1408,124 @@ bool ImageStyleValue::equals(StyleValue const& other) const return m_url == other.as_image().m_url; } +String LinearGradientStyleValue::to_string() const +{ + StringBuilder builder; + auto side_or_corner_to_string = [](SideOrCorner value) { + switch (value) { + case SideOrCorner::Top: + return "top"sv; + case SideOrCorner::Bottom: + return "bottom"sv; + case SideOrCorner::Left: + return "left"sv; + case SideOrCorner::Right: + return "right"sv; + case SideOrCorner::TopLeft: + return "top left"sv; + case SideOrCorner::TopRight: + return "top right"sv; + case SideOrCorner::BottomLeft: + return "bottom left"sv; + case SideOrCorner::BottomRight: + return "bottom right"sv; + default: + VERIFY_NOT_REACHED(); + } + }; + + builder.append("linear-gradient("sv); + m_direction.visit( + [&](SideOrCorner side_or_corner) { + builder.appendff("to {}, "sv, side_or_corner_to_string(side_or_corner)); + }, + [&](Angle const& angle) { + builder.appendff("{}, "sv, angle.to_string()); + }); + + bool first = true; + for (auto const& element : m_color_stop_list) { + if (!first) + builder.append(", "sv); + + if (element.transition_hint.has_value()) { + builder.appendff("{}, "sv, element.transition_hint->value.to_string()); + } + + serialize_a_srgb_value(builder, element.color_stop.color); + if (element.color_stop.length.has_value()) { + builder.appendff(" {}"sv, element.color_stop.length->to_string()); + } + first = false; + } + builder.append(")"sv); + return builder.to_string(); +} + +static bool operator==(LinearGradientStyleValue::GradientDirection a, LinearGradientStyleValue::GradientDirection b) +{ + if (a.has() && b.has()) + return a.get() == b.get(); + if (a.has() && b.has()) + return a.get() == b.get(); + return false; +} + +static bool operator==(GradientColorHint a, GradientColorHint b) +{ + return a.value == b.value; +} + +static bool operator==(GradientColorStop a, GradientColorStop b) +{ + return a.color == b.color && a.length == b.length; +} + +static bool operator==(ColorStopListElement a, ColorStopListElement b) +{ + return a.transition_hint == b.transition_hint && a.color_stop == b.color_stop; +} + +bool LinearGradientStyleValue::equals(StyleValue const& other_) const +{ + if (type() != other_.type()) + return false; + auto& other = other_.as_linear_gradient(); + + if (m_direction != other.m_direction || m_color_stop_list.size() != other.m_color_stop_list.size()) + return false; + + for (size_t i = 0; i < m_color_stop_list.size(); i++) { + if (m_color_stop_list[i] != other.m_color_stop_list[i]) + return false; + } + return true; +} + +float LinearGradientStyleValue::angle(Gfx::FloatRect const& background_box) const +{ + (void)background_box; + return m_direction.visit( + [&](SideOrCorner side_or_corner) { + switch (side_or_corner) { + case SideOrCorner::Top: + return 0.0f; + case SideOrCorner::Bottom: + return 180.0f; + case SideOrCorner::Left: + return 270.0f; + case SideOrCorner::Right: + return 90.0f; + default: + // FIXME: Angle gradients towards corners + return 0.0f; + } + }, + [&](Angle const& angle) { + return angle.to_degrees(); + }); +} + bool InheritStyleValue::equals(StyleValue const& other) const { return type() == other.type(); diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h index d6eb02855b..d42a51723f 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.h @@ -58,6 +58,31 @@ enum class FlexBasis { Auto, }; +enum class SideOrCorner { + Top, + Bottom, + Left, + Right, + TopLeft, + TopRight, + BottomLeft, + BottomRight +}; + +struct GradientColorStop { + Color color; + Optional length; +}; + +struct GradientColorHint { + LengthPercentage value; +}; + +struct ColorStopListElement { + Optional transition_hint; + GradientColorStop color_stop; +}; + // FIXME: Find a better place for this helper. inline Gfx::Painter::ScalingMode to_gfx_scaling_mode(CSS::ImageRendering css_value) { @@ -99,6 +124,7 @@ public: Initial, Invalid, Length, + LinearGradient, ListStyle, Numeric, Overflow, @@ -112,7 +138,7 @@ public: Transformation, Unresolved, Unset, - ValueList, + ValueList }; Type type() const { return m_type; } @@ -136,6 +162,7 @@ public: bool is_inherit() const { return type() == Type::Inherit; } bool is_initial() const { return type() == Type::Initial; } bool is_length() const { return type() == Type::Length; } + bool is_linear_gradient() const { return type() == Type::LinearGradient; } bool is_list_style() const { return type() == Type::ListStyle; } bool is_numeric() const { return type() == Type::Numeric; } bool is_overflow() const { return type() == Type::Overflow; } @@ -172,6 +199,7 @@ public: InheritStyleValue const& as_inherit() const; InitialStyleValue const& as_initial() const; LengthStyleValue const& as_length() const; + LinearGradientStyleValue const& as_linear_gradient() const; ListStyleStyleValue const& as_list_style() const; NumericStyleValue const& as_numeric() const; OverflowStyleValue const& as_overflow() const; @@ -206,6 +234,7 @@ public: InheritStyleValue& as_inherit() { return const_cast(const_cast(*this).as_inherit()); } InitialStyleValue& as_initial() { return const_cast(const_cast(*this).as_initial()); } LengthStyleValue& as_length() { return const_cast(const_cast(*this).as_length()); } + LinearGradientStyleValue& as_linear_gradient() { return const_cast(const_cast(*this).as_linear_gradient()); } ListStyleStyleValue& as_list_style() { return const_cast(const_cast(*this).as_list_style()); } NumericStyleValue& as_numeric() { return const_cast(const_cast(*this).as_numeric()); } OverflowStyleValue& as_overflow() { return const_cast(const_cast(*this).as_overflow()); } @@ -887,6 +916,39 @@ private: RefPtr m_bitmap; }; +class LinearGradientStyleValue final : public StyleValue { +public: + using GradientDirection = Variant; + + static NonnullRefPtr create(GradientDirection direction, Vector color_stop_list) + { + VERIFY(color_stop_list.size() >= 2); + return adopt_ref(*new LinearGradientStyleValue(direction, move(color_stop_list))); + } + + virtual String to_string() const override; + virtual ~LinearGradientStyleValue() override = default; + virtual bool equals(StyleValue const& other) const override; + + Vector const& color_stop_list() const + { + return m_color_stop_list; + } + + float angle(Gfx::FloatRect const& background_box) const; + +private: + LinearGradientStyleValue(GradientDirection direction, Vector color_stop_list) + : StyleValue(Type::LinearGradient) + , m_direction(direction) + , m_color_stop_list(move(color_stop_list)) + { + } + + GradientDirection m_direction; + Vector m_color_stop_list; +}; + class InheritStyleValue final : public StyleValue { public: static NonnullRefPtr the() diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 6309c66de0..efd9620f68 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -62,6 +62,7 @@ class InitialStyleValue; class Length; class LengthPercentage; class LengthStyleValue; +class LinearGradientStyleValue; class ListStyleStyleValue; class MediaList; class MediaQuery;