1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
4 | ** Contact: http://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL3$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see http://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at http://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or later as published by the Free |
28 | ** Software Foundation and appearing in the file LICENSE.GPL included in |
29 | ** the packaging of this file. Please review the following information to |
30 | ** ensure the GNU General Public License version 2.0 requirements will be |
31 | ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
32 | ** |
33 | ** $QT_END_LICENSE$ |
34 | ** |
35 | ****************************************************************************/ |
36 | |
37 | #include "qquickstyle.h" |
38 | #include "qquickstyle_p.h" |
39 | |
40 | #include <QtCore/qdir.h> |
41 | #include <QtCore/qfile.h> |
42 | #include <QtCore/qdebug.h> |
43 | #include <QtCore/qsettings.h> |
44 | #include <QtCore/qfileselector.h> |
45 | #include <QtCore/qlibraryinfo.h> |
46 | #include <QtCore/qmetaobject.h> |
47 | #include <QtGui/qcolor.h> |
48 | #include <QtGui/qfont.h> |
49 | #include <QtGui/qpalette.h> |
50 | #include <QtGui/private/qguiapplication_p.h> |
51 | #include <QtGui/qpa/qplatformtheme.h> |
52 | #include <QtQml/private/qqmlmetatype_p.h> |
53 | #include <QtQml/qqmlengine.h> |
54 | #include <QtQml/qqmlfile.h> |
55 | |
56 | #include <functional> |
57 | |
58 | QT_BEGIN_NAMESPACE |
59 | |
60 | /*! |
61 | \class QQuickStyle |
62 | \brief The QQuickStyle class allows configuring the application style. |
63 | \inmodule QtQuickControls2 |
64 | \since 5.7 |
65 | |
66 | QQuickStyle provides API for querying and configuring the application |
67 | \l {Styling Qt Quick Controls}{styles} of Qt Quick Controls. |
68 | |
69 | \code |
70 | #include <QGuiApplication> |
71 | #include <QQmlApplicationEngine> |
72 | #include <QQuickStyle> |
73 | |
74 | int main(int argc, char *argv[]) |
75 | { |
76 | QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); |
77 | QGuiApplication app(argc, argv); |
78 | |
79 | QQuickStyle::setStyle("Material"); |
80 | |
81 | QQmlApplicationEngine engine; |
82 | engine.load(QUrl("qrc:/main.qml")); |
83 | |
84 | return app.exec(); |
85 | } |
86 | \endcode |
87 | |
88 | \note The style must be configured \b before loading QML that imports |
89 | Qt Quick Controls. It is not possible to change the style after the QML |
90 | types have been registered. |
91 | |
92 | The style can also be specified as a path to a custom style, such as |
93 | \c ":/mystyle". See \l {Creating a Custom Style} for more details about |
94 | building custom styles. Custom styles do not need to implement all controls. |
95 | By default, the styling system uses the \l {Default style} as a fallback |
96 | for controls that a custom style does not provide. It is possible to |
97 | specify a different fallback style to customize or extend one of the |
98 | built-in styles. |
99 | |
100 | \code |
101 | QQuickStyle::setStyle(":/mystyle"); |
102 | QQuickStyle::setFallbackStyle("Material"); |
103 | \endcode |
104 | |
105 | \sa {Styling Qt Quick Controls} |
106 | */ |
107 | |
108 | static QStringList envPathList(const QByteArray &var) |
109 | { |
110 | QStringList paths; |
111 | if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty(var))) { |
112 | const QByteArray value = qgetenv(varName: var); |
113 | paths += QString::fromLocal8Bit(str: value).split(sep: QDir::listSeparator(), behavior: Qt::SkipEmptyParts); |
114 | } |
115 | return paths; |
116 | } |
117 | |
118 | static QStringList defaultImportPathList() |
119 | { |
120 | QStringList importPaths; |
121 | importPaths.reserve(alloc: 3); |
122 | #ifdef Q_OS_ANDROID |
123 | // androiddeployqt puts the QML files inside a resource file and they are not |
124 | // showing up in the Qml2ImportsPath as a result |
125 | importPaths += QStringLiteral(":/android_rcc_bundle/qml" ); |
126 | #else |
127 | # ifndef QT_STATIC |
128 | importPaths += QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath); |
129 | # endif |
130 | #endif |
131 | importPaths += envPathList(var: "QML2_IMPORT_PATH" ); |
132 | importPaths += QStringLiteral(":/qt-project.org/imports" ); |
133 | importPaths += QCoreApplication::applicationDirPath(); |
134 | return importPaths; |
135 | } |
136 | |
137 | struct QQuickStyleSpec |
138 | { |
139 | QQuickStyleSpec() : custom(false), resolved(false) { } |
140 | |
141 | QString name() |
142 | { |
143 | if (!resolved) |
144 | resolve(); |
145 | return style.mid(position: style.lastIndexOf(c: QLatin1Char('/')) + 1); |
146 | } |
147 | |
148 | QString path() |
149 | { |
150 | if (!resolved) |
151 | resolve(); |
152 | QString s = style; |
153 | if (QQmlFile::isLocalFile(url: s)) |
154 | s = QQmlFile::urlToLocalFileOrQrc(s); |
155 | return s.left(n: s.lastIndexOf(c: QLatin1Char('/')) + 1); |
156 | } |
157 | |
158 | void setStyle(const QString &s) |
159 | { |
160 | style = s; |
161 | resolved = false; |
162 | resolve(); |
163 | } |
164 | |
165 | void setFallbackStyle(const QString &fallback, const QByteArray &method) |
166 | { |
167 | fallbackStyle = fallback; |
168 | fallbackMethod = method; |
169 | } |
170 | |
171 | static QString findStyle(const QString &path, const QString &name) |
172 | { |
173 | QDir dir(path); |
174 | if (!dir.exists()) |
175 | return QString(); |
176 | |
177 | if (name.isEmpty()) |
178 | return dir.absolutePath() + QLatin1Char('/'); |
179 | |
180 | const QStringList entries = dir.entryList(nameFilters: QStringList(), filters: QDir::Dirs | QDir::NoDotAndDotDot); |
181 | for (const QString &entry : entries) { |
182 | if (entry.compare(s: name, cs: Qt::CaseInsensitive) == 0) |
183 | return dir.absoluteFilePath(fileName: entry); |
184 | } |
185 | |
186 | return QString(); |
187 | } |
188 | |
189 | void resolve(const QUrl &baseUrl = QUrl()) |
190 | { |
191 | if (style.isEmpty()) |
192 | style = QGuiApplicationPrivate::styleOverride; |
193 | if (style.isEmpty()) |
194 | style = QString::fromLocal8Bit(str: qgetenv(varName: "QT_QUICK_CONTROLS_STYLE" )); |
195 | if (fallbackStyle.isEmpty()) |
196 | setFallbackStyle(fallback: QString::fromLocal8Bit(str: qgetenv(varName: "QT_QUICK_CONTROLS_FALLBACK_STYLE" )), method: "QT_QUICK_CONTROLS_FALLBACK_STYLE" ); |
197 | #if QT_CONFIG(settings) |
198 | if (style.isEmpty() || fallbackStyle.isEmpty()) { |
199 | QSharedPointer<QSettings> settings = QQuickStylePrivate::settings(QStringLiteral("Controls" )); |
200 | if (settings) { |
201 | if (style.isEmpty()) |
202 | style = settings->value(QStringLiteral("Style" )).toString(); |
203 | if (fallbackStyle.isEmpty()) |
204 | setFallbackStyle(fallback: settings->value(QStringLiteral("FallbackStyle" )).toString(), method: ":/qtquickcontrols2.conf" ); |
205 | } |
206 | } |
207 | #endif |
208 | |
209 | // resolve a path relative to the config |
210 | QString configPath = QFileInfo(resolveConfigFilePath()).path(); |
211 | QString stylePath = findStyle(path: configPath, name: style); |
212 | if (!stylePath.isEmpty()) { |
213 | style = stylePath; |
214 | resolved = true; |
215 | } |
216 | |
217 | custom = style.contains(c: QLatin1Char('/')); |
218 | |
219 | if (baseUrl.isValid()) { |
220 | QString path = QQmlFile::urlToLocalFileOrQrc(baseUrl); |
221 | QString stylePath = findStyle(path, name: style); |
222 | if (!stylePath.isEmpty()) { |
223 | style = stylePath; |
224 | resolved = true; |
225 | } |
226 | } |
227 | |
228 | if (QGuiApplication::instance()) { |
229 | if (!custom) { |
230 | const QStringList stylePaths = QQuickStylePrivate::stylePaths(); |
231 | for (const QString &path : stylePaths) { |
232 | QString stylePath = findStyle(path, name: style); |
233 | if (!stylePath.isEmpty()) { |
234 | custom = !stylePath.startsWith(s: QQmlFile::urlToLocalFileOrQrc(baseUrl)); |
235 | style = stylePath; |
236 | resolved = true; |
237 | break; |
238 | } |
239 | } |
240 | } |
241 | resolved = true; |
242 | } |
243 | } |
244 | |
245 | void reset() |
246 | { |
247 | custom = false; |
248 | resolved = false; |
249 | style.clear(); |
250 | fallbackStyle.clear(); |
251 | fallbackMethod.clear(); |
252 | configFilePath.clear(); |
253 | } |
254 | |
255 | QString resolveConfigFilePath() |
256 | { |
257 | if (configFilePath.isEmpty()) { |
258 | configFilePath = QFile::decodeName(localFileName: qgetenv(varName: "QT_QUICK_CONTROLS_CONF" )); |
259 | if (configFilePath.isEmpty() || !QFile::exists(fileName: configFilePath)) { |
260 | if (!configFilePath.isEmpty()) |
261 | qWarning(msg: "QT_QUICK_CONTROLS_CONF=%s: No such file" , qPrintable(configFilePath)); |
262 | |
263 | configFilePath = QStringLiteral(":/qtquickcontrols2.conf" ); |
264 | } |
265 | } |
266 | return configFilePath; |
267 | } |
268 | |
269 | bool custom; |
270 | bool resolved; |
271 | QString style; |
272 | QString fallbackStyle; |
273 | QByteArray fallbackMethod; |
274 | QString configFilePath; |
275 | QStringList customStylePaths; |
276 | }; |
277 | |
278 | Q_GLOBAL_STATIC(QQuickStyleSpec, styleSpec) |
279 | |
280 | static QStringList parseStylePathsWithColon(const QString &var) |
281 | { |
282 | QStringList paths; |
283 | const QChar colon = QLatin1Char(':'); |
284 | int currentIndex = 0; |
285 | |
286 | do { |
287 | int nextColonIndex = -1; |
288 | QString path; |
289 | |
290 | if (var.at(i: currentIndex) == colon) { |
291 | // This is either a list separator, or a qrc path. |
292 | if (var.at(i: currentIndex + 1) == colon) { |
293 | // It's a double colon (list separator followed by qrc path); |
294 | // find the end of the path. |
295 | nextColonIndex = var.indexOf(c: colon, from: currentIndex + 2); |
296 | path = var.mid(position: currentIndex + 1, |
297 | n: nextColonIndex == -1 ? -1 : nextColonIndex - currentIndex - 1); |
298 | } else { |
299 | // It's a single colon. |
300 | nextColonIndex = var.indexOf(c: colon, from: currentIndex + 1); |
301 | if (currentIndex == 0) { |
302 | // If we're at the start of the string, then it's a qrc path. |
303 | path = var.mid(position: currentIndex, |
304 | n: nextColonIndex == -1 ? -1 : nextColonIndex - currentIndex); |
305 | } else { |
306 | // Otherwise, it's a separator. |
307 | path = var.mid(position: currentIndex + 1, |
308 | n: nextColonIndex == -1 ? -1 : nextColonIndex - currentIndex - 1); |
309 | } |
310 | } |
311 | } else { |
312 | // It's a file path. |
313 | nextColonIndex = var.indexOf(c: colon, from: currentIndex); |
314 | path = var.mid(position: currentIndex, |
315 | n: nextColonIndex == -1 ? -1 : nextColonIndex - currentIndex); |
316 | } |
317 | |
318 | paths += path; |
319 | currentIndex = nextColonIndex; |
320 | |
321 | // Keep going until we can't find any more colons, |
322 | // or we're at the last character. |
323 | } while (currentIndex != -1 && currentIndex < var.size() - 1); |
324 | |
325 | return paths; |
326 | } |
327 | |
328 | QStringList QQuickStylePrivate::stylePaths(bool resolve) |
329 | { |
330 | // user-requested style path |
331 | QStringList paths; |
332 | if (resolve) { |
333 | QString path = styleSpec()->path(); |
334 | if (path.endsWith(c: QLatin1Char('/'))) |
335 | path.chop(n: 1); |
336 | if (!path.isEmpty()) |
337 | paths += path; |
338 | } |
339 | |
340 | if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE_PATH" ))) { |
341 | const QString value = QString::fromLocal8Bit(str: qgetenv(varName: "QT_QUICK_CONTROLS_STYLE_PATH" )); |
342 | const QChar listSeparator = QDir::listSeparator(); |
343 | if (listSeparator == QLatin1Char(':')) { |
344 | // Split manually to avoid breaking paths on systems where : is the list separator, |
345 | // since it's also used for qrc paths. |
346 | paths += parseStylePathsWithColon(var: value); |
347 | } else { |
348 | // Fast/simpler path for systems where something other than : is used as |
349 | // the list separator (such as ';'). |
350 | const QStringList customPaths = value.split(sep: listSeparator, behavior: Qt::SkipEmptyParts); |
351 | paths += customPaths; |
352 | } |
353 | } |
354 | |
355 | // system/custom style paths |
356 | paths += styleSpec()->customStylePaths; |
357 | paths += envPathList(var: "QT_QUICK_CONTROLS_STYLE_PATH" ); |
358 | |
359 | // built-in import paths |
360 | const QString targetPath = QStringLiteral("QtQuick/Controls.2" ); |
361 | const QStringList importPaths = defaultImportPathList(); |
362 | for (const QString &importPath : importPaths) { |
363 | QDir dir(importPath); |
364 | if (dir.cd(dirName: targetPath)) |
365 | paths += dir.absolutePath(); |
366 | } |
367 | |
368 | paths.removeDuplicates(); |
369 | return paths; |
370 | } |
371 | |
372 | QString QQuickStylePrivate::fallbackStyle() |
373 | { |
374 | return styleSpec()->fallbackStyle; |
375 | } |
376 | |
377 | bool QQuickStylePrivate::isCustomStyle() |
378 | { |
379 | return styleSpec()->custom; |
380 | } |
381 | |
382 | void QQuickStylePrivate::init(const QUrl &baseUrl) |
383 | { |
384 | QQuickStyleSpec *spec = styleSpec(); |
385 | spec->resolve(baseUrl); |
386 | |
387 | if (!spec->fallbackStyle.isEmpty()) { |
388 | QString fallbackStyle; |
389 | const QStringList stylePaths = QQuickStylePrivate::stylePaths(); |
390 | for (const QString &path : stylePaths) { |
391 | fallbackStyle = spec->findStyle(path, name: spec->fallbackStyle); |
392 | if (!fallbackStyle.isEmpty()) |
393 | break; |
394 | } |
395 | if (fallbackStyle.isEmpty()) { |
396 | if (spec->fallbackStyle.compare(QStringLiteral("Default" )) != 0) { |
397 | qWarning() << "ERROR: unable to locate fallback style" << spec->fallbackStyle; |
398 | qInfo().nospace().noquote() << spec->fallbackMethod << ": the fallback style must be the name of one of the built-in Qt Quick Controls 2 styles." ; |
399 | } |
400 | spec->fallbackStyle.clear(); |
401 | } |
402 | } |
403 | } |
404 | |
405 | void QQuickStylePrivate::reset() |
406 | { |
407 | if (styleSpec()) |
408 | styleSpec()->reset(); |
409 | } |
410 | |
411 | QString QQuickStylePrivate::configFilePath() |
412 | { |
413 | return styleSpec()->resolveConfigFilePath(); |
414 | } |
415 | |
416 | QSharedPointer<QSettings> QQuickStylePrivate::settings(const QString &group) |
417 | { |
418 | #ifndef QT_NO_SETTINGS |
419 | const QString filePath = QQuickStylePrivate::configFilePath(); |
420 | if (QFile::exists(fileName: filePath)) { |
421 | QFileSelector selector; |
422 | QSettings *settings = new QSettings(selector.select(filePath), QSettings::IniFormat); |
423 | if (!group.isEmpty()) |
424 | settings->beginGroup(prefix: group); |
425 | return QSharedPointer<QSettings>(settings); |
426 | } |
427 | #endif // QT_NO_SETTINGS |
428 | return QSharedPointer<QSettings>(); |
429 | } |
430 | |
431 | #if QT_CONFIG(settings) |
432 | static void readValue(const QSharedPointer<QSettings> &settings, const QString &name, std::function<void(const QVariant &)> setValue) |
433 | { |
434 | const QVariant var = settings->value(key: name); |
435 | if (var.isValid()) |
436 | setValue(var); |
437 | } |
438 | |
439 | template <typename Enum> |
440 | static Enum toEnumValue(const QVariant &var) |
441 | { |
442 | // ### TODO: expose QFont enums to the meta object system using Q_ENUM |
443 | //QMetaEnum enumeration = QMetaEnum::fromType<Enum>(); |
444 | //bool ok = false; |
445 | //int value = enumeration.keyToValue(var.toByteArray(), &ok); |
446 | //if (!ok) |
447 | // value = var.toInt(); |
448 | //return static_cast<Enum>(value); |
449 | |
450 | return static_cast<Enum>(var.toInt()); |
451 | } |
452 | |
453 | const QFont *QQuickStylePrivate::readFont(const QSharedPointer<QSettings> &settings) |
454 | { |
455 | const QVariant var = settings->value(QStringLiteral("Font" )); |
456 | if (var.isValid()) |
457 | return new QFont(var.value<QFont>()); |
458 | |
459 | QFont f; |
460 | settings->beginGroup(QStringLiteral("Font" )); |
461 | readValue(settings, QStringLiteral("Family" ), setValue: [&f](const QVariant &var) { f.setFamily(var.toString()); }); |
462 | readValue(settings, QStringLiteral("PointSize" ), setValue: [&f](const QVariant &var) { f.setPointSizeF(var.toReal()); }); |
463 | readValue(settings, QStringLiteral("PixelSize" ), setValue: [&f](const QVariant &var) { f.setPixelSize(var.toInt()); }); |
464 | readValue(settings, QStringLiteral("StyleHint" ), setValue: [&f](const QVariant &var) { f.setStyleHint(toEnumValue<QFont::StyleHint>(var: var.toInt())); }); |
465 | readValue(settings, QStringLiteral("Weight" ), setValue: [&f](const QVariant &var) { f.setWeight(toEnumValue<QFont::Weight>(var)); }); |
466 | readValue(settings, QStringLiteral("Style" ), setValue: [&f](const QVariant &var) { f.setStyle(toEnumValue<QFont::Style>(var: var.toInt())); }); |
467 | settings->endGroup(); |
468 | return new QFont(f); |
469 | } |
470 | |
471 | static void readColorGroup(const QSharedPointer<QSettings> &settings, QPalette::ColorGroup group, QPalette *palette) |
472 | { |
473 | const QStringList keys = settings->childKeys(); |
474 | if (keys.isEmpty()) |
475 | return; |
476 | |
477 | static const int index = QPalette::staticMetaObject.indexOfEnumerator(name: "ColorRole" ); |
478 | Q_ASSERT(index != -1); |
479 | QMetaEnum metaEnum = QPalette::staticMetaObject.enumerator(index); |
480 | |
481 | for (const QString &key : keys) { |
482 | bool ok = false; |
483 | int role = metaEnum.keyToValue(key: key.toUtf8(), ok: &ok); |
484 | if (ok) |
485 | palette->setColor(acg: group, acr: static_cast<QPalette::ColorRole>(role), acolor: settings->value(key).value<QColor>()); |
486 | } |
487 | } |
488 | |
489 | const QPalette *QQuickStylePrivate::readPalette(const QSharedPointer<QSettings> &settings) |
490 | { |
491 | QPalette p; |
492 | settings->beginGroup(QStringLiteral("Palette" )); |
493 | readColorGroup(settings, group: QPalette::All, palette: &p); |
494 | |
495 | settings->beginGroup(QStringLiteral("Normal" )); |
496 | readColorGroup(settings, group: QPalette::Normal, palette: &p); |
497 | settings->endGroup(); |
498 | |
499 | settings->beginGroup(QStringLiteral("Disabled" )); |
500 | readColorGroup(settings, group: QPalette::Disabled, palette: &p); |
501 | settings->endGroup(); |
502 | return new QPalette(p); |
503 | } |
504 | #endif // QT_CONFIG(settings) |
505 | |
506 | static bool qt_is_dark_system_theme() |
507 | { |
508 | if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { |
509 | if (const QPalette *systemPalette = theme->palette(type: QPlatformTheme::SystemPalette)) { |
510 | const QColor &textColor = systemPalette->color(cr: QPalette::WindowText); |
511 | return textColor.red() > 128 && textColor.blue() > 128 && textColor.green() > 128; |
512 | } |
513 | } |
514 | return false; |
515 | } |
516 | |
517 | bool QQuickStylePrivate::isDarkSystemTheme() |
518 | { |
519 | static bool dark = qt_is_dark_system_theme(); |
520 | return dark; |
521 | } |
522 | |
523 | /*! |
524 | Returns the name of the application style. |
525 | |
526 | \note The application style can be specified by passing a \c -style command |
527 | line argument. Therefore \c name() may not return a fully resolved |
528 | value if called before constructing a QGuiApplication. |
529 | */ |
530 | QString QQuickStyle::name() |
531 | { |
532 | return styleSpec()->name(); |
533 | } |
534 | |
535 | /*! |
536 | Returns the path of an overridden application style, or an empty |
537 | string if the style is one of the built-in Qt Quick Controls 2 styles. |
538 | |
539 | \note The application style can be specified by passing a \c -style command |
540 | line argument. Therefore \c path() may not return a fully resolved |
541 | value if called before constructing a QGuiApplication. |
542 | */ |
543 | QString QQuickStyle::path() |
544 | { |
545 | return styleSpec()->path(); |
546 | } |
547 | |
548 | /*! |
549 | Sets the application style to \a style. |
550 | |
551 | \note The style must be configured \b before loading QML that imports Qt Quick Controls. |
552 | It is not possible to change the style after the QML types have been registered. |
553 | |
554 | \sa setFallbackStyle(), {Using Styles in Qt Quick Controls} |
555 | */ |
556 | void QQuickStyle::setStyle(const QString &style) |
557 | { |
558 | if (QQmlMetaType::isModule(QStringLiteral("QtQuick.Controls" ), versionMajor: 2, versionMinor: 0)) { |
559 | qWarning() << "ERROR: QQuickStyle::setStyle() must be called before loading QML that imports Qt Quick Controls 2." ; |
560 | return; |
561 | } |
562 | |
563 | styleSpec()->setStyle(style); |
564 | } |
565 | |
566 | /*! |
567 | \since 5.8 |
568 | Sets the application fallback style to \a style. |
569 | |
570 | \note The fallback style must be the name of one of the built-in Qt Quick Controls styles, e.g. "Material". |
571 | |
572 | \note The style must be configured \b before loading QML that imports Qt Quick Controls. |
573 | It is not possible to change the style after the QML types have been registered. |
574 | |
575 | The fallback style can be also specified by setting the \c QT_QUICK_CONTROLS_FALLBACK_STYLE |
576 | \l {Supported Environment Variables in Qt Quick Controls}{environment variable}. |
577 | |
578 | \sa setStyle(), {Using Styles in Qt Quick Controls} |
579 | */ |
580 | void QQuickStyle::setFallbackStyle(const QString &style) |
581 | { |
582 | if (QQmlMetaType::isModule(QStringLiteral("QtQuick.Controls" ), versionMajor: 2, versionMinor: 0)) { |
583 | qWarning() << "ERROR: QQuickStyle::setFallbackStyle() must be called before loading QML that imports Qt Quick Controls 2." ; |
584 | return; |
585 | } |
586 | |
587 | styleSpec()->setFallbackStyle(fallback: style, method: "QQuickStyle::setFallbackStyle()" ); |
588 | } |
589 | |
590 | /*! |
591 | \since 5.9 |
592 | Returns the names of the available styles. |
593 | |
594 | \note The method must be called \b after creating an instance of QGuiApplication. |
595 | |
596 | \sa stylePathList(), addStylePath() |
597 | */ |
598 | QStringList QQuickStyle::availableStyles() |
599 | { |
600 | QStringList styles; |
601 | if (!QGuiApplication::instance()) { |
602 | qWarning() << "ERROR: QQuickStyle::availableStyles() must be called after creating an instance of QGuiApplication." ; |
603 | return styles; |
604 | } |
605 | |
606 | const QStringList stylePaths = QQuickStylePrivate::stylePaths(); |
607 | for (const QString &path : stylePaths) { |
608 | const QList<QFileInfo> entries = QDir(path).entryInfoList(filters: QDir::Dirs | QDir::NoDotAndDotDot); |
609 | for (const QFileInfo &entry : entries) { |
610 | const QString name = entry.fileName(); |
611 | if (!name.endsWith(s: QLatin1String(".dSYM" )) && name != QLatin1String("designer" )) |
612 | styles += name; |
613 | } |
614 | } |
615 | styles.prepend(QStringLiteral("Default" )); |
616 | styles.removeDuplicates(); |
617 | return styles; |
618 | } |
619 | |
620 | /*! |
621 | \since 5.12 |
622 | |
623 | Returns the list of directories where Qt Quick Controls 2 searches for available styles. |
624 | |
625 | By default, the list contains paths specified in the \c QT_QUICK_CONTROLS_STYLE_PATH |
626 | environment variable, and any existing \c QtQuick/Controls.2 sub-directories in |
627 | \l QQmlEngine::importPathList(). |
628 | |
629 | \sa addStylePath(), availableStyles() |
630 | */ |
631 | QStringList QQuickStyle::stylePathList() |
632 | { |
633 | return QQuickStylePrivate::stylePaths(); |
634 | } |
635 | |
636 | /*! |
637 | \since 5.12 |
638 | |
639 | Adds \a path as a directory where Qt Quick Controls 2 searches for available styles. |
640 | |
641 | The \a path may be any local filesystem directory or \l {The Qt Resource System}{Qt Resource} directory. |
642 | For example, the following paths are all valid: |
643 | |
644 | \list |
645 | \li \c {/path/to/styles/} |
646 | \li \c {file:///path/to/styles/} |
647 | \li \c {:/path/to/styles/} |
648 | \li \c {qrc:/path/to/styles/}) |
649 | \endlist |
650 | |
651 | The \a path will be converted into \l {QDir::canonicalPath}{canonical form} before it is added to |
652 | the style path list. |
653 | |
654 | The newly added \a path will be first in the stylePathList(). |
655 | |
656 | \sa stylePathList(), availableStyles() |
657 | */ |
658 | void QQuickStyle::addStylePath(const QString &path) |
659 | { |
660 | if (path.isEmpty()) |
661 | return; |
662 | |
663 | const QUrl url = QUrl(path); |
664 | if (url.isRelative() || url.scheme() == QLatin1String("file" ) |
665 | || (url.scheme().length() == 1 && QFile::exists(fileName: path)) ) { // windows path |
666 | styleSpec()->customStylePaths.prepend(t: QDir(path).canonicalPath()); |
667 | } else if (url.scheme() == QLatin1String("qrc" )) { |
668 | styleSpec()->customStylePaths.prepend(t: QLatin1Char(':') + url.path()); |
669 | } else { |
670 | styleSpec()->customStylePaths.prepend(t: path); |
671 | } |
672 | } |
673 | |
674 | QT_END_NAMESPACE |
675 | |