From 35f64af3a46a3f4128b0fe7dc4f89d70abcddbe5 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 27 Jan 2022 14:50:31 +0000 Subject: [PATCH] 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 `` or `` which don't use those, and we don't want to force users to pass them in unnecessarily. --- Userland/Libraries/LibWeb/CSS/StyleValue.cpp | 108 +++++++++++++++++++ Userland/Libraries/LibWeb/CSS/StyleValue.h | 36 +++++-- 2 files changed, 135 insertions(+), 9 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp index 67d057ca83..3c17869a30 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp @@ -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 and the other is an ". + // Though, having the same type may mean that one side is a and the other a . + // Note: This is almost identical to ::add() + + m_value.visit( + [&](float f) { + if (op == SumOperation::Add) + m_value = f + other.m_value.get(); + else + m_value = f - other.m_value.get(); + }, + [&](Length const& length) { + auto this_px = length.to_px(*layout_node); + if (other.m_value.has()) { + auto other_px = other.m_value.get().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()).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()) { + if (op == SumOperation::Add) + m_value = Percentage { percentage.value() + other.m_value.get().value() }; + else + m_value = Percentage { percentage.value() - other.m_value.get().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 or . + // Both of these are represented as a float. + VERIFY(m_value.has() || other.m_value.has()); + bool other_is_number = other.m_value.has(); + + m_value.visit( + [&](float f) { + if (other_is_number) { + m_value = f * other.m_value.get(); + } 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()); + }, + [&](Percentage const& percentage) { + m_value = Percentage { percentage.value() * other.m_value.get() }; + }); +} + +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 or . + // Both of these are represented as a float. + float denominator = other.m_value.get(); + // 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); diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h index 04afe626a9..3bfd434bf9 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.h @@ -664,6 +664,33 @@ public: Time, }; + enum class SumOperation { + Add, + Subtract, + }; + enum class ProductOperation { + Multiply, + Divide, + }; + + class CalculationResult { + public: + CalculationResult(Variant value) + : m_value(move(value)) + { + } + void add(CalculationResult const& other, Layout::Node const*, Length const& percentage_basis); + void subtract(CalculationResult const& other, Layout::Node const*, Length const& percentage_basis); + void multiply_by(CalculationResult const& other, Layout::Node const*); + void divide_by(CalculationResult const& other, Layout::Node const*); + + Variant const& value() const { return m_value; } + + private: + void add_or_subtract_internal(SumOperation op, CalculationResult const& other, Layout::Node const*, Length const& percentage_basis); + Variant m_value; + }; + struct CalcSum; struct CalcSumPartWithOperator; struct CalcProduct; @@ -683,15 +710,6 @@ public: Optional resolved_type() const; }; - enum class SumOperation { - Add, - Subtract, - }; - enum class ProductOperation { - Multiply, - Divide, - }; - // This represents that: https://www.w3.org/TR/css-values-3/#calc-syntax struct CalcSum { CalcSum(NonnullOwnPtr first_calc_product, NonnullOwnPtrVector additional)