11#include " scan.hpp"
2+ #include < cmath>
23
34#include < qcontainerfwd.h>
45#include < qdir.h>
56#include < qfileinfo.h>
7+ #include < qjsonarray.h>
8+ #include < qjsondocument.h>
9+ #include < qjsonobject.h>
10+ #include < qjsonvalue.h>
611#include < qlogging.h>
712#include < qloggingcategory.h>
13+ #include < qpair.h>
814#include < qstring.h>
15+ #include < qstringliteral.h>
916#include < qtextstream.h>
1017
1118Q_LOGGING_CATEGORY (logQmlScanner, " quickshell.qmlscanner" , QtWarningMsg);
@@ -32,6 +39,9 @@ void QmlScanner::scanDir(const QString& path) {
3239 } else {
3340 entries.push_back (entry);
3441 }
42+ } else if (entry.at (0 ).isUpper () && entry.endsWith (" .qml.json" )) {
43+ this ->scanQmlJson (dir.filePath (entry));
44+ singletons.push_back (entry.first (entry.length () - 5 ));
3545 }
3646 }
3747
@@ -53,7 +63,7 @@ void QmlScanner::scanDir(const QString& path) {
5363 }
5464
5565 qCDebug (logQmlScanner) << " Synthesized qmldir for" << path << qPrintable (" \n " + qmldir);
56- this ->qmldirIntercepts .insert (QDir (path).filePath (" qmldir" ), qmldir);
66+ this ->fileIntercepts .insert (QDir (path).filePath (" qmldir" ), qmldir);
5767 }
5868}
5969
@@ -125,3 +135,84 @@ bool QmlScanner::scanQmlFile(const QString& path) {
125135
126136 return singleton;
127137}
138+
139+ void QmlScanner::scanQmlJson (const QString& path) {
140+ qCDebug (logQmlScanner) << " Scanning qml.json file" << path;
141+
142+ auto file = QFile (path);
143+ if (!file.open (QFile::ReadOnly | QFile::Text)) {
144+ qCWarning (logQmlScanner) << " Failed to open file" << path;
145+ return ;
146+ }
147+
148+ auto data = file.readAll ();
149+
150+ // Importing this makes CI builds fail for some reason.
151+ QJsonParseError error; // NOLINT (misc-include-cleaner)
152+ auto json = QJsonDocument::fromJson (data, &error);
153+
154+ if (error.error != QJsonParseError::NoError) {
155+ qCCritical (logQmlScanner).nospace ()
156+ << " Failed to parse qml.json file at " << path << " : " << error.errorString ();
157+ return ;
158+ }
159+
160+ const QString body =
161+ " pragma Singleton\n import QtQuick as Q\n\n " % QmlScanner::jsonToQml (json.object ()).second ;
162+
163+ qCDebug (logQmlScanner) << " Synthesized qml file for" << path << qPrintable (" \n " + body);
164+
165+ this ->fileIntercepts .insert (path.first (path.length () - 5 ), body);
166+ this ->scannedFiles .push_back (path);
167+ }
168+
169+ QPair<QString, QString> QmlScanner::jsonToQml (const QJsonValue& value, int indent) {
170+ if (value.isObject ()) {
171+ const auto & object = value.toObject ();
172+
173+ auto valIter = object.constBegin ();
174+
175+ QString accum = " Q.QtObject {\n " ;
176+ for (const auto & key: object.keys ()) {
177+ const auto & val = *valIter++;
178+ auto [type, repr] = QmlScanner::jsonToQml (val, indent + 2 );
179+ accum += QString (' ' ).repeated (indent + 2 ) % " readonly property " % type % ' ' % key % " : "
180+ % repr % " ;\n " ;
181+ }
182+
183+ accum += QString (' ' ).repeated (indent) % ' }' ;
184+ return qMakePair (QStringLiteral (" Q.QtObject" ), accum);
185+ } else if (value.isArray ()) {
186+ return qMakePair (
187+ QStringLiteral (" var" ),
188+ QJsonDocument (value.toArray ()).toJson (QJsonDocument::Compact)
189+ );
190+ } else if (value.isString ()) {
191+ const auto & str = value.toString ();
192+
193+ if (str.startsWith (' #' ) && (str.length () == 4 || str.length () == 7 || str.length () == 9 )) {
194+ for (auto c: str.sliced (1 )) {
195+ if (!((c >= ' 0' && c <= ' 9' ) || (c >= ' a' && c <= ' f' ) || (c >= ' A' && c <= ' F' ))) {
196+ goto noncolor;
197+ }
198+ }
199+
200+ return qMakePair (QStringLiteral (" Q.color" ), ' "' % str % ' "' );
201+ }
202+
203+ noncolor:
204+ return qMakePair (QStringLiteral (" string" ), ' "' % QString (str).replace (" \" " , " \\\" " ) % ' "' );
205+ } else if (value.isDouble ()) {
206+ auto num = value.toDouble ();
207+ double whole = 0 ;
208+ if (std::modf (num, &whole) == 0.0 ) {
209+ return qMakePair (QStringLiteral (" int" ), QString::number (static_cast <int >(whole)));
210+ } else {
211+ return qMakePair (QStringLiteral (" real" ), QString::number (num));
212+ }
213+ } else if (value.isBool ()) {
214+ return qMakePair (QStringLiteral (" bool" ), value.toBool () ? " true" : " false" );
215+ } else {
216+ return qMakePair (QStringLiteral (" var" ), " null" );
217+ }
218+ }
0 commit comments