diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 7b28958d19..0240cfc322 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -1926,6 +1926,109 @@ RefPtr Parser::parse_border_value(ParsingContext const& context, Pro return BorderStyleValue::create(border_width.release_nonnull(), border_style.release_nonnull(), border_color.release_nonnull()); } +RefPtr Parser::parse_border_radius_value(ParsingContext const& context, Vector 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 Parser::parse_border_radius_shorthand_value(ParsingContext const& context, Vector const& component_values) +{ + auto top_left = [&](Vector& radii) { return radii[0]; }; + auto top_right = [&](Vector& 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& 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& 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 horizontal_radii; + Vector 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 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 Parser::parse_box_shadow_value(ParsingContext const& context, Vector const& component_values) { // FIXME: Also support inset, spread-radius and multiple comma-seperated box-shadows @@ -2516,6 +2619,17 @@ RefPtr Parser::parse_css_value(PropertyID property_id, TokenStream parse_image_value(ParsingContext const&, StyleComponentValueRule const&); static RefPtr parse_background_value(ParsingContext const&, Vector const&); static RefPtr parse_border_value(ParsingContext const&, PropertyID, Vector const&); + static RefPtr parse_border_radius_value(ParsingContext const&, Vector const&); + static RefPtr parse_border_radius_shorthand_value(ParsingContext const&, Vector const&); static RefPtr parse_box_shadow_value(ParsingContext const&, Vector const&); static RefPtr parse_flex_value(ParsingContext const&, Vector const&); static RefPtr parse_flex_flow_value(ParsingContext const&, Vector const&); diff --git a/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp b/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp index c58cc4ee0d..669f6842b7 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp +++ b/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(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(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; } diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h index a08860bfd2..66e1dd3f09 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.h +++ b/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 m_border_color; }; +class BorderRadiusStyleValue final : public StyleValue { +public: + static NonnullRefPtr 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 create( diff --git a/Userland/Libraries/LibWeb/Layout/Node.cpp b/Userland/Libraries/LibWeb/Layout/Node.cpp index b6069936ae..982d969258 100644 --- a/Userland/Libraries/LibWeb/Layout/Node.cpp +++ b/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(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());