Pārlūkot izejas kodu

LibWeb: Add CSS::Percentage, PercentageOr and LengthPercentage types

Length and Percentage are different types, and sometimes only one or the
other is allowed in a given CSS property. This is a first step towards
separating them.
Sam Atkins 3 gadi atpakaļ
vecāks
revīzija
01b57fa8b7

+ 2 - 1
Meta/Lagom/Tools/CodeGenerators/LibWeb/Generate_CSS_PropertyID_cpp.cpp

@@ -311,8 +311,9 @@ bool property_accepts_value(PropertyID property_id, StyleValue& style_value)
             return true;
 )~~~");
                         } else if (type_name == "percentage") {
+                            // FIXME: Detecting lengths here is temporary until Length/Percentage are fully separated.
                             property_generator.append(R"~~~(
-        if ((style_value.has_length() && style_value.to_length().is_percentage()) || style_value.is_calculated())
+        if (style_value.is_percentage() || style_value.is_calculated() || (style_value.has_length() && !style_value.to_length().is_percentage()))
             return true;
 )~~~");
                         } else if (type_name == "number" || type_name == "integer") {

+ 12 - 0
Userland/Libraries/LibWeb/CSS/Length.cpp

@@ -10,6 +10,7 @@
 #include <LibGfx/Font.h>
 #include <LibGfx/Rect.h>
 #include <LibWeb/CSS/Length.h>
+#include <LibWeb/CSS/Percentage.h>
 #include <LibWeb/DOM/Document.h>
 #include <LibWeb/HTML/BrowsingContext.h>
 #include <LibWeb/HTML/HTMLHtmlElement.h>
@@ -32,11 +33,22 @@ Length Length::make_auto()
 {
     return Length(0, Type::Auto);
 }
+
 Length Length::make_px(float value)
 {
     return Length(value, Type::Px);
 }
 
+Length Length::percentage_of(Percentage const& percentage) const
+{
+    if (is_undefined_or_auto()) {
+        dbgln("Attempting to get percentage of an undefined or auto length, this seems wrong? But for now we just return the original length.");
+        return *this;
+    }
+
+    return Length { percentage.as_fraction() * raw_value(), m_type };
+}
+
 Length Length::resolved(const Length& fallback_for_undefined, const Layout::Node& layout_node, float reference_for_percent) const
 {
     if (is_undefined())

+ 1 - 0
Userland/Libraries/LibWeb/CSS/Length.h

@@ -44,6 +44,7 @@ public:
 
     static Length make_auto();
     static Length make_px(float value);
+    Length percentage_of(Percentage const&) const;
 
     Length resolved(const Length& fallback_for_undefined, const Layout::Node& layout_node, float reference_for_percent) const;
     Length resolved_or_auto(const Layout::Node& layout_node, float reference_for_percent) const;

+ 141 - 0
Userland/Libraries/LibWeb/CSS/Percentage.h

@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/String.h>
+#include <AK/Variant.h>
+#include <LibWeb/CSS/Length.h>
+
+namespace Web::CSS {
+
+class Percentage {
+public:
+    explicit Percentage(int value)
+        : m_value(value)
+    {
+    }
+
+    explicit Percentage(float value)
+        : m_value(value)
+    {
+    }
+
+    float value() const { return m_value; }
+    float as_fraction() const { return m_value * 0.01f; }
+
+    String to_string() const
+    {
+        return String::formatted("{}%", m_value);
+    }
+
+    bool operator==(Percentage const& other) const { return m_value == other.m_value; }
+    bool operator!=(Percentage const& other) const { return !(*this == other); }
+
+private:
+    float m_value;
+};
+
+template<typename T>
+class PercentageOr {
+public:
+    PercentageOr(T t)
+        : m_value(move(t))
+    {
+    }
+
+    PercentageOr(Percentage percentage)
+        : m_value(move(percentage))
+    {
+    }
+
+    PercentageOr<T>& operator=(T t)
+    {
+        m_value = move(t);
+        return *this;
+    }
+
+    PercentageOr<T>& operator=(Percentage percentage)
+    {
+        m_value = move(percentage);
+        return *this;
+    }
+
+    bool is_percentage() const { return m_value.template has<Percentage>(); }
+
+    Percentage const& percentage() const
+    {
+        VERIFY(is_percentage());
+        return m_value.template get<Percentage>();
+    }
+
+    T resolved(T const& reference_value) const
+    {
+        if (is_percentage())
+            return reference_value.percentage_of(m_value.template get<Percentage>());
+
+        return m_value.template get<T>();
+    }
+
+    String to_string() const
+    {
+        if (is_percentage())
+            return m_value.template get<Percentage>().to_string();
+
+        return m_value.template get<T>().to_string();
+    }
+
+    bool operator==(PercentageOr<T> const& other) const
+    {
+        if (is_percentage() != other.is_percentage())
+            return false;
+        if (is_percentage())
+            return (m_value.template get<Percentage>() == other.m_value.template get<Percentage>());
+        return (m_value.template get<T>() == other.m_value.template get<T>());
+    }
+    bool operator!=(PercentageOr<T> const& other) const { return !(*this == other); }
+
+protected:
+    bool is_non_percentage_value() const { return m_value.template has<T>(); }
+    T const& non_percentage_value() const { return m_value.template get<T>(); }
+
+private:
+    Variant<T, Percentage> m_value;
+};
+
+template<typename T>
+bool operator==(PercentageOr<T> const& percentage_or, T const& t)
+{
+    return percentage_or == PercentageOr<T> { t };
+}
+
+template<typename T>
+bool operator==(T const& t, PercentageOr<T> const& percentage_or)
+{
+    return t == percentage_or;
+}
+
+template<typename T>
+bool operator==(PercentageOr<T> const& percentage_or, Percentage const& percentage)
+{
+    return percentage_or == PercentageOr<T> { percentage };
+}
+
+template<typename T>
+bool operator==(Percentage const& percentage, PercentageOr<T> const& percentage_or)
+{
+    return percentage == percentage_or;
+}
+
+class LengthPercentage : public PercentageOr<Length> {
+public:
+    using PercentageOr<Length>::PercentageOr;
+
+    bool is_length() const { return is_non_percentage_value(); }
+    Length const& length() const { return non_percentage_value(); }
+};
+
+}

+ 2 - 1
Userland/Libraries/LibWeb/Forward.h

@@ -26,7 +26,6 @@ class BorderRadiusStyleValue;
 class BorderStyleValue;
 class BoxShadowStyleValue;
 class CalculatedStyleValue;
-class CalculatedStyleValue;
 class ColorStyleValue;
 class CSSImportRule;
 class CSSMediaRule;
@@ -46,6 +45,7 @@ class ImageStyleValue;
 class InheritStyleValue;
 class InitialStyleValue;
 class Length;
+class LengthPercentage;
 class LengthStyleValue;
 class ListStyleStyleValue;
 class MediaList;
@@ -54,6 +54,7 @@ class MediaQueryList;
 class MediaQueryListEvent;
 class NumericStyleValue;
 class OverflowStyleValue;
+class Percentage;
 class PositionStyleValue;
 class PropertyOwningCSSStyleDeclaration;
 class Screen;