1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 09:38:11 +00:00

LibWeb: Implement CalculationResult type for calc() results

calc() sub-expressions can return a variety of different types, which
then can be combined using the basic arithmetic operators. This class
should make that easier to deal with, instead of having to handle all
the possible combinations at each call site. :^)

We take the Layout::Node as a pointer not a reference, since later we'll
need to call these functions when resolving to `<number>` or `<integer>`
which don't use those, and we don't want to force users to pass them in
unnecessarily.
This commit is contained in:
Sam Atkins 2022-01-27 14:50:31 +00:00 committed by Andreas Kling
parent b69f6097de
commit 35f64af3a4
2 changed files with 135 additions and 9 deletions

View file

@ -249,6 +249,7 @@ String BackgroundStyleValue::to_string() const
return builder.to_string();
}
<<<<<<< HEAD
String BackgroundRepeatStyleValue::to_string() const
{
return String::formatted("{} {}", CSS::to_string(m_repeat_x), CSS::to_string(m_repeat_y));
@ -272,6 +273,113 @@ String BorderRadiusStyleValue::to_string() const
String BoxShadowStyleValue::to_string() const
{
return String::formatted("{} {} {} {}", m_offset_x.to_string(), m_offset_y.to_string(), m_blur_radius.to_string(), m_color.to_string());
=======
void CalculatedStyleValue::CalculationResult::add(CalculationResult const& other, Layout::Node const* layout_node, Length const& percentage_basis)
{
add_or_subtract_internal(SumOperation::Add, other, layout_node, percentage_basis);
}
void CalculatedStyleValue::CalculationResult::subtract(CalculationResult const& other, Layout::Node const* layout_node, Length const& percentage_basis)
{
add_or_subtract_internal(SumOperation::Subtract, other, layout_node, percentage_basis);
}
void CalculatedStyleValue::CalculationResult::add_or_subtract_internal(SumOperation op, CalculationResult const& other, Layout::Node const* layout_node, Length const& percentage_basis)
{
// We know from validation when resolving the type, that "both sides have the same type, or that one side is a <number> and the other is an <integer>".
// Though, having the same type may mean that one side is a <dimension> and the other a <percentage>.
// Note: This is almost identical to ::add()
m_value.visit(
[&](float f) {
if (op == SumOperation::Add)
m_value = f + other.m_value.get<float>();
else
m_value = f - other.m_value.get<float>();
},
[&](Length const& length) {
auto this_px = length.to_px(*layout_node);
if (other.m_value.has<Length>()) {
auto other_px = other.m_value.get<Length>().to_px(*layout_node);
if (op == SumOperation::Add)
m_value = Length::make_px(this_px + other_px);
else
m_value = Length::make_px(this_px - other_px);
} else {
VERIFY(!percentage_basis.is_undefined());
auto other_px = percentage_basis.percentage_of(other.m_value.get<Percentage>()).to_px(*layout_node);
if (op == SumOperation::Add)
m_value = Length::make_px(this_px + other_px);
else
m_value = Length::make_px(this_px - other_px);
}
},
[&](Percentage const& percentage) {
if (other.m_value.has<Percentage>()) {
if (op == SumOperation::Add)
m_value = Percentage { percentage.value() + other.m_value.get<Percentage>().value() };
else
m_value = Percentage { percentage.value() - other.m_value.get<Percentage>().value() };
return;
}
// Other side isn't a percentage, so the easiest way to handle it without duplicating all the logic, is just to swap `this` and `other`.
CalculationResult new_value = other;
if (op == SumOperation::Add)
new_value.add(*this, layout_node, percentage_basis);
else
new_value.subtract(*this, layout_node, percentage_basis);
*this = new_value;
});
}
void CalculatedStyleValue::CalculationResult::multiply_by(CalculationResult const& other, Layout::Node const* layout_node)
{
// We know from validation when resolving the type, that at least one side must be a <number> or <integer>.
// Both of these are represented as a float.
VERIFY(m_value.has<float>() || other.m_value.has<float>());
bool other_is_number = other.m_value.has<float>();
m_value.visit(
[&](float f) {
if (other_is_number) {
m_value = f * other.m_value.get<float>();
} else {
// Avoid duplicating all the logic by swapping `this` and `other`.
CalculationResult new_value = other;
new_value.multiply_by(*this, layout_node);
*this = new_value;
}
},
[&](Length const& length) {
m_value = Length::make_px(length.to_px(*layout_node) * other.m_value.get<float>());
},
[&](Percentage const& percentage) {
m_value = Percentage { percentage.value() * other.m_value.get<float>() };
});
}
void CalculatedStyleValue::CalculationResult::divide_by(CalculationResult const& other, Layout::Node const* layout_node)
{
// We know from validation when resolving the type, that `other` must be a <number> or <integer>.
// Both of these are represented as a float.
float denominator = other.m_value.get<float>();
// FIXME: Dividing by 0 is invalid, and should be caught during parsing.
VERIFY(denominator != 0.0f);
m_value.visit(
[&](float f) {
m_value = f / denominator;
},
[&](Length const& length) {
m_value = Length::make_px(length.to_px(*layout_node) / denominator);
},
[&](Percentage const& percentage) {
m_value = Percentage { percentage.value() / denominator };
});
>>>>>>> d91d120251 (LibWeb: Implement CalculationResult type for calc() results)
}
static float resolve_calc_value(CalculatedStyleValue::CalcValue const& calc_value, Layout::Node const& layout_node);