LibWeb: Parse easing values manually

The values aren't that complex, so it doesn't make much sense to have a
dedicated generator for them. Parsing them manually also allows us to
have much more control over the produced values, so as a result of this
change, EasingStyleValue becomes much more ergonomic.
This commit is contained in:
Matthew Olsson 2024-06-14 21:27:23 -07:00 committed by Andreas Kling
commit 667e313731
Notes: sideshowbarker 2024-07-16 22:51:10 +09:00
9 changed files with 405 additions and 192 deletions

View file

@ -13,33 +13,105 @@
namespace Web::CSS {
// NOTE: Magic cubic bezier values from https://www.w3.org/TR/css-easing-1/#valdef-cubic-bezier-easing-function-ease
EasingStyleValue::CubicBezier EasingStyleValue::CubicBezier::ease()
{
static CubicBezier bezier { 0.25, 0.1, 0.25, 1.0 };
return bezier;
}
EasingStyleValue::CubicBezier EasingStyleValue::CubicBezier::ease_in()
{
static CubicBezier bezier { 0.42, 0.0, 1.0, 1.0 };
return bezier;
}
EasingStyleValue::CubicBezier EasingStyleValue::CubicBezier::ease_out()
{
static CubicBezier bezier { 0.0, 0.0, 0.58, 1.0 };
return bezier;
}
EasingStyleValue::CubicBezier EasingStyleValue::CubicBezier::ease_in_out()
{
static CubicBezier bezier { 0.42, 0.0, 0.58, 1.0 };
return bezier;
}
EasingStyleValue::Steps EasingStyleValue::Steps::step_start() {
static Steps steps { 1, Steps::Position::Start };
return steps;
}
EasingStyleValue::Steps EasingStyleValue::Steps::step_end() {
static Steps steps { 1, Steps::Position::End };
return steps;
}
String EasingStyleValue::to_string() const
{
if (m_properties.easing_function == EasingFunction::StepStart)
return "steps(1, start)"_string;
if (m_properties.easing_function == EasingFunction::StepEnd)
return "steps(1, end)"_string;
StringBuilder builder;
builder.append(CSS::to_string(m_properties.easing_function));
m_function.visit(
[&](Linear const& linear) {
builder.append("linear"sv);
if (!linear.stops.is_empty()) {
builder.append('(');
if (m_properties.values.is_empty())
return MUST(builder.to_string());
builder.append('(');
for (size_t i = 0; i < m_properties.values.size(); ++i) {
builder.append(m_properties.values[i]->to_string());
if (i != m_properties.values.size() - 1)
builder.append(", "sv);
}
builder.append(')');
bool first = true;
for (auto const& stop : linear.stops) {
if (!first)
builder.append(", "sv);
first = false;
builder.appendff("{}"sv, stop.offset);
if (stop.position.has_value())
builder.appendff(" {}"sv, stop.position.value());
}
builder.append(')');
}
},
[&](CubicBezier const& bezier) {
if (bezier == CubicBezier::ease()) {
builder.append("ease"sv);
} else if (bezier == CubicBezier::ease_in()) {
builder.append("ease-in"sv);
} else if (bezier == CubicBezier::ease_out()) {
builder.append("ease-out"sv);
} else if (bezier == CubicBezier::ease_in_out()) {
builder.append("ease-in-out"sv);
} else {
builder.appendff("cubic-bezier({}, {}, {}, {})", bezier.x1, bezier.y1, bezier.x2, bezier.y2);
}
},
[&](Steps const& steps) {
if (steps == Steps::step_start()) {
builder.append("step-start"sv);
} else if (steps == Steps::step_end()) {
builder.append("step-end"sv);
} else {
auto position = [&] -> Optional<StringView> {
switch (steps.position) {
case Steps::Position::JumpStart:
return "jump-start"sv;
case Steps::Position::JumpNone:
return "jump-none"sv;
case Steps::Position::JumpBoth:
return "jump-both"sv;
case Steps::Position::Start:
return "start"sv;
default:
return {};
}
}();
if (position.has_value()) {
builder.appendff("steps({}, {})", steps.number_of_intervals, position.value());
} else {
builder.appendff("steps({})", steps.number_of_intervals);
}
}
});
return MUST(builder.to_string());
}
bool EasingStyleValue::Properties::operator==(Properties const& other) const
{
return easing_function == other.easing_function && values == other.values;
}
}

View file

@ -10,38 +10,80 @@
#pragma once
#include <LibWeb/CSS/EasingFunctions.h>
#include <LibWeb/CSS/StyleValue.h>
namespace Web::CSS {
class EasingStyleValue final : public StyleValueWithDefaultOperators<EasingStyleValue> {
public:
static ValueComparingNonnullRefPtr<EasingStyleValue> create(CSS::EasingFunction easing_function, StyleValueVector&& values)
struct Linear {
struct Stop {
double offset;
Optional<double> position;
bool operator==(Stop const&) const = default;
};
Vector<Stop> stops;
bool operator==(Linear const&) const = default;
};
struct CubicBezier {
static CubicBezier ease();
static CubicBezier ease_in();
static CubicBezier ease_out();
static CubicBezier ease_in_out();
double x1;
double y1;
double x2;
double y2;
bool operator==(CubicBezier const&) const = default;
};
struct Steps {
enum class Position {
JumpStart,
JumpEnd,
JumpNone,
JumpBoth,
Start,
End,
};
static Steps step_start();
static Steps step_end();
unsigned int number_of_intervals;
Position position { Position::End };
bool operator==(Steps const&) const = default;
};
using Function = Variant<Linear, CubicBezier, Steps>;
static ValueComparingNonnullRefPtr<EasingStyleValue> create(Function const& function)
{
return adopt_ref(*new (nothrow) EasingStyleValue(easing_function, move(values)));
return adopt_ref(*new (nothrow) EasingStyleValue(function));
}
virtual ~EasingStyleValue() override = default;
CSS::EasingFunction easing_function() const { return m_properties.easing_function; }
StyleValueVector values() const { return m_properties.values; }
Function const& function() const { return m_function; }
virtual String to_string() const override;
bool properties_equal(EasingStyleValue const& other) const { return m_properties == other.m_properties; }
bool properties_equal(EasingStyleValue const& other) const { return m_function == other.m_function; }
private:
EasingStyleValue(CSS::EasingFunction easing_function, StyleValueVector&& values)
EasingStyleValue(Function function)
: StyleValueWithDefaultOperators(Type::Easing)
, m_properties { .easing_function = easing_function, .values = move(values) }
, m_function(function)
{
}
struct Properties {
CSS::EasingFunction easing_function;
StyleValueVector values;
bool operator==(Properties const& other) const;
} m_properties;
Function m_function;
};
}