
This makes it possible to do arithmetic on them without having to resolve to their canonical unit, which often requires context information that is not available until the last minute. For example, a Length cannot be resolved to px without knowing the font size, parent element's size, etc. Only Length currently requires such context, but treating all these types the same means that code that manipulates them does not need to know or care if a new unit gets added that does require contextual information.
97 lines
2.6 KiB
C++
97 lines
2.6 KiB
C++
/*
|
|
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/String.h>
|
|
#include <AK/Types.h>
|
|
#include <math.h>
|
|
|
|
namespace Web::CSS {
|
|
|
|
class Number {
|
|
public:
|
|
enum class Type {
|
|
Number,
|
|
IntegerWithExplicitSign, // This only exists for the nightmarish An+B parsing algorithm
|
|
Integer
|
|
};
|
|
|
|
Number()
|
|
: m_value(0)
|
|
, m_type(Type::Number)
|
|
{
|
|
}
|
|
Number(Type type, float value)
|
|
: m_value(value)
|
|
, m_type(type)
|
|
{
|
|
}
|
|
|
|
Type type() const { return m_type; }
|
|
float value() const { return m_value; }
|
|
i64 integer_value() const
|
|
{
|
|
// https://www.w3.org/TR/css-values-4/#numeric-types
|
|
// When a value cannot be explicitly supported due to range/precision limitations, it must be converted
|
|
// to the closest value supported by the implementation, but how the implementation defines "closest"
|
|
// is explicitly undefined as well.
|
|
return llroundf(m_value);
|
|
}
|
|
bool is_integer() const { return m_type == Type::Integer || m_type == Type::IntegerWithExplicitSign; }
|
|
bool is_integer_with_explicit_sign() const { return m_type == Type::IntegerWithExplicitSign; }
|
|
|
|
Number operator+(Number const& other) const
|
|
{
|
|
if (is_integer() && other.is_integer())
|
|
return { Type::Integer, m_value + other.m_value };
|
|
return { Type::Number, m_value + other.m_value };
|
|
}
|
|
|
|
Number operator-(Number const& other) const
|
|
{
|
|
if (is_integer() && other.is_integer())
|
|
return { Type::Integer, m_value - other.m_value };
|
|
return { Type::Number, m_value - other.m_value };
|
|
}
|
|
|
|
Number operator*(Number const& other) const
|
|
{
|
|
if (is_integer() && other.is_integer())
|
|
return { Type::Integer, m_value * other.m_value };
|
|
return { Type::Number, m_value * other.m_value };
|
|
}
|
|
|
|
Number operator/(Number const& other) const
|
|
{
|
|
return { Type::Number, m_value / other.m_value };
|
|
}
|
|
|
|
ErrorOr<String> to_string() const
|
|
{
|
|
if (m_type == Type::IntegerWithExplicitSign)
|
|
return String::formatted("{:+}", m_value);
|
|
return String::number(m_value);
|
|
}
|
|
|
|
bool operator==(Number const& other) const
|
|
{
|
|
return m_type == other.m_type && m_value == other.m_value;
|
|
}
|
|
|
|
private:
|
|
float m_value { 0 };
|
|
Type m_type;
|
|
};
|
|
}
|
|
|
|
template<>
|
|
struct AK::Formatter<Web::CSS::Number> : Formatter<StringView> {
|
|
ErrorOr<void> format(FormatBuilder& builder, Web::CSS::Number const& number)
|
|
{
|
|
return Formatter<StringView>::format(builder, TRY(number.to_string()));
|
|
}
|
|
};
|