From d2b4d13c877c9875be11db07b1a559975473f25e Mon Sep 17 00:00:00 2001 From: Robin Linden Date: Mon, 16 Oct 2023 23:00:11 +0200 Subject: [PATCH] css: Expand the flex-flow shorthand property --- css/parser.cpp | 58 +++++++++++++++++++++++++++++++++++++++++++++ css/parser.h | 3 +++ css/parser_test.cpp | 42 ++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+) diff --git a/css/parser.cpp b/css/parser.cpp index 41f5573b..524b02e6 100644 --- a/css/parser.cpp +++ b/css/parser.cpp @@ -437,6 +437,8 @@ void Parser::add_declaration( expand_border_radius_values(declarations, value); } else if (name == "text-decoration") { expand_text_decoration_values(declarations, value); + } else if (name == "flex-flow") { + expand_flex_flow(declarations, value); } else if (is_in_array(name)) { expand_border(name, declarations, value); } else { @@ -638,6 +640,62 @@ void Parser::expand_text_decoration_values(std::map &de declarations.insert_or_assign(PropertyId::TextDecorationStyle, "solid"); } +// https://developer.mozilla.org/en-US/docs/Web/CSS/flex-flow +void Parser::expand_flex_flow(std::map &declarations, std::string_view value) { + static constexpr std::array kGlobalValues{"inherit", "initial", "revert", "revert-layer", "unset"}; + + auto is_wrap = [](std::string_view str) { + return str == "wrap" || str == "nowrap" || str == "wrap-reverse"; + }; + auto is_direction = [](std::string_view str) { + return str == "row" || str == "row-reverse" || str == "column" || str == "column-reverse"; + }; + + std::string direction{"row"}; + std::string wrap{"nowrap"}; + + Tokenizer tokenizer{value, ' '}; + if (tokenizer.size() != 1 && tokenizer.size() != 2) { + spdlog::warn("Unsupported flex-flow value: '{}'", value); + return; + } + + auto first = tokenizer.get(); + auto second = tokenizer.next().get(); + // Global values are only allowed if there's a single value. + if (first && !second && is_in_array(*first)) { + wrap = direction = *first; + declarations.insert_or_assign(PropertyId::FlexDirection, std::move(direction)); + declarations.insert_or_assign(PropertyId::FlexWrap, std::move(wrap)); + return; + } + + // No duplicates of wrap or direction allowed. + if ((first && second) + && ((is_wrap(*first) && !is_direction(*second)) || (is_direction(*first) && !is_wrap(*second)))) { + spdlog::warn("Unsupported flex-flow value: '{}'", value); + return; + } + + for (auto const &v : std::array{first, second}) { + if (!v) { + continue; + } + + if (is_wrap(*v)) { + wrap = *v; + } else if (is_direction(*v)) { + direction = *v; + } else { + spdlog::warn("Unsupported flex-flow value: '{}'", value); + return; + } + } + + declarations.insert_or_assign(PropertyId::FlexDirection, std::move(direction)); + declarations.insert_or_assign(PropertyId::FlexWrap, std::move(wrap)); +} + void Parser::expand_edge_values( std::map &declarations, std::string property, std::string_view value) const { std::string_view top = "", bottom = "", left = "", right = ""; diff --git a/css/parser.h b/css/parser.h index df5aea40..25e66207 100644 --- a/css/parser.h +++ b/css/parser.h @@ -71,6 +71,9 @@ class Parser { static void expand_text_decoration_values(std::map &declarations, std::string_view value); + // https://developer.mozilla.org/en-US/docs/Web/CSS/flex-flow + static void expand_flex_flow(std::map &, std::string_view); + void expand_edge_values( std::map &declarations, std::string property, std::string_view value) const; diff --git a/css/parser_test.cpp b/css/parser_test.cpp index f6ad2d66..e0ce9cf3 100644 --- a/css/parser_test.cpp +++ b/css/parser_test.cpp @@ -1025,5 +1025,47 @@ int main() { std::ignore = css::parse("p { font-size:"); }); + etest::test("parser: flex-flow shorthand, global value", [] { + expect_eq(css::parse("p { flex-flow: revert; }").rules.at(0).declarations, + std::map{ + {css::PropertyId::FlexDirection, "revert"s}, + {css::PropertyId::FlexWrap, "revert"s}, + }); + expect_eq(css::parse("p { flex-flow: revert row; }").rules.at(0).declarations, + std::map{}); + }); + + etest::test("parser: flex-flow shorthand, one value", [] { + expect_eq(css::parse("p { flex-flow: column; }").rules.at(0).declarations, + std::map{ + {css::PropertyId::FlexDirection, "column"s}, + {css::PropertyId::FlexWrap, "nowrap"s}, + }); + expect_eq(css::parse("p { flex-flow: wrap; }").rules.at(0).declarations, + std::map{ + {css::PropertyId::FlexDirection, "row"s}, + {css::PropertyId::FlexWrap, "wrap"s}, + }); + expect_eq(css::parse("p { flex-flow: aaaaaaaaaa; }").rules.at(0).declarations, + std::map{}); + }); + + etest::test("parser: flex-flow shorthand, two values", [] { + expect_eq(css::parse("p { flex-flow: column wrap; }").rules.at(0).declarations, + std::map{ + {css::PropertyId::FlexDirection, "column"s}, + {css::PropertyId::FlexWrap, "wrap"s}, + }); + expect_eq(css::parse("p { flex-flow: wrap wrap; }").rules.at(0).declarations, // + std::map{}); + expect_eq(css::parse("p { flex-flow: wrap asdf; }").rules.at(0).declarations, // + std::map{}); + }); + + etest::test("parser: flex-flow shorthand, too many values :(", [] { + expect_eq(css::parse("p { flex-flow: column wrap nowrap; }").rules.at(0).declarations, + std::map{}); + }); + return etest::run_all_tests(); }