1
1
#include " scan.hpp"
2
+ #include < cmath>
2
3
3
4
#include < qcontainerfwd.h>
4
5
#include < qdir.h>
5
6
#include < qfileinfo.h>
7
+ #include < qjsonarray.h>
8
+ #include < qjsondocument.h>
9
+ #include < qjsonobject.h>
10
+ #include < qjsonvalue.h>
6
11
#include < qlogging.h>
7
12
#include < qloggingcategory.h>
13
+ #include < qpair.h>
8
14
#include < qstring.h>
15
+ #include < qstringliteral.h>
9
16
#include < qtextstream.h>
10
17
11
18
Q_LOGGING_CATEGORY (logQmlScanner, " quickshell.qmlscanner" , QtWarningMsg);
@@ -32,6 +39,9 @@ void QmlScanner::scanDir(const QString& path) {
32
39
} else {
33
40
entries.push_back (entry);
34
41
}
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 ));
35
45
}
36
46
}
37
47
@@ -53,7 +63,7 @@ void QmlScanner::scanDir(const QString& path) {
53
63
}
54
64
55
65
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);
57
67
}
58
68
}
59
69
@@ -125,3 +135,84 @@ bool QmlScanner::scanQmlFile(const QString& path) {
125
135
126
136
return singleton;
127
137
}
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