1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-29 17:15:11 +00:00

LibWeb: Implement CSS Frequency class

This corresponds to `<frequency>` in the grammar.
This commit is contained in:
Sam Atkins 2022-02-21 17:49:47 +00:00 committed by Andreas Kling
parent 355d1936f2
commit bd79c303f6
9 changed files with 267 additions and 5 deletions

View file

@ -28,6 +28,7 @@ set(SOURCES
CSS/CSSSupportsRule.cpp
CSS/DefaultStyleSheetSource.cpp
CSS/Display.cpp
CSS/Frequency.cpp
CSS/Length.cpp
CSS/MediaList.cpp
CSS/MediaQuery.cpp

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "Frequency.h"
#include <LibWeb/CSS/StyleValue.h>
namespace Web::CSS {
Frequency::Frequency(int value, Type type)
: m_type(type)
, m_value(value)
{
}
Frequency::Frequency(float value, Type type)
: m_type(type)
, m_value(value)
{
}
Frequency Frequency::make_calculated(NonnullRefPtr<CalculatedStyleValue> calculated_style_value)
{
Frequency frequency { 0, Type::Calculated };
frequency.m_calculated_style = move(calculated_style_value);
return frequency;
}
Frequency Frequency::make_hertz(float value)
{
return { value, Type::Hz };
}
Frequency Frequency::percentage_of(Percentage const& percentage) const
{
VERIFY(!is_calculated());
return Frequency { percentage.as_fraction() * m_value, m_type };
}
String Frequency::to_string() const
{
if (is_calculated())
return m_calculated_style->to_string();
return String::formatted("{}{}", m_value, unit_name());
}
float Frequency::to_hertz() const
{
switch (m_type) {
case Type::Calculated:
return m_calculated_style->resolve_frequency()->to_hertz();
case Type::Hz:
return m_value;
case Type::kHz:
return m_value * 1000;
}
VERIFY_NOT_REACHED();
}
StringView Frequency::unit_name() const
{
switch (m_type) {
case Type::Calculated:
return "calculated"sv;
case Type::Hz:
return "hz"sv;
case Type::kHz:
return "khz"sv;
}
VERIFY_NOT_REACHED();
}
Optional<Frequency::Type> Frequency::unit_from_name(StringView name)
{
if (name.equals_ignoring_case("hz"sv)) {
return Type::Hz;
} else if (name.equals_ignoring_case("khz"sv)) {
return Type::kHz;
}
return {};
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/RefPtr.h>
#include <LibWeb/Forward.h>
namespace Web::CSS {
class Frequency {
public:
enum class Type {
Calculated,
Hz,
kHz
};
static Optional<Type> unit_from_name(StringView);
Frequency(int value, Type type);
Frequency(float value, Type type);
static Frequency make_calculated(NonnullRefPtr<CalculatedStyleValue>);
static Frequency make_hertz(float);
Frequency percentage_of(Percentage const&) const;
bool is_calculated() const { return m_type == Type::Calculated; }
String to_string() const;
float to_hertz() const;
bool operator==(Frequency const& other) const
{
if (is_calculated())
return m_calculated_style == other.m_calculated_style;
return m_type == other.m_type && m_value == other.m_value;
}
bool operator!=(Frequency const& other) const
{
return !(*this == other);
}
private:
StringView unit_name() const;
Type m_type;
float m_value { 0 };
RefPtr<CalculatedStyleValue> m_calculated_style;
};
}

View file

@ -4300,6 +4300,8 @@ Optional<CalculatedStyleValue::CalcValue> Parser::parse_calc_value(TokenStream<S
if (dimension.is_angle())
return CalculatedStyleValue::CalcValue { dimension.angle() };
if (dimension.is_frequency())
return CalculatedStyleValue::CalcValue { dimension.frequency() };
if (dimension.is_length())
return CalculatedStyleValue::CalcValue { dimension.length() };
if (dimension.is_percentage())

View file

@ -14,6 +14,11 @@ Angle AnglePercentage::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> co
return calculated->resolve_angle_percentage(reference_value)->resolved(layout_node, reference_value);
}
Frequency FrequencyPercentage::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node, Frequency const& reference_value) const
{
return calculated->resolve_frequency_percentage(reference_value)->resolved(layout_node, reference_value);
}
Length LengthPercentage::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node, Length const& reference_value) const
{
return calculated->resolve_length_percentage(layout_node, reference_value)->resolved(layout_node, reference_value);

View file

@ -9,6 +9,7 @@
#include <AK/String.h>
#include <AK/Variant.h>
#include <LibWeb/CSS/Angle.h>
#include <LibWeb/CSS/Frequency.h>
#include <LibWeb/CSS/Length.h>
namespace Web::CSS {
@ -160,6 +161,16 @@ public:
Angle const& angle() const { return get_t(); }
virtual Angle resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const&, Layout::Node const&, Angle const& reference_value) const override;
};
class FrequencyPercentage : public PercentageOr<Frequency> {
public:
using PercentageOr<Frequency>::PercentageOr;
bool is_frequency() const { return is_t(); }
Frequency const& frequency() const { return get_t(); }
virtual Frequency resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const&, Layout::Node const&, Frequency const& reference_value) const override;
};
class LengthPercentage : public PercentageOr<Length> {
public:
using PercentageOr<Length>::PercentageOr;
@ -187,6 +198,15 @@ struct AK::Formatter<Web::CSS::AnglePercentage> : Formatter<StringView> {
}
};
template<>
struct AK::Formatter<Web::CSS::FrequencyPercentage> : Formatter<StringView> {
ErrorOr<void> format(FormatBuilder& builder, Web::CSS::FrequencyPercentage const& frequency_percentage)
{
return Formatter<StringView>::format(builder, frequency_percentage.to_string());
}
};
template<>
struct AK::Formatter<Web::CSS::LengthPercentage> : Formatter<StringView> {
ErrorOr<void> format(FormatBuilder& builder, Web::CSS::LengthPercentage const& length_percentage)
{

View file

@ -99,6 +99,12 @@ FontStyleValue const& StyleValue::as_font() const
return static_cast<FontStyleValue const&>(*this);
}
FrequencyStyleValue const& StyleValue::as_frequency() const
{
VERIFY(is_frequency());
return static_cast<FrequencyStyleValue const&>(*this);
}
IdentifierStyleValue const& StyleValue::as_identifier() const
{
VERIFY(is_identifier());
@ -333,6 +339,24 @@ void CalculatedStyleValue::CalculationResult::add_or_subtract_internal(SumOperat
m_value = Angle::make_degrees(this_degrees - other_degrees);
}
},
[&](Frequency const& frequency) {
auto this_hertz = frequency.to_hertz();
if (other.m_value.has<Frequency>()) {
auto other_hertz = other.m_value.get<Frequency>().to_hertz();
if (op == SumOperation::Add)
m_value = Frequency::make_hertz(this_hertz + other_hertz);
else
m_value = Frequency::make_hertz(this_hertz - other_hertz);
} else {
VERIFY(percentage_basis.has<Frequency>());
auto other_hertz = percentage_basis.get<Frequency>().percentage_of(other.m_value.get<Percentage>()).to_hertz();
if (op == SumOperation::Add)
m_value = Frequency::make_hertz(this_hertz + other_hertz);
else
m_value = Frequency::make_hertz(this_hertz - other_hertz);
}
},
[&](Length const& length) {
auto this_px = length.to_px(*layout_node);
if (other.m_value.has<Length>()) {
@ -396,6 +420,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);
},
[&](Frequency const& frequency) {
m_value = Frequency::make_hertz(frequency.to_hertz() * other.m_value.get<Number>().value);
},
[&](Length const& length) {
VERIFY(layout_node);
m_value = Length::make_px(length.to_px(*layout_node) * other.m_value.get<Number>().value);
@ -423,6 +450,9 @@ void CalculatedStyleValue::CalculationResult::divide_by(CalculationResult const&
[&](Angle const& angle) {
m_value = Angle::make_degrees(angle.to_degrees() / denominator);
},
[&](Frequency const& frequency) {
m_value = Frequency::make_hertz(frequency.to_hertz() / denominator);
},
[&](Length const& length) {
VERIFY(layout_node);
m_value = Length::make_px(length.to_px(*layout_node) / denominator);
@ -536,6 +566,31 @@ Optional<AnglePercentage> CalculatedStyleValue::resolve_angle_percentage(Angle c
});
}
Optional<Frequency> CalculatedStyleValue::resolve_frequency() const
{
auto result = m_expression->resolve(nullptr, {});
if (result.value().has<Frequency>())
return result.value().get<Frequency>();
return {};
}
Optional<FrequencyPercentage> CalculatedStyleValue::resolve_frequency_percentage(Frequency const& percentage_basis) const
{
auto result = m_expression->resolve(nullptr, percentage_basis);
return result.value().visit(
[&](Frequency const& frequency) -> Optional<FrequencyPercentage> {
return frequency;
},
[&](Percentage const& percentage) -> Optional<FrequencyPercentage> {
return percentage;
},
[&](auto const&) -> Optional<FrequencyPercentage> {
return {};
});
}
Optional<Length> CalculatedStyleValue::resolve_length(Layout::Node const& layout_node) const
{
auto result = m_expression->resolve(&layout_node, {});
@ -745,6 +800,7 @@ Optional<CalculatedStyleValue::ResolvedType> CalculatedStyleValue::CalcValue::re
return { number.is_integer ? ResolvedType::Integer : ResolvedType::Number };
},
[](Angle const&) -> Optional<CalculatedStyleValue::ResolvedType> { return { ResolvedType::Angle }; },
[](Frequency const&) -> Optional<CalculatedStyleValue::ResolvedType> { return { ResolvedType::Frequency }; },
[](Length const&) -> Optional<CalculatedStyleValue::ResolvedType> { return { ResolvedType::Length }; },
[](Percentage const&) -> Optional<CalculatedStyleValue::ResolvedType> { return { ResolvedType::Percentage }; },
[](NonnullOwnPtr<CalcSum> const& sum) { return sum->resolved_type(); });

View file

@ -24,6 +24,7 @@
#include <LibGfx/Color.h>
#include <LibWeb/CSS/Angle.h>
#include <LibWeb/CSS/Display.h>
#include <LibWeb/CSS/Frequency.h>
#include <LibWeb/CSS/Length.h>
#include <LibWeb/CSS/Parser/StyleComponentValueRule.h>
#include <LibWeb/CSS/Percentage.h>
@ -297,6 +298,7 @@ public:
Flex,
FlexFlow,
Font,
Frequency,
Identifier,
Image,
Inherit,
@ -330,6 +332,7 @@ public:
bool is_flex() const { return type() == Type::Flex; }
bool is_flex_flow() const { return type() == Type::FlexFlow; }
bool is_font() const { return type() == Type::Font; }
bool is_frequency() const { return type() == Type::Frequency; }
bool is_identifier() const { return type() == Type::Identifier; }
bool is_image() const { return type() == Type::Image; }
bool is_inherit() const { return type() == Type::Inherit; }
@ -361,6 +364,7 @@ public:
FlexFlowStyleValue const& as_flex_flow() const;
FlexStyleValue const& as_flex() const;
FontStyleValue const& as_font() const;
FrequencyStyleValue const& as_frequency() const;
IdentifierStyleValue const& as_identifier() const;
ImageStyleValue const& as_image() const;
InheritStyleValue const& as_inherit() const;
@ -390,6 +394,7 @@ public:
FlexFlowStyleValue& as_flex_flow() { return const_cast<FlexFlowStyleValue&>(const_cast<StyleValue const&>(*this).as_flex_flow()); }
FlexStyleValue& as_flex() { return const_cast<FlexStyleValue&>(const_cast<StyleValue const&>(*this).as_flex()); }
FontStyleValue& as_font() { return const_cast<FontStyleValue&>(const_cast<StyleValue const&>(*this).as_font()); }
FrequencyStyleValue& as_frequency() { return const_cast<FrequencyStyleValue&>(const_cast<StyleValue const&>(*this).as_frequency()); }
IdentifierStyleValue& as_identifier() { return const_cast<IdentifierStyleValue&>(const_cast<StyleValue const&>(*this).as_identifier()); }
ImageStyleValue& as_image() { return const_cast<ImageStyleValue&>(const_cast<StyleValue const&>(*this).as_image()); }
InheritStyleValue& as_inherit() { return const_cast<InheritStyleValue&>(const_cast<StyleValue const&>(*this).as_inherit()); }
@ -730,11 +735,11 @@ public:
float value;
};
using PercentageBasis = Variant<Empty, Angle, Length>;
using PercentageBasis = Variant<Empty, Angle, Frequency, Length>;
class CalculationResult {
public:
CalculationResult(Variant<Number, Angle, Length, Percentage> value)
CalculationResult(Variant<Number, Angle, Frequency, Length, Percentage> value)
: m_value(move(value))
{
}
@ -743,11 +748,11 @@ public:
void multiply_by(CalculationResult const& other, Layout::Node const*);
void divide_by(CalculationResult const& other, Layout::Node const*);
Variant<Number, Angle, Length, Percentage> const& value() const { return m_value; }
Variant<Number, Angle, Frequency, Length, Percentage> const& value() const { return m_value; }
private:
void add_or_subtract_internal(SumOperation op, CalculationResult const& other, Layout::Node const*, PercentageBasis const& percentage_basis);
Variant<Number, Angle, Length, Percentage> m_value;
Variant<Number, Angle, Frequency, Length, Percentage> m_value;
};
struct CalcSum;
@ -767,7 +772,7 @@ public:
};
struct CalcValue {
Variant<Number, Angle, Length, Percentage, NonnullOwnPtr<CalcSum>> value;
Variant<Number, Angle, Frequency, Length, Percentage, NonnullOwnPtr<CalcSum>> value;
String to_string() const;
Optional<ResolvedType> resolved_type() const;
CalculationResult resolve(Layout::Node const*, PercentageBasis const& percentage_basis) const;
@ -873,6 +878,8 @@ public:
Optional<Angle> resolve_angle() const;
Optional<AnglePercentage> resolve_angle_percentage(Angle const& percentage_basis) const;
Optional<Frequency> resolve_frequency() const;
Optional<FrequencyPercentage> resolve_frequency_percentage(Frequency const& percentage_basis) const;
Optional<Length> resolve_length(Layout::Node const& layout_node) const;
Optional<LengthPercentage> resolve_length_percentage(Layout::Node const&, Length const& percentage_basis) const;
Optional<Percentage> resolve_percentage() const;
@ -1040,6 +1047,35 @@ private:
// FIXME: Implement font-stretch and font-variant.
};
class FrequencyStyleValue : public StyleValue {
public:
static NonnullRefPtr<FrequencyStyleValue> create(Frequency frequency)
{
return adopt_ref(*new FrequencyStyleValue(move(frequency)));
}
virtual ~FrequencyStyleValue() override { }
Frequency const& frequency() const { return m_frequency; }
virtual String to_string() const override { return m_frequency.to_string(); }
virtual bool equals(StyleValue const& other) const override
{
if (type() != other.type())
return false;
return m_frequency == static_cast<FrequencyStyleValue const&>(other).m_frequency;
}
private:
explicit FrequencyStyleValue(Frequency frequency)
: StyleValue(Type::Frequency)
, m_frequency(move(frequency))
{
}
Frequency m_frequency;
};
class IdentifierStyleValue final : public StyleValue {
public:
static NonnullRefPtr<IdentifierStyleValue> create(CSS::ValueID id)

View file

@ -43,6 +43,9 @@ class ElementInlineCSSStyleDeclaration;
class FlexFlowStyleValue;
class FlexStyleValue;
class FontStyleValue;
class Frequency;
class FrequencyPercentage;
class FrequencyStyleValue;
class IdentifierStyleValue;
class ImageStyleValue;
class InheritStyleValue;