Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickstyle.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qquickstyle.h"
7
8#include <QtCore/qdir.h>
9#include <QtCore/qfile.h>
10#include <QtCore/qdebug.h>
11#if QT_CONFIG(settings)
12#include <QtCore/qsettings.h>
13#endif
14#include <QtCore/qfileselector.h>
15#include <QtCore/qlibraryinfo.h>
16#include <QtCore/qloggingcategory.h>
17#include <QtCore/qmetaobject.h>
18#include <QtGui/qcolor.h>
19#include <QtGui/qfont.h>
20#include <QtGui/qpalette.h>
21#include <QtGui/private/qguiapplication_p.h>
22#include <QtGui/qpa/qplatformtheme.h>
23#include <QtQml/private/qqmlmetatype_p.h>
24#include <QtQml/qqmlengine.h>
25#include <QtQml/qqmlfile.h>
26
27#include <QtCore/qtyperevision.h>
28
29#include <functional>
30
32
33Q_STATIC_LOGGING_CATEGORY(lcQtQuickControlsStyle, "qt.quick.controls.style")
34
35/*!
36 \class QQuickStyle
37 \brief The QQuickStyle class allows configuring the application style.
38 \inmodule QtQuickControls2
39 \since 5.7
40
41 QQuickStyle provides API for querying and configuring the application
42 \l {Styling Qt Quick Controls}{styles} of Qt Quick Controls.
43
44 \code
45 #include <QGuiApplication>
46 #include <QQmlApplicationEngine>
47 #include <QQuickStyle>
48
49 int main(int argc, char *argv[])
50 {
51 QGuiApplication app(argc, argv);
52
53 QQuickStyle::setStyle("Material");
54
55 QQmlApplicationEngine engine;
56 engine.load(QUrl("qrc:/main.qml"));
57
58 return app.exec();
59 }
60 \endcode
61
62 \note The style must be configured \b before loading QML that imports
63 Qt Quick Controls. It is not possible to change the style after the QML
64 types have been registered.
65
66 \note QQuickStyle is not supported when using
67 \l {Compile-Time Style Selection}{compile-time style selection}.
68
69 To create your own custom style, see \l {Creating a Custom Style}. Custom
70 styles do not need to implement all controls. By default, the styling
71 system uses the \l {Basic style} as a fallback for controls that a custom
72 style does not provide. It is possible to specify a different fallback
73 style to customize or extend one of the built-in styles.
74
75 \code
76 QQuickStyle::setStyle("MyStyle");
77 QQuickStyle::setFallbackStyle("Material");
78 \endcode
79
80 \sa {Styling Qt Quick Controls}
81*/
82
84{
86
88 {
89 if (!resolved)
91 return style.mid(style.lastIndexOf(QLatin1Char('/')) + 1);
92 }
93
95 {
96 if (!resolved)
98 QString s = style;
99 if (QQmlFile::isLocalFile(s))
100 s = QQmlFile::urlToLocalFileOrQrc(s);
101 return s.left(s.lastIndexOf(QLatin1Char('/')) + 1);
102 }
103
104 void setStyle(const QString &s)
105 {
106 qCDebug(lcQtQuickControlsStyle) << "style" << s << "set on QQuickStyleSpec";
107 if (s.contains(QLatin1Char('/'))) {
108 qWarning() << "Style names must not contain paths; see the \"Definition of a Style\" documentation for more information";
109 return;
110 }
111
112 qCDebug(lcQtQuickControlsStyle) << "clearing resolved flag and resolving";
113 style = s;
114 resolved = false;
115 resolve();
116 }
117
118 void setFallbackStyle(const QString &fallback, const QByteArray &method)
119 {
120 if (!fallback.isEmpty())
121 qCDebug(lcQtQuickControlsStyle) << "fallback style" << fallback << "set on QQuickStyleSpec via" << method;
122
123 fallbackStyle = fallback;
124 fallbackMethod = method;
125 }
126
127 void resolve()
128 {
129 qCDebug(lcQtQuickControlsStyle) << "resolving style";
130
131 if (style.isEmpty())
132 style = QGuiApplicationPrivate::styleOverride;
133 if (style.isEmpty())
134 style = QString::fromLocal8Bit(qgetenv("QT_QUICK_CONTROLS_STYLE"));
135 if (fallbackStyle.isEmpty())
136 setFallbackStyle(QString::fromLocal8Bit(qgetenv("QT_QUICK_CONTROLS_FALLBACK_STYLE")), "QT_QUICK_CONTROLS_FALLBACK_STYLE");
137#if QT_CONFIG(settings)
138 if (style.isEmpty() || fallbackStyle.isEmpty()) {
139 QSharedPointer<QSettings> settings = QQuickStylePrivate::settings(QStringLiteral("Controls"));
140 if (settings) {
141 if (style.isEmpty())
142 style = settings->value(QStringLiteral("Style")).toString();
143 if (fallbackStyle.isEmpty())
144 setFallbackStyle(settings->value(QStringLiteral("FallbackStyle")).toString(), ":/qtquickcontrols2.conf");
145 }
146 }
147#endif
148
149 auto builtInStyleList = QQuickStylePrivate::builtInStyles();
150 if (!fallbackStyle.isEmpty() && !builtInStyleList.contains(fallbackStyle)) {
151 qWarning().nospace().noquote() << fallbackMethod << ": the specified fallback style \"" <<
152 fallbackStyle << "\" is not one of the built-in Qt Quick Controls 2 styles";
153 fallbackStyle.clear();
154 }
155
156 // Find the config file.
157 resolveConfigFilePath();
158
159 usingDefaultStyle = false;
160
161 if (style.isEmpty() || style.toLower() == QStringLiteral("default")) {
162 usingDefaultStyle = true;
163 style.clear();
164
165 qCDebug(lcQtQuickControlsStyle) << "no style (or Default) was specified;"
166 << "checking if we have an appropriate style for this platform";
167
168 // If these defaults are changed, ensure that the "Using Styles in Qt Quick Controls"
169 // section of qtquickcontrols-styles.qdoc is updated.
170#if defined(Q_OS_MACOS)
171 style = QLatin1String("macOS");
172#elif defined(Q_OS_WINDOWS)
173 style = QLatin1String("Windows");
174#elif defined(Q_OS_ANDROID)
175 style = QLatin1String("Material");
176#elif defined(Q_OS_LINUX)
177 style = QLatin1String("Fusion");
178#elif defined(Q_OS_IOS)
179 style = QLatin1String("iOS");
180#endif
181 if (!style.isEmpty())
182 qCDebug(lcQtQuickControlsStyle) << "using" << style << "as a default";
183 else
184 qCDebug(lcQtQuickControlsStyle) << "no appropriate style found; using Basic as a default";
185 }
186
187 // If it's still empty by this point, then it means we have no native style available for this platform,
188 // as is the case on e.g. embedded. In that case, we want to default to the Basic style,
189 // which is what effectiveStyleName() returns when "style" is empty.
190 custom = !builtInStyleList.contains(QQuickStylePrivate::effectiveStyleName(style));
191
192 resolved = true;
193
194 qCDebug(lcQtQuickControlsStyle).nospace() << "done resolving:"
195 << "\n style=" << style
196 << "\n custom=" << custom
197 << "\n resolved=" << resolved
198 << "\n fallbackStyle=" << fallbackStyle
199 << "\n fallbackMethod=" << fallbackMethod
200 << "\n configFilePath=" << configFilePath;
201 }
202
203 void reset()
204 {
205 qCDebug(lcQtQuickControlsStyle) << "resetting values to their defaults";
206
207 custom = false;
208 resolved = false;
209 usingDefaultStyle = false;
210 style.clear();
211 fallbackStyle.clear();
212 fallbackMethod.clear();
213 configFilePath.clear();
214 }
215
217 {
218 if (configFilePath.isEmpty()) {
219 configFilePath = QFile::decodeName(qgetenv("QT_QUICK_CONTROLS_CONF"));
220 if (configFilePath.isEmpty() || !QFile::exists(configFilePath)) {
221 if (!configFilePath.isEmpty())
222 qWarning("QT_QUICK_CONTROLS_CONF=%s: No such file", qPrintable(configFilePath));
223
224 configFilePath = QStringLiteral(":/qtquickcontrols2.conf");
225 }
226 }
227 return configFilePath;
228 }
229
230 // Is this a custom style defined by the user and not "built-in" style?
231 bool custom = false;
232 // Have we resolved the style yet?
233 bool resolved = false;
234 // Are we using the default style for this platform (because no style was specified)?
235 bool usingDefaultStyle = false;
236 // The name of the style.
238 // The built-in style to use if the requested style cannot be found.
240 // A description of the way in which fallbackStyle was set, used in e.g. warning messages shown to the user.
242 // The path to the qtquickcontrols2.conf file.
244};
245
246Q_GLOBAL_STATIC(QQuickStyleSpec, styleSpec)
247
248/*
249 Note that most of these functions (with the exception of e.g. isResolved())
250 should not be called before the style has been resolved, as it's only after
251 that happens that they will have been set.
252*/
253QString QQuickStylePrivate::style()
254{
255 return styleSpec()->style;
256}
257
258QString QQuickStylePrivate::effectiveStyleName(const QString &styleName)
259{
260 return !styleName.isEmpty() ? styleName : QLatin1String("Basic");
261}
262
263QString QQuickStylePrivate::fallbackStyle()
264{
265 return styleSpec()->fallbackStyle;
266}
267
268bool QQuickStylePrivate::isCustomStyle()
269{
270 return styleSpec()->custom;
271}
272
273bool QQuickStylePrivate::isResolved()
274{
275 return styleSpec()->resolved;
276}
277
278bool QQuickStylePrivate::isUsingDefaultStyle()
279{
280 return styleSpec()->usingDefaultStyle;
281}
282
283void QQuickStylePrivate::init()
284{
285 QQuickStyleSpec *spec = styleSpec();
286 spec->resolve();
287}
288
289void QQuickStylePrivate::reset()
290{
291 if (styleSpec())
292 styleSpec()->reset();
293}
294
295QString QQuickStylePrivate::configFilePath()
296{
297 return styleSpec()->resolveConfigFilePath();
298}
299
300QSharedPointer<QSettings> QQuickStylePrivate::settings(const QString &group)
301{
302#ifndef QT_NO_SETTINGS
303 const QString filePath = QQuickStylePrivate::configFilePath();
304 if (QFile::exists(filePath)) {
305 QFileSelector selector;
306 QSettings *settings = new QSettings(selector.select(filePath), QSettings::IniFormat);
307 if (!group.isEmpty())
308 settings->beginGroup(group);
309 return QSharedPointer<QSettings>(settings);
310 }
311#endif // QT_NO_SETTINGS
312 Q_UNUSED(group)
313 return QSharedPointer<QSettings>();
314}
315
316#if QT_CONFIG(settings)
317static void readValue(const QSharedPointer<QSettings> &settings, const QString &name, std::function<void(const QVariant &)> setValue)
318{
319 const QVariant var = settings->value(name);
320 if (var.isValid())
321 setValue(var);
322}
323
324template <typename Enum>
325static Enum toEnumValue(const QVariant &var)
326{
327 // ### TODO: expose QFont enums to the meta object system using Q_ENUM
328 //QMetaEnum enumeration = QMetaEnum::fromType<Enum>();
329 //bool ok = false;
330 //int value = enumeration.keyToValue(var.toByteArray(), &ok);
331 //if (!ok)
332 // value = var.toInt();
333 //return static_cast<Enum>(value);
334
335 return static_cast<Enum>(var.toInt());
336}
337
338const QFont *QQuickStylePrivate::readFont(const QSharedPointer<QSettings> &settings)
339{
340 const QVariant var = settings->value(QStringLiteral("Font"));
341 if (var.isValid())
342 return new QFont(var.value<QFont>());
343
344 QFont f;
345 settings->beginGroup(QStringLiteral("Font"));
346 readValue(settings, QStringLiteral("Family"), [&f](const QVariant &var) { f.setFamilies(QStringList{var.toString()}); });
347 readValue(settings, QStringLiteral("PointSize"), [&f](const QVariant &var) { f.setPointSizeF(var.toReal()); });
348 readValue(settings, QStringLiteral("PixelSize"), [&f](const QVariant &var) { f.setPixelSize(var.toInt()); });
349 readValue(settings, QStringLiteral("StyleHint"), [&f](const QVariant &var) { f.setStyleHint(toEnumValue<QFont::StyleHint>(var.toInt())); });
350 readValue(settings, QStringLiteral("Weight"), [&f](const QVariant &var) { f.setWeight(toEnumValue<QFont::Weight>(var)); });
351 readValue(settings, QStringLiteral("Style"), [&f](const QVariant &var) { f.setStyle(toEnumValue<QFont::Style>(var.toInt())); });
352 settings->endGroup();
353 return new QFont(f);
354}
355
356static void readColorGroup(const QSharedPointer<QSettings> &settings, QPalette::ColorGroup group, QPalette *palette)
357{
358 const QStringList keys = settings->childKeys();
359 if (keys.isEmpty())
360 return;
361
362 static const int index = QPalette::staticMetaObject.indexOfEnumerator("ColorRole");
363 Q_ASSERT(index != -1);
364 QMetaEnum metaEnum = QPalette::staticMetaObject.enumerator(index);
365
366 for (const QString &key : keys) {
367 bool ok = false;
368 int role = metaEnum.keyToValue(key.toUtf8(), &ok);
369 if (ok)
370 palette->setColor(group, static_cast<QPalette::ColorRole>(role), settings->value(key).value<QColor>());
371 }
372}
373
374const QPalette *QQuickStylePrivate::readPalette(const QSharedPointer<QSettings> &settings)
375{
376 QPalette p;
377 settings->beginGroup(QStringLiteral("Palette"));
378 readColorGroup(settings, QPalette::All, &p);
379
380 settings->beginGroup(QStringLiteral("Normal"));
381 readColorGroup(settings, QPalette::Normal, &p);
382 settings->endGroup();
383
384 settings->beginGroup(QStringLiteral("Disabled"));
385 readColorGroup(settings, QPalette::Disabled, &p);
386 settings->endGroup();
387 return new QPalette(p);
388}
389#endif // QT_CONFIG(settings)
390
391bool QQuickStylePrivate::isDarkSystemTheme()
392{
393 const bool dark = [](){
394 if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
395 if (theme->colorScheme() == Qt::ColorScheme::Unknown)
396 return theme->palette()->windowText().color().lightnessF() > theme->palette()->window().color().lightnessF();
397 return theme->colorScheme() == Qt::ColorScheme::Dark;
398 }
399 return false;
400 }();
401 return dark;
402}
403
404QStringList QQuickStylePrivate::builtInStyles()
405{
406 return {
407 QLatin1String("Basic"),
408 QLatin1String("Fusion"),
409 QLatin1String("FluentWinUI3"),
410 QLatin1String("Imagine"),
411#ifdef Q_OS_MACOS
412 QLatin1String("macOS"),
413 QLatin1String("iOS"),
414#endif
415#ifdef Q_OS_IOS
416 QLatin1String("iOS"),
417#endif
418 QLatin1String("Material"),
419 QLatin1String("Universal"),
420#ifdef Q_OS_WINDOWS
421 QLatin1String("Windows")
422#endif
423 };
424}
425
426/*!
427 Returns the name of the application style.
428
429 \note The application style can be specified by passing a \c -style command
430 line argument. Therefore \c name() may not return a fully resolved
431 value if called before constructing a QGuiApplication.
432*/
433QString QQuickStyle::name()
434{
435 return styleSpec()->name();
436}
437
438/*!
439 Sets the application style to \a style.
440
441 \note The style must be configured \b before loading QML that imports Qt Quick Controls.
442 It is not possible to change the style after the QML types have been registered.
443
444 \sa setFallbackStyle(), {Using Styles in Qt Quick Controls}
445*/
446void QQuickStyle::setStyle(const QString &style)
447{
448 qCDebug(lcQtQuickControlsStyle) << "setStyle called with" << style;
449
450 if (QQmlMetaType::matchingModuleVersion(
451 QStringLiteral("QtQuick.Controls"), QTypeRevision::fromVersion(2, 0)).isValid()) {
452 qWarning() << "ERROR: QQuickStyle::setStyle() must be called before loading QML that imports Qt Quick Controls 2.";
453 return;
454 }
455
456 styleSpec()->setStyle(style);
457}
458
459/*!
460 \since 5.8
461 Sets the application fallback style to \a style.
462
463 \note The fallback style must be the name of one of the built-in Qt Quick Controls styles, e.g. "Material".
464
465 \note The style must be configured \b before loading QML that imports Qt Quick Controls.
466 It is not possible to change the style after the QML types have been registered.
467
468 The fallback style can be also specified by setting the \c QT_QUICK_CONTROLS_FALLBACK_STYLE
469 \l {Supported Environment Variables in Qt Quick Controls}{environment variable}.
470
471 \sa setStyle(), {Using Styles in Qt Quick Controls}
472*/
473void QQuickStyle::setFallbackStyle(const QString &style)
474{
475 if (QQmlMetaType::matchingModuleVersion(
476 QStringLiteral("QtQuick.Controls"), QTypeRevision::fromVersion(2, 0)).isValid()) {
477 qWarning() << "ERROR: QQuickStyle::setFallbackStyle() must be called before loading QML that imports Qt Quick Controls 2.";
478 return;
479 }
480
481 styleSpec()->setFallbackStyle(style, "QQuickStyle::setFallbackStyle()");
482}
483
484QT_END_NAMESPACE
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
QByteArray fallbackMethod
void setFallbackStyle(const QString &fallback, const QByteArray &method)
QString resolveConfigFilePath()
void setStyle(const QString &s)