Explorar o código

LibWeb: Implement Flex and FlexStyleValue types

Sam Atkins hai 1 ano
pai
achega
b0317bb3a1

+ 6 - 1
Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPropertyID.cpp

@@ -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);

+ 1 - 0
Meta/gn/secondary/Userland/Libraries/LibWeb/CSS/BUILD.gn

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

+ 1 - 0
Userland/Libraries/LibWeb/CMakeLists.txt

@@ -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

+ 2 - 0
Userland/Libraries/LibWeb/CSS/CSSNumericType.cpp

@@ -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:

+ 0 - 1
Userland/Libraries/LibWeb/CSS/CSSNumericType.h

@@ -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); }

+ 59 - 0
Userland/Libraries/LibWeb/CSS/Flex.cpp

@@ -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 {};
+}
+
+}

+ 66 - 0
Userland/Libraries/LibWeb/CSS/Flex.h

@@ -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());
+    }
+};

+ 10 - 1
Userland/Libraries/LibWeb/CSS/Parser/Dimension.h

@@ -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;
 };
 
 }

+ 15 - 0
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -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 };

+ 1 - 0
Userland/Libraries/LibWeb/CSS/StyleValue.cpp

@@ -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>

+ 1 - 0
Userland/Libraries/LibWeb/CSS/StyleValue.h

@@ -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)     \

+ 48 - 1
Userland/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.cpp

@@ -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({}, {});

+ 7 - 2
Userland/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.h

@@ -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;

+ 39 - 0
Userland/Libraries/LibWeb/CSS/StyleValues/FlexStyleValue.h

@@ -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;
+};
+
+}

+ 2 - 0
Userland/Libraries/LibWeb/Forward.h

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