summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHatem ElKharashy <[email protected]>2024-09-30 18:21:43 +0300
committerHatem ElKharashy <[email protected]>2024-10-04 09:48:23 +0300
commitfdb8c6fc03450c827c7b69c6c152ced1d7bf8841 (patch)
tree82f953f33582db98699cd422b1a83ab45850bfef
parent518fa1baf7bbc93b80567691d682ae64032fde76 (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.cpp70
-rw-r--r--src/gui/text/qcssparser_p.h16
-rw-r--r--tests/auto/gui/text/qcssparser/tst_qcssparser.cpp43
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*/{ }");