diff options
author | Hatem ElKharashy <[email protected]> | 2024-09-30 18:21:43 +0300 |
---|---|---|
committer | Hatem ElKharashy <[email protected]> | 2024-10-04 09:48:23 +0300 |
commit | fdb8c6fc03450c827c7b69c6c152ced1d7bf8841 (patch) | |
tree | 82f953f33582db98699cd422b1a83ab45850bfef | |
parent | 518fa1baf7bbc93b80567691d682ae64032fde76 (diff) |
Support parsing animations in the CSS parser
Extend the CSS parser to include animations in the parsed rules. This
will help extending QtSvg module to support CSS animations.
Task-number: QTBUG-127592
Change-Id: I70d776c721f05f28126ae7406bd8fa499a5c2729
Reviewed-by: Eskil Abrahamsen Blomfeldt <[email protected]>
Reviewed-by: Eirik Aavitsland <[email protected]>
-rw-r--r-- | src/gui/text/qcssparser.cpp | 70 | ||||
-rw-r--r-- | src/gui/text/qcssparser_p.h | 16 | ||||
-rw-r--r-- | tests/auto/gui/text/qcssparser/tst_qcssparser.cpp | 43 |
3 files changed, 129 insertions, 0 deletions
diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp index b894f539779..b58c17bcf79 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -2420,6 +2420,10 @@ bool Parser::parse(StyleSheet *styleSheet, Qt::CaseSensitivity nameCaseSensitivi PageRule rule; if (!parsePage(&rule)) return false; styleSheet->pageRules.append(rule); + } else if (testAnimation()) { + AnimationRule rule; + if (!parseAnimation(&rule)) return false; + styleSheet->animationRules.append(rule); } else if (testRuleset()) { StyleRule rule; if (!parseRuleset(&rule)) return false; @@ -2546,6 +2550,72 @@ bool Parser::parseNextOperator(Value *value) return true; } +bool Parser::parseAnimation(AnimationRule *animationRule) +{ + skipSpace(); + if (!test(IDENT)) return false; + + animationRule->animName = lexem(); + + if (!next(LBRACE)) return false; + skipSpace(); + + while (test(PERCENTAGE) || test(IDENT)) { + AnimationRule::AnimationRuleSet set; + if (lookup() == PERCENTAGE) { + QString name = lexem(); + name.removeLast(); + float keyFrame = name.toFloat() / 100; + set.keyFrame = keyFrame; + } else if (lookup() == IDENT) { + QString name; + if (parseElementName(&name)) { + if (name == QStringLiteral("from")) + set.keyFrame = 0; + else if (name == QStringLiteral("to")) + set.keyFrame = 1; + } + } + + skipSpace(); + if (!next(LBRACE)) return false; + const int declarationStart = index; + + do { + skipSpace(); + Declaration decl; + const int rewind = index; + if (!parseNextDeclaration(&decl)) { + index = rewind; + const bool foundSemicolon = until(SEMICOLON); + const int semicolonIndex = index; + + index = declarationStart; + const bool foundRBrace = until(RBRACE); + + if (foundSemicolon && semicolonIndex < index) { + decl = Declaration(); + index = semicolonIndex - 1; + } else { + skipSpace(); + return foundRBrace; + } + } + if (!decl.isEmpty()) + set.declarations.append(decl); + } while (test(SEMICOLON)); + + if (!next(RBRACE)) return false; + skipSpace(); + animationRule->ruleSets.append(set); + } + + if (!next(RBRACE)) return false; + skipSpace(); + + return true; +} + bool Parser::parseCombinator(BasicSelector::Relation *relation) { *relation = BasicSelector::NoRelation; diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h index ba4a611df34..22f71e0e8c2 100644 --- a/src/gui/text/qcssparser_p.h +++ b/src/gui/text/qcssparser_p.h @@ -600,6 +600,19 @@ struct PageRule }; QT_CSS_DECLARE_TYPEINFO(PageRule, Q_RELOCATABLE_TYPE) +struct AnimationRule +{ + struct AnimationRuleSet + { + float keyFrame; + QList<Declaration> declarations; + }; + + QString animName; + QList<AnimationRuleSet> ruleSets; +}; +QT_CSS_DECLARE_TYPEINFO(AnimationRule, Q_RELOCATABLE_TYPE) + struct ImportRule { QString href; @@ -621,6 +634,7 @@ struct StyleSheet QList<StyleRule> styleRules; // only contains rules that are not indexed QList<MediaRule> mediaRules; QList<PageRule> pageRules; + QList<AnimationRule> animationRules; QList<ImportRule> importRules; StyleSheetOrigin origin; int depth; // applicable only for inline style sheets @@ -752,6 +766,7 @@ public: bool parsePage(PageRule *pageRule); bool parsePseudoPage(QString *selector); bool parseNextOperator(Value *value); + bool parseAnimation(AnimationRule *animationRule); bool parseCombinator(BasicSelector::Relation *relation); bool parseProperty(Declaration *decl); bool parseRuleset(StyleRule *styleRule); @@ -784,6 +799,7 @@ public: inline bool testImport() { return testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1StringView("import")); } inline bool testMedia() { return testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1StringView("media")); } inline bool testPage() { return testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1StringView("page")); } + inline bool testAnimation() { return testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1StringView("keyframes")); } inline bool testCombinator() { return test(PLUS) || test(GREATER) || test(TILDE) || test(S); } inline bool testProperty() { return test(IDENT); } bool testTerm(); diff --git a/tests/auto/gui/text/qcssparser/tst_qcssparser.cpp b/tests/auto/gui/text/qcssparser/tst_qcssparser.cpp index 61731313317..ac1f54772bb 100644 --- a/tests/auto/gui/text/qcssparser/tst_qcssparser.cpp +++ b/tests/auto/gui/text/qcssparser/tst_qcssparser.cpp @@ -21,6 +21,7 @@ private slots: void expr(); void import(); void media(); + void animation(); void page(); void ruleset(); void selector_data(); @@ -398,6 +399,48 @@ void tst_QCssParser::media() QVERIFY(rule.styleRules.isEmpty()); } +void tst_QCssParser::animation() +{ + QCss::Parser parser("@keyframes emptyAnimation{} motion{from {x : 10;} to {x : 50;}} color{0% {fill : blue;} 25% {fill : yellow;} 100% {fill : red;}}"); + QVERIFY(parser.testAnimation()); + + { + QCss::AnimationRule rule; + QVERIFY(parser.parseAnimation(&rule)); + QCOMPARE(rule.animName, QStringLiteral("emptyAnimation")); + QCOMPARE(rule.ruleSets.size(), 0); + } + + { + QCss::AnimationRule rule; + QVERIFY(parser.parseAnimation(&rule)); + QCOMPARE(rule.animName, QStringLiteral("motion")); + QCOMPARE(rule.ruleSets.size(), 2); + QCOMPARE(rule.ruleSets[0].keyFrame, 0); + QCOMPARE(rule.ruleSets[1].keyFrame, 1); + QCOMPARE(rule.ruleSets[0].declarations[0].d->property, QStringLiteral("x")); + QCOMPARE(rule.ruleSets[0].declarations[0].d->values[0].toString(), QStringLiteral("10")); + QCOMPARE(rule.ruleSets[1].declarations[0].d->property, QStringLiteral("x")); + QCOMPARE(rule.ruleSets[1].declarations[0].d->values[0].toString(), QStringLiteral("50")); + } + + { + QCss::AnimationRule rule; + QVERIFY(parser.parseAnimation(&rule)); + QCOMPARE(rule.animName, QStringLiteral("color")); + QCOMPARE(rule.ruleSets.size(), 3); + QCOMPARE(rule.ruleSets[0].keyFrame, 0); + QCOMPARE(rule.ruleSets[1].keyFrame, 0.25); + QCOMPARE(rule.ruleSets[2].keyFrame, 1); + QCOMPARE(rule.ruleSets[0].declarations[0].d->property, QStringLiteral("fill")); + QCOMPARE(rule.ruleSets[0].declarations[0].d->values[0].toString(), QStringLiteral("blue")); + QCOMPARE(rule.ruleSets[1].declarations[0].d->property, QStringLiteral("fill")); + QCOMPARE(rule.ruleSets[1].declarations[0].d->values[0].toString(), QStringLiteral("yellow")); + QCOMPARE(rule.ruleSets[2].declarations[0].d->property, QStringLiteral("fill")); + QCOMPARE(rule.ruleSets[2].declarations[0].d->values[0].toString(), QStringLiteral("red")); + } +} + void tst_QCssParser::page() { QCss::Parser parser("@page :first/*comment to ignore*/{ }"); |