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:
parent
355d1936f2
commit
bd79c303f6
9 changed files with 267 additions and 5 deletions
|
@ -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
|
||||
|
|
86
Userland/Libraries/LibWeb/CSS/Frequency.cpp
Normal file
86
Userland/Libraries/LibWeb/CSS/Frequency.cpp
Normal 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 {};
|
||||
}
|
||||
|
||||
}
|
53
Userland/Libraries/LibWeb/CSS/Frequency.h
Normal file
53
Userland/Libraries/LibWeb/CSS/Frequency.h
Normal 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;
|
||||
};
|
||||
}
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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(); });
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -43,6 +43,9 @@ class ElementInlineCSSStyleDeclaration;
|
|||
class FlexFlowStyleValue;
|
||||
class FlexStyleValue;
|
||||
class FontStyleValue;
|
||||
class Frequency;
|
||||
class FrequencyPercentage;
|
||||
class FrequencyStyleValue;
|
||||
class IdentifierStyleValue;
|
||||
class ImageStyleValue;
|
||||
class InheritStyleValue;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue