Ver Fonte

LibWeb: Implement and use BorderRadiusStyleValue

This parses the elliptical border-radius values, though currently we
only support circular ones. So, to_length() is overloaded to return the
horizontal-radius. This means the code in Layout/Node.cpp does not need
to change for now.
Sam Atkins há 4 anos atrás
pai
commit
168865dbdc

+ 114 - 0
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -1926,6 +1926,109 @@ RefPtr<StyleValue> Parser::parse_border_value(ParsingContext const& context, Pro
     return BorderStyleValue::create(border_width.release_nonnull(), border_style.release_nonnull(), border_color.release_nonnull());
 }
 
+RefPtr<StyleValue> Parser::parse_border_radius_value(ParsingContext const& context, Vector<StyleComponentValueRule> const& component_values)
+{
+    if (component_values.size() == 2) {
+        auto horizontal = parse_length(context, component_values[0]);
+        auto vertical = parse_length(context, component_values[1]);
+        if (horizontal.has_value() && vertical.has_value())
+            return BorderRadiusStyleValue::create(horizontal.value(), vertical.value());
+
+        return nullptr;
+    }
+
+    if (component_values.size() == 1) {
+        auto radius = parse_length(context, component_values[0]);
+        if (radius.has_value())
+            return BorderRadiusStyleValue::create(radius.value(), radius.value());
+        return nullptr;
+    }
+
+    return nullptr;
+}
+
+RefPtr<StyleValue> Parser::parse_border_radius_shorthand_value(ParsingContext const& context, Vector<StyleComponentValueRule> const& component_values)
+{
+    auto top_left = [&](Vector<Length>& radii) { return radii[0]; };
+    auto top_right = [&](Vector<Length>& radii) {
+        switch (radii.size()) {
+        case 4:
+        case 3:
+        case 2:
+            return radii[1];
+        case 1:
+            return radii[0];
+        default:
+            VERIFY_NOT_REACHED();
+        }
+    };
+    auto bottom_right = [&](Vector<Length>& radii) {
+        switch (radii.size()) {
+        case 4:
+        case 3:
+            return radii[2];
+        case 2:
+        case 1:
+            return radii[0];
+        default:
+            VERIFY_NOT_REACHED();
+        }
+    };
+    auto bottom_left = [&](Vector<Length>& radii) {
+        switch (radii.size()) {
+        case 4:
+            return radii[3];
+        case 3:
+        case 2:
+            return radii[1];
+        case 1:
+            return radii[0];
+        default:
+            VERIFY_NOT_REACHED();
+        }
+    };
+
+    Vector<Length> horizontal_radii;
+    Vector<Length> vertical_radii;
+    bool reading_vertical = false;
+
+    for (auto& value : component_values) {
+        if (value.is(Token::Type::Delim) && value.token().delim() == "/"sv) {
+            if (reading_vertical || horizontal_radii.is_empty())
+                return nullptr;
+
+            reading_vertical = true;
+            continue;
+        }
+
+        auto maybe_length = parse_length(context, value);
+        if (!maybe_length.has_value())
+            return nullptr;
+        if (reading_vertical) {
+            vertical_radii.append(maybe_length.value());
+        } else {
+            horizontal_radii.append(maybe_length.value());
+        }
+    }
+
+    if (horizontal_radii.size() > 4 || vertical_radii.size() > 4
+        || horizontal_radii.is_empty()
+        || (reading_vertical && vertical_radii.is_empty()))
+        return nullptr;
+
+    NonnullRefPtrVector<StyleValue> border_radii;
+    border_radii.append(BorderRadiusStyleValue::create(top_left(horizontal_radii),
+        vertical_radii.is_empty() ? top_left(horizontal_radii) : top_left(vertical_radii)));
+    border_radii.append(BorderRadiusStyleValue::create(top_right(horizontal_radii),
+        vertical_radii.is_empty() ? top_right(horizontal_radii) : top_right(vertical_radii)));
+    border_radii.append(BorderRadiusStyleValue::create(bottom_right(horizontal_radii),
+        vertical_radii.is_empty() ? bottom_right(horizontal_radii) : bottom_right(vertical_radii)));
+    border_radii.append(BorderRadiusStyleValue::create(bottom_left(horizontal_radii),
+        vertical_radii.is_empty() ? bottom_left(horizontal_radii) : bottom_left(vertical_radii)));
+
+    return StyleValueList::create(move(border_radii));
+}
+
 RefPtr<StyleValue> Parser::parse_box_shadow_value(ParsingContext const& context, Vector<StyleComponentValueRule> const& component_values)
 {
     // FIXME: Also support inset, spread-radius and multiple comma-seperated box-shadows
@@ -2516,6 +2619,17 @@ RefPtr<StyleValue> Parser::parse_css_value(PropertyID property_id, TokenStream<S
         if (auto parsed_value = parse_border_value(m_context, property_id, component_values))
             return parsed_value;
         break;
+    case PropertyID::BorderTopLeftRadius:
+    case PropertyID::BorderTopRightRadius:
+    case PropertyID::BorderBottomRightRadius:
+    case PropertyID::BorderBottomLeftRadius:
+        if (auto parsed_value = parse_border_radius_value(m_context, component_values))
+            return parsed_value;
+        break;
+    case PropertyID::BorderRadius:
+        if (auto parsed_value = parse_border_radius_shorthand_value(m_context, component_values))
+            return parsed_value;
+        break;
     case PropertyID::BoxShadow:
         if (auto parsed_box_shadow = parse_box_shadow_value(m_context, component_values))
             return parsed_box_shadow;

+ 2 - 0
Userland/Libraries/LibWeb/CSS/Parser/Parser.h

@@ -177,6 +177,8 @@ private:
     static RefPtr<StyleValue> parse_image_value(ParsingContext const&, StyleComponentValueRule const&);
     static RefPtr<StyleValue> parse_background_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);
     static RefPtr<StyleValue> parse_border_value(ParsingContext const&, PropertyID, Vector<StyleComponentValueRule> const&);
+    static RefPtr<StyleValue> parse_border_radius_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);
+    static RefPtr<StyleValue> parse_border_radius_shorthand_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);
     static RefPtr<StyleValue> parse_box_shadow_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);
     static RefPtr<StyleValue> parse_flex_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);
     static RefPtr<StyleValue> parse_flex_flow_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);

+ 6 - 45
Userland/Libraries/LibWeb/CSS/StyleResolver.cpp

@@ -235,57 +235,18 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
     }
 
     if (property_id == CSS::PropertyID::BorderRadius) {
-        // FIXME: Allow for two values per corner to support elliptical radii.
-        // FIXME: Add support the '/' to specify elliptical radii.
-        if (value.is_length()) {
+        if (value.is_value_list()) {
+            auto& values_list = static_cast<StyleValueList const&>(value);
+            assign_edge_values(PropertyID::BorderTopLeftRadius, PropertyID::BorderTopRightRadius, PropertyID::BorderBottomRightRadius, PropertyID::BorderBottomLeftRadius, values_list.values());
+            return;
+        }
+        if (value.is_builtin()) {
             style.set_property(CSS::PropertyID::BorderTopLeftRadius, value);
             style.set_property(CSS::PropertyID::BorderTopRightRadius, value);
             style.set_property(CSS::PropertyID::BorderBottomRightRadius, value);
             style.set_property(CSS::PropertyID::BorderBottomLeftRadius, value);
             return;
         }
-
-        if (value.is_component_value_list()) {
-            auto& parts = static_cast<CSS::ValueListStyleValue const&>(value).values();
-            if (parts.size() == 2) {
-                auto diagonal1 = Parser::parse_css_value(context, property_id, parts[0]);
-                auto diagonal2 = Parser::parse_css_value(context, property_id, parts[1]);
-                if (diagonal1 && diagonal2) {
-                    style.set_property(CSS::PropertyID::BorderTopLeftRadius, *diagonal1);
-                    style.set_property(CSS::PropertyID::BorderBottomRightRadius, *diagonal1);
-                    style.set_property(CSS::PropertyID::BorderTopRightRadius, *diagonal2);
-                    style.set_property(CSS::PropertyID::BorderBottomLeftRadius, *diagonal2);
-                }
-                return;
-            }
-            if (parts.size() == 3) {
-                auto top_left = Parser::parse_css_value(context, property_id, parts[0]);
-                auto diagonal = Parser::parse_css_value(context, property_id, parts[1]);
-                auto bottom_right = Parser::parse_css_value(context, property_id, parts[2]);
-                if (top_left && diagonal && bottom_right) {
-                    style.set_property(CSS::PropertyID::BorderTopLeftRadius, *top_left);
-                    style.set_property(CSS::PropertyID::BorderBottomRightRadius, *bottom_right);
-                    style.set_property(CSS::PropertyID::BorderTopRightRadius, *diagonal);
-                    style.set_property(CSS::PropertyID::BorderBottomLeftRadius, *diagonal);
-                }
-                return;
-            }
-            if (parts.size() == 4) {
-                auto top_left = Parser::parse_css_value(context, property_id, parts[0]);
-                auto top_right = Parser::parse_css_value(context, property_id, parts[1]);
-                auto bottom_right = Parser::parse_css_value(context, property_id, parts[2]);
-                auto bottom_left = Parser::parse_css_value(context, property_id, parts[3]);
-                if (top_left && top_right && bottom_right && bottom_left) {
-                    style.set_property(CSS::PropertyID::BorderTopLeftRadius, *top_left);
-                    style.set_property(CSS::PropertyID::BorderBottomRightRadius, *bottom_right);
-                    style.set_property(CSS::PropertyID::BorderTopRightRadius, *top_right);
-                    style.set_property(CSS::PropertyID::BorderBottomLeftRadius, *bottom_left);
-                }
-                return;
-            }
-            dbgln("Unsure what to do with CSS border-radius value '{}'", value.to_string());
-            return;
-        }
         return;
     }
 

+ 36 - 0
Userland/Libraries/LibWeb/CSS/StyleValue.h

@@ -231,6 +231,7 @@ public:
         Calculated,
         Background,
         Border,
+        BorderRadius,
         BoxShadow,
         Flex,
         FlexFlow,
@@ -255,6 +256,7 @@ public:
     bool is_calculated() const { return type() == Type::Calculated; }
     bool is_background() const { return type() == Type::Background; }
     bool is_border() const { return type() == Type::Border; }
+    bool is_border_radius() const { return type() == Type::BorderRadius; }
     bool is_box_shadow() const { return type() == Type::BoxShadow; }
     bool is_flex() const { return type() == Type::Flex; }
     bool is_flex_flow() const { return type() == Type::FlexFlow; }
@@ -720,6 +722,40 @@ private:
     NonnullRefPtr<StyleValue> m_border_color;
 };
 
+class BorderRadiusStyleValue final : public StyleValue {
+public:
+    static NonnullRefPtr<BorderRadiusStyleValue> create(Length const& horizontal_radius, Length const& vertical_radius)
+    {
+        return adopt_ref(*new BorderRadiusStyleValue(horizontal_radius, vertical_radius));
+    }
+    virtual ~BorderRadiusStyleValue() override { }
+
+    Length const& horizontal_radius() const { return m_horizontal_radius; }
+    Length const& vertical_radius() const { return m_vertical_radius; }
+    bool is_elliptical() const { return m_is_elliptical; }
+
+    // FIXME: Remove this once we support elliptical border-radius in Layout/Node.
+    virtual Length to_length() const override { return horizontal_radius(); }
+
+    virtual String to_string() const override
+    {
+        return String::formatted("{} / {}", m_horizontal_radius.to_string(), m_vertical_radius.to_string());
+    }
+
+private:
+    BorderRadiusStyleValue(Length const& horizontal_radius, Length const& vertical_radius)
+        : StyleValue(Type::BorderRadius)
+        , m_horizontal_radius(horizontal_radius)
+        , m_vertical_radius(vertical_radius)
+    {
+        m_is_elliptical = (m_horizontal_radius != m_vertical_radius);
+    }
+
+    bool m_is_elliptical;
+    Length m_horizontal_radius;
+    Length m_vertical_radius;
+};
+
 class FlexStyleValue final : public StyleValue {
 public:
     static NonnullRefPtr<FlexStyleValue> create(

+ 1 - 0
Userland/Libraries/LibWeb/Layout/Node.cpp

@@ -233,6 +233,7 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& specified_style)
         m_background_image = static_ptr_cast<CSS::ImageStyleValue>(bgimage.value());
     }
 
+    // FIXME: BorderXRadius properties are now BorderRadiusStyleValues, so make use of that.
     auto border_bottom_left_radius = specified_style.property(CSS::PropertyID::BorderBottomLeftRadius);
     if (border_bottom_left_radius.has_value())
         computed_values.set_border_bottom_left_radius(border_bottom_left_radius.value()->to_length());