aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2023-01-17 12:01:02 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-01-21 00:48:53 +0000
commitc62113f7b96d1ef40b8526c0b968c4eea5038dfb (patch)
tree7d1449eda6e94a033f7c995b37ca477fa49d723b
parente05d8f735ee2c35f5446130dd938273ec96dbccf (diff)
Controls: Fix module imports of fallback styles
If you specify an explicit fallback, it should take precedence over any implicit fallback the style might specify. If you don't, the implicit fallback should take precedence. If, however, the main style is a custom one, we cannot indirectly import QtQuick.Controls.Basic through it as the versions may not match. For that case, add a QtQuick.Controls.IndirectBasic module that does nothing but import QtQuick.Controls.Basic. Also, add a test that checks whether the expected fallback was imported and fix some mistaken test data along the way. In Qt 6.4 and earlier you could not override the implicit fallback since the precedence between imports was wrong. After that was fixed in commit a0290dcc53a14b67c0179c43ce8dc21b43cfc83b the fallback style set from QtQuick.Controls ("Basic" if not requested explicitly) would always override the implicit fallback style. Both behaviors are equally wrong. We need to actually check whether the fallback is explicitly set or not. Fixes: QTBUG-110247 Change-Id: I1aeee05c1c720bebf8b1b55361fd035a5565fb1b Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> (cherry picked from commit aa7f0b9731f4650ad57a4023374e7ddaeb168a7f) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/quickcontrols/CMakeLists.txt7
-rw-r--r--src/quickcontrols/qmldir2
-rw-r--r--src/quickcontrols/qtquickcontrols2plugin.cpp154
-rw-r--r--tests/auto/quickcontrols/styleimports/data/styles/StyleThatImportsFusion/Action.qml4
-rw-r--r--tests/auto/quickcontrols/styleimports/data/styles/StyleThatImportsFusion/qmldir3
-rw-r--r--tests/auto/quickcontrols/styleimports/tst_styleimports.cpp27
6 files changed, 170 insertions, 27 deletions
diff --git a/src/quickcontrols/CMakeLists.txt b/src/quickcontrols/CMakeLists.txt
index e5d8e68e39..6eff9b10d3 100644
--- a/src/quickcontrols/CMakeLists.txt
+++ b/src/quickcontrols/CMakeLists.txt
@@ -55,6 +55,13 @@ qt_internal_extend_target(qtquickcontrols2plugin
Qt::QuickTemplates2Private
)
+qt_internal_add_resource(qtquickcontrols2plugin "indirectBasic"
+ PREFIX
+ "/qt-project.org/imports/QtQuick/Controls/IndirectBasic"
+ FILES
+ qmldir
+)
+
if(QT_FEATURE_quick_designer)
add_subdirectory(designer)
endif()
diff --git a/src/quickcontrols/qmldir b/src/quickcontrols/qmldir
new file mode 100644
index 0000000000..02967c0b0f
--- /dev/null
+++ b/src/quickcontrols/qmldir
@@ -0,0 +1,2 @@
+module QtQuick.Controls.IndirectBasic
+import QtQuick.Controls.Basic auto
diff --git a/src/quickcontrols/qtquickcontrols2plugin.cpp b/src/quickcontrols/qtquickcontrols2plugin.cpp
index 8e30e8428b..9a29401841 100644
--- a/src/quickcontrols/qtquickcontrols2plugin.cpp
+++ b/src/quickcontrols/qtquickcontrols2plugin.cpp
@@ -38,6 +38,7 @@ private:
bool customStyle = false;
QString registeredStyleUri;
QString registeredFallbackStyleUri;
+ QString rawFallbackStyleName;
};
static const char *qtQuickControlsUri = "QtQuick.Controls";
@@ -78,6 +79,58 @@ QtQuickControls2Plugin::~QtQuickControls2Plugin()
// initialization and cleanup, as plugins are not unloaded on macOS.
}
+/*!
+ \internal
+
+ If this function is called, it means QtQuick.Controls was imported,
+ and we're doing runtime style selection.
+
+ For example, where:
+ \list
+ \li styleName="Material"
+ \li rawFallbackStyleName=""
+ \li fallbackStyleName="Basic"
+ \li registeredStyleUri="QtQuick.Controls.Material"
+ \li rawFallbackStyleName is empty => parentModule="QtQuick.Controls.Material"
+ \li registeredFallbackStyleUri="QtQuick.Controls.Basic"
+ \endlist
+
+ The following registrations would be made:
+
+ qmlRegisterModuleImport("QtQuick.Controls.Material", "QtQuick.Controls.Basic")
+ qmlRegisterModuleImport("QtQuick.Controls", "QtQuick.Controls.Material")
+
+ As another example, where:
+ \list
+ \li styleName="Material"
+ \li rawFallbackStyleName="Fusion"
+ \li fallbackStyleName="Fusion"
+ \li registeredStyleUri="QtQuick.Controls.Material"
+ \li rawFallbackStyleName is not empty => parentModule="QtQuick.Controls"
+ \li registeredFallbackStyleUri="QtQuick.Controls.Fusion"
+ \endlist
+
+ The following registrations would be made:
+
+ qmlRegisterModuleImport("QtQuick.Controls", "QtQuick.Controls.Fusion")
+ qmlRegisterModuleImport("QtQuick.Controls", "QtQuick.Controls.Material")
+
+ In this case, the Material style imports a fallback (Basic) via the IMPORTS
+ section in its CMakeLists.txt, \e and the user specifies a different fallback
+ using an env var/.conf/C++. We want the user's fallback to take priority,
+ which means we have to place the user-specified fallback at a more immediate place,
+ and that place is as an import of QtQuick.Controls itself rather than as an
+ import of the current style, Material (as we did in the first example).
+
+ If the style to be imported is a custom style and no specific fallback was
+ selected, we need to indirectly import Basic, but we cannot import Basic through
+ the custom style since the versions don't match. For that case we have a
+ "QtQuick.Controls.IndirectBasic" which does nothing but import
+ QtQuick.Controls.Basic. Instead of QtQuick.Controls.Basic we import that one:
+
+ qmlRegisterModuleImport("QtQuick.Controls", "Some.Custom.Style")
+ qmlRegisterModuleImport("QtQuick.Controls", "QtQuick.Controls.IndirectBasic")
+*/
void QtQuickControls2Plugin::registerTypes(const char *uri)
{
qCDebug(lcQtQuickControls2Plugin) << "registerTypes() called with uri" << uri;
@@ -85,34 +138,71 @@ void QtQuickControls2Plugin::registerTypes(const char *uri)
// It's OK that the style is resolved more than once; some accessors like name() cause it to be called, for example.
QQuickStylePrivate::init();
+ // The fallback style that was set via env var/.conf/C++.
+ rawFallbackStyleName = QQuickStylePrivate::fallbackStyle();
+ // The style that was set via env var/.conf/C++, or Basic if none was set.
const QString styleName = QQuickStylePrivate::effectiveStyleName(QQuickStyle::name());
- const QString fallbackStyleName = QQuickStylePrivate::effectiveStyleName(QQuickStylePrivate::fallbackStyle());
+ // The effective fallback style: rawFallbackStyleName, or Basic if empty.
+ const QString fallbackStyleName = QQuickStylePrivate::effectiveStyleName(rawFallbackStyleName);
qCDebug(lcQtQuickControls2Plugin) << "style:" << QQuickStyle::name() << "effective style:" << styleName
- << "fallback style:" << QQuickStylePrivate::fallbackStyle() << "effective fallback style:" << fallbackStyleName;
+ << "fallback style:" << rawFallbackStyleName << "effective fallback style:" << fallbackStyleName;
+
+ customStyle = QQuickStylePrivate::isCustomStyle();
+ // The URI of the current style. For built-in styles, the style name is appended to "QtQuick.Controls.".
+ // For custom styles that are embedded in resources, we need to remove the ":/" prefix.
+ registeredStyleUri = ::styleUri();
// If the style is Basic, we don't need to register the fallback because the Basic style
// provides all controls. Also, if we didn't return early here, we can get an infinite import loop
// when the style is set to Basic.
if (styleName != fallbackStyleName && styleName != QLatin1String("Basic")) {
+ // If no specific fallback is given, the fallback is of lower precedence than recursive
+ // imports of the main style (i.e. IMPORTS in a style's CMakeLists.txt).
+ // If a specific fallback is given, it is of higher precedence.
+
+ QString parentModule;
+ QString fallbackModule;
+
+ // The fallback style has to be a built-in style, so it will become "QtQuick.Controls.<fallback>".
registeredFallbackStyleUri = ::fallbackStyleUri();
- qCDebug(lcQtQuickControls2Plugin) << "calling qmlRegisterModuleImport() to register fallback style with"
- << " uri \"" << qtQuickControlsUri << "\" moduleMajor" << QQmlModuleImportModuleAny
- << "import" << registeredFallbackStyleUri << "importMajor" << QQmlModuleImportAuto;
+
+ if (!rawFallbackStyleName.isEmpty()) {
+ parentModule = qtQuickControlsUri;
+ fallbackModule = registeredFallbackStyleUri;
+ } else if (customStyle) {
+ // Since we don't know the versioning scheme of custom styles, but we want the
+ // version of QtQuick.Controls to be propagated, we need to do our own indirection.
+ // QtQuick.Controls.IndirectBasic indirectly imports QtQuick.Controls.Basic
+ Q_ASSERT(registeredFallbackStyleUri == QLatin1String("QtQuick.Controls.Basic"));
+ parentModule = qtQuickControlsUri;
+ fallbackModule = QLatin1String("QtQuick.Controls.IndirectBasic");
+ } else {
+ parentModule = registeredStyleUri;
+ fallbackModule = registeredFallbackStyleUri;
+ }
+
+ qCDebug(lcQtQuickControls2Plugin)
+ << "calling qmlRegisterModuleImport() to register fallback style with"
+ << " uri \"" << parentModule << "\" moduleMajor" << QQmlModuleImportModuleAny
+ << "import" << fallbackModule << "importMajor" << QQmlModuleImportAuto;
+ // Whenever parentModule is imported, registeredFallbackStyleUri will be imported too.
// The fallback style must be a built-in style, so we match the version number.
- qmlRegisterModuleImport(qtQuickControlsUri, QQmlModuleImportModuleAny, registeredFallbackStyleUri.toUtf8().constData(),
- QQmlModuleImportAuto, QQmlModuleImportAuto);
+ qmlRegisterModuleImport(parentModule.toUtf8().constData(), QQmlModuleImportModuleAny,
+ fallbackModule.toUtf8().constData(),
+ QQmlModuleImportAuto, QQmlModuleImportAuto);
}
// If the user imports QtQuick.Controls 2.15, and they're using the Material style, we should import version 2.15.
// However, if they import QtQuick.Controls 2.15, but are using a custom style, we want to use the latest version
// number of their style.
- customStyle = QQuickStylePrivate::isCustomStyle();
- registeredStyleUri = ::styleUri();
- const int importMajor = !customStyle ? QQmlModuleImportAuto : QQmlModuleImportLatest;
- qCDebug(lcQtQuickControls2Plugin).nospace() << "calling qmlRegisterModuleImport() to register primary style with"
- << " uri \"" << qtQuickControlsUri << "\" moduleMajor " << importMajor
- << " import " << registeredStyleUri << " importMajor " << importMajor;
- qmlRegisterModuleImport(qtQuickControlsUri, QQmlModuleImportModuleAny, registeredStyleUri.toUtf8().constData(), importMajor);
+ const int importMajor = customStyle ? QQmlModuleImportLatest : QQmlModuleImportAuto;
+ qCDebug(lcQtQuickControls2Plugin).nospace()
+ << "calling qmlRegisterModuleImport() to register primary style with"
+ << " uri \"" << qtQuickControlsUri << "\" moduleMajor " << importMajor
+ << " import " << registeredStyleUri << " importMajor " << importMajor;
+ // When QtQuick.Controls is imported, the selected style will be imported too.
+ qmlRegisterModuleImport(qtQuickControlsUri, QQmlModuleImportModuleAny,
+ registeredStyleUri.toUtf8().constData(), importMajor);
if (customStyle)
QFileSelectorPrivate::addStatics(QStringList() << styleName);
@@ -122,15 +212,41 @@ void QtQuickControls2Plugin::unregisterTypes()
{
qCDebug(lcQtQuickControls2Plugin) << "unregisterTypes() called";
+ const int importMajor = customStyle ? QQmlModuleImportLatest : QQmlModuleImportAuto;
+ qCDebug(lcQtQuickControls2Plugin).nospace()
+ << "calling qmlUnregisterModuleImport() to unregister primary style with"
+ << " uri \"" << qtQuickControlsUri << "\" moduleMajor " << importMajor
+ << " import " << registeredStyleUri << " importMajor " << importMajor;
+ qmlUnregisterModuleImport(qtQuickControlsUri, QQmlModuleImportModuleAny,
+ registeredStyleUri.toUtf8().constData(), importMajor);
+
if (!registeredFallbackStyleUri.isEmpty()) {
- // We registered a fallback style, so now we need to unregister it.
- qmlUnregisterModuleImport(qtQuickControlsUri, QQmlModuleImportModuleAny, registeredFallbackStyleUri.toUtf8().constData(),
- QQmlModuleImportAuto, QQmlModuleImportAuto);
+ QString parentModule;
+ QString fallbackModule;
+
+ if (!rawFallbackStyleName.isEmpty()) {
+ parentModule = qtQuickControlsUri;
+ fallbackModule = registeredFallbackStyleUri;
+ rawFallbackStyleName.clear();
+ } else if (customStyle) {
+ parentModule = qtQuickControlsUri;
+ fallbackModule = QLatin1String("QtQuick.Controls.IndirectBasic");
+ } else {
+ parentModule = registeredStyleUri;
+ fallbackModule = registeredFallbackStyleUri;
+ }
+
+ qCDebug(lcQtQuickControls2Plugin)
+ << "calling qmlUnregisterModuleImport() to unregister fallback style with"
+ << " uri \"" << parentModule << "\" moduleMajor" << QQmlModuleImportModuleAny
+ << "import" << fallbackModule << "importMajor" << QQmlModuleImportAuto;
+ qmlUnregisterModuleImport(parentModule.toUtf8().constData(), QQmlModuleImportModuleAny,
+ fallbackModule.toUtf8().constData(),
+ QQmlModuleImportAuto, QQmlModuleImportAuto);
+
registeredFallbackStyleUri.clear();
}
- const int importMajor = !customStyle ? QQmlModuleImportAuto : QQmlModuleImportLatest;
- qmlUnregisterModuleImport(qtQuickControlsUri, QQmlModuleImportModuleAny, registeredStyleUri.toUtf8().constData(), importMajor);
customStyle = false;
registeredStyleUri.clear();
}
diff --git a/tests/auto/quickcontrols/styleimports/data/styles/StyleThatImportsFusion/Action.qml b/tests/auto/quickcontrols/styleimports/data/styles/StyleThatImportsFusion/Action.qml
new file mode 100644
index 0000000000..f0a1459891
--- /dev/null
+++ b/tests/auto/quickcontrols/styleimports/data/styles/StyleThatImportsFusion/Action.qml
@@ -0,0 +1,4 @@
+import QtQuick.Templates as T
+T.Action {
+ objectName: "StyleThatImportsFusion"
+}
diff --git a/tests/auto/quickcontrols/styleimports/data/styles/StyleThatImportsFusion/qmldir b/tests/auto/quickcontrols/styleimports/data/styles/StyleThatImportsFusion/qmldir
new file mode 100644
index 0000000000..824792a4e5
--- /dev/null
+++ b/tests/auto/quickcontrols/styleimports/data/styles/StyleThatImportsFusion/qmldir
@@ -0,0 +1,3 @@
+module StyleThatImportsFusion
+Action 6.0 Action.qml
+import QtQuick.Controls.Fusion auto
diff --git a/tests/auto/quickcontrols/styleimports/tst_styleimports.cpp b/tests/auto/quickcontrols/styleimports/tst_styleimports.cpp
index dbc56e1022..175541296a 100644
--- a/tests/auto/quickcontrols/styleimports/tst_styleimports.cpp
+++ b/tests/auto/quickcontrols/styleimports/tst_styleimports.cpp
@@ -69,44 +69,52 @@ void tst_StyleImports::select_data()
QTest::newRow("control=Action,style=fs,fallback=empty") << "Action.qml" << "FileSystemStyle" << "" << "FileSystemStyle";
QTest::newRow("control=Action,style=qrc,fallback=empty") << "Action.qml" << "ResourceStyle" << "" << "Basic";
QTest::newRow("control=Action,style=nosuch,fallback=empty") << "Action.qml" << "NoSuchStyle" << "" << "Basic";
+ QTest::newRow("control=Action,style=import,fallback=empty") << "Action.qml" << "StyleThatImportsFusion" << "" << "StyleThatImportsFusion";
QTest::newRow("control=Action,style=basic,fallback=mat") << "Action.qml" << "Basic" << "Material" << "";
QTest::newRow("control=Action,style=fs,fallback=mat") << "Action.qml" << "FileSystemStyle" << "Material" << "FileSystemStyle";
QTest::newRow("control=Action,style=qrc,fallback=mat") << "Action.qml" << "ResourceStyle" << "Material" << "Basic";
QTest::newRow("control=Action,style=nosuch,fallback=mat") << "Action.qml" << "NoSuchStyle" << "Material" << "Basic";
+ QTest::newRow("control=Action,style=import,fallback=mat") << "Action.qml" << "StyleThatImportsFusion" << "Material" << "StyleThatImportsFusion";
// Amongst the styles we're testing here, ScrollView.qml only exists in the Basic style.
QTest::newRow("control=ScrollView,style=basic,fallback=empty") << "ScrollView.qml" << "Basic" << "" << "Basic";
QTest::newRow("control=ScrollView,style=fs,fallback=empty") << "ScrollView.qml" << "FileSystemStyle" << "" << "Basic";
QTest::newRow("control=ScrollView,style=qrc,fallback=empty") << "ScrollView.qml" << "ResourceStyle" << "" << "Basic";
QTest::newRow("control=ScrollView,style=nosuch,fallback=empty") << "ScrollView.qml" << "NoSuchStyle" << "" << "Basic";
+ QTest::newRow("control=ScrollView,style=import,fallback=empty") << "ScrollView.qml" << "StyleThatImportsFusion" << "" << "Fusion";
QTest::newRow("control=ScrollView,style=basic,fallback=mat") << "ScrollView.qml" << "Basic" << "Material" << "Basic";
- QTest::newRow("control=ScrollView,style=fs,fallback=mat") << "ScrollView.qml" << "FileSystemStyle" << "Material" << "Basic";
- QTest::newRow("control=ScrollView,style=qrc,fallback=mat") << "ScrollView.qml" << "ResourceStyle" << "Material" << "Basic";
+ QTest::newRow("control=ScrollView,style=fs,fallback=mat") << "ScrollView.qml" << "FileSystemStyle" << "Material" << "Material";
+ QTest::newRow("control=ScrollView,style=qrc,fallback=mat") << "ScrollView.qml" << "ResourceStyle" << "Material" << "Material";
QTest::newRow("control=ScrollView,style=nosuch,fallback=mat") << "ScrollView.qml" << "NoSuchStyle" << "Material" << "Basic";
+ QTest::newRow("control=ScrollView,style=import,fallback=mat") << "ScrollView.qml" << "StyleThatImportsFusion" << "Material" << "Material";
// Label.qml exists in the FileSystemStyle, Basic and Material styles.
QTest::newRow("control=Label,style=basic,fallback=empty") << "Label.qml" << "Basic" << "" << "Basic";
QTest::newRow("control=Label,style=fs,fallback=empty") << "Label.qml" << "FileSystemStyle" << "" << "FileSystemStyle";
QTest::newRow("control=Label,style=qrc,fallback=empty") << "Label.qml" << "ResourceStyle" << "" << "Basic";
QTest::newRow("control=Label,style=nosuch,fallback=empty") << "Label.qml" << "NoSuchStyle" << "" << "Basic";
+ QTest::newRow("control=Label,style=import,fallback=empty") << "Label.qml" << "StyleThatImportsFusion" << "" << "Fusion";
QTest::newRow("control=Label,style=basic,fallback=mat") << "Label.qml" << "Basic" << "Material" << "Basic";
QTest::newRow("control=Label,style=fs,fallback=mat") << "Label.qml" << "FileSystemStyle" << "Material" << "FileSystemStyle";
- QTest::newRow("control=Label,style=qrc,fallback=mat") << "Label.qml" << "ResourceStyle" << "Material" << "Basic";
+ QTest::newRow("control=Label,style=qrc,fallback=mat") << "Label.qml" << "ResourceStyle" << "Material" << "Material";
QTest::newRow("control=Label,style=nosuch,fallback=mat") << "Label.qml" << "NoSuchStyle" << "Material" << "Basic";
+ QTest::newRow("control=Label,style=import,fallback=mat") << "Label.qml" << "StyleThatImportsFusion" << "Material" << "Material";
// Button.qml exists in all styles including the fs and qrc styles
QTest::newRow("control=Button,style=basic,fallback=empty") << "Button.qml" << "Basic" << "" << "Basic";
QTest::newRow("control=Button,style=fs,fallback=empty") << "Button.qml" << "FileSystemStyle" << "" << "FileSystemStyle";
QTest::newRow("control=Button,style=qrc,fallback=empty") << "Button.qml" << "ResourceStyle" << "" << "ResourceStyle";
QTest::newRow("control=Button,style=nosuch,fallback=empty") << "Button.qml" << "NoSuchStyle" << "" << "Basic";
+ QTest::newRow("control=Button,style=import,fallback=empty") << "Button.qml" << "StyleThatImportsFusion" << "" << "Fusion";
QTest::newRow("control=Button,style=basic,fallback=mat") << "Button.qml" << "Basic" << "Material" << "Basic";
QTest::newRow("control=Button,style=fs,fallback=mat") << "Button.qml" << "FileSystemStyle" << "Material" << "FileSystemStyle";
QTest::newRow("control=Button,style=qrc,fallback=mat") << "Button.qml" << "ResourceStyle" << "Material" << "ResourceStyle";
QTest::newRow("control=Button,style=nosuch,fallback=mat") << "Button.qml" << "NoSuchStyle" << "Material" << "Basic";
+ QTest::newRow("control=Button,style=import,fallback=mat") << "Button.qml" << "StyleThatImportsFusion" << "Material" << "Material";
}
void tst_StyleImports::select()
@@ -144,14 +152,17 @@ void tst_StyleImports::select()
QVERIFY2(!object.isNull(), qPrintable(component.errorString()));
- // TODO: test built-in styles below too
- // We can't check for the attached style object since that API is in a plugin,
- // and it's not possible to use e.g. the baseUrl of the QQmlContext
- // nor the metaObject to test it either.
-
if (!QQuickStylePrivate::builtInStyles().contains(expected)) {
// We're expecting a custom style.
QCOMPARE(object->objectName(), expected);
+ } else {
+ const QUrl expectedUrl(
+ QLatin1String("qrc:///qt-project.org/imports/QtQuick/Controls/%1/%2")
+ .arg(expected, file));
+ QQmlComponent c2(&engine, expectedUrl);
+ QVERIFY2(c2.isReady(), qPrintable(c2.errorString()));
+ QScopedPointer<QObject> o2(c2.create());
+ QCOMPARE(object->metaObject(), o2->metaObject());
}
}