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 | |
14 | QT_BEGIN_NAMESPACE |
15 | |
16 | Q_GHS_KEEP_REFERENCE(qml_register_types_QtQuick_Controls); |
17 | |
18 | Q_LOGGING_CATEGORY(lcQtQuickControls2Plugin, "qt.quick.controls.qtquickcontrols2plugin" ) |
19 | |
20 | class QtQuickControls2Plugin : public QQmlExtensionPlugin |
21 | { |
22 | Q_OBJECT |
23 | Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) |
24 | |
25 | public: |
26 | QtQuickControls2Plugin(QObject *parent = nullptr); |
27 | ~QtQuickControls2Plugin(); |
28 | |
29 | void registerTypes(const char *uri) override; |
30 | void unregisterTypes() override; |
31 | |
32 | private: |
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 | |
44 | static const char *qtQuickControlsUri = "QtQuick.Controls" ; |
45 | |
46 | QString 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 | |
62 | QString 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 | |
70 | QtQuickControls2Plugin::QtQuickControls2Plugin(QObject *parent) : QQmlExtensionPlugin(parent) |
71 | { |
72 | volatile auto registration = &qml_register_types_QtQuick_Controls; |
73 | Q_UNUSED(registration); |
74 | } |
75 | |
76 | QtQuickControls2Plugin::~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 | */ |
134 | void 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 | |
211 | void 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 | |
254 | QT_END_NAMESPACE |
255 | |
256 | #include "qtquickcontrols2plugin.moc" |
257 | |