mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 05:37:43 +00:00
LibWeb: Add stop-color as a CSS property
(This is to set the color of a stop for an SVG gradient)
This commit is contained in:
parent
f099ee3d47
commit
297d8eebcd
7 changed files with 86 additions and 2 deletions
|
@ -41,6 +41,7 @@ public:
|
||||||
static CSS::TextTransform text_transform() { return CSS::TextTransform::None; }
|
static CSS::TextTransform text_transform() { return CSS::TextTransform::None; }
|
||||||
static CSS::Display display() { return CSS::Display { CSS::Display::Outside::Inline, CSS::Display::Inside::Flow }; }
|
static CSS::Display display() { return CSS::Display { CSS::Display::Outside::Inline, CSS::Display::Inside::Flow }; }
|
||||||
static Color color() { return Color::Black; }
|
static Color color() { return Color::Black; }
|
||||||
|
static Color stop_color() { return Color::Black; }
|
||||||
static CSS::BackdropFilter backdrop_filter() { return BackdropFilter::make_none(); }
|
static CSS::BackdropFilter backdrop_filter() { return BackdropFilter::make_none(); }
|
||||||
static Color background_color() { return Color::Transparent; }
|
static Color background_color() { return Color::Transparent; }
|
||||||
static CSS::ListStyleType list_style_type() { return CSS::ListStyleType::Disc; }
|
static CSS::ListStyleType list_style_type() { return CSS::ListStyleType::Disc; }
|
||||||
|
@ -259,6 +260,7 @@ public:
|
||||||
Optional<Color> const& fill() const { return m_inherited.fill; }
|
Optional<Color> const& fill() const { return m_inherited.fill; }
|
||||||
Optional<Color> const& stroke() const { return m_inherited.stroke; }
|
Optional<Color> const& stroke() const { return m_inherited.stroke; }
|
||||||
Optional<LengthPercentage> const& stroke_width() const { return m_inherited.stroke_width; }
|
Optional<LengthPercentage> const& stroke_width() const { return m_inherited.stroke_width; }
|
||||||
|
Color stop_color() const { return m_noninherited.stop_color; }
|
||||||
|
|
||||||
Vector<CSS::Transformation> const& transformations() const { return m_noninherited.transformations; }
|
Vector<CSS::Transformation> const& transformations() const { return m_noninherited.transformations; }
|
||||||
CSS::TransformOrigin const& transform_origin() const { return m_noninherited.transform_origin; }
|
CSS::TransformOrigin const& transform_origin() const { return m_noninherited.transform_origin; }
|
||||||
|
@ -359,6 +361,7 @@ protected:
|
||||||
CSS::Size row_gap { InitialValues::row_gap() };
|
CSS::Size row_gap { InitialValues::row_gap() };
|
||||||
CSS::BorderCollapse border_collapse { InitialValues::border_collapse() };
|
CSS::BorderCollapse border_collapse { InitialValues::border_collapse() };
|
||||||
Vector<Vector<String>> grid_template_areas { InitialValues::grid_template_areas() };
|
Vector<Vector<String>> grid_template_areas { InitialValues::grid_template_areas() };
|
||||||
|
Gfx::Color stop_color { InitialValues::stop_color() };
|
||||||
} m_noninherited;
|
} m_noninherited;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -446,6 +449,7 @@ public:
|
||||||
void set_fill(Color value) { m_inherited.fill = value; }
|
void set_fill(Color value) { m_inherited.fill = value; }
|
||||||
void set_stroke(Color value) { m_inherited.stroke = value; }
|
void set_stroke(Color value) { m_inherited.stroke = value; }
|
||||||
void set_stroke_width(LengthPercentage value) { m_inherited.stroke_width = value; }
|
void set_stroke_width(LengthPercentage value) { m_inherited.stroke_width = value; }
|
||||||
|
void set_stop_color(Color value) { m_noninherited.stop_color = value; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1411,6 +1411,14 @@
|
||||||
"none"
|
"none"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"stop-color": {
|
||||||
|
"affects-layout": false,
|
||||||
|
"inherited": false,
|
||||||
|
"initial": "black",
|
||||||
|
"valid-types": [
|
||||||
|
"color"
|
||||||
|
]
|
||||||
|
},
|
||||||
"stroke-width": {
|
"stroke-width": {
|
||||||
"affects-layout": false,
|
"affects-layout": false,
|
||||||
"inherited": true,
|
"inherited": true,
|
||||||
|
|
|
@ -818,4 +818,15 @@ String StyleProperties::grid_area() const
|
||||||
return value->as_string().to_string().release_value_but_fixme_should_propagate_errors();
|
return value->as_string().to_string().release_value_but_fixme_should_propagate_errors();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Color StyleProperties::stop_color() const
|
||||||
|
{
|
||||||
|
auto value = property(CSS::PropertyID::StopColor);
|
||||||
|
if (value->has_color()) {
|
||||||
|
// FIXME: This is used by the SVGStopElement, which does not participate in layout,
|
||||||
|
// so can't pass a layout node (so can't resolve some colors, e.g. palette ones or currentColor)
|
||||||
|
return value->to_color({});
|
||||||
|
}
|
||||||
|
return Color::Black;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,8 @@ public:
|
||||||
Vector<CSS::Transformation> transformations() const;
|
Vector<CSS::Transformation> transformations() const;
|
||||||
CSS::TransformOrigin transform_origin() const;
|
CSS::TransformOrigin transform_origin() const;
|
||||||
|
|
||||||
|
Color stop_color() const;
|
||||||
|
|
||||||
Gfx::Font const& computed_font() const
|
Gfx::Font const& computed_font() const
|
||||||
{
|
{
|
||||||
VERIFY(m_font);
|
VERIFY(m_font);
|
||||||
|
|
|
@ -650,6 +650,8 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
|
||||||
computed_values.set_fill(fill->to_color(*this));
|
computed_values.set_fill(fill->to_color(*this));
|
||||||
if (auto stroke = computed_style.property(CSS::PropertyID::Stroke); stroke->has_color())
|
if (auto stroke = computed_style.property(CSS::PropertyID::Stroke); stroke->has_color())
|
||||||
computed_values.set_stroke(stroke->to_color(*this));
|
computed_values.set_stroke(stroke->to_color(*this));
|
||||||
|
if (auto stop_color = computed_style.property(CSS::PropertyID::StopColor); stop_color->has_color())
|
||||||
|
computed_values.set_stop_color(stop_color->to_color(*this));
|
||||||
auto stroke_width = computed_style.property(CSS::PropertyID::StrokeWidth);
|
auto stroke_width = computed_style.property(CSS::PropertyID::StrokeWidth);
|
||||||
// FIXME: Converting to pixels isn't really correct - values should be in "user units"
|
// FIXME: Converting to pixels isn't really correct - values should be in "user units"
|
||||||
// https://svgwg.org/svg2-draft/coords.html#TermUserUnits
|
// https://svgwg.org/svg2-draft/coords.html#TermUserUnits
|
||||||
|
|
|
@ -66,6 +66,30 @@ Optional<float> AttributeParser::parse_length(StringView input)
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float NumberPercentage::resolve_relative_to(float length)
|
||||||
|
{
|
||||||
|
if (!m_is_percentage)
|
||||||
|
return m_value;
|
||||||
|
return m_value * length;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<NumberPercentage> AttributeParser::parse_number_percentage(StringView input)
|
||||||
|
{
|
||||||
|
AttributeParser parser { input };
|
||||||
|
parser.parse_whitespace();
|
||||||
|
if (parser.match_number()) {
|
||||||
|
float number = parser.parse_number();
|
||||||
|
bool is_percentage = parser.match('%');
|
||||||
|
if (is_percentage)
|
||||||
|
parser.consume();
|
||||||
|
parser.parse_whitespace();
|
||||||
|
if (parser.done())
|
||||||
|
return NumberPercentage(number, is_percentage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
Optional<float> AttributeParser::parse_positive_length(StringView input)
|
Optional<float> AttributeParser::parse_positive_length(StringView input)
|
||||||
{
|
{
|
||||||
// FIXME: Where this is used, the spec usually (always?) says "A negative value is an error (see Error processing)."
|
// FIXME: Where this is used, the spec usually (always?) says "A negative value is an error (see Error processing)."
|
||||||
|
@ -482,11 +506,10 @@ Optional<Vector<Transform>> AttributeParser::parse_transform()
|
||||||
m_lexer.consume_specific(',');
|
m_lexer.consume_specific(',');
|
||||||
consume_whitespace();
|
consume_whitespace();
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: AttributeParser currently does not handle invalid parses in most cases (e.g. parse_number()) and just crashes.
|
// FIXME: AttributeParser currently does not handle invalid parses in most cases (e.g. parse_number()) and just crashes.
|
||||||
auto parse_optional_number = [&](float default_value = 0.0f) {
|
auto parse_optional_number = [&](float default_value = 0.0f) {
|
||||||
consume_comma_whitespace();
|
consume_comma_whitespace();
|
||||||
if (m_lexer.next_is(isdigit))
|
if (match_number())
|
||||||
return parse_number();
|
return parse_number();
|
||||||
return default_value;
|
return default_value;
|
||||||
};
|
};
|
||||||
|
@ -586,6 +609,11 @@ bool AttributeParser::match_coordinate() const
|
||||||
return match_length();
|
return match_length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AttributeParser::match_number() const
|
||||||
|
{
|
||||||
|
return match_length();
|
||||||
|
}
|
||||||
|
|
||||||
bool AttributeParser::match_length() const
|
bool AttributeParser::match_length() const
|
||||||
{
|
{
|
||||||
return !done() && (isdigit(ch()) || ch() == '-' || ch() == '+' || ch() == '.');
|
return !done() && (isdigit(ch()) || ch() == '-' || ch() == '+' || ch() == '.');
|
||||||
|
|
|
@ -88,12 +88,40 @@ struct PreserveAspectRatio {
|
||||||
MeetOrSlice meet_or_slice { MeetOrSlice::Meet };
|
MeetOrSlice meet_or_slice { MeetOrSlice::Meet };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class NumberPercentage {
|
||||||
|
public:
|
||||||
|
NumberPercentage(float value, bool is_percentage)
|
||||||
|
: m_value(is_percentage ? value / 100 : value)
|
||||||
|
, m_is_percentage(is_percentage)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static NumberPercentage create_percentage(float value)
|
||||||
|
{
|
||||||
|
return NumberPercentage(value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static NumberPercentage create_number(float value)
|
||||||
|
{
|
||||||
|
return NumberPercentage(value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
float resolve_relative_to(float length);
|
||||||
|
|
||||||
|
float value() { return m_value; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
float m_value;
|
||||||
|
bool m_is_percentage { false };
|
||||||
|
};
|
||||||
|
|
||||||
class AttributeParser final {
|
class AttributeParser final {
|
||||||
public:
|
public:
|
||||||
~AttributeParser() = default;
|
~AttributeParser() = default;
|
||||||
|
|
||||||
static Optional<float> parse_coordinate(StringView input);
|
static Optional<float> parse_coordinate(StringView input);
|
||||||
static Optional<float> parse_length(StringView input);
|
static Optional<float> parse_length(StringView input);
|
||||||
|
static Optional<NumberPercentage> parse_number_percentage(StringView input);
|
||||||
static Optional<float> parse_positive_length(StringView input);
|
static Optional<float> parse_positive_length(StringView input);
|
||||||
static Vector<Gfx::FloatPoint> parse_points(StringView input);
|
static Vector<Gfx::FloatPoint> parse_points(StringView input);
|
||||||
static Vector<PathInstruction> parse_path_data(StringView input);
|
static Vector<PathInstruction> parse_path_data(StringView input);
|
||||||
|
@ -137,6 +165,7 @@ private:
|
||||||
bool match_comma_whitespace() const;
|
bool match_comma_whitespace() const;
|
||||||
bool match_coordinate() const;
|
bool match_coordinate() const;
|
||||||
bool match_length() const;
|
bool match_length() const;
|
||||||
|
bool match_number() const;
|
||||||
bool match(char c) const { return !done() && ch() == c; }
|
bool match(char c) const { return !done() && ch() == c; }
|
||||||
|
|
||||||
bool done() const { return m_lexer.is_eof(); }
|
bool done() const { return m_lexer.is_eof(); }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue