diff options
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()); } } |