diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPropertyID.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPropertyID.cpp index 15335f26fc..2cbec5abdf 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPropertyID.cpp +++ b/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 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); diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/CSS/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibWeb/CSS/BUILD.gn index 7cb1eec785..7dc029699b 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWeb/CSS/BUILD.gn +++ b/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", diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 82f14a9e49..ff5be5ae34 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/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 diff --git a/Userland/Libraries/LibWeb/CSS/CSSNumericType.cpp b/Userland/Libraries/LibWeb/CSS/CSSNumericType.cpp index b2a2ad8ddf..677627b162 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSNumericType.cpp +++ b/Userland/Libraries/LibWeb/CSS/CSSNumericType.cpp @@ -20,6 +20,8 @@ Optional 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: diff --git a/Userland/Libraries/LibWeb/CSS/CSSNumericType.h b/Userland/Libraries/LibWeb/CSS/CSSNumericType.h index e283870f8b..09ab31d1bc 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSNumericType.h +++ b/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); } diff --git a/Userland/Libraries/LibWeb/CSS/Flex.cpp b/Userland/Libraries/LibWeb/CSS/Flex.cpp new file mode 100644 index 0000000000..f33cfa6645 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/Flex.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "Flex.h" +#include + +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::unit_from_name(StringView name) +{ + if (name.equals_ignoring_ascii_case("fr"sv)) + return Type::Fr; + + return {}; +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/Flex.h b/Userland/Libraries/LibWeb/CSS/Flex.h new file mode 100644 index 0000000000..c6fe3bcacb --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/Flex.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Web::CSS { + +// https://drafts.csswg.org/css-grid-2/#typedef-flex +class Flex { +public: + enum class Type { + Fr, + }; + + static Optional 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 : Formatter { + ErrorOr format(FormatBuilder& builder, Web::CSS::Flex const& flex) + { + return Formatter::format(builder, flex.to_string()); + } +}; diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Dimension.h b/Userland/Libraries/LibWeb/CSS/Parser/Dimension.h index 70e0da572b..e661ee51f0 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Dimension.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Dimension.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -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() const { return m_value.get(); } + bool is_frequency() const { return m_value.has(); } Frequency frequency() const { return m_value.get(); } @@ -99,7 +108,7 @@ public: } private: - Variant m_value; + Variant m_value; }; } diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index cf32355aa0..d2722b4780 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -1757,6 +1758,9 @@ Optional 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::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::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::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 }; diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp index 39b370b2df..6e96b723f6 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h index f7e3c49b26..9896131a63 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.h @@ -96,6 +96,7 @@ using StyleValueVector = Vector>; __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) \ diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.cpp index e09329a9c5..503d9e0837 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.cpp +++ b/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 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 NumericCalculationNode::determine_type(PropertyID prope return CSSNumericType { CSSNumericType::BaseType::Frequency, 1 }; }, // FIXME: - // FIXME: + [](Flex const&) { + // -> + // the type is «[ "flex" → 1 ]» + return CSSNumericType { CSSNumericType::BaseType::Flex, 1 }; + }, // NOTE: is a separate node type. (FIXME: Should it be?) [property_id](Percentage const&) { // -> @@ -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()) { + auto other_fr = other.m_value.get().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()); + + auto other_fr = percentage_basis.get().percentage_of(other.m_value.get()).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()) { @@ -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().value()); }, + [&](Flex const& flex) { + m_value = Flex::make_fr(flex.to_fr() * other.m_value.get().value()); + }, [&](Frequency const& frequency) { m_value = Frequency::make_hertz(frequency.to_hertz() * other.m_value.get().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 CalculatedStyleValue::resolve_angle_percentage(Angle const& perc }); } +Optional CalculatedStyleValue::resolve_flex() const +{ + auto result = m_calculation->resolve({}, {}); + + if (result.value().has()) + return result.value().get(); + return {}; +} + Optional CalculatedStyleValue::resolve_frequency() const { auto result = m_calculation->resolve({}, {}); diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.h index 989efd85be..58a59b8f13 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -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; + using PercentageBasis = Variant; class CalculationResult { public: - using Value = Variant; + using Value = Variant; CalculationResult(Value value) : m_value(move(value)) { @@ -79,6 +81,9 @@ public: Optional resolve_angle() const; Optional resolve_angle_percentage(Angle const& percentage_basis) const; + bool resolves_to_flex() const { return m_resolved_type.matches_flex(); } + Optional 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 resolve_frequency() const; diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/FlexStyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/FlexStyleValue.h new file mode 100644 index 0000000000..3ffc14e48b --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/FlexStyleValue.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Web::CSS { + +class FlexStyleValue final : public StyleValueWithDefaultOperators { +public: + static ValueComparingNonnullRefPtr 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; +}; + +} diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 8c655e8c7a..2a8ff4ba71 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/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;