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