LibWeb: Implement Flex and FlexStyleValue types

This commit is contained in:
Sam Atkins 2023-09-28 15:18:14 +01:00 committed by Sam Atkins
parent f1d7ea67c0
commit b0317bb3a1
Notes: sideshowbarker 2024-07-17 06:45:52 +09:00
15 changed files with 258 additions and 6 deletions

View file

@ -21,7 +21,7 @@ void generate_bounds_checking_function(JsonObject& properties, SourceGenerator&
static bool type_name_is_enum(StringView type_name)
{
return !AK::first_is_one_of(type_name,
"angle"sv, "color"sv, "custom-ident"sv, "easing-function"sv, "frequency"sv, "image"sv,
"angle"sv, "color"sv, "custom-ident"sv, "easing-function"sv, "flex"sv, "frequency"sv, "image"sv,
"integer"sv, "length"sv, "number"sv, "paint"sv, "percentage"sv, "ratio"sv, "rect"sv,
"resolution"sv, "string"sv, "time"sv, "url"sv);
}
@ -172,6 +172,7 @@ enum class ValueType {
CustomIdent,
EasingFunction,
FilterValueList,
Flex,
Frequency,
Image,
Integer,
@ -193,6 +194,7 @@ Optional<ValueType> property_resolves_percentages_relative_to(PropertyID);
// These perform range-checking, but are also safe to call with properties that don't accept that type. (They'll just return false.)
bool property_accepts_angle(PropertyID, Angle const&);
bool property_accepts_flex(PropertyID, Flex const&);
bool property_accepts_frequency(PropertyID, Frequency const&);
bool property_accepts_integer(PropertyID, i64 const&);
bool property_accepts_length(PropertyID, Length const&);
@ -620,6 +622,8 @@ bool property_accepts_type(PropertyID property_id, ValueType value_type)
property_generator.appendln(" case ValueType::CustomIdent:");
} else if (type_name == "easing-function") {
property_generator.appendln(" case ValueType::EasingFunction:");
} else if (type_name == "flex") {
property_generator.appendln(" case ValueType::Flex:");
} else if (type_name == "frequency") {
property_generator.appendln(" case ValueType::Frequency:");
} else if (type_name == "image") {
@ -774,6 +778,7 @@ size_t property_maximum_value_count(PropertyID property_id)
})~~~");
generate_bounds_checking_function(properties, generator, "angle"sv, "Angle"sv, "Deg"sv);
generate_bounds_checking_function(properties, generator, "flex"sv, "Flex"sv, "Fr"sv);
generate_bounds_checking_function(properties, generator, "frequency"sv, "Frequency"sv, "Hertz"sv);
generate_bounds_checking_function(properties, generator, "integer"sv, "i64"sv, {}, "value"sv);
generate_bounds_checking_function(properties, generator, "length"sv, "Length"sv, {}, "value.raw_value()"sv);

View file

@ -28,6 +28,7 @@ source_set("CSS") {
"Clip.cpp",
"Display.cpp",
"EdgeRect.cpp",
"Flex.cpp",
"FontFace.cpp",
"Frequency.cpp",
"GridTrackPlacement.cpp",

View file

@ -38,6 +38,7 @@ set(SOURCES
CSS/CSSSupportsRule.cpp
CSS/Display.cpp
CSS/EdgeRect.cpp
CSS/Flex.cpp
CSS/FontFace.cpp
CSS/Frequency.cpp
CSS/GridTrackPlacement.cpp

View file

@ -20,6 +20,8 @@ Optional<CSSNumericType::BaseType> CSSNumericType::base_type_from_value_type(Val
switch (value_type) {
case ValueType::Angle:
return BaseType::Angle;
case ValueType::Flex:
return BaseType::Flex;
case ValueType::Frequency:
return BaseType::Frequency;
case ValueType::Length:

View file

@ -66,7 +66,6 @@ public:
bool matches_angle() const { return matches_dimension(BaseType::Angle); }
bool matches_angle_percentage() const { return matches_dimension_percentage(BaseType::Angle); }
bool matches_flex() const { return matches_dimension(BaseType::Flex); }
bool matches_flex_percentage() const { return matches_dimension_percentage(BaseType::Flex); }
bool matches_frequency() const { return matches_dimension(BaseType::Frequency); }
bool matches_frequency_percentage() const { return matches_dimension_percentage(BaseType::Frequency); }
bool matches_length() const { return matches_dimension(BaseType::Length); }

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "Flex.h"
#include <LibWeb/CSS/Percentage.h>
namespace Web::CSS {
Flex::Flex(double value, Type type)
: m_type(type)
, m_value(value)
{
}
Flex Flex::make_fr(double value)
{
return { value, Type::Fr };
}
Flex Flex::percentage_of(Percentage const& percentage) const
{
return Flex { percentage.as_fraction() * m_value, m_type };
}
String Flex::to_string() const
{
return MUST(String::formatted("{}fr", to_fr()));
}
double Flex::to_fr() const
{
switch (m_type) {
case Type::Fr:
return m_value;
}
VERIFY_NOT_REACHED();
}
StringView Flex::unit_name() const
{
switch (m_type) {
case Type::Fr:
return "fr"sv;
}
VERIFY_NOT_REACHED();
}
Optional<Flex::Type> Flex::unit_from_name(StringView name)
{
if (name.equals_ignoring_ascii_case("fr"sv))
return Type::Fr;
return {};
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Optional.h>
#include <AK/String.h>
#include <LibWeb/Forward.h>
namespace Web::CSS {
// https://drafts.csswg.org/css-grid-2/#typedef-flex
class Flex {
public:
enum class Type {
Fr,
};
static Optional<Type> unit_from_name(StringView);
Flex(double value, Type type);
static Flex make_fr(double);
Flex percentage_of(Percentage const&) const;
String to_string() const;
double to_fr() const;
Type type() const { return m_type; }
double raw_value() const { return m_value; }
bool operator==(Flex const& other) const
{
return m_type == other.m_type && m_value == other.m_value;
}
int operator<=>(Flex const& other) const
{
auto this_fr = to_fr();
auto other_fr = other.to_fr();
if (this_fr < other_fr)
return -1;
if (this_fr > other_fr)
return 1;
return 0;
}
private:
StringView unit_name() const;
Type m_type;
double m_value { 0 };
};
}
template<>
struct AK::Formatter<Web::CSS::Flex> : Formatter<StringView> {
ErrorOr<void> format(FormatBuilder& builder, Web::CSS::Flex const& flex)
{
return Formatter<StringView>::format(builder, flex.to_string());
}
};

View file

@ -8,6 +8,7 @@
#include <AK/Variant.h>
#include <LibWeb/CSS/Angle.h>
#include <LibWeb/CSS/Flex.h>
#include <LibWeb/CSS/Frequency.h>
#include <LibWeb/CSS/Length.h>
#include <LibWeb/CSS/Percentage.h>
@ -24,6 +25,11 @@ public:
{
}
Dimension(Flex&& value)
: m_value(move(value))
{
}
Dimension(Frequency&& value)
: m_value(move(value))
{
@ -59,6 +65,9 @@ public:
return percentage();
}
bool is_flex() const { return m_value.has<Flex>(); }
Flex flex() const { return m_value.get<Flex>(); }
bool is_frequency() const { return m_value.has<Frequency>(); }
Frequency frequency() const { return m_value.get<Frequency>(); }
@ -99,7 +108,7 @@ public:
}
private:
Variant<Angle, Frequency, Length, Percentage, Resolution, Time> m_value;
Variant<Angle, Flex, Frequency, Length, Percentage, Resolution, Time> m_value;
};
}

View file

@ -45,6 +45,7 @@
#include <LibWeb/CSS/StyleValues/EasingStyleValue.h>
#include <LibWeb/CSS/StyleValues/EdgeStyleValue.h>
#include <LibWeb/CSS/StyleValues/FilterValueListStyleValue.h>
#include <LibWeb/CSS/StyleValues/FlexStyleValue.h>
#include <LibWeb/CSS/StyleValues/FrequencyStyleValue.h>
#include <LibWeb/CSS/StyleValues/GridAutoFlowStyleValue.h>
#include <LibWeb/CSS/StyleValues/GridTemplateAreaStyleValue.h>
@ -1757,6 +1758,9 @@ Optional<Dimension> Parser::parse_dimension(ComponentValue const& component_valu
if (auto angle_type = Angle::unit_from_name(unit_string); angle_type.has_value())
return Angle { numeric_value, angle_type.release_value() };
if (auto flex_type = Flex::unit_from_name(unit_string); flex_type.has_value())
return Flex { numeric_value, flex_type.release_value() };
if (auto frequency_type = Frequency::unit_from_name(unit_string); frequency_type.has_value())
return Frequency { numeric_value, frequency_type.release_value() };
@ -6194,6 +6198,7 @@ Optional<Parser::PropertyAndValue> Parser::parse_css_value_for_properties(Readon
}
bool property_accepts_dimension = any_property_accepts_type(property_ids, ValueType::Angle).has_value()
|| any_property_accepts_type(property_ids, ValueType::Flex).has_value()
|| any_property_accepts_type(property_ids, ValueType::Frequency).has_value()
|| any_property_accepts_type(property_ids, ValueType::Length).has_value()
|| any_property_accepts_type(property_ids, ValueType::Percentage).has_value()
@ -6231,6 +6236,13 @@ Optional<Parser::PropertyAndValue> Parser::parse_css_value_for_properties(Readon
return PropertyAndValue { *property, AngleStyleValue::create(angle) };
}
}
if (dimension.is_flex()) {
auto flex = dimension.flex();
if (auto property = any_property_accepts_type(property_ids, ValueType::Flex); property.has_value() && property_accepts_flex(*property, flex)) {
transaction.commit();
return PropertyAndValue { *property, FlexStyleValue::create(flex) };
}
}
if (dimension.is_frequency()) {
auto frequency = dimension.frequency();
if (auto property = any_property_accepts_type(property_ids, ValueType::Frequency); property.has_value() && property_accepts_frequency(*property, frequency)) {
@ -6281,6 +6293,9 @@ Optional<Parser::PropertyAndValue> Parser::parse_css_value_for_properties(Readon
} else if (calculated.resolves_to_angle_percentage()) {
if (auto property = any_property_accepts_type_percentage(property_ids, ValueType::Angle); property.has_value())
return PropertyAndValue { *property, calculated };
} else if (calculated.resolves_to_flex()) {
if (auto property = any_property_accepts_type(property_ids, ValueType::Flex); property.has_value())
return PropertyAndValue { *property, calculated };
} else if (calculated.resolves_to_frequency()) {
if (auto property = any_property_accepts_type(property_ids, ValueType::Frequency); property.has_value())
return PropertyAndValue { *property, calculated };

View file

@ -24,6 +24,7 @@
#include <LibWeb/CSS/StyleValues/EasingStyleValue.h>
#include <LibWeb/CSS/StyleValues/EdgeStyleValue.h>
#include <LibWeb/CSS/StyleValues/FilterValueListStyleValue.h>
#include <LibWeb/CSS/StyleValues/FlexStyleValue.h>
#include <LibWeb/CSS/StyleValues/FrequencyStyleValue.h>
#include <LibWeb/CSS/StyleValues/GridAutoFlowStyleValue.h>
#include <LibWeb/CSS/StyleValues/GridTemplateAreaStyleValue.h>

View file

@ -96,6 +96,7 @@ using StyleValueVector = Vector<ValueComparingNonnullRefPtr<StyleValue const>>;
__ENUMERATE_STYLE_VALUE_TYPE(Easing, easing) \
__ENUMERATE_STYLE_VALUE_TYPE(Edge, edge) \
__ENUMERATE_STYLE_VALUE_TYPE(FilterValueList, filter_value_list) \
__ENUMERATE_STYLE_VALUE_TYPE(Flex, flex) \
__ENUMERATE_STYLE_VALUE_TYPE(Frequency, frequency) \
__ENUMERATE_STYLE_VALUE_TYPE(GridAutoFlow, grid_auto_flow) \
__ENUMERATE_STYLE_VALUE_TYPE(GridTemplateArea, grid_template_area) \

View file

@ -38,6 +38,7 @@ static double resolve_value(CalculatedStyleValue::CalculationResult::Value value
return value.visit(
[](Number const& number) { return number.value(); },
[](Angle const& angle) { return angle.to_degrees(); },
[](Flex const& flex) { return flex.to_fr(); },
[](Frequency const& frequency) { return frequency.to_hertz(); },
[&context](Length const& length) { return length.to_px(*context).to_double(); },
[](Percentage const& percentage) { return percentage.value(); },
@ -74,6 +75,8 @@ static CalculatedStyleValue::CalculationResult to_resolved_type(CalculatedStyleV
return { Number(Number::Type::Number, value) };
case CalculatedStyleValue::ResolvedType::Angle:
return { Angle::make_degrees(value) };
case CalculatedStyleValue::ResolvedType::Flex:
return { Flex::make_fr(value) };
case CalculatedStyleValue::ResolvedType::Frequency:
return { Frequency::make_hertz(value) };
case CalculatedStyleValue::ResolvedType::Length:
@ -137,6 +140,7 @@ Optional<CalculatedStyleValue::ResolvedType> NumericCalculationNode::resolved_ty
return m_value.visit(
[](Number const&) { return CalculatedStyleValue::ResolvedType::Number; },
[](Angle const&) { return CalculatedStyleValue::ResolvedType::Angle; },
[](Flex const&) { return CalculatedStyleValue::ResolvedType::Flex; },
[](Frequency const&) { return CalculatedStyleValue::ResolvedType::Frequency; },
[](Length const&) { return CalculatedStyleValue::ResolvedType::Length; },
[](Percentage const&) { return CalculatedStyleValue::ResolvedType::Percentage; },
@ -175,7 +179,11 @@ Optional<CSSNumericType> NumericCalculationNode::determine_type(PropertyID prope
return CSSNumericType { CSSNumericType::BaseType::Frequency, 1 };
},
// FIXME: <resolution>
// FIXME: <flex>
[](Flex const&) {
// -> <flex>
// the type is «[ "flex" → 1 ]»
return CSSNumericType { CSSNumericType::BaseType::Flex, 1 };
},
// NOTE: <calc-constant> is a separate node type. (FIXME: Should it be?)
[property_id](Percentage const&) {
// -> <percentage>
@ -2053,6 +2061,24 @@ void CalculatedStyleValue::CalculationResult::add_or_subtract_internal(SumOperat
m_value = Angle::make_degrees(this_degrees - other_degrees);
}
},
[&](Flex const& flex) {
auto this_fr = flex.to_fr();
if (other.m_value.has<Flex>()) {
auto other_fr = other.m_value.get<Flex>().to_fr();
if (op == SumOperation::Add)
m_value = Flex::make_fr(this_fr + other_fr);
else
m_value = Flex::make_fr(this_fr - other_fr);
} else {
VERIFY(percentage_basis.has<Flex>());
auto other_fr = percentage_basis.get<Flex>().percentage_of(other.m_value.get<Percentage>()).to_fr();
if (op == SumOperation::Add)
m_value = Flex::make_fr(this_fr + other_fr);
else
m_value = Flex::make_fr(this_fr - other_fr);
}
},
[&](Frequency const& frequency) {
auto this_hertz = frequency.to_hertz();
if (other.m_value.has<Frequency>()) {
@ -2151,6 +2177,9 @@ void CalculatedStyleValue::CalculationResult::multiply_by(CalculationResult cons
[&](Angle const& angle) {
m_value = Angle::make_degrees(angle.to_degrees() * other.m_value.get<Number>().value());
},
[&](Flex const& flex) {
m_value = Flex::make_fr(flex.to_fr() * other.m_value.get<Number>().value());
},
[&](Frequency const& frequency) {
m_value = Frequency::make_hertz(frequency.to_hertz() * other.m_value.get<Number>().value());
},
@ -2183,6 +2212,9 @@ void CalculatedStyleValue::CalculationResult::divide_by(CalculationResult const&
[&](Angle const& angle) {
m_value = Angle::make_degrees(angle.to_degrees() / denominator);
},
[&](Flex const& flex) {
m_value = Flex::make_fr(flex.to_fr() / denominator);
},
[&](Frequency const& frequency) {
m_value = Frequency::make_hertz(frequency.to_hertz() / denominator);
},
@ -2206,6 +2238,9 @@ void CalculatedStyleValue::CalculationResult::negate()
[&](Angle const& angle) {
m_value = Angle { 0 - angle.raw_value(), angle.type() };
},
[&](Flex const& flex) {
m_value = Flex { 0 - flex.raw_value(), flex.type() };
},
[&](Frequency const& frequency) {
m_value = Frequency { 0 - frequency.raw_value(), frequency.type() };
},
@ -2230,6 +2265,9 @@ void CalculatedStyleValue::CalculationResult::invert()
[&](Angle const& angle) {
m_value = Angle { 1 / angle.raw_value(), angle.type() };
},
[&](Flex const& flex) {
m_value = Flex { 1 / flex.raw_value(), flex.type() };
},
[&](Frequency const& frequency) {
m_value = Frequency { 1 / frequency.raw_value(), frequency.type() };
},
@ -2283,6 +2321,15 @@ Optional<Angle> CalculatedStyleValue::resolve_angle_percentage(Angle const& perc
});
}
Optional<Flex> CalculatedStyleValue::resolve_flex() const
{
auto result = m_calculation->resolve({}, {});
if (result.value().has<Flex>())
return result.value().get<Flex>();
return {};
}
Optional<Frequency> CalculatedStyleValue::resolve_frequency() const
{
auto result = m_calculation->resolve({}, {});

View file

@ -12,6 +12,7 @@
#include <AK/Function.h>
#include <LibWeb/CSS/Angle.h>
#include <LibWeb/CSS/CSSNumericType.h>
#include <LibWeb/CSS/Flex.h>
#include <LibWeb/CSS/Frequency.h>
#include <LibWeb/CSS/Length.h>
#include <LibWeb/CSS/Percentage.h>
@ -26,6 +27,7 @@ class CalculatedStyleValue : public StyleValue {
public:
enum class ResolvedType {
Angle,
Flex,
Frequency,
Integer,
Length,
@ -43,11 +45,11 @@ public:
Divide,
};
using PercentageBasis = Variant<Empty, Angle, Frequency, Length, Time>;
using PercentageBasis = Variant<Empty, Angle, Flex, Frequency, Length, Time>;
class CalculationResult {
public:
using Value = Variant<Number, Angle, Frequency, Length, Percentage, Time>;
using Value = Variant<Number, Angle, Flex, Frequency, Length, Percentage, Time>;
CalculationResult(Value value)
: m_value(move(value))
{
@ -79,6 +81,9 @@ public:
Optional<Angle> resolve_angle() const;
Optional<Angle> resolve_angle_percentage(Angle const& percentage_basis) const;
bool resolves_to_flex() const { return m_resolved_type.matches_flex(); }
Optional<Flex> resolve_flex() const;
bool resolves_to_frequency() const { return m_resolved_type.matches_frequency(); }
bool resolves_to_frequency_percentage() const { return m_resolved_type.matches_frequency_percentage(); }
Optional<Frequency> resolve_frequency() const;

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/CSS/Flex.h>
#include <LibWeb/CSS/StyleValue.h>
namespace Web::CSS {
class FlexStyleValue final : public StyleValueWithDefaultOperators<FlexStyleValue> {
public:
static ValueComparingNonnullRefPtr<FlexStyleValue> create(Flex flex)
{
return adopt_ref(*new (nothrow) FlexStyleValue(move(flex)));
}
virtual ~FlexStyleValue() override = default;
Flex const& flex() const { return m_flex; }
Flex& flex() { return m_flex; }
virtual String to_string() const override { return m_flex.to_string(); }
bool properties_equal(FlexStyleValue const& other) const { return m_flex == other.m_flex; }
private:
FlexStyleValue(Flex&& flex)
: StyleValueWithDefaultOperators(Type::Flex)
, m_flex(flex)
{
}
Flex m_flex;
};
}

View file

@ -101,6 +101,8 @@ class EdgeStyleValue;
class ElementInlineCSSStyleDeclaration;
class ExplicitGridTrack;
class FilterValueListStyleValue;
class Flex;
class FlexStyleValue;
class FontFace;
class Frequency;
class FrequencyOrCalculated;