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
4#include <QtCore/private/qfileselector_p.h>
5#include <QtCore/qloggingcategory.h>
6#include <QtQml/qqmlengine.h>
7#include <QtQml/qqmlextensionplugin.h>
8#include <QtQuickTemplates2/private/qquicktheme_p_p.h>
9#include <QtQuickControls2/private/qquickstyle_p.h>
10#include <QtQuickControls2/private/qquickstyleplugin_p.h>
11#include <QtQuickControls2/qquickstyle.h>
12#include <QtQuickControls2/qtquickcontrols2global.h>
13
14QT_BEGIN_NAMESPACE
15
16Q_GHS_KEEP_REFERENCE(qml_register_types_QtQuick_Controls);
17
18Q_LOGGING_CATEGORY(lcQtQuickControls2Plugin, "qt.quick.controls.qtquickcontrols2plugin")
19
20class QtQuickControls2Plugin : public QQmlExtensionPlugin
21{
22 Q_OBJECT
23 Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
24
25public:
26 QtQuickControls2Plugin(QObject *parent = nullptr);
27 ~QtQuickControls2Plugin();
28
29 void registerTypes(const char *uri) override;
30 void unregisterTypes() override;
31
32private:
33 // We store these because the style plugins can be unregistered before
34 // QtQuickControls2Plugin, and since QQuickStylePlugin calls QQuickStylePrivate::reset(),
35 // the style information can be lost when it comes time to call qmlUnregisterModuleImport().
36 // It also avoids unnecessarily resolving the style after resetting it just to get the style
37 // name in unregisterTypes().
38 bool customStyle = false;
39 QString registeredStyleUri;
40 QString registeredFallbackStyleUri;
41 QString rawFallbackStyleName;
42};
43
44static const char *qtQuickControlsUri = "QtQuick.Controls";
45
46QString styleUri()
47{
48 const QString style = QQuickStyle::name();
49 if (!QQuickStylePrivate::isCustomStyle()) {
50 // The style set is a built-in style.
51 const QString styleName = QQuickStylePrivate::effectiveStyleName(styleName: style);
52 return QString::fromLatin1(ba: "QtQuick.Controls.%1").arg(a: styleName);
53 }
54
55 // This is a custom style, so just use the name as the import uri.
56 QString styleName = style;
57 if (styleName.startsWith(s: QLatin1String(":/")))
58 styleName.remove(i: 0, len: 2);
59 return styleName;
60}
61
62QString fallbackStyleUri()
63{
64 // The fallback style must be a built-in style, so we don't need to check for custom styles here.
65 const QString fallbackStyle = QQuickStylePrivate::fallbackStyle();
66 const QString fallbackStyleName = QQuickStylePrivate::effectiveStyleName(styleName: fallbackStyle);
67 return QString::fromLatin1(ba: "QtQuick.Controls.%1").arg(a: fallbackStyleName);
68}
69
70QtQuickControls2Plugin::QtQuickControls2Plugin(QObject *parent) : QQmlExtensionPlugin(parent)
71{
72 volatile auto registration = &qml_register_types_QtQuick_Controls;
73 Q_UNUSED(registration);
74}
75
76QtQuickControls2Plugin::~QtQuickControls2Plugin()
77{
78 // Intentionally empty: we use register/unregisterTypes() to do
79 // initialization and cleanup, as plugins are not unloaded on macOS.
80}
81
82/*!
83 \internal
84
85 If this function is called, it means QtQuick.Controls was imported,
86 and we're doing runtime style selection.
87
88 For example, where:
89 \list
90 \li styleName="Material"
91 \li rawFallbackStyleName=""
92 \li fallbackStyleName="Basic"
93 \li registeredStyleUri="QtQuick.Controls.Material"
94 \li rawFallbackStyleName is empty => parentModule="QtQuick.Controls.Material"
95 \li registeredFallbackStyleUri="QtQuick.Controls.Basic"
96 \endlist
97
98 The following registrations would be made:
99
100 qmlRegisterModuleImport("QtQuick.Controls.Material", "QtQuick.Controls.Basic")
101 qmlRegisterModuleImport("QtQuick.Controls", "QtQuick.Controls.Material")
102
103 As another example, where:
104 \list
105 \li styleName="Material"
106 \li rawFallbackStyleName="Fusion"
107 \li fallbackStyleName="Fusion"
108 \li registeredStyleUri="QtQuick.Controls.Material"
109 \li rawFallbackStyleName is not empty => parentModule="QtQuick.Controls"
110 \li registeredFallbackStyleUri="QtQuick.Controls.Fusion"
111 \endlist
112
113 The following registrations would be made:
114
115 qmlRegisterModuleImport("QtQuick.Controls", "QtQuick.Controls.Fusion")
116 qmlRegisterModuleImport("QtQuick.Controls", "QtQuick.Controls.Material")
117
118 In this case, the Material style imports a fallback (Basic) via the IMPORTS
119 section in its CMakeLists.txt, \e and the user specifies a different fallback
120 using an env var/.conf/C++. We want the user's fallback to take priority,
121 which means we have to place the user-specified fallback at a more immediate place,
122 and that place is as an import of QtQuick.Controls itself rather than as an
123 import of the current style, Material (as we did in the first example).
124
125 If the style to be imported is a custom style and no specific fallback was
126 selected, we need to indirectly import Basic, but we cannot import Basic through
127 the custom style since the versions don't match. For that case we have a
128 "QtQuick.Controls.IndirectBasic" which does nothing but import
129 QtQuick.Controls.Basic. Instead of QtQuick.Controls.Basic we import that one:
130
131 qmlRegisterModuleImport("QtQuick.Controls", "Some.Custom.Style")
132 qmlRegisterModuleImport("QtQuick.Controls", "QtQuick.Controls.IndirectBasic")
133*/
134void QtQuickControls2Plugin::registerTypes(const char *uri)
135{
136 qCDebug(lcQtQuickControls2Plugin) << "registerTypes() called with uri" << uri;
137
138 // It's OK that the style is resolved more than once; some accessors like name() cause it to be called, for example.
139 QQuickStylePrivate::init();
140
141 // The fallback style that was set via env var/.conf/C++.
142 rawFallbackStyleName = QQuickStylePrivate::fallbackStyle();
143 // The style that was set via env var/.conf/C++, or Basic if none was set.
144 const QString styleName = QQuickStylePrivate::effectiveStyleName(styleName: QQuickStyle::name());
145 // The effective fallback style: rawFallbackStyleName, or Basic if empty.
146 const QString fallbackStyleName = QQuickStylePrivate::effectiveStyleName(styleName: rawFallbackStyleName);
147 qCDebug(lcQtQuickControls2Plugin) << "style:" << QQuickStyle::name() << "effective style:" << styleName
148 << "fallback style:" << rawFallbackStyleName << "effective fallback style:" << fallbackStyleName;
149
150 customStyle = QQuickStylePrivate::isCustomStyle();
151 // The URI of the current style. For built-in styles, the style name is appended to "QtQuick.Controls.".
152 // For custom styles that are embedded in resources, we need to remove the ":/" prefix.
153 registeredStyleUri = ::styleUri();
154
155 // If the style is Basic, we don't need to register the fallback because the Basic style
156 // provides all controls. Also, if we didn't return early here, we can get an infinite import loop
157 // when the style is set to Basic.
158 if (styleName != fallbackStyleName && styleName != QLatin1String("Basic")) {
159 // If no specific fallback is given, the fallback is of lower precedence than recursive
160 // imports of the main style (i.e. IMPORTS in a style's CMakeLists.txt).
161 // If a specific fallback is given, it is of higher precedence.
162
163 QString parentModule;
164 QString fallbackModule;
165
166 // The fallback style has to be a built-in style, so it will become "QtQuick.Controls.<fallback>".
167 registeredFallbackStyleUri = ::fallbackStyleUri();
168
169 if (!rawFallbackStyleName.isEmpty()) {
170 parentModule = qtQuickControlsUri;
171 fallbackModule = registeredFallbackStyleUri;
172 } else if (customStyle) {
173 // Since we don't know the versioning scheme of custom styles, but we want the
174 // version of QtQuick.Controls to be propagated, we need to do our own indirection.
175 // QtQuick.Controls.IndirectBasic indirectly imports QtQuick.Controls.Basic
176 Q_ASSERT(registeredFallbackStyleUri == QLatin1String("QtQuick.Controls.Basic"));
177 parentModule = qtQuickControlsUri;
178 fallbackModule = QLatin1String("QtQuick.Controls.IndirectBasic");
179 } else {
180 parentModule = registeredStyleUri;
181 fallbackModule = registeredFallbackStyleUri;
182 }
183
184 qCDebug(lcQtQuickControls2Plugin)
185 << "calling qmlRegisterModuleImport() to register fallback style with"
186 << " uri \"" << parentModule << "\" moduleMajor" << QQmlModuleImportModuleAny
187 << "import" << fallbackModule << "importMajor" << QQmlModuleImportAuto;
188 // Whenever parentModule is imported, registeredFallbackStyleUri will be imported too.
189 // The fallback style must be a built-in style, so we match the version number.
190 qmlRegisterModuleImport(uri: parentModule.toUtf8().constData(), moduleMajor: QQmlModuleImportModuleAny,
191 import: fallbackModule.toUtf8().constData(),
192 importMajor: QQmlModuleImportAuto, importMinor: QQmlModuleImportAuto);
193 }
194
195 // If the user imports QtQuick.Controls 2.15, and they're using the Material style, we should import version 2.15.
196 // However, if they import QtQuick.Controls 2.15, but are using a custom style, we want to use the latest version
197 // number of their style.
198 const int importMajor = customStyle ? QQmlModuleImportLatest : QQmlModuleImportAuto;
199 qCDebug(lcQtQuickControls2Plugin).nospace()
200 << "calling qmlRegisterModuleImport() to register primary style with"
201 << " uri \"" << qtQuickControlsUri << "\" moduleMajor " << importMajor
202 << " import " << registeredStyleUri << " importMajor " << importMajor;
203 // When QtQuick.Controls is imported, the selected style will be imported too.
204 qmlRegisterModuleImport(uri: qtQuickControlsUri, moduleMajor: QQmlModuleImportModuleAny,
205 import: registeredStyleUri.toUtf8().constData(), importMajor);
206
207 if (customStyle)
208 QFileSelectorPrivate::addStatics(QStringList() << styleName);
209}
210
211void QtQuickControls2Plugin::unregisterTypes()
212{
213 qCDebug(lcQtQuickControls2Plugin) << "unregisterTypes() called";
214
215 const int importMajor = customStyle ? QQmlModuleImportLatest : QQmlModuleImportAuto;
216 qCDebug(lcQtQuickControls2Plugin).nospace()
217 << "calling qmlUnregisterModuleImport() to unregister primary style with"
218 << " uri \"" << qtQuickControlsUri << "\" moduleMajor " << importMajor
219 << " import " << registeredStyleUri << " importMajor " << importMajor;
220 qmlUnregisterModuleImport(uri: qtQuickControlsUri, moduleMajor: QQmlModuleImportModuleAny,
221 import: registeredStyleUri.toUtf8().constData(), importMajor);
222
223 if (!registeredFallbackStyleUri.isEmpty()) {
224 QString parentModule;
225 QString fallbackModule;
226
227 if (!rawFallbackStyleName.isEmpty()) {
228 parentModule = qtQuickControlsUri;
229 fallbackModule = registeredFallbackStyleUri;
230 rawFallbackStyleName.clear();
231 } else if (customStyle) {
232 parentModule = qtQuickControlsUri;
233 fallbackModule = QLatin1String("QtQuick.Controls.IndirectBasic");
234 } else {
235 parentModule = registeredStyleUri;
236 fallbackModule = registeredFallbackStyleUri;
237 }
238
239 qCDebug(lcQtQuickControls2Plugin)
240 << "calling qmlUnregisterModuleImport() to unregister fallback style with"
241 << " uri \"" << parentModule << "\" moduleMajor" << QQmlModuleImportModuleAny
242 << "import" << fallbackModule << "importMajor" << QQmlModuleImportAuto;
243 qmlUnregisterModuleImport(uri: parentModule.toUtf8().constData(), moduleMajor: QQmlModuleImportModuleAny,
244 import: fallbackModule.toUtf8().constData(),
245 importMajor: QQmlModuleImportAuto, importMinor: QQmlModuleImportAuto);
246
247 registeredFallbackStyleUri.clear();
248 }
249
250 customStyle = false;
251 registeredStyleUri.clear();
252}
253
254QT_END_NAMESPACE
255
256#include "qtquickcontrols2plugin.moc"
257

source code of qtdeclarative/src/quickcontrols/qtquickcontrols2plugin.cpp