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