1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 06:37:35 +00:00

LibWeb: Add transform property to the system

This patch adds parsing support as well as all the needed stuctures all
over LibWeb to pass Transformations around.
This commit is contained in:
Tobias Christiansen 2021-09-18 17:20:00 +02:00 committed by Andreas Kling
parent 74b88a8156
commit 9ebfafafbe
8 changed files with 133 additions and 0 deletions

View file

@ -41,6 +41,11 @@ public:
float width { 0 };
};
struct Transformation {
CSS::TransformFunction function;
Vector<Variant<CSS::Length, float>> values;
};
struct FlexBasisData {
CSS::FlexBasis type { CSS::FlexBasis::Auto };
CSS::Length length {};
@ -109,6 +114,8 @@ public:
Optional<Color> stroke() const { return m_inherited.stroke; }
Optional<Length> const& stroke_width() const { return m_inherited.stroke_width; }
Vector<CSS::Transformation> transformations() const { return m_noninherited.transformations; }
ComputedValues clone_inherited_values() const
{
ComputedValues clone;
@ -168,6 +175,7 @@ protected:
CSS::Overflow overflow_y { InitialValues::overflow() };
Optional<float> opacity;
Optional<BoxShadowData> box_shadow {};
Vector<CSS::Transformation> transformations {};
} m_noninherited;
};
@ -219,6 +227,7 @@ public:
void set_opacity(Optional<float> value) { m_noninherited.opacity = value; }
void set_justify_content(CSS::JustifyContent value) { m_noninherited.justify_content = value; }
void set_box_shadow(Optional<BoxShadowData> value) { m_noninherited.box_shadow = move(value); }
void set_transformations(Vector<CSS::Transformation> value) { m_noninherited.transformations = move(value); }
void set_fill(Color value) { m_inherited.fill = value; }
void set_stroke(Color value) { m_inherited.stroke = value; }

View file

@ -2788,6 +2788,45 @@ RefPtr<StyleValue> Parser::parse_text_decoration_value(ParsingContext const& con
return TextDecorationStyleValue::create(decoration_line.release_nonnull(), decoration_style.release_nonnull(), decoration_color.release_nonnull());
}
static Optional<CSS::TransformFunction> parse_transform_function_name(StringView name)
{
if (name == "translateY")
return CSS::TransformFunction::TranslateY;
return {};
}
RefPtr<StyleValue> Parser::parse_transform_value(ParsingContext const& context, Vector<StyleComponentValueRule> const& component_values)
{
NonnullRefPtrVector<StyleValue> transformations;
for (auto& part : component_values) {
if (!part.is_function())
return nullptr;
auto maybe_function = parse_transform_function_name(part.function().name());
if (!maybe_function.has_value())
return nullptr;
NonnullRefPtrVector<StyleValue> values;
for (auto& value : part.function().values()) {
if (value.is(Token::Type::Dimension)) {
auto maybe_length = parse_length(context, value);
if (!maybe_length.has_value())
return nullptr;
values.append(LengthStyleValue::create(maybe_length.release_value()));
} else if (value.is(Token::Type::Number)) {
auto number = parse_numeric_value(context, value);
values.append(number.release_nonnull());
} else {
dbgln("FIXME: Unsupported value type for transformation!");
return nullptr;
}
}
transformations.append(TransformationStyleValue::create(maybe_function.value(), move(values)));
}
return StyleValueList::create(move(transformations));
}
RefPtr<StyleValue> Parser::parse_as_css_value(PropertyID property_id)
{
auto component_values = parse_as_list_of_component_values();
@ -2893,6 +2932,10 @@ Result<NonnullRefPtr<StyleValue>, Parser::ParsingResult> Parser::parse_css_value
if (auto parsed_value = parse_text_decoration_value(m_context, component_values))
return parsed_value.release_nonnull();
break;
case PropertyID::Transform:
if (auto parsed_value = parse_transform_value(m_context, component_values))
return parsed_value.release_nonnull();
break;
default:
break;
}

View file

@ -198,6 +198,7 @@ private:
static RefPtr<StyleValue> parse_list_style_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);
static RefPtr<StyleValue> parse_overflow_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);
static RefPtr<StyleValue> parse_text_decoration_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);
static RefPtr<StyleValue> parse_transform_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);
// calc() parsing, according to https://www.w3.org/TR/css-values-3/#calc-syntax
static OwnPtr<CalculatedStyleValue::CalcSum> parse_calc_sum(ParsingContext const&, TokenStream<StyleComponentValueRule>&);

View file

@ -605,6 +605,10 @@
"unitless-length"
]
},
"transform": {
"inherited": false,
"initial": "none"
},
"user-select": {
"inherited": false,
"initial": "auto"

View file

@ -424,6 +424,44 @@ Optional<CSS::JustifyContent> StyleProperties::justify_content() const
}
}
Vector<CSS::Transformation> StyleProperties::transformations() const
{
auto value = property(CSS::PropertyID::Transform);
if (!value.has_value())
return {};
if (value.value()->is_identifier() && value.value()->to_identifier() == CSS::ValueID::None)
return {};
if (!value.value()->is_value_list())
return {};
auto& list = static_cast<const StyleValueList&>(*value.value());
Vector<CSS::Transformation> transformations;
for (auto& it : list.values()) {
if (!it.is_transformation())
return {};
auto& transformation_style_value = static_cast<TransformationStyleValue const&>(it);
CSS::Transformation transformation;
transformation.function = transformation_style_value.transform_function();
Vector<Variant<CSS::Length, float>> values;
for (auto& transformation_value : transformation_style_value.values()) {
if (transformation_value.is_length()) {
values.append({ transformation_value.to_length() });
} else if (transformation_value.is_numeric()) {
values.append({ static_cast<NumericStyleValue const&>(transformation_value).value() });
} else {
dbgln("FIXME: Unsupported value in transform!");
}
}
transformation.values = move(values);
transformations.append(move(transformation));
}
return transformations;
}
Optional<CSS::AlignItems> StyleProperties::align_items() const
{
auto value = property(CSS::PropertyID::AlignItems);

View file

@ -64,6 +64,8 @@ public:
Optional<CSS::Repeat> background_repeat_y() const;
Optional<CSS::BoxShadowData> box_shadow() const;
Vector<CSS::Transformation> transformations() const;
const Gfx::Font& font(Layout::Node const& node) const
{
if (!m_font)

View file

@ -219,6 +219,10 @@ enum class AlignItems {
Stretch,
};
enum class TransformFunction {
TranslateY,
};
class StyleValue : public RefCounted<StyleValue> {
public:
virtual ~StyleValue();
@ -249,6 +253,7 @@ public:
ListStyle,
Overflow,
TextDecoration,
Transformation,
};
Type type() const { return m_type; }
@ -276,6 +281,7 @@ public:
bool is_list_style() const { return type() == Type::ListStyle; }
bool is_overflow() const { return type() == Type::Overflow; }
bool is_text_decoration() const { return type() == Type::TextDecoration; }
bool is_transformation() const { return type() == Type::Transformation; }
bool is_builtin() const { return is_inherit() || is_initial() || is_unset(); }
@ -1060,6 +1066,34 @@ private:
NonnullRefPtr<StyleValue> m_color;
};
class TransformationStyleValue final : public StyleValue {
public:
static NonnullRefPtr<TransformationStyleValue> create(CSS::TransformFunction transform_function, NonnullRefPtrVector<StyleValue>&& values)
{
return adopt_ref(*new TransformationStyleValue(transform_function, move(values)));
}
virtual ~TransformationStyleValue() override { }
CSS::TransformFunction transform_function() const { return m_transform_function; }
NonnullRefPtrVector<StyleValue> values() const { return m_values; }
virtual String to_string() const override
{
return String::formatted("TransformationStyleValue");
}
private:
TransformationStyleValue(CSS::TransformFunction transform_function, NonnullRefPtrVector<StyleValue>&& values)
: StyleValue(Type::Transformation)
, m_transform_function(transform_function)
, m_values(move(values))
{
}
CSS::TransformFunction m_transform_function;
NonnullRefPtrVector<StyleValue> m_values;
};
class StyleValueList final : public StyleValue {
public:
static NonnullRefPtr<StyleValueList> create(NonnullRefPtrVector<StyleValue>&& values) { return adopt_ref(*new StyleValueList(move(values))); }

View file

@ -347,6 +347,8 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& specified_style)
computed_values.set_box_shadow(specified_style.box_shadow());
computed_values.set_transformations(specified_style.transformations());
auto do_border_style = [&](CSS::BorderData& border, CSS::PropertyID width_property, CSS::PropertyID color_property, CSS::PropertyID style_property) {
// FIXME: The default border color value is `currentcolor`, but since we can't resolve that easily,
// we just manually grab the value from `color`. This makes it dependent on `color` being