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\nimport 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