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,83 @@ 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 () << " Failed to parse qml.json file at " << path << " : " << error.errorString ();
156+ return ;
157+ }
158+
159+ const QString body =
160+ " pragma Singleton\n import QtQuick as Q\n\n " % QmlScanner::jsonToQml (json.object ()).second ;
161+
162+ qCDebug (logQmlScanner) << " Synthesized qml file for" << path << qPrintable (" \n " + body);
163+
164+ this ->fileIntercepts .insert (path.first (path.length () - 5 ), body);
165+ this ->scannedFiles .push_back (path);
166+ }
167+
168+ QPair<QString, QString> QmlScanner::jsonToQml (const QJsonValue& value, int indent) {
169+ if (value.isObject ()) {
170+ const auto & object = value.toObject ();
171+
172+ auto valIter = object.constBegin ();
173+
174+ QString accum = " Q.QtObject {\n " ;
175+ for (const auto & key: object.keys ()) {
176+ const auto & val = *valIter++;
177+ auto [type, repr] = QmlScanner::jsonToQml (val, indent + 2 );
178+ accum += QString (' ' ).repeated (indent + 2 ) % " readonly property " % type % ' ' % key % " : "
179+ % repr % " ;\n " ;
180+ }
181+
182+ accum += QString (' ' ).repeated (indent) % ' }' ;
183+ return qMakePair (QStringLiteral (" Q.QtObject" ), accum);
184+ } else if (value.isArray ()) {
185+ return qMakePair (
186+ QStringLiteral (" var" ),
187+ QJsonDocument (value.toArray ()).toJson (QJsonDocument::Compact)
188+ );
189+ } else if (value.isString ()) {
190+ const auto & str = value.toString ();
191+
192+ if (str.startsWith (' #' ) && (str.length () == 4 || str.length () == 7 || str.length () == 9 )) {
193+ for (auto c: str.sliced (1 )) {
194+ if (!((c >= ' 0' && c <= ' 9' ) || (c >= ' a' && c <= ' f' ) || (c >= ' A' && c <= ' F' ))) {
195+ goto noncolor;
196+ }
197+ }
198+
199+ return qMakePair (QStringLiteral (" Q.color" ), ' "' % str % ' "' );
200+ }
201+
202+ noncolor:
203+ return qMakePair (QStringLiteral (" string" ), ' "' % QString (str).replace (" \" " , " \\\" " ) % ' "' );
204+ } else if (value.isDouble ()) {
205+ auto num = value.toDouble ();
206+ double whole = 0 ;
207+ if (std::modf (num, &whole) == 0.0 ) {
208+ return qMakePair (QStringLiteral (" int" ), QString::number (static_cast <int >(whole)));
209+ } else {
210+ return qMakePair (QStringLiteral (" real" ), QString::number (num));
211+ }
212+ } else if (value.isBool ()) {
213+ return qMakePair (QStringLiteral (" bool" ), value.toBool () ? " true" : " false" );
214+ } else {
215+ return qMakePair (QStringLiteral (" var" ), " null" );
216+ }
217+ }
0 commit comments