From 501bc44bb006ee8021cbaaa7a696e7c9263395d3 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Fri, 17 Apr 2020 14:32:55 +0200 Subject: Use qmlRegisterModuleImport() to register styles This patch completes the cumulative work done in previous patches. - Uses qmlRegisterModuleImport() to register styles. This has some added requirements: - Each style must now be a QML module -- that is, it must have a qmldir file. - As a result of the above, the module must be available within the QML import path in order to be found. - The various forms of accepted style names have been reduced down to one ("Material", "MyStyle", etc). See below for an explanation of why. - The following API in QQuickStyle is removed: addStylePath(), availableStyles(), path(), stylePathList(). These no longer make sense now that we reuse the existing QML import system. - Adds the tst_qquickstyleselector auto test back as "styleimports". qmlRegisterModuleImport() vs resolvedUrl() Previously we would use QQuickStyleSelector to select individual QML files based on which style was set. We'd do this once when QtQuick.Controls was first imported. With Qt 6, and the requirement that each style be a proper QML module, qmlRegisterModuleImport() was introduced. This allows us to "link" one import with another. For an example of what this looks like in practice, suppose the style was set to "MyStyle", and the fallback to "Material". The "QtQuick.Controls" import will be linked to "MyStyle", "MyStyle" to "QtQuick.Controls.Material", and as a final fallback (for controls like Action which only the Default style implements), "QtQuick.Controls.Material" to "QtQuick.Controls.Default". This is the same behavior as in Qt 5 (see qquickstyleselector.cpp): // 1) requested style (e.g. "MyStyle", included in d->selectors) // 2) fallback style (e.g. "Material", included in d->selectors) // 3) default style (empty selector, not in d->selectors) This is a necessary step to enable compilation of QML to C++. Reducing the set of accepted style names The problem In QtQuickControls2Plugin() we need to call QQuickStylePrivate::init(baseUrl()) in order to detect if the style is a custom style in QQuickStyleSpec::resolve() (by checking if the style path starts with the base URL). In Qt 5, init() is called in QtQuickControls2Plugin::registerTypes(), but in Qt 6 that's too late, because we need to call qmlRegisterModuleImport() in the constructor. qmlRegisterModuleImport() itself requires the style to have already been set in order to create the correct import URI ("QtQuick.Controls.X" for built-in styles, "MyCustomStyle" for custom styles). The solution By reducing the valid forms for style names down to one: ./myapp -style MyStyle we solve the problem of needing baseUrl() to determine if the style is a custom style or not, but needing to call it too early (since we now call qmlRegisterModuleImport() in QtQuickControls2Plugin(), which itself requires the style to have already been set). baseUrl() can't have been set before the constructor is finished. All of the various forms for _setting_ a style are still valid; environment variables, qtquickcontrols2.conf, etc. [ChangeLog][Important Behavior Changes] Custom styles must now have a qmldir that lists the files that the style implements. For example, for a style that only implements Button: --- module MyStyle Button 1.0 Button.qml --- In addition, there is now only one valid, case-sensitive form for style names: "Material", "MyStyle", etc. These changes are done to help enable the compilation of QML code to C++, as well as improve tooling capabilities. [ChangeLog][Important Behavior Changes] The following API was removed: - QQuickStyle::addStylePath() - QQuickStyle::availableStyles() - QQuickStyle::path() - QQuickStyle::stylePathList() - QT_QUICK_CONTROLS_STYLE_PATH This API is no longer necessary and/or able to be provided now that styles are treated as regular QML modules. Task-number: QTBUG-82922 Change-Id: I3b281131903c7c3c1cf0616eb7486a872dccd730 Reviewed-by: Fabian Kosmale --- examples/quickcontrols2/gallery/gallery.cpp | 6 +- examples/quickcontrols2/gallery/gallery.qml | 2 +- .../default/qtquickcontrols2defaultstyleplugin.cpp | 12 +- .../doc/src/qtquickcontrols2-customize.qdoc | 44 ++- .../doc/src/qtquickcontrols2-environment.qdoc | 8 +- src/imports/controls/fusion/qmldir | 2 +- .../fusion/qtquickcontrols2fusionstyleplugin.cpp | 18 +- src/imports/controls/imagine/qmldir | 2 +- .../imagine/qtquickcontrols2imaginestyleplugin.cpp | 20 +- src/imports/controls/material/qmldir | 2 +- .../qtquickcontrols2materialstyleplugin.cpp | 17 +- src/imports/controls/qtquickcontrols2plugin.cpp | 160 +++++---- src/imports/controls/universal/qmldir | 2 +- .../qtquickcontrols2universalstyleplugin.cpp | 18 +- src/quickcontrols2/qquickstyle.cpp | 350 +++----------------- src/quickcontrols2/qquickstyle.h | 4 - src/quickcontrols2/qquickstyle_p.h | 8 +- src/quickcontrols2/qquickstyleplugin.cpp | 16 + src/quickcontrols2/qquickstyleplugin_p.h | 2 +- tests/auto/auto.pro | 1 + tests/auto/customization/customization.pro | 2 +- tests/auto/customization/data/styles/empty/qmldir | 59 ++++ .../customization/data/styles/identified/qmldir | 59 ++++ .../customization/data/styles/incomplete/qmldir | 59 ++++ .../auto/customization/data/styles/override/qmldir | 59 ++++ tests/auto/customization/data/styles/simple/qmldir | 59 ++++ tests/auto/customization/tst_customization.cpp | 14 +- .../qquickstyle/data/CmdLineArgStyle/Control.qml | 2 + tests/auto/qquickstyle/data/CmdLineArgStyle/qmldir | 2 + tests/auto/qquickstyle/data/Custom/Label.qml | 2 + tests/auto/qquickstyle/data/Custom/qmldir | 2 + .../data/EnvVarFallbackStyle/Control.qml | 2 + .../qquickstyle/data/EnvVarFallbackStyle/qmldir | 2 + .../auto/qquickstyle/data/EnvVarStyle/Control.qml | 2 + tests/auto/qquickstyle/data/EnvVarStyle/qmldir | 2 + tests/auto/qquickstyle/data/custom.conf | 2 +- tests/auto/qquickstyle/qquickstyle.pro | 28 +- .../qquickstyle/qrcStyles1/QrcStyle1/Button.qml | 2 - .../qquickstyle/qrcStyles2/QrcStyle2/Button.qml | 2 - .../qquickstyle/qrcStyles3/QrcStyle3/Button.qml | 2 - .../qquickstyle/qrcStyles4/QrcStyle4/Button.qml | 2 - tests/auto/qquickstyle/tst_qquickstyle.cpp | 190 +---------- tests/auto/sanity/BLACKLIST | 4 +- tests/auto/sanity/sanity.pro | 2 +- tests/auto/sanity/tst_sanity.cpp | 367 +++++++++++++++++++-- tests/auto/shared/qtest_quickcontrols.h | 3 +- tests/auto/shared/util.pri | 2 +- tests/auto/shared/visualtestutil.cpp | 17 +- tests/auto/shared/visualtestutil.h | 33 +- tests/auto/snippets/snippets.pro | 2 +- tests/auto/snippets/tst_snippets.cpp | 13 +- tests/auto/styleimports/ResourceStyle/Button.qml | 4 + tests/auto/styleimports/ResourceStyle/qmldir | 2 + tests/auto/styleimports/data/Action.qml | 4 + tests/auto/styleimports/data/Button.qml | 4 + .../styleimports/data/FileSystemStyle/Button.qml | 4 + .../auto/styleimports/data/FileSystemStyle/qmldir | 2 + tests/auto/styleimports/data/Label.qml | 4 + .../data/PlatformStyle/+linux/Button.qml | 4 + .../data/PlatformStyle/+macos/Button.qml | 4 + .../data/PlatformStyle/+windows/Button.qml | 4 + .../styleimports/data/PlatformStyle/Button.qml | 4 + tests/auto/styleimports/data/PlatformStyle/qmldir | 2 + tests/auto/styleimports/data/platformSelectors.qml | 64 ++++ tests/auto/styleimports/data/qmldir | 4 + tests/auto/styleimports/styleimports.pro | 29 ++ tests/auto/styleimports/tst_styleimports.cpp | 205 ++++++++++++ tests/benchmarks/creationtime/creationtime.pro | 2 +- tests/benchmarks/creationtime/tst_creationtime.cpp | 53 +-- tests/benchmarks/objectcount/tst_objectcount.cpp | 2 +- tests/manual/testbench/main.cpp | 3 +- tests/manual/testbench/testbench.pro | 2 +- 72 files changed, 1382 insertions(+), 716 deletions(-) create mode 100644 tests/auto/customization/data/styles/empty/qmldir create mode 100644 tests/auto/customization/data/styles/identified/qmldir create mode 100644 tests/auto/customization/data/styles/incomplete/qmldir create mode 100644 tests/auto/customization/data/styles/override/qmldir create mode 100644 tests/auto/customization/data/styles/simple/qmldir create mode 100644 tests/auto/qquickstyle/data/CmdLineArgStyle/Control.qml create mode 100644 tests/auto/qquickstyle/data/CmdLineArgStyle/qmldir create mode 100644 tests/auto/qquickstyle/data/Custom/Label.qml create mode 100644 tests/auto/qquickstyle/data/Custom/qmldir create mode 100644 tests/auto/qquickstyle/data/EnvVarFallbackStyle/Control.qml create mode 100644 tests/auto/qquickstyle/data/EnvVarFallbackStyle/qmldir create mode 100644 tests/auto/qquickstyle/data/EnvVarStyle/Control.qml create mode 100644 tests/auto/qquickstyle/data/EnvVarStyle/qmldir delete mode 100644 tests/auto/qquickstyle/qrcStyles1/QrcStyle1/Button.qml delete mode 100644 tests/auto/qquickstyle/qrcStyles2/QrcStyle2/Button.qml delete mode 100644 tests/auto/qquickstyle/qrcStyles3/QrcStyle3/Button.qml delete mode 100644 tests/auto/qquickstyle/qrcStyles4/QrcStyle4/Button.qml create mode 100644 tests/auto/styleimports/ResourceStyle/Button.qml create mode 100644 tests/auto/styleimports/ResourceStyle/qmldir create mode 100644 tests/auto/styleimports/data/Action.qml create mode 100644 tests/auto/styleimports/data/Button.qml create mode 100644 tests/auto/styleimports/data/FileSystemStyle/Button.qml create mode 100644 tests/auto/styleimports/data/FileSystemStyle/qmldir create mode 100644 tests/auto/styleimports/data/Label.qml create mode 100644 tests/auto/styleimports/data/PlatformStyle/+linux/Button.qml create mode 100644 tests/auto/styleimports/data/PlatformStyle/+macos/Button.qml create mode 100644 tests/auto/styleimports/data/PlatformStyle/+windows/Button.qml create mode 100644 tests/auto/styleimports/data/PlatformStyle/Button.qml create mode 100644 tests/auto/styleimports/data/PlatformStyle/qmldir create mode 100644 tests/auto/styleimports/data/platformSelectors.qml create mode 100644 tests/auto/styleimports/data/qmldir create mode 100644 tests/auto/styleimports/styleimports.pro create mode 100644 tests/auto/styleimports/tst_styleimports.cpp diff --git a/examples/quickcontrols2/gallery/gallery.cpp b/examples/quickcontrols2/gallery/gallery.cpp index b4d59685..bbcd2d14 100644 --- a/examples/quickcontrols2/gallery/gallery.cpp +++ b/examples/quickcontrols2/gallery/gallery.cpp @@ -73,7 +73,11 @@ int main(int argc, char *argv[]) QQuickStyle::setStyle(settings.value("style").toString()); QQmlApplicationEngine engine; - engine.rootContext()->setContextProperty("availableStyles", QQuickStyle::availableStyles()); + + const QStringList builtInStyles = { QLatin1String("Default"), QLatin1String("Fusion"), + QLatin1String("Imagine"), QLatin1String("Material"), QLatin1String("Universal") }; + engine.rootContext()->setContextProperty("builtInStyles", builtInStyles); + engine.load(QUrl("qrc:/gallery.qml")); if (engine.rootObjects().isEmpty()) return -1; diff --git a/examples/quickcontrols2/gallery/gallery.qml b/examples/quickcontrols2/gallery/gallery.qml index f76315cc..872bdb44 100644 --- a/examples/quickcontrols2/gallery/gallery.qml +++ b/examples/quickcontrols2/gallery/gallery.qml @@ -287,7 +287,7 @@ ApplicationWindow { ComboBox { id: styleBox property int styleIndex: -1 - model: availableStyles + model: builtInStyles Component.onCompleted: { styleIndex = find(settings.style, Qt.MatchFixedString) if (styleIndex !== -1) diff --git a/src/imports/controls/default/qtquickcontrols2defaultstyleplugin.cpp b/src/imports/controls/default/qtquickcontrols2defaultstyleplugin.cpp index 0ed33d12..a486658e 100644 --- a/src/imports/controls/default/qtquickcontrols2defaultstyleplugin.cpp +++ b/src/imports/controls/default/qtquickcontrols2defaultstyleplugin.cpp @@ -38,6 +38,7 @@ #include "qquickdefaulttheme_p.h" #include +#include QT_BEGIN_NAMESPACE @@ -50,7 +51,10 @@ public: QtQuickControls2DefaultStylePlugin(QObject *parent = nullptr); QString name() const override; - void initializeTheme(QQuickTheme *theme) override; + + void registerTypes(const char *uri) override; + + QQuickDefaultTheme theme; }; QtQuickControls2DefaultStylePlugin::QtQuickControls2DefaultStylePlugin(QObject *parent) : QQuickStylePlugin(parent) @@ -62,9 +66,11 @@ QString QtQuickControls2DefaultStylePlugin::name() const return QStringLiteral("Default"); } -void QtQuickControls2DefaultStylePlugin::initializeTheme(QQuickTheme *theme) +void QtQuickControls2DefaultStylePlugin::registerTypes(const char *uri) { - QQuickDefaultTheme::initialize(theme); + QQuickStylePlugin::registerTypes(uri); + + theme.initialize(QQuickTheme::instance()); } QT_END_NAMESPACE diff --git a/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc b/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc index 6ab649d0..58138a07 100644 --- a/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc +++ b/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc @@ -112,7 +112,7 @@ \section2 Definition of a Style In Qt Quick Controls, a style is essentially an interchangeable set of - QML files within a single directory. There are three requirements for a style + QML files within a single directory. There are four requirements for a style to be \l {Using Styles in Qt Quick Controls}{usable}: \list @@ -125,21 +125,37 @@ If we instead used the corresponding type from the \l {Qt Quick Controls} {QtQuick.Controls} import as we did in the previous section, it would not work: the control we were defining would try to derive from itself. - \li The files must be in a directory in the filesystem or in the - \l {The Qt Resource System}{resource system}. + \li A \l {Module Definition qmldir Files}{qmldir} file must exist alongside + the QML file(s). Below is an example of a simple \c qmldir file for a style that + provides a button: - For example, these are all valid paths to a style: + \badcode + module MyStyle + Button 2.15 Button.qml + \endcode + + The directory structure for such a style looks like this: + + \badcode + MyStyle + ├─── Button.qml + └─── qmldir + \endcode + \li The files must be in a directory that is findable via the \l {QML Import Path}. + + For example, if the path to \e MyStyle directory mentioned above was + \c /home/user/MyApp/MyStyle, then \c /home/user/MyApp must be added to + the QML import path. + + To \l {Using Styles in Qt Quick Controls}{use} \e MyStyle in \e MyApp, + refer to it by name: \list - \li \c {./myapp -style /home/absolute/path/to/my/style} - \li \c {./myapp -style :/mystyle} - \li \c {./myapp -style relative/path/to/my/style} - \li \c {./myapp -style MyStyle} + \li \c {./MyApp -style MyStyle} \endlist - The third and fourth paths will be looked up within the QML engine's import path - list. This is the same as what happens when you pass \c Material as the style, - for example. + The style name must match the casing of the style directory; passing + \e mystyle or \e MYSTYLE is not supported. \endlist By default, the styling system uses the Default style as a fallback for @@ -302,7 +318,7 @@ style will illustrate the elevation with a drop shadow; the higher the elevation, the larger the shadow. - The first step is to \l {Qt Creator: Creating Qt Quick Projects}{create a new Qt Quick + The first step is to \l {Creating Qt Quick Projects}{create a new Qt Quick Controls 2 application} in Qt Creator. After that, we \l {Qt Creator: Creating C++ Classes}{add a C++ type} that stores the elevation. Since the type will be used for every control supported by our style, and because @@ -390,6 +406,8 @@ qmlRegisterUncreatableType("MyStyle", 1, 0, "MyStyle", "MyStyle is an attached property"); QQmlApplicationEngine engine; + // Make the directory containing our style known to the QML engine. + engine.addImportPath(":/"); engine.load(QUrl(QLatin1String("qrc:/main.qml"))); return app.exec(); @@ -465,7 +483,7 @@ One button has no elevation, and the other has an elevation of \c 10. With that in place, we can run our example. To tell the application to - use our new style, we pass \c {-style :/mystyle} as an application + use our new style, we pass \c {-style MyStyle} as an application argument, but there are \l {Using Styles in Qt Quick Controls}{many ways} to specify the style to use. diff --git a/src/imports/controls/doc/src/qtquickcontrols2-environment.qdoc b/src/imports/controls/doc/src/qtquickcontrols2-environment.qdoc index e1461ee4..4484acf3 100644 --- a/src/imports/controls/doc/src/qtquickcontrols2-environment.qdoc +++ b/src/imports/controls/doc/src/qtquickcontrols2-environment.qdoc @@ -41,17 +41,11 @@ \li \c QT_QUICK_CONTROLS_STYLE \li Specifies the default \l {Styling Qt Quick Controls}{Qt Quick Controls style}. The value can be either one of the built-in styles, for example \c "Material", - or the path to a custom style such as \c ":/mystyle". + or a custom style such as \c "MyStyle". \row \li \c QT_QUICK_CONTROLS_FALLBACK_STYLE \li Specifies a fallback style for \l {Creating a Custom Style}{custom styles}. The value can be one of the built-in styles, for example \c "Material", - \row - \li \c QT_QUICK_CONTROLS_STYLE_PATH - \li Specifies a list of additional paths that are used to lookup \l {Styling Qt Quick Controls} - {Qt Quick Controls styles}. Multiple path entries must be \l {QDir::listSeparator}{separated} - by \c ':' under Unix and \c ';' under Windows. By default, styles are looked up from - \c $QML2_IMPORT_PATH/QtQuick/Controls. \row \li \c QT_QUICK_CONTROLS_CONF \li Specifies the location of the \l {Qt Quick Controls configuration file}. diff --git a/src/imports/controls/fusion/qmldir b/src/imports/controls/fusion/qmldir index 572a6dd2..84c0b211 100644 --- a/src/imports/controls/fusion/qmldir +++ b/src/imports/controls/fusion/qmldir @@ -1,7 +1,7 @@ module QtQuick.Controls.Fusion plugin qtquickcontrols2fusionstyleplugin classname QtQuickControls2FusionStylePlugin -depends QtQuick.Controls 2.5 +import QtQuick.Controls.Default auto # QtQuick.Controls 2.0 (originally introduced in Qt 5.7) ApplicationWindow 2.0 ApplicationWindow.qml diff --git a/src/imports/controls/fusion/qtquickcontrols2fusionstyleplugin.cpp b/src/imports/controls/fusion/qtquickcontrols2fusionstyleplugin.cpp index b9df2e7d..c70c9602 100644 --- a/src/imports/controls/fusion/qtquickcontrols2fusionstyleplugin.cpp +++ b/src/imports/controls/fusion/qtquickcontrols2fusionstyleplugin.cpp @@ -34,12 +34,13 @@ ** ****************************************************************************/ -#include -#include - #include "qquickfusionstyle_p.h" #include "qquickfusiontheme_p.h" +#include +#include +#include + QT_BEGIN_NAMESPACE class QtQuickControls2FusionStylePlugin : public QQuickStylePlugin @@ -51,7 +52,10 @@ public: QtQuickControls2FusionStylePlugin(QObject *parent = nullptr); QString name() const override; - void initializeTheme(QQuickTheme *theme) override; + + void registerTypes(const char *uri) override; + + QQuickFusionTheme theme; }; QtQuickControls2FusionStylePlugin::QtQuickControls2FusionStylePlugin(QObject *parent) : QQuickStylePlugin(parent) @@ -63,9 +67,11 @@ QString QtQuickControls2FusionStylePlugin::name() const return QStringLiteral("Fusion"); } -void QtQuickControls2FusionStylePlugin::initializeTheme(QQuickTheme *theme) +void QtQuickControls2FusionStylePlugin::registerTypes(const char *uri) { - QQuickFusionTheme::initialize(theme); + QQuickStylePlugin::registerTypes(uri); + + theme.initialize(QQuickTheme::instance()); } QT_END_NAMESPACE diff --git a/src/imports/controls/imagine/qmldir b/src/imports/controls/imagine/qmldir index 380a0dc3..e3d7d8d5 100644 --- a/src/imports/controls/imagine/qmldir +++ b/src/imports/controls/imagine/qmldir @@ -1,7 +1,7 @@ module QtQuick.Controls.Imagine plugin qtquickcontrols2imaginestyleplugin classname QtQuickControls2ImagineStylePlugin -depends QtQuick.Controls 2.5 +import QtQuick.Controls.Default auto # QtQuick.Controls 2.0 (originally introduced in Qt 5.7) ApplicationWindow 2.0 ApplicationWindow.qml diff --git a/src/imports/controls/imagine/qtquickcontrols2imaginestyleplugin.cpp b/src/imports/controls/imagine/qtquickcontrols2imaginestyleplugin.cpp index d59cf555..0c864254 100644 --- a/src/imports/controls/imagine/qtquickcontrols2imaginestyleplugin.cpp +++ b/src/imports/controls/imagine/qtquickcontrols2imaginestyleplugin.cpp @@ -34,13 +34,14 @@ ** ****************************************************************************/ -#include -#include -#include - #include "qquickimaginestyle_p.h" #include "qquickimaginetheme_p.h" +#include +#include +#include +#include + QT_BEGIN_NAMESPACE class QtQuickControls2ImagineStylePlugin : public QQuickStylePlugin @@ -52,7 +53,10 @@ public: QtQuickControls2ImagineStylePlugin(QObject *parent = nullptr); QString name() const override; - void initializeTheme(QQuickTheme *theme) override; + + void registerTypes(const char *uri) override; + + QQuickImagineTheme theme; }; QtQuickControls2ImagineStylePlugin::QtQuickControls2ImagineStylePlugin(QObject *parent) : QQuickStylePlugin(parent) @@ -64,9 +68,11 @@ QString QtQuickControls2ImagineStylePlugin::name() const return QStringLiteral("Imagine"); } -void QtQuickControls2ImagineStylePlugin::initializeTheme(QQuickTheme *theme) +void QtQuickControls2ImagineStylePlugin::registerTypes(const char *uri) { - QQuickImagineTheme::initialize(theme); + QQuickStylePlugin::registerTypes(uri); + + theme.initialize(QQuickTheme::instance()); } QT_END_NAMESPACE diff --git a/src/imports/controls/material/qmldir b/src/imports/controls/material/qmldir index a1064229..39dae48a 100644 --- a/src/imports/controls/material/qmldir +++ b/src/imports/controls/material/qmldir @@ -1,7 +1,7 @@ module QtQuick.Controls.Material plugin qtquickcontrols2materialstyleplugin classname QtQuickControls2MaterialStylePlugin -depends QtQuick.Controls 2.5 +import QtQuick.Controls.Default auto # QtQuick.Controls 2.0 (originally introduced in Qt 5.7) ApplicationWindow 2.0 ApplicationWindow.qml diff --git a/src/imports/controls/material/qtquickcontrols2materialstyleplugin.cpp b/src/imports/controls/material/qtquickcontrols2materialstyleplugin.cpp index dc6873bb..6066d3f5 100644 --- a/src/imports/controls/material/qtquickcontrols2materialstyleplugin.cpp +++ b/src/imports/controls/material/qtquickcontrols2materialstyleplugin.cpp @@ -34,12 +34,12 @@ ** ****************************************************************************/ -#include - #include "qquickmaterialstyle_p.h" #include "qquickmaterialtheme_p.h" +#include #include +#include QT_BEGIN_NAMESPACE @@ -52,12 +52,14 @@ public: QtQuickControls2MaterialStylePlugin(QObject *parent = nullptr); QString name() const override; - void initializeTheme(QQuickTheme *theme) override; + + void registerTypes(const char *uri) override; + + QQuickMaterialTheme theme; }; QtQuickControls2MaterialStylePlugin::QtQuickControls2MaterialStylePlugin(QObject *parent) : QQuickStylePlugin(parent) { - QQuickMaterialStyle::initGlobals(); } QString QtQuickControls2MaterialStylePlugin::name() const @@ -65,9 +67,12 @@ QString QtQuickControls2MaterialStylePlugin::name() const return QStringLiteral("Material"); } -void QtQuickControls2MaterialStylePlugin::initializeTheme(QQuickTheme *theme) +void QtQuickControls2MaterialStylePlugin::registerTypes(const char *uri) { - QQuickMaterialTheme::initialize(theme); + QQuickStylePlugin::registerTypes(uri); + + QQuickMaterialStyle::initGlobals(); + theme.initialize(QQuickTheme::instance()); } QT_END_NAMESPACE diff --git a/src/imports/controls/qtquickcontrols2plugin.cpp b/src/imports/controls/qtquickcontrols2plugin.cpp index 17eb2be9..ef642905 100644 --- a/src/imports/controls/qtquickcontrols2plugin.cpp +++ b/src/imports/controls/qtquickcontrols2plugin.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,8 @@ QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcQtQuickControlsStylePlugin, "qt.quick.controls.qtquickcontrols2plugin") + class QtQuickControls2Plugin : public QQmlExtensionPlugin { Q_OBJECT @@ -59,17 +62,41 @@ public: QtQuickControls2Plugin(QObject *parent = nullptr); ~QtQuickControls2Plugin(); - void initializeEngine(QQmlEngine *engine, const char *uri) override; void registerTypes(const char *uri) override; void unregisterTypes() override; private: - void init(); - - QList loadStylePlugins(); QQuickTheme *createTheme(const QString &name); + + bool registeredFallbackImport = false; }; +static const char *qtQuickControlsUri = "QtQuick.Controls"; + +QString styleUri() +{ + const QString style = QQuickStyle::name(); + if (!QQuickStylePrivate::isCustomStyle()) { + // The style set is a built-in style. + const QString styleName = QQuickStylePrivate::effectiveStyleName(style); + return QString::fromLatin1("QtQuick.Controls.%1").arg(styleName); + } + + // This is a custom style, so just use the name as the import uri. + QString styleName = style; + if (styleName.startsWith(QLatin1String(":/"))) + styleName.remove(0, 2); + return styleName; +} + +QString fallbackStyleUri() +{ + // The fallback style must be a built-in style, so we don't need to check for custom styles here. + const QString fallbackStyle = QQuickStylePrivate::fallbackStyle(); + const QString fallbackStyleName = QQuickStylePrivate::effectiveStyleName(fallbackStyle); + return QString::fromLatin1("QtQuick.Controls.%1").arg(fallbackStyleName); +} + QtQuickControls2Plugin::QtQuickControls2Plugin(QObject *parent) : QQmlExtensionPlugin(parent) { } @@ -80,88 +107,83 @@ QtQuickControls2Plugin::~QtQuickControls2Plugin() // initialization and cleanup, as plugins are not unloaded on macOS. } -void QtQuickControls2Plugin::initializeEngine(QQmlEngine *engine, const char */*uri*/) +void QtQuickControls2Plugin::registerTypes(const char *uri) { - engine->addUrlInterceptor(&QQuickStylePrivate::urlInterceptor); - init(); -} + qCDebug(lcQtQuickControlsStylePlugin) << "registerTypes() called with uri" << 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(); + + const QString styleName = QQuickStylePrivate::effectiveStyleName(QQuickStyle::name()); + const QString fallbackStyleName = QQuickStylePrivate::effectiveStyleName(QQuickStylePrivate::fallbackStyle()); + qCDebug(lcQtQuickControlsStylePlugin) << "style:" << QQuickStyle::name() << "effective style:" << styleName + << "fallback style:" << QQuickStylePrivate::fallbackStyle() << "effective fallback style:" << fallbackStyleName; + + createTheme(styleName); + + // If the style is Default, we don't need to register the fallback because the Default 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 Default. + if (styleName != fallbackStyleName && styleName != QLatin1String("Default")) { + const QString fallbackstyleUri = ::fallbackStyleUri(); + qCDebug(lcQtQuickControlsStylePlugin) << "calling qmlRegisterModuleImport() to register fallback style with" + << "uri \"" << qtQuickControlsUri << "\" moduleMajor" << QQmlModuleImportModuleAny << "import" << fallbackstyleUri + << "importMajor" << QQmlModuleImportAuto; + // The fallback style must be a built-in style, so we match the version number. + qmlRegisterModuleImport(qtQuickControlsUri, QQmlModuleImportModuleAny, fallbackstyleUri.toUtf8().constData(), + QQmlModuleImportAuto, QQmlModuleImportAuto); + registeredFallbackImport = true; + } -void QtQuickControls2Plugin::registerTypes(const char */*uri*/) -{ - QQuickStylePrivate::init(baseUrl()); + const QString styleUri = ::styleUri(); + // 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. + const int importMajor = !QQuickStylePrivate::isCustomStyle() ? QQmlModuleImportAuto : QQmlModuleImportLatest; + qCDebug(lcQtQuickControlsStylePlugin).nospace() << "calling qmlRegisterModuleImport() to register primary style with" + << " uri \"" << qtQuickControlsUri << "\" moduleMajor " << importMajor << " import " << styleUri + << " importMajor " << importMajor; + qmlRegisterModuleImport(qtQuickControlsUri, QQmlModuleImportModuleAny, styleUri.toUtf8().constData(), importMajor); const QString style = QQuickStyle::name(); if (!style.isEmpty()) - QFileSelectorPrivate::addStatics(QStringList() << style.toLower()); + QFileSelectorPrivate::addStatics(QStringList() << style); } void QtQuickControls2Plugin::unregisterTypes() { + qCDebug(lcQtQuickControlsStylePlugin) << "unregisterTypes() called"; + + if (registeredFallbackImport) { + const QString fallbackStyleUri = ::fallbackStyleUri(); + qmlUnregisterModuleImport(qtQuickControlsUri, QQmlModuleImportModuleAny, fallbackStyleUri.toUtf8().constData(), + QQmlModuleImportAuto, QQmlModuleImportAuto); + } + + const QString primary = QQuickStylePrivate::effectiveStyleName(QQuickStyle::name()); + const QString styleUri = ::styleUri(); + const int importMajor = !QQuickStylePrivate::isCustomStyle() ? QQmlModuleImportAuto : QQmlModuleImportLatest; + qmlUnregisterModuleImport(qtQuickControlsUri, QQmlModuleImportModuleAny, styleUri.toUtf8().constData(), importMajor); + QQuickStylePrivate::reset(); } -void QtQuickControls2Plugin::init() -{ - const QString style = QQuickStyle::name(); - QQuickTheme *theme = createTheme(style.isEmpty() ? QLatin1String("Default") : style); +/*! + \internal - // load the style's plugins to get access to its resources and initialize the theme - QList stylePlugins = loadStylePlugins(); - for (QQuickStylePlugin *stylePlugin : stylePlugins) - stylePlugin->initializeTheme(theme); - qDeleteAll(stylePlugins); -} + Responsible for setting the font and palette settings that were specified in the + qtquickcontrols2.conf file. -QList QtQuickControls2Plugin::loadStylePlugins() -{ - QList stylePlugins; - - QFileInfo fileInfo = QQmlFile::urlToLocalFileOrQrc(resolvedUrl(QStringLiteral("qmldir"))); - if (fileInfo.exists() && fileInfo.path() != QQmlFile::urlToLocalFileOrQrc(baseUrl())) { - QFile file(fileInfo.filePath()); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - QQmlDirParser parser; - parser.parse(QString::fromUtf8(file.readAll())); - if (!parser.hasError()) { -#ifdef QT_STATIC - const auto plugins = QPluginLoader::staticInstances(); - for (QObject *instance : plugins) { - QQuickStylePlugin *stylePlugin = qobject_cast(instance); - if (!stylePlugin || !parser.classNames().contains(QLatin1String(instance->metaObject()->className()))) - continue; - stylePlugins += stylePlugin; - } -#elif QT_CONFIG(library) - QPluginLoader loader; - const auto plugins = parser.plugins(); - for (const QQmlDirParser::Plugin &plugin : plugins) { - QDir dir = fileInfo.dir(); - if (!plugin.path.isEmpty() && !dir.cd(plugin.path)) - continue; - QString filePath = dir.filePath(plugin.name); -#if defined(Q_OS_MACOS) && defined(QT_DEBUG) - // Avoid mismatching plugins on macOS so that we don't end up loading both debug and - // release versions of the same Qt libraries (due to the plugin's dependencies). - filePath += QStringLiteral("_debug"); -#endif // Q_OS_MACOS && QT_DEBUG -#if defined(Q_OS_WIN) && defined(QT_DEBUG) - // Debug versions of plugins have a "d" prefix on Windows. - filePath += QLatin1Char('d'); -#endif // Q_OS_WIN && QT_DEBUG - loader.setFileName(filePath); - QQuickStylePlugin *stylePlugin = qobject_cast(loader.instance()); - if (stylePlugin) - stylePlugins += stylePlugin; - } -#endif - } - } - } - return stylePlugins; -} + Style-specific settings (e.g. Variant=Dense) are read in the constructor of the + appropriate style plugin (e.g. QtQuickControls2MaterialStylePlugin). + Implicit style-specific font and palette values are assigned in the relevant theme + (e.g. QQuickMaterialTheme). +*/ QQuickTheme *QtQuickControls2Plugin::createTheme(const QString &name) { + qCDebug(lcQtQuickControlsStylePlugin) << "creating QQuickTheme instance to be initialized by style-specific theme of" << name; + QQuickTheme *theme = new QQuickTheme; #if QT_CONFIG(settings) QQuickThemePrivate *p = QQuickThemePrivate::get(theme); diff --git a/src/imports/controls/universal/qmldir b/src/imports/controls/universal/qmldir index 28bd5706..e2faf020 100644 --- a/src/imports/controls/universal/qmldir +++ b/src/imports/controls/universal/qmldir @@ -1,7 +1,7 @@ module QtQuick.Controls.Universal plugin qtquickcontrols2universalstyleplugin classname QtQuickControls2UniversalStylePlugin -depends QtQuick.Controls 2.5 +import QtQuick.Controls.Default auto # QtQuick.Controls 2.0 (originally introduced in Qt 5.7) ApplicationWindow 2.0 ApplicationWindow.qml diff --git a/src/imports/controls/universal/qtquickcontrols2universalstyleplugin.cpp b/src/imports/controls/universal/qtquickcontrols2universalstyleplugin.cpp index ac154911..d6f7746d 100644 --- a/src/imports/controls/universal/qtquickcontrols2universalstyleplugin.cpp +++ b/src/imports/controls/universal/qtquickcontrols2universalstyleplugin.cpp @@ -34,11 +34,12 @@ ** ****************************************************************************/ -#include - #include "qquickuniversalstyle_p.h" #include "qquickuniversaltheme_p.h" +#include +#include + QT_BEGIN_NAMESPACE class QtQuickControls2UniversalStylePlugin : public QQuickStylePlugin @@ -50,12 +51,14 @@ public: QtQuickControls2UniversalStylePlugin(QObject *parent = nullptr); QString name() const override; - void initializeTheme(QQuickTheme *theme) override; + + void registerTypes(const char *uri) override; + + QQuickUniversalTheme theme; }; QtQuickControls2UniversalStylePlugin::QtQuickControls2UniversalStylePlugin(QObject *parent) : QQuickStylePlugin(parent) { - QQuickUniversalStyle::initGlobals(); } QString QtQuickControls2UniversalStylePlugin::name() const @@ -63,9 +66,12 @@ QString QtQuickControls2UniversalStylePlugin::name() const return QStringLiteral("Universal"); } -void QtQuickControls2UniversalStylePlugin::initializeTheme(QQuickTheme *theme) +void QtQuickControls2UniversalStylePlugin::registerTypes(const char *uri) { - QQuickUniversalTheme::initialize(theme); + QQuickStylePlugin::registerTypes(uri); + + QQuickUniversalStyle::initGlobals(); + theme.initialize(QQuickTheme::instance()); } QT_END_NAMESPACE diff --git a/src/quickcontrols2/qquickstyle.cpp b/src/quickcontrols2/qquickstyle.cpp index 836eb2f4..72078a0e 100644 --- a/src/quickcontrols2/qquickstyle.cpp +++ b/src/quickcontrols2/qquickstyle.cpp @@ -92,54 +92,23 @@ Q_LOGGING_CATEGORY(lcQtQuickControlsStyle, "qt.quick.controls.style") Qt Quick Controls. It is not possible to change the style after the QML types have been registered. - The style can also be specified as a path to a custom style, such as - \c ":/mystyle". See \l {Creating a Custom Style} for more details about - building custom styles. Custom styles do not need to implement all controls. - By default, the styling system uses the \l {Default style} as a fallback - for controls that a custom style does not provide. It is possible to - specify a different fallback style to customize or extend one of the - built-in styles. + To create your own custom style, see \l {Creating a Custom Style}. Custom + styles do not need to implement all controls. By default, the styling + system uses the \l {Default style} as a fallback for controls that a custom + style does not provide. It is possible to specify a different fallback + style to customize or extend one of the built-in styles. \code - QQuickStyle::setStyle(":/mystyle"); + QQuickStyle::setStyle("MyStyle"); QQuickStyle::setFallbackStyle("Material"); \endcode \sa {Styling Qt Quick Controls} */ -static QStringList envPathList(const QByteArray &var) -{ - QStringList paths; - if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty(var))) { - const QByteArray value = qgetenv(var); - paths += QString::fromLocal8Bit(value).split(QDir::listSeparator(), Qt::SkipEmptyParts); - } - return paths; -} - -static QStringList defaultImportPathList() -{ - QStringList importPaths; - importPaths.reserve(3); -#ifdef Q_OS_ANDROID - // androiddeployqt puts the QML files inside a resource file and they are not - // showing up in the Qml2ImportsPath as a result - importPaths += QStringLiteral(":/android_rcc_bundle/qml"); -#else -# ifndef QT_STATIC - importPaths += QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath); -# endif -#endif - importPaths += envPathList("QML2_IMPORT_PATH"); - importPaths += QStringLiteral(":/qt-project.org/imports"); - importPaths += QCoreApplication::applicationDirPath(); - return importPaths; -} - struct QQuickStyleSpec { - QQuickStyleSpec() : custom(false), resolved(false) { } + QQuickStyleSpec() { } QString name() { @@ -160,6 +129,13 @@ struct QQuickStyleSpec void setStyle(const QString &s) { + qCDebug(lcQtQuickControlsStyle) << "style" << s << "set on QQuickStyleSpec"; + if (s.contains(QLatin1Char('/'))) { + qWarning() << "Style names must not contain paths; see the \"Definition of a Style\" documentation for more information"; + return; + } + + qCDebug(lcQtQuickControlsStyle) << "clearing resolved flag and resolving"; style = s; resolved = false; resolve(); @@ -171,27 +147,9 @@ struct QQuickStyleSpec fallbackMethod = method; } - static QString findStyle(const QString &path, const QString &name) + void resolve() { - QDir dir(path); - if (!dir.exists()) - return QString(); - - if (name.isEmpty()) - return dir.absolutePath() + QLatin1Char('/'); - - const QStringList entries = dir.entryList(QStringList(), QDir::Dirs | QDir::NoDotAndDotDot); - for (const QString &entry : entries) { - if (entry.compare(name, Qt::CaseInsensitive) == 0) - return dir.absoluteFilePath(entry); - } - - return QString(); - } - - void resolve(const QUrl &baseUrl = QUrl()) - { - qCDebug(lcQtQuickControlsStyle) << "resolving style with baseUrl" << baseUrl; + qCDebug(lcQtQuickControlsStyle) << "resolving style"; if (style.isEmpty()) style = QGuiApplicationPrivate::styleOverride; @@ -211,53 +169,33 @@ struct QQuickStyleSpec } #endif - // resolve a path relative to the config - QString configPath = QFileInfo(resolveConfigFilePath()).path(); - QString stylePath = findStyle(configPath, style); - if (!stylePath.isEmpty()) { - style = stylePath; - resolved = true; + auto builtInStyleList = QQuickStylePrivate::builtInStyles(); + if (!fallbackStyle.isEmpty() && !builtInStyleList.contains(fallbackStyle)) { + qWarning().nospace().noquote() << fallbackMethod << ": the specified fallback style \"" << + fallbackStyle << "\" is not one of the built-in Qt Quick Controls 2 styles"; + fallbackStyle.clear(); } - custom = style.contains(QLatin1Char('/')); + // Find the config file. + resolveConfigFilePath(); - if (baseUrl.isValid()) { - QString path = QQmlFile::urlToLocalFileOrQrc(baseUrl); - QString stylePath = findStyle(path, style); - if (!stylePath.isEmpty()) { - style = stylePath; - resolved = true; - } - } + custom = !builtInStyleList.contains(QQuickStylePrivate::effectiveStyleName(style)); - if (QGuiApplication::instance()) { - if (!custom) { - const QStringList stylePaths = QQuickStylePrivate::stylePaths(); - for (const QString &path : stylePaths) { - QString stylePath = findStyle(path, style); - if (!stylePath.isEmpty()) { - custom = !stylePath.startsWith(QQmlFile::urlToLocalFileOrQrc(baseUrl)); - style = stylePath; - resolved = true; - break; - } - } - } - resolved = true; - } + resolved = true; qCDebug(lcQtQuickControlsStyle).nospace() << "done resolving:" + << "\n style=" << style << "\n custom=" << custom << "\n resolved=" << resolved - << "\n style=" << style << "\n fallbackStyle=" << fallbackStyle << "\n fallbackMethod=" << fallbackMethod - << "\n configFilePath=" << configFilePath - << "\n customStylePaths=" << customStylePaths; + << "\n configFilePath=" << configFilePath; } void reset() { + qCDebug(lcQtQuickControlsStyle) << "resetting values to their defaults"; + custom = false; resolved = false; style.clear(); @@ -281,10 +219,10 @@ struct QQuickStyleSpec } // Is this a custom style defined by the user and not "built-in" style? - bool custom; - // Did we manage to find a valid style path? - bool resolved; - // The full path to the style. + bool custom = false; + // Have we resolved the style yet? + bool resolved = false; + // The name of the style. QString style; // The built-in style to use if the requested style cannot be found. QString fallbackStyle; @@ -292,102 +230,13 @@ struct QQuickStyleSpec QByteArray fallbackMethod; // The path to the qtquickcontrols2.conf file. QString configFilePath; - // An extra list of directories where we search for available styles before any other directories. - QStringList customStylePaths; }; Q_GLOBAL_STATIC(QQuickStyleSpec, styleSpec) -static QStringList parseStylePathsWithColon(const QString &var) +QString QQuickStylePrivate::effectiveStyleName(const QString &styleName) { - QStringList paths; - const QChar colon = QLatin1Char(':'); - int currentIndex = 0; - - do { - int nextColonIndex = -1; - QString path; - - if (var.at(currentIndex) == colon) { - // This is either a list separator, or a qrc path. - if (var.at(currentIndex + 1) == colon) { - // It's a double colon (list separator followed by qrc path); - // find the end of the path. - nextColonIndex = var.indexOf(colon, currentIndex + 2); - path = var.mid(currentIndex + 1, - nextColonIndex == -1 ? -1 : nextColonIndex - currentIndex - 1); - } else { - // It's a single colon. - nextColonIndex = var.indexOf(colon, currentIndex + 1); - if (currentIndex == 0) { - // If we're at the start of the string, then it's a qrc path. - path = var.mid(currentIndex, - nextColonIndex == -1 ? -1 : nextColonIndex - currentIndex); - } else { - // Otherwise, it's a separator. - path = var.mid(currentIndex + 1, - nextColonIndex == -1 ? -1 : nextColonIndex - currentIndex - 1); - } - } - } else { - // It's a file path. - nextColonIndex = var.indexOf(colon, currentIndex); - path = var.mid(currentIndex, - nextColonIndex == -1 ? -1 : nextColonIndex - currentIndex); - } - - paths += path; - currentIndex = nextColonIndex; - - // Keep going until we can't find any more colons, - // or we're at the last character. - } while (currentIndex != -1 && currentIndex < var.size() - 1); - - return paths; -} - -QStringList QQuickStylePrivate::stylePaths(bool resolve) -{ - // user-requested style path - QStringList paths; - if (resolve) { - QString path = styleSpec()->path(); - if (path.endsWith(QLatin1Char('/'))) - path.chop(1); - if (!path.isEmpty()) - paths += path; - } - - if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE_PATH"))) { - const QString value = QString::fromLocal8Bit(qgetenv("QT_QUICK_CONTROLS_STYLE_PATH")); - const QChar listSeparator = QDir::listSeparator(); - if (listSeparator == QLatin1Char(':')) { - // Split manually to avoid breaking paths on systems where : is the list separator, - // since it's also used for qrc paths. - paths += parseStylePathsWithColon(value); - } else { - // Fast/simpler path for systems where something other than : is used as - // the list separator (such as ';'). - const QStringList customPaths = value.split(listSeparator, Qt::SkipEmptyParts); - paths += customPaths; - } - } - - // system/custom style paths - paths += styleSpec()->customStylePaths; - paths += envPathList("QT_QUICK_CONTROLS_STYLE_PATH"); - - // built-in import paths - const QString targetPath = QStringLiteral("QtQuick/Controls"); - const QStringList importPaths = defaultImportPathList(); - for (const QString &importPath : importPaths) { - QDir dir(importPath); - if (dir.cd(targetPath)) - paths += dir.absolutePath(); - } - - paths.removeDuplicates(); - return paths; + return !styleName.isEmpty() ? styleName : QLatin1String("Default"); } QString QQuickStylePrivate::fallbackStyle() @@ -400,27 +249,15 @@ bool QQuickStylePrivate::isCustomStyle() return styleSpec()->custom; } -void QQuickStylePrivate::init(const QUrl &baseUrl) +bool QQuickStylePrivate::isResolved() +{ + return styleSpec()->resolved; +} + +void QQuickStylePrivate::init() { QQuickStyleSpec *spec = styleSpec(); - spec->resolve(baseUrl); - - if (!spec->fallbackStyle.isEmpty()) { - QString fallbackStyle; - const QStringList stylePaths = QQuickStylePrivate::stylePaths(); - for (const QString &path : stylePaths) { - fallbackStyle = spec->findStyle(path, spec->fallbackStyle); - if (!fallbackStyle.isEmpty()) - break; - } - if (fallbackStyle.isEmpty()) { - if (spec->fallbackStyle.compare(QStringLiteral("Default")) != 0) { - qWarning() << "ERROR: unable to locate fallback style" << spec->fallbackStyle; - qInfo().nospace().noquote() << spec->fallbackMethod << ": the fallback style must be the name of one of the built-in Qt Quick Controls 2 styles."; - } - spec->fallbackStyle.clear(); - } - } + spec->resolve(); } void QQuickStylePrivate::reset() @@ -541,6 +378,12 @@ bool QQuickStylePrivate::isDarkSystemTheme() return dark; } +QStringList QQuickStylePrivate::builtInStyles() +{ + return { QLatin1String("Default"), QLatin1String("Fusion"), + QLatin1String("Imagine"), QLatin1String("Material"), QLatin1String("Universal") }; +} + /*! Returns the name of the application style. @@ -553,19 +396,6 @@ QString QQuickStyle::name() return styleSpec()->name(); } -/*! - Returns the path of an overridden application style, or an empty - string if the style is one of the built-in Qt Quick Controls 2 styles. - - \note The application style can be specified by passing a \c -style command - line argument. Therefore \c path() may not return a fully resolved - value if called before constructing a QGuiApplication. -*/ -QString QQuickStyle::path() -{ - return styleSpec()->path(); -} - /*! Sets the application style to \a style. @@ -612,88 +442,4 @@ void QQuickStyle::setFallbackStyle(const QString &style) styleSpec()->setFallbackStyle(style, "QQuickStyle::setFallbackStyle()"); } -/*! - \since 5.9 - Returns the names of the available styles. - - \note The method must be called \b after creating an instance of QGuiApplication. - - \sa stylePathList(), addStylePath() -*/ -QStringList QQuickStyle::availableStyles() -{ - QStringList styles; - if (!QGuiApplication::instance()) { - qWarning() << "ERROR: QQuickStyle::availableStyles() must be called after creating an instance of QGuiApplication."; - return styles; - } - - const QStringList stylePaths = QQuickStylePrivate::stylePaths(); - for (const QString &path : stylePaths) { - const QList entries = QDir(path).entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); - for (const QFileInfo &entry : entries) { - const QString name = entry.fileName(); - if (!name.endsWith(QLatin1String(".dSYM")) && name != QLatin1String("designer")) - styles += name; - } - } - styles.prepend(QStringLiteral("Default")); - styles.removeDuplicates(); - return styles; -} - -/*! - \since 5.12 - - Returns the list of directories where Qt Quick Controls 2 searches for available styles. - - By default, the list contains paths specified in the \c QT_QUICK_CONTROLS_STYLE_PATH - environment variable, and any existing \c QtQuick/Controls sub-directories in - \l QQmlEngine::importPathList(). - - \sa addStylePath(), availableStyles() -*/ -QStringList QQuickStyle::stylePathList() -{ - return QQuickStylePrivate::stylePaths(); -} - -/*! - \since 5.12 - - Adds \a path as a directory where Qt Quick Controls 2 searches for available styles. - - The \a path may be any local filesystem directory or \l {The Qt Resource System}{Qt Resource} directory. - For example, the following paths are all valid: - - \list - \li \c {/path/to/styles/} - \li \c {file:///path/to/styles/} - \li \c {:/path/to/styles/} - \li \c {qrc:/path/to/styles/}) - \endlist - - The \a path will be converted into \l {QDir::canonicalPath}{canonical form} before it is added to - the style path list. - - The newly added \a path will be first in the stylePathList(). - - \sa stylePathList(), availableStyles() -*/ -void QQuickStyle::addStylePath(const QString &path) -{ - if (path.isEmpty()) - return; - - const QUrl url = QUrl(path); - if (url.isRelative() || url.scheme() == QLatin1String("file") - || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path - styleSpec()->customStylePaths.prepend(QDir(path).canonicalPath()); - } else if (url.scheme() == QLatin1String("qrc")) { - styleSpec()->customStylePaths.prepend(QLatin1Char(':') + url.path()); - } else { - styleSpec()->customStylePaths.prepend(path); - } -} - QT_END_NAMESPACE diff --git a/src/quickcontrols2/qquickstyle.h b/src/quickcontrols2/qquickstyle.h index ce55b76b..e26ec90b 100644 --- a/src/quickcontrols2/qquickstyle.h +++ b/src/quickcontrols2/qquickstyle.h @@ -47,12 +47,8 @@ class Q_QUICKCONTROLS2_EXPORT QQuickStyle { public: static QString name(); - static QString path(); static void setStyle(const QString &style); static void setFallbackStyle(const QString &style); - static QStringList availableStyles(); - static QStringList stylePathList(); - static void addStylePath(const QString &path); }; QT_END_NAMESPACE diff --git a/src/quickcontrols2/qquickstyle_p.h b/src/quickcontrols2/qquickstyle_p.h index 7c837423..e2db35d7 100644 --- a/src/quickcontrols2/qquickstyle_p.h +++ b/src/quickcontrols2/qquickstyle_p.h @@ -48,7 +48,6 @@ // We mean it. // -#include #include #include @@ -59,16 +58,19 @@ class QSettings; class Q_QUICKCONTROLS2_EXPORT QQuickStylePrivate { public: - static QStringList stylePaths(bool resolve = false); + static QString effectiveStyleName(const QString &styleName); static QString fallbackStyle(); static bool isCustomStyle(); - static void init(const QUrl &baseUrl); + static bool isResolved(); + static bool exists(); + static void init(); static void reset(); static QString configFilePath(); static QSharedPointer settings(const QString &group = QString()); static const QFont *readFont(const QSharedPointer &settings); static const QPalette *readPalette(const QSharedPointer &settings); static bool isDarkSystemTheme(); + static QStringList builtInStyles(); }; QT_END_NAMESPACE diff --git a/src/quickcontrols2/qquickstyleplugin.cpp b/src/quickcontrols2/qquickstyleplugin.cpp index 18f0d485..1f1c3875 100644 --- a/src/quickcontrols2/qquickstyleplugin.cpp +++ b/src/quickcontrols2/qquickstyleplugin.cpp @@ -34,14 +34,20 @@ ** ****************************************************************************/ +#include "qquickstyle.h" #include "qquickstyle_p.h" #include "qquickstyleplugin_p.h" +#include #include #include +#include + QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcStylePlugin, "qt.quick.controls.styleplugin") + QQuickStylePlugin::QQuickStylePlugin(QObject *parent) : QQmlExtensionPlugin(parent) { @@ -56,4 +62,14 @@ QString QQuickStylePlugin::name() const return QString(); } +void QQuickStylePlugin::registerTypes(const char *uri) +{ + qCDebug(lcStylePlugin).nospace() << "registerTypes called with uri " << uri << "; plugin name is " << name(); +} + +void QQuickStylePlugin::unregisterTypes() +{ + qCDebug(lcStylePlugin) << "unregisterTypes called; plugin name is" << name(); +} + QT_END_NAMESPACE diff --git a/src/quickcontrols2/qquickstyleplugin_p.h b/src/quickcontrols2/qquickstyleplugin_p.h index 7ae129d9..37d070db 100644 --- a/src/quickcontrols2/qquickstyleplugin_p.h +++ b/src/quickcontrols2/qquickstyleplugin_p.h @@ -64,9 +64,9 @@ public: ~QQuickStylePlugin(); virtual QString name() const; - virtual void initializeTheme(QQuickTheme *theme) = 0; void registerTypes(const char *uri) override; + void unregisterTypes() override; private: Q_DISABLE_COPY(QQuickStylePlugin) diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index e41723ea..28d00c2a 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -29,4 +29,5 @@ SUBDIRS += \ revisions \ sanity \ snippets \ + styleimports \ translation diff --git a/tests/auto/customization/customization.pro b/tests/auto/customization/customization.pro index 472367a3..e40e4d05 100644 --- a/tests/auto/customization/customization.pro +++ b/tests/auto/customization/customization.pro @@ -4,7 +4,7 @@ SOURCES += tst_customization.cpp macos:CONFIG -= app_bundle -QT += core-private gui-private qml-private quick-private testlib quicktemplates2-private quickcontrols2 +QT += core-private gui-private qml-private quick-private testlib quicktemplates2-private quickcontrols2 quickcontrols2-private include (../shared/util.pri) diff --git a/tests/auto/customization/data/styles/empty/qmldir b/tests/auto/customization/data/styles/empty/qmldir new file mode 100644 index 00000000..a5fcc4c7 --- /dev/null +++ b/tests/auto/customization/data/styles/empty/qmldir @@ -0,0 +1,59 @@ +module empty + +AbstractButton 6.0 AbstractButton.qml +Action 6.0 Action.qml +ActionGroup 6.0 ActionGroup.qml +ApplicationWindow 6.0 ApplicationWindow.qml +BusyIndicator 6.0 BusyIndicator.qml +Button 6.0 Button.qml +ButtonGroup 6.0 ButtonGroup.qml +CheckBox 6.0 CheckBox.qml +CheckDelegate 6.0 CheckDelegate.qml +ComboBox 6.0 ComboBox.qml +Container 6.0 Container.qml +Control 6.0 Control.qml +DelayButton 6.0 DelayButton.qml +Dial 6.0 Dial.qml +Dialog 6.0 Dialog.qml +DialogButtonBox 6.0 DialogButtonBox.qml +Drawer 6.0 Drawer.qml +Frame 6.0 Frame.qml +GroupBox 6.0 GroupBox.qml +HorizontalHeaderView 6.0 HorizontalHeaderView.qml +ItemDelegate 6.0 ItemDelegate.qml +Label 6.0 Label.qml +Menu 6.0 Menu.qml +MenuBar 6.0 MenuBar.qml +MenuBarItem 6.0 MenuBarItem.qml +MenuItem 6.0 MenuItem.qml +MenuSeparator 6.0 MenuSeparator.qml +Page 6.0 Page.qml +PageIndicator 6.0 PageIndicator.qml +Pane 6.0 Pane.qml +Popup 6.0 Popup.qml +ProgressBar 6.0 ProgressBar.qml +RadioButton 6.0 RadioButton.qml +RadioDelegate 6.0 RadioDelegate.qml +RangeSlider 6.0 RangeSlider.qml +RoundButton 6.0 RoundButton.qml +ScrollBar 6.0 ScrollBar.qml +ScrollIndicator 6.0 ScrollIndicator.qml +ScrollView 6.0 ScrollView.qml +Slider 6.0 Slider.qml +SpinBox 6.0 SpinBox.qml +SplitView 6.0 SplitView.qml +StackView 6.0 StackView.qml +SwipeDelegate 6.0 SwipeDelegate.qml +SwipeView 6.0 SwipeView.qml +Switch 6.0 Switch.qml +SwitchDelegate 6.0 SwitchDelegate.qml +TabBar 6.0 TabBar.qml +TabButton 6.0 TabButton.qml +TextArea 6.0 TextArea.qml +TextField 6.0 TextField.qml +ToolBar 6.0 ToolBar.qml +ToolButton 6.0 ToolButton.qml +ToolSeparator 6.0 ToolSeparator.qml +ToolTip 6.0 ToolTip.qml +Tumbler 6.0 Tumbler.qml +VerticalHeaderView 6.0 VerticalHeaderView.qml diff --git a/tests/auto/customization/data/styles/identified/qmldir b/tests/auto/customization/data/styles/identified/qmldir new file mode 100644 index 00000000..1ee2a92e --- /dev/null +++ b/tests/auto/customization/data/styles/identified/qmldir @@ -0,0 +1,59 @@ +module identified + +AbstractButton 6.0 AbstractButton.qml +Action 6.0 Action.qml +ActionGroup 6.0 ActionGroup.qml +ApplicationWindow 6.0 ApplicationWindow.qml +BusyIndicator 6.0 BusyIndicator.qml +Button 6.0 Button.qml +ButtonGroup 6.0 ButtonGroup.qml +CheckBox 6.0 CheckBox.qml +CheckDelegate 6.0 CheckDelegate.qml +ComboBox 6.0 ComboBox.qml +Container 6.0 Container.qml +Control 6.0 Control.qml +DelayButton 6.0 DelayButton.qml +Dial 6.0 Dial.qml +Dialog 6.0 Dialog.qml +DialogButtonBox 6.0 DialogButtonBox.qml +Drawer 6.0 Drawer.qml +Frame 6.0 Frame.qml +GroupBox 6.0 GroupBox.qml +HorizontalHeaderView 6.0 HorizontalHeaderView.qml +ItemDelegate 6.0 ItemDelegate.qml +Label 6.0 Label.qml +Menu 6.0 Menu.qml +MenuBar 6.0 MenuBar.qml +MenuBarItem 6.0 MenuBarItem.qml +MenuItem 6.0 MenuItem.qml +MenuSeparator 6.0 MenuSeparator.qml +Page 6.0 Page.qml +PageIndicator 6.0 PageIndicator.qml +Pane 6.0 Pane.qml +Popup 6.0 Popup.qml +ProgressBar 6.0 ProgressBar.qml +RadioButton 6.0 RadioButton.qml +RadioDelegate 6.0 RadioDelegate.qml +RangeSlider 6.0 RangeSlider.qml +RoundButton 6.0 RoundButton.qml +ScrollBar 6.0 ScrollBar.qml +ScrollIndicator 6.0 ScrollIndicator.qml +ScrollView 6.0 ScrollView.qml +Slider 6.0 Slider.qml +SpinBox 6.0 SpinBox.qml +SplitView 6.0 SplitView.qml +StackView 6.0 StackView.qml +SwipeDelegate 6.0 SwipeDelegate.qml +SwipeView 6.0 SwipeView.qml +Switch 6.0 Switch.qml +SwitchDelegate 6.0 SwitchDelegate.qml +TabBar 6.0 TabBar.qml +TabButton 6.0 TabButton.qml +TextArea 6.0 TextArea.qml +TextField 6.0 TextField.qml +ToolBar 6.0 ToolBar.qml +ToolButton 6.0 ToolButton.qml +ToolSeparator 6.0 ToolSeparator.qml +ToolTip 6.0 ToolTip.qml +Tumbler 6.0 Tumbler.qml +VerticalHeaderView 6.0 VerticalHeaderView.qml diff --git a/tests/auto/customization/data/styles/incomplete/qmldir b/tests/auto/customization/data/styles/incomplete/qmldir new file mode 100644 index 00000000..e8813bd5 --- /dev/null +++ b/tests/auto/customization/data/styles/incomplete/qmldir @@ -0,0 +1,59 @@ +module incomplete + +AbstractButton 6.0 AbstractButton.qml +Action 6.0 Action.qml +ActionGroup 6.0 ActionGroup.qml +ApplicationWindow 6.0 ApplicationWindow.qml +BusyIndicator 6.0 BusyIndicator.qml +Button 6.0 Button.qml +ButtonGroup 6.0 ButtonGroup.qml +CheckBox 6.0 CheckBox.qml +CheckDelegate 6.0 CheckDelegate.qml +ComboBox 6.0 ComboBox.qml +Container 6.0 Container.qml +Control 6.0 Control.qml +DelayButton 6.0 DelayButton.qml +Dial 6.0 Dial.qml +Dialog 6.0 Dialog.qml +DialogButtonBox 6.0 DialogButtonBox.qml +Drawer 6.0 Drawer.qml +Frame 6.0 Frame.qml +GroupBox 6.0 GroupBox.qml +HorizontalHeaderView 6.0 HorizontalHeaderView.qml +ItemDelegate 6.0 ItemDelegate.qml +Label 6.0 Label.qml +Menu 6.0 Menu.qml +MenuBar 6.0 MenuBar.qml +MenuBarItem 6.0 MenuBarItem.qml +MenuItem 6.0 MenuItem.qml +MenuSeparator 6.0 MenuSeparator.qml +Page 6.0 Page.qml +PageIndicator 6.0 PageIndicator.qml +Pane 6.0 Pane.qml +Popup 6.0 Popup.qml +ProgressBar 6.0 ProgressBar.qml +RadioButton 6.0 RadioButton.qml +RadioDelegate 6.0 RadioDelegate.qml +RangeSlider 6.0 RangeSlider.qml +RoundButton 6.0 RoundButton.qml +ScrollBar 6.0 ScrollBar.qml +ScrollIndicator 6.0 ScrollIndicator.qml +ScrollView 6.0 ScrollView.qml +Slider 6.0 Slider.qml +SpinBox 6.0 SpinBox.qml +SplitView 6.0 SplitView.qml +StackView 6.0 StackView.qml +SwipeDelegate 6.0 SwipeDelegate.qml +SwipeView 6.0 SwipeView.qml +Switch 6.0 Switch.qml +SwitchDelegate 6.0 SwitchDelegate.qml +TabBar 6.0 TabBar.qml +TabButton 6.0 TabButton.qml +TextArea 6.0 TextArea.qml +TextField 6.0 TextField.qml +ToolBar 6.0 ToolBar.qml +ToolButton 6.0 ToolButton.qml +ToolSeparator 6.0 ToolSeparator.qml +ToolTip 6.0 ToolTip.qml +Tumbler 6.0 Tumbler.qml +VerticalHeaderView 6.0 VerticalHeaderView.qml diff --git a/tests/auto/customization/data/styles/override/qmldir b/tests/auto/customization/data/styles/override/qmldir new file mode 100644 index 00000000..f15ba96a --- /dev/null +++ b/tests/auto/customization/data/styles/override/qmldir @@ -0,0 +1,59 @@ +module override + +AbstractButton 6.0 AbstractButton.qml +Action 6.0 Action.qml +ActionGroup 6.0 ActionGroup.qml +ApplicationWindow 6.0 ApplicationWindow.qml +BusyIndicator 6.0 BusyIndicator.qml +Button 6.0 Button.qml +ButtonGroup 6.0 ButtonGroup.qml +CheckBox 6.0 CheckBox.qml +CheckDelegate 6.0 CheckDelegate.qml +ComboBox 6.0 ComboBox.qml +Container 6.0 Container.qml +Control 6.0 Control.qml +DelayButton 6.0 DelayButton.qml +Dial 6.0 Dial.qml +Dialog 6.0 Dialog.qml +DialogButtonBox 6.0 DialogButtonBox.qml +Drawer 6.0 Drawer.qml +Frame 6.0 Frame.qml +GroupBox 6.0 GroupBox.qml +HorizontalHeaderView 6.0 HorizontalHeaderView.qml +ItemDelegate 6.0 ItemDelegate.qml +Label 6.0 Label.qml +Menu 6.0 Menu.qml +MenuBar 6.0 MenuBar.qml +MenuBarItem 6.0 MenuBarItem.qml +MenuItem 6.0 MenuItem.qml +MenuSeparator 6.0 MenuSeparator.qml +Page 6.0 Page.qml +PageIndicator 6.0 PageIndicator.qml +Pane 6.0 Pane.qml +Popup 6.0 Popup.qml +ProgressBar 6.0 ProgressBar.qml +RadioButton 6.0 RadioButton.qml +RadioDelegate 6.0 RadioDelegate.qml +RangeSlider 6.0 RangeSlider.qml +RoundButton 6.0 RoundButton.qml +ScrollBar 6.0 ScrollBar.qml +ScrollIndicator 6.0 ScrollIndicator.qml +ScrollView 6.0 ScrollView.qml +Slider 6.0 Slider.qml +SpinBox 6.0 SpinBox.qml +SplitView 6.0 SplitView.qml +StackView 6.0 StackView.qml +SwipeDelegate 6.0 SwipeDelegate.qml +SwipeView 6.0 SwipeView.qml +Switch 6.0 Switch.qml +SwitchDelegate 6.0 SwitchDelegate.qml +TabBar 6.0 TabBar.qml +TabButton 6.0 TabButton.qml +TextArea 6.0 TextArea.qml +TextField 6.0 TextField.qml +ToolBar 6.0 ToolBar.qml +ToolButton 6.0 ToolButton.qml +ToolSeparator 6.0 ToolSeparator.qml +ToolTip 6.0 ToolTip.qml +Tumbler 6.0 Tumbler.qml +VerticalHeaderView 6.0 VerticalHeaderView.qml diff --git a/tests/auto/customization/data/styles/simple/qmldir b/tests/auto/customization/data/styles/simple/qmldir new file mode 100644 index 00000000..e5159885 --- /dev/null +++ b/tests/auto/customization/data/styles/simple/qmldir @@ -0,0 +1,59 @@ +module simple + +AbstractButton 6.0 AbstractButton.qml +Action 6.0 Action.qml +ActionGroup 6.0 ActionGroup.qml +ApplicationWindow 6.0 ApplicationWindow.qml +BusyIndicator 6.0 BusyIndicator.qml +Button 6.0 Button.qml +ButtonGroup 6.0 ButtonGroup.qml +CheckBox 6.0 CheckBox.qml +CheckDelegate 6.0 CheckDelegate.qml +ComboBox 6.0 ComboBox.qml +Container 6.0 Container.qml +Control 6.0 Control.qml +DelayButton 6.0 DelayButton.qml +Dial 6.0 Dial.qml +Dialog 6.0 Dialog.qml +DialogButtonBox 6.0 DialogButtonBox.qml +Drawer 6.0 Drawer.qml +Frame 6.0 Frame.qml +GroupBox 6.0 GroupBox.qml +HorizontalHeaderView 6.0 HorizontalHeaderView.qml +ItemDelegate 6.0 ItemDelegate.qml +Label 6.0 Label.qml +Menu 6.0 Menu.qml +MenuBar 6.0 MenuBar.qml +MenuBarItem 6.0 MenuBarItem.qml +MenuItem 6.0 MenuItem.qml +MenuSeparator 6.0 MenuSeparator.qml +Page 6.0 Page.qml +PageIndicator 6.0 PageIndicator.qml +Pane 6.0 Pane.qml +Popup 6.0 Popup.qml +ProgressBar 6.0 ProgressBar.qml +RadioButton 6.0 RadioButton.qml +RadioDelegate 6.0 RadioDelegate.qml +RangeSlider 6.0 RangeSlider.qml +RoundButton 6.0 RoundButton.qml +ScrollBar 6.0 ScrollBar.qml +ScrollIndicator 6.0 ScrollIndicator.qml +ScrollView 6.0 ScrollView.qml +Slider 6.0 Slider.qml +SpinBox 6.0 SpinBox.qml +SplitView 6.0 SplitView.qml +StackView 6.0 StackView.qml +SwipeDelegate 6.0 SwipeDelegate.qml +SwipeView 6.0 SwipeView.qml +Switch 6.0 Switch.qml +SwitchDelegate 6.0 SwitchDelegate.qml +TabBar 6.0 TabBar.qml +TabButton 6.0 TabButton.qml +TextArea 6.0 TextArea.qml +TextField 6.0 TextField.qml +ToolBar 6.0 ToolBar.qml +ToolButton 6.0 ToolButton.qml +ToolSeparator 6.0 ToolSeparator.qml +ToolTip 6.0 ToolTip.qml +Tumbler 6.0 Tumbler.qml +VerticalHeaderView 6.0 VerticalHeaderView.qml diff --git a/tests/auto/customization/tst_customization.cpp b/tests/auto/customization/tst_customization.cpp index 6cb3d82c..add48cc7 100644 --- a/tests/auto/customization/tst_customization.cpp +++ b/tests/auto/customization/tst_customization.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include "../shared/visualtestutil.h" @@ -227,6 +228,7 @@ void tst_customization::cleanupTestCase() void tst_customization::init() { engine = new QQmlEngine(this); + engine->addImportPath(testFile("styles")); qtHookData[QHooks::AddQObject] = reinterpret_cast(&qt_addQObject); qtHookData[QHooks::RemoveQObject] = reinterpret_cast(&qt_removeQObject); @@ -296,7 +298,7 @@ void tst_customization::creation() QFETCH(QString, type); QFETCH(QStringList, delegates); - QQuickStyle::setStyle(testFile("styles/" + style)); + QQuickStyle::setStyle(style); QString error; QScopedPointer control(createControl(type, "", &error)); @@ -363,7 +365,7 @@ void tst_customization::override_data() #ifndef Q_OS_MACOS // QTBUG-65671 // test that the built-in styles don't have undesired IDs in their delegates - const QStringList styles = QStringList() << "Default" << "Fusion" << "Material" << "Universal"; // ### TODO: QQuickStyle::availableStyles(); + const QStringList styles = QQuickStylePrivate::builtInStyles(); for (const QString &style : styles) { for (const ControlInfo &control : ControlInfos) QTest::newRow(qPrintable(style + ":" + control.type)) << style << control.type << control.delegates << "" << false; @@ -380,11 +382,7 @@ void tst_customization::override() QFETCH(QString, nonDeferred); QFETCH(bool, identify); - const QString testStyle = testFile("styles/" + style); - if (QDir(testStyle).exists()) - QQuickStyle::setStyle(testStyle); - else - QQuickStyle::setStyle(style); + QQuickStyle::setStyle(style); QString qml; qml += QString("objectName: '%1-%2-override'; ").arg(type.toLower()).arg(style); @@ -478,7 +476,7 @@ void tst_customization::override() void tst_customization::comboPopup() { - QQuickStyle::setStyle(testFile("styles/simple")); + QQuickStyle::setStyle("simple"); { // test that ComboBox::popup is created when accessed diff --git a/tests/auto/qquickstyle/data/CmdLineArgStyle/Control.qml b/tests/auto/qquickstyle/data/CmdLineArgStyle/Control.qml new file mode 100644 index 00000000..744dcd8f --- /dev/null +++ b/tests/auto/qquickstyle/data/CmdLineArgStyle/Control.qml @@ -0,0 +1,2 @@ +import QtQuick.Templates as T +T.Label {} diff --git a/tests/auto/qquickstyle/data/CmdLineArgStyle/qmldir b/tests/auto/qquickstyle/data/CmdLineArgStyle/qmldir new file mode 100644 index 00000000..c2cc85c2 --- /dev/null +++ b/tests/auto/qquickstyle/data/CmdLineArgStyle/qmldir @@ -0,0 +1,2 @@ +module CmdLineArgStyle +Control 1.0 Control.qml diff --git a/tests/auto/qquickstyle/data/Custom/Label.qml b/tests/auto/qquickstyle/data/Custom/Label.qml new file mode 100644 index 00000000..744dcd8f --- /dev/null +++ b/tests/auto/qquickstyle/data/Custom/Label.qml @@ -0,0 +1,2 @@ +import QtQuick.Templates as T +T.Label {} diff --git a/tests/auto/qquickstyle/data/Custom/qmldir b/tests/auto/qquickstyle/data/Custom/qmldir new file mode 100644 index 00000000..08de6fa8 --- /dev/null +++ b/tests/auto/qquickstyle/data/Custom/qmldir @@ -0,0 +1,2 @@ +module Custom +Label 1.0 Label.qml diff --git a/tests/auto/qquickstyle/data/EnvVarFallbackStyle/Control.qml b/tests/auto/qquickstyle/data/EnvVarFallbackStyle/Control.qml new file mode 100644 index 00000000..744dcd8f --- /dev/null +++ b/tests/auto/qquickstyle/data/EnvVarFallbackStyle/Control.qml @@ -0,0 +1,2 @@ +import QtQuick.Templates as T +T.Label {} diff --git a/tests/auto/qquickstyle/data/EnvVarFallbackStyle/qmldir b/tests/auto/qquickstyle/data/EnvVarFallbackStyle/qmldir new file mode 100644 index 00000000..bebd8e07 --- /dev/null +++ b/tests/auto/qquickstyle/data/EnvVarFallbackStyle/qmldir @@ -0,0 +1,2 @@ +module EnvVarFallbackStyle +Control 1.0 Control.qml diff --git a/tests/auto/qquickstyle/data/EnvVarStyle/Control.qml b/tests/auto/qquickstyle/data/EnvVarStyle/Control.qml new file mode 100644 index 00000000..744dcd8f --- /dev/null +++ b/tests/auto/qquickstyle/data/EnvVarStyle/Control.qml @@ -0,0 +1,2 @@ +import QtQuick.Templates as T +T.Label {} diff --git a/tests/auto/qquickstyle/data/EnvVarStyle/qmldir b/tests/auto/qquickstyle/data/EnvVarStyle/qmldir new file mode 100644 index 00000000..42223a19 --- /dev/null +++ b/tests/auto/qquickstyle/data/EnvVarStyle/qmldir @@ -0,0 +1,2 @@ +module EnvVarStyle +Control 1.0 Control.qml diff --git a/tests/auto/qquickstyle/data/custom.conf b/tests/auto/qquickstyle/data/custom.conf index 2230b452..ac79338c 100644 --- a/tests/auto/qquickstyle/data/custom.conf +++ b/tests/auto/qquickstyle/data/custom.conf @@ -1,5 +1,5 @@ [Controls] -Style=:/Custom +Style=Custom [Custom] Font\PixelSize=3 diff --git a/tests/auto/qquickstyle/qquickstyle.pro b/tests/auto/qquickstyle/qquickstyle.pro index ef1dd2e0..eed434b5 100644 --- a/tests/auto/qquickstyle/qquickstyle.pro +++ b/tests/auto/qquickstyle/qquickstyle.pro @@ -11,18 +11,16 @@ include (../shared/util.pri) TESTDATA = $$PWD/data/* -qrcStyles1.files = $$files(qrcStyles1/QrcStyle1/*.qml) -qrcStyles1.prefix = / -RESOURCES += qrcStyles1 - -qrcStyles2.files = $$files(qrcStyles2/QrcStyle2/*.qml) -qrcStyles2.prefix = / -RESOURCES += qrcStyles2 - -qrcStyles3.files = $$files(qrcStyles3/QrcStyle3/*.qml) -qrcStyles3.prefix = / -RESOURCES += qrcStyles3 - -qrcStyles4.files = $$files(qrcStyles4/QrcStyle4/*.qml) -qrcStyles4.prefix = / -RESOURCES += qrcStyles4 +OTHER_FILES += \ + data/CmdLineArgStyle/Control.qml \ + data/CmdLineArgStyle/qmldir \ + data/EnvVarStyle/Control.qml \ + data/EnvVarStyle/qmldir \ + data/EnvVarFallbackStyle/Control.qml \ + data/EnvVarFallbackStyle/qmldir + +custom.files = \ + data/Custom/Label.qml \ + data/Custom/qmldir +custom.prefix = / +RESOURCES += custom diff --git a/tests/auto/qquickstyle/qrcStyles1/QrcStyle1/Button.qml b/tests/auto/qquickstyle/qrcStyles1/QrcStyle1/Button.qml deleted file mode 100644 index dcacc011..00000000 --- a/tests/auto/qquickstyle/qrcStyles1/QrcStyle1/Button.qml +++ /dev/null @@ -1,2 +0,0 @@ -import QtQuick.Templates as T -T.Button { } diff --git a/tests/auto/qquickstyle/qrcStyles2/QrcStyle2/Button.qml b/tests/auto/qquickstyle/qrcStyles2/QrcStyle2/Button.qml deleted file mode 100644 index dcacc011..00000000 --- a/tests/auto/qquickstyle/qrcStyles2/QrcStyle2/Button.qml +++ /dev/null @@ -1,2 +0,0 @@ -import QtQuick.Templates as T -T.Button { } diff --git a/tests/auto/qquickstyle/qrcStyles3/QrcStyle3/Button.qml b/tests/auto/qquickstyle/qrcStyles3/QrcStyle3/Button.qml deleted file mode 100644 index dcacc011..00000000 --- a/tests/auto/qquickstyle/qrcStyles3/QrcStyle3/Button.qml +++ /dev/null @@ -1,2 +0,0 @@ -import QtQuick.Templates as T -T.Button { } diff --git a/tests/auto/qquickstyle/qrcStyles4/QrcStyle4/Button.qml b/tests/auto/qquickstyle/qrcStyles4/QrcStyle4/Button.qml deleted file mode 100644 index dcacc011..00000000 --- a/tests/auto/qquickstyle/qrcStyles4/QrcStyle4/Button.qml +++ /dev/null @@ -1,2 +0,0 @@ -import QtQuick.Templates as T -T.Button { } diff --git a/tests/auto/qquickstyle/tst_qquickstyle.cpp b/tests/auto/qquickstyle/tst_qquickstyle.cpp index 48cc88b7..e675d335 100644 --- a/tests/auto/qquickstyle/tst_qquickstyle.cpp +++ b/tests/auto/qquickstyle/tst_qquickstyle.cpp @@ -57,11 +57,6 @@ private slots: void configurationFile(); void commandLineArgument(); void environmentVariables(); - void availableStyles(); - void qrcStylePaths_data(); - void qrcStylePaths(); - void qrcInQtQuickControlsStylePathEnvVar_data(); - void qrcInQtQuickControlsStylePathEnvVar(); private: void loadControls(); @@ -74,7 +69,6 @@ void tst_QQuickStyle::cleanup() QGuiApplicationPrivate::styleOverride.clear(); qunsetenv("QT_QUICK_CONTROLS_STYLE"); - qunsetenv("QT_QUICK_CONTROLS_STYLE_PATH"); qunsetenv("QT_QUICK_CONTROLS_FALLBACK_STYLE"); qunsetenv("QT_QUICK_CONTROLS_CONF"); } @@ -82,6 +76,7 @@ void tst_QQuickStyle::cleanup() void tst_QQuickStyle::loadControls() { QQmlEngine engine; + engine.addImportPath(dataDirectory()); QQmlComponent component(&engine); component.setData("import QtQuick 2.0; import QtQuick.Controls 2.1; Control { }", QUrl()); @@ -97,11 +92,9 @@ void tst_QQuickStyle::unloadControls() void tst_QQuickStyle::lookup() { QVERIFY(QQuickStyle::name().isEmpty()); - QVERIFY(!QQuickStyle::path().isEmpty()); - QQuickStyle::setStyle("material"); + QQuickStyle::setStyle("Material"); QCOMPARE(QQuickStyle::name(), QString("Material")); - QVERIFY(!QQuickStyle::path().isEmpty()); loadControls(); @@ -110,7 +103,6 @@ void tst_QQuickStyle::lookup() QCOMPARE(QQuickTheme::instance()->font(QQuickTheme::TextArea).pixelSize(), 16); QCOMPARE(QQuickStyle::name(), QString("Material")); - QVERIFY(!QQuickStyle::path().isEmpty()); } void tst_QQuickStyle::configurationFile_data() @@ -137,6 +129,7 @@ void tst_QQuickStyle::configurationFile() // Load a control. The import causes the configuration file to be read. QQmlEngine engine; + engine.addImportPath(":/data"); QQmlComponent labelComponent(&engine); labelComponent.setData("import QtQuick 2.0; import QtQuick.Controls 2.12; Label {}", QUrl()); @@ -144,8 +137,6 @@ void tst_QQuickStyle::configurationFile() QVERIFY2(!object.isNull(), qPrintable(labelComponent.errorString())); QCOMPARE(QQuickStyle::name(), expectedStyle); - if (!expectedPath.isEmpty()) - QCOMPARE(QQuickStyle::path(), expectedPath); // Test that fonts and palettes specified in configuration files are respected. QQuickLabel *label = qobject_cast(object.data()); @@ -169,179 +160,10 @@ void tst_QQuickStyle::environmentVariables() { qputenv("QT_QUICK_CONTROLS_STYLE", "EnvVarStyle"); qputenv("QT_QUICK_CONTROLS_FALLBACK_STYLE", "EnvVarFallbackStyle"); + QTest::ignoreMessage(QtWarningMsg, "QT_QUICK_CONTROLS_FALLBACK_STYLE: the specified fallback style" \ + " \"EnvVarFallbackStyle\" is not one of the built-in Qt Quick Controls 2 styles"); QCOMPARE(QQuickStyle::name(), QString("EnvVarStyle")); - QCOMPARE(QQuickStylePrivate::fallbackStyle(), QString("EnvVarFallbackStyle")); -} - -void tst_QQuickStyle::availableStyles() -{ - QString path = QFINDTESTDATA("data"); - QVERIFY(!path.isEmpty()); - - QQuickStyle::addStylePath(path); - QStringList paths = QQuickStylePrivate::stylePaths(); -#ifndef Q_OS_WIN - QVERIFY(paths.contains(path)); -#else - QVERIFY(paths.contains(path, Qt::CaseInsensitive)); -#endif - - const QStringList styles = QQuickStyle::availableStyles(); - QVERIFY(!styles.isEmpty()); - QCOMPARE(styles.first(), QString("Default")); - QVERIFY(!styles.contains("designer")); - - // QTBUG-60973 - for (const QString &style : styles) { - QVERIFY2(!style.endsWith(".dSYM"), qPrintable(style)); - } -} - -void tst_QQuickStyle::qrcStylePaths_data() -{ - QTest::addColumn("stylePath"); - QTest::addColumn("expectedStyleName"); - - QTest::addRow("qrc:/qrcStyles1") << QString::fromLatin1("qrc:/qrcStyles1") << QString::fromLatin1("QrcStyle1"); - QTest::addRow(":/qrcStyles2") << QString::fromLatin1(":/qrcStyles2") << QString::fromLatin1("QrcStyle2"); -} - -void tst_QQuickStyle::qrcStylePaths() -{ - QFETCH(QString, stylePath); - QFETCH(QString, expectedStyleName); - - QQuickStyle::addStylePath(stylePath); - - const QStringList paths = QQuickStylePrivate::stylePaths(); - QString expectedStylePath = stylePath; - if (expectedStylePath.startsWith(QLatin1String("qrc"))) - expectedStylePath.remove(0, 3); - if (!paths.contains(expectedStylePath)) { - QString message; - QDebug stream(&message); - stream.nospace() << "QQuickStylePrivate::stylePaths() doesn't contain " << expectedStylePath << ":\n" << paths; - QFAIL(qPrintable(message)); - } - - const QStringList styles = QQuickStyle::availableStyles(); - QVERIFY(!styles.isEmpty()); - if (!styles.contains(expectedStyleName)) { - QString message; - QDebug stream(&message); - stream.nospace() << "QQuickStyle::availableStyles() doesn't contain " << expectedStyleName << ":\n" << styles; - QFAIL(qPrintable(message)); - } -} - -void tst_QQuickStyle::qrcInQtQuickControlsStylePathEnvVar_data() -{ - QTest::addColumn("environmentVariable"); - QTest::addColumn("expectedAvailableStyles"); - - const QChar listSeparator = QDir::listSeparator(); - const QStringList defaultAvailableStyles = QQuickStyle::availableStyles(); - - { - QString environmentVariable; - QDebug stream(&environmentVariable); - // We use qrcStyles3 and qrcStyles4 in order to not conflict with - // qrcStylePaths(), since we currently have no way of clearing customStylePaths. - stream.noquote().nospace() << "/some/bogus/path/" << listSeparator - << ":/qrcStyles3"; - - QStringList expectedAvailableStyles = defaultAvailableStyles; - // We need to keep the Default style at the start of the list, - // as that's what availableStyles() does. - expectedAvailableStyles.insert(1, QLatin1String("QrcStyle3")); - - QTest::addRow("%s", qPrintable(environmentVariable)) - << environmentVariable << expectedAvailableStyles; - } - - { - QString environmentVariable; - QDebug stream(&environmentVariable); - stream.noquote().nospace() << ":/qrcStyles4" << listSeparator - << "/some/bogus/path"; - - QStringList expectedAvailableStyles = defaultAvailableStyles; - expectedAvailableStyles.insert(1, QLatin1String("QrcStyle4")); - - QTest::addRow("%s", qPrintable(environmentVariable)) - << environmentVariable << expectedAvailableStyles; - } - - { - QString environmentVariable; - QDebug stream(&environmentVariable); - stream.noquote().nospace() << ":/qrcStyles3" << listSeparator - << ":/qrcStyles4" << listSeparator - << QFINDTESTDATA("data/dummyStyles"); - - QStringList expectedAvailableStyles = defaultAvailableStyles; - expectedAvailableStyles.insert(1, QLatin1String("DummyStyle")); - expectedAvailableStyles.insert(1, QLatin1String("QrcStyle4")); - expectedAvailableStyles.insert(1, QLatin1String("QrcStyle3")); - - QTest::addRow("%s", qPrintable(environmentVariable)) - << environmentVariable << expectedAvailableStyles; - } - - { - QString environmentVariable; - QDebug stream(&environmentVariable); - stream.noquote().nospace() << QFINDTESTDATA("data/dummyStyles") << listSeparator - << ":/qrcStyles3" << listSeparator - << ":/qrcStyles4"; - - QStringList expectedAvailableStyles = defaultAvailableStyles; - expectedAvailableStyles.insert(1, QLatin1String("QrcStyle4")); - expectedAvailableStyles.insert(1, QLatin1String("QrcStyle3")); - expectedAvailableStyles.insert(1, QLatin1String("DummyStyle")); - - QTest::addRow("%s", qPrintable(environmentVariable)) - << environmentVariable << expectedAvailableStyles; - } - - { - QString environmentVariable; - QDebug stream(&environmentVariable); - // Same as the last row, except it adds a superfluous separator - // to ensure that it handles it gracefully rather than failing an assertion. - stream.noquote().nospace() << QFINDTESTDATA("data/dummyStyles") << listSeparator - << ":/qrcStyles3" << listSeparator - << ":/qrcStyles4" << listSeparator; - - QStringList expectedAvailableStyles = defaultAvailableStyles; - expectedAvailableStyles.insert(1, QLatin1String("QrcStyle4")); - expectedAvailableStyles.insert(1, QLatin1String("QrcStyle3")); - expectedAvailableStyles.insert(1, QLatin1String("DummyStyle")); - - QTest::addRow("%s", qPrintable(environmentVariable)) - << environmentVariable << expectedAvailableStyles; - } -} - -/* - Tests that qrc paths work with QT_QUICK_CONTROLS_STYLE_PATH. -*/ -void tst_QQuickStyle::qrcInQtQuickControlsStylePathEnvVar() -{ - QFETCH(QString, environmentVariable); - QFETCH(QStringList, expectedAvailableStyles); - - qputenv("QT_QUICK_CONTROLS_STYLE_PATH", environmentVariable.toLocal8Bit()); - - const QStringList availableStyles = QQuickStyle::availableStyles(); - if (availableStyles != expectedAvailableStyles) { - QString failureMessage; - QDebug stream(&failureMessage); - stream << "Mismatch in actual vs expected available styles:" - << "\n Expected:" << expectedAvailableStyles - << "\n Actual:" << availableStyles; - QFAIL(qPrintable(failureMessage)); - } + QCOMPARE(QQuickStylePrivate::fallbackStyle(), QString()); } QTEST_MAIN(tst_QQuickStyle) diff --git a/tests/auto/sanity/BLACKLIST b/tests/auto/sanity/BLACKLIST index 2a157b87..d6d4b0d9 100644 --- a/tests/auto/sanity/BLACKLIST +++ b/tests/auto/sanity/BLACKLIST @@ -2,9 +2,9 @@ * [attachedObjects:material/SwitchDelegate.qml] * -[ids:controls/HorizontalHeaderView.qml] +[ids:default/HorizontalHeaderView.qml] * -[ids:controls/VerticalHeaderView.qml] +[ids:default/VerticalHeaderView.qml] * [ids:fusion/HorizontalHeaderView.qml] * diff --git a/tests/auto/sanity/sanity.pro b/tests/auto/sanity/sanity.pro index c792c073..d7bf5d87 100644 --- a/tests/auto/sanity/sanity.pro +++ b/tests/auto/sanity/sanity.pro @@ -1,7 +1,7 @@ TEMPLATE = app TARGET = tst_sanity -QT += qml testlib core-private qml-private +QT += qml testlib core-private qml-private quickcontrols2 CONFIG += testcase macos:CONFIG -= app_bundle diff --git a/tests/auto/sanity/tst_sanity.cpp b/tests/auto/sanity/tst_sanity.cpp index f2135fa4..5dc0676c 100644 --- a/tests/auto/sanity/tst_sanity.cpp +++ b/tests/auto/sanity/tst_sanity.cpp @@ -37,6 +37,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -81,8 +84,9 @@ private slots: void ids_data(); private: - QQmlEngine engine; - QMap files; + QMap sourceQmlFiles; + QMap installedQmlFiles; + QQuickStyleHelper styleHelper; }; void tst_Sanity::init() @@ -160,19 +164,34 @@ void tst_Sanity::initTestCase() const QStringList qmlTypeNames = QQmlMetaType::qmlTypeNames(); + // Collect the files from each style in the source tree. QDirIterator it(QQC2_IMPORT_PATH, QStringList() << "*.qml" << "*.js", QDir::Files, QDirIterator::Subdirectories); while (it.hasNext()) { it.next(); QFileInfo info = it.fileInfo(); if (qmlTypeNames.contains(QStringLiteral("QtQuick.Templates/") + info.baseName())) - files.insert(info.dir().dirName() + "/" + info.fileName(), info.filePath()); + sourceQmlFiles.insert(info.dir().dirName() + "/" + info.fileName(), info.filePath()); + } + + // Then, collect the files from each installed style directory. + const QVector> styleRelativePaths = { + { "controls/default", "QtQuick/Controls/Default" }, + { "controls/fusion", "QtQuick/Controls/Fusion" }, + { "controls/material", "QtQuick/Controls/Material" }, + { "controls/universal", "QtQuick/Controls/Universal" }, + }; + for (const auto stylePathPair : styleRelativePaths) { + forEachControl(&engine, stylePathPair.first, stylePathPair.second, QStringList(), + [&](const QString &relativePath, const QUrl &absoluteUrl) { + installedQmlFiles.insert(relativePath, absoluteUrl.toLocalFile()); + }); } } void tst_Sanity::jsFiles() { QMap::const_iterator it; - for (it = files.constBegin(); it != files.constEnd(); ++it) { + for (it = sourceQmlFiles.constBegin(); it != sourceQmlFiles.constEnd(); ++it) { if (QFileInfo(it.value()).suffix() == QStringLiteral("js")) QFAIL(qPrintable(it.value() + ": JS files are not allowed")); } @@ -204,7 +223,7 @@ void tst_Sanity::functions_data() QTest::addColumn("filePath"); QMap::const_iterator it; - for (it = files.constBegin(); it != files.constEnd(); ++it) + for (it = sourceQmlFiles.constBegin(); it != sourceQmlFiles.constEnd(); ++it) QTest::newRow(qPrintable(it.key())) << it.key() << it.value(); } @@ -241,7 +260,7 @@ void tst_Sanity::signalHandlers_data() QTest::addColumn("filePath"); QMap::const_iterator it; - for (it = files.constBegin(); it != files.constEnd(); ++it) + for (it = sourceQmlFiles.constBegin(); it != sourceQmlFiles.constEnd(); ++it) QTest::newRow(qPrintable(it.key())) << it.key() << it.value(); } @@ -273,7 +292,7 @@ void tst_Sanity::anchors_data() QTest::addColumn("filePath"); QMap::const_iterator it; - for (it = files.constBegin(); it != files.constEnd(); ++it) + for (it = sourceQmlFiles.constBegin(); it != sourceQmlFiles.constEnd(); ++it) QTest::newRow(qPrintable(it.key())) << it.key() << it.value(); } @@ -338,37 +357,339 @@ void tst_Sanity::ids_data() QTest::addColumn("filePath"); QMap::const_iterator it; - for (it = files.constBegin(); it != files.constEnd(); ++it) + for (it = sourceQmlFiles.constBegin(); it != sourceQmlFiles.constEnd(); ++it) QTest::newRow(qPrintable(it.key())) << it.key() << it.value(); } +typedef QPair StringPair; +typedef QSet StringPairSet; + void tst_Sanity::attachedObjects() { - QFETCH(QUrl, url); + QFETCH(QStringList, ignoredAttachedClassNames); + QFETCH(StringPairSet, expectedAttachedClassNames); - QQmlComponent component(&engine); - component.loadUrl(url); + const QString tagStr = QString::fromLatin1(QTest::currentDataTag()); + QStringList styleAndFileName = tagStr.split('/'); + QCOMPARE(styleAndFileName.size(), 2); + QString style = styleAndFileName.first(); + + if (styleHelper.updateStyle(style)) + qt_qobjects->clear(); + + // Turn e.g. "Default/Button.qml" into "default/Button.qml". + QString styleRelativePath = tagStr; + styleRelativePath[0] = styleRelativePath.at(0).toLower(); + // Get the absolute path to the installed file. + const QString controlFilePath = installedQmlFiles.value(styleRelativePath); + + QQmlComponent component(styleHelper.engine.data()); + component.loadUrl(QUrl::fromLocalFile(controlFilePath)); - QSet classNames; QScopedPointer object(component.create()); QVERIFY2(object.data(), qPrintable(component.errorString())); - for (QObject *object : qAsConst(*qt_qobjects)) { - if (object->parent() == &engine) + + // The goal of this test is to check that every unique attached type is used only once + // within each QML file. To track this, we remove expected pairs of class names as we + // encounter them, so that we know when something unexpected shows up. + StringPairSet remainingAttachedClassNames = expectedAttachedClassNames; + + // Intentional copy, as QDebug creates a QObject-derived instance which would modify the list. + const auto qobjectsCopy = *qt_qobjects; + for (QObject *object : qobjectsCopy) { + const QString attachedClassName = object->metaObject()->className(); + if (object->parent() == styleHelper.engine.data()) continue; // allow "global" instances - QString className = object->metaObject()->className(); - if (className.endsWith("Attached") || className.endsWith("Style")) - QVERIFY2(!classNames.contains(className), qPrintable(QString("Multiple %1 instances").arg(className))); - classNames.insert(className); + + // objects without parents would be singletons such as QQuickFusionStyle, and we're not interested in them. + if ((attachedClassName.endsWith("Attached") || attachedClassName.endsWith("Style")) && object->parent()) { + QString attacheeClassName = QString::fromLatin1(object->parent()->metaObject()->className()); + const QString qmlTypeToken = QStringLiteral("QMLTYPE"); + if (attacheeClassName.contains(qmlTypeToken)) { + // Remove the numbers from the class name, as they can change between runs; e.g.: + // Menu_QMLTYPE_222 => Menu_QMLTYPE + const int qmlTypeTokenIndex = attacheeClassName.indexOf(qmlTypeToken); + QVERIFY(qmlTypeTokenIndex != -1); + attacheeClassName = attacheeClassName.mid(0, attacheeClassName.indexOf(qmlTypeToken) + qmlTypeToken.size()); + } + + const StringPair classNamePair = { attachedClassName, attacheeClassName }; + QVERIFY2(remainingAttachedClassNames.contains(classNamePair), qPrintable(QString::fromLatin1( + "Found an unexpected usage of an attached type: %1 is attached to %2. Either an incorrect usage was added, or the list of expected usages needs to be updated. Expected attached class names for %3 are:\n %4") + .arg(attachedClassName).arg(attacheeClassName).arg(tagStr).arg(QDebug::toString(expectedAttachedClassNames)))); + remainingAttachedClassNames.remove(classNamePair); + } } + + QVERIFY2(remainingAttachedClassNames.isEmpty(), qPrintable(QString::fromLatin1( + "Not all expected attached class name usages were found; the following usages are missing:\n %1") + .arg(QDebug::toString(remainingAttachedClassNames)))); } void tst_Sanity::attachedObjects_data() { - QTest::addColumn("url"); - addTestRowForEachControl(&engine, "controls/default", "QtQuick/Controls/Default"); - addTestRowForEachControl(&engine, "controls/fusion", "QtQuick/Controls/Fusion", QStringList() << "CheckIndicator" << "RadioIndicator" << "SliderGroove" << "SliderHandle" << "SwitchIndicator"); - addTestRowForEachControl(&engine, "controls/material", "QtQuick/Controls/Material", QStringList() << "Ripple" << "SliderHandle" << "CheckIndicator" << "RadioIndicator" << "SwitchIndicator" << "BoxShadow" << "ElevationEffect" << "CursorDelegate"); - addTestRowForEachControl(&engine, "controls/universal", "QtQuick/Controls/Universal", QStringList() << "CheckIndicator" << "RadioIndicator" << "SwitchIndicator"); + QTest::addColumn("ignoredAttachedClassNames"); + QTest::addColumn("expectedAttachedClassNames"); + + QStringList ignoredNames; + + // We used to just check that there were no duplicate QMetaObject class names, + // but that doesn't account for attached objects loaded by composite controls, + // such as DialogButtonBox, which is loaded by Dialog. + // So now we list all controls and the attached types we expect them to use. + + QTest::newRow("Default/AbstractButton.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/Action.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/ActionGroup.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/ApplicationWindow.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/BusyIndicator.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/Button.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/ButtonGroup.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/CheckBox.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/CheckDelegate.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/ComboBox.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/Container.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/Control.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/DelayButton.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/Dial.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/Dialog.qml") << ignoredNames << StringPairSet {{ "QQuickOverlayAttached", "Dialog_QMLTYPE" }}; + QTest::newRow("Default/DialogButtonBox.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/Drawer.qml") << ignoredNames << StringPairSet {{ "QQuickOverlayAttached", "Drawer_QMLTYPE" }}; + QTest::newRow("Default/Frame.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/GroupBox.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/HorizontalHeaderView.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/ItemDelegate.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/Label.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/Menu.qml") << ignoredNames << StringPairSet { + { "QQuickOverlayAttached", "Menu_QMLTYPE" }, + { "QQuickScrollIndicatorAttached", "QQuickListView" }, + { "QQuickWindowAttached", "QQuickListView" } + }; + QTest::newRow("Default/MenuBar.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/MenuBarItem.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/MenuItem.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/MenuSeparator.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/Page.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/PageIndicator.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/Pane.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/Popup.qml") << ignoredNames << StringPairSet {{ "QQuickOverlayAttached", "Popup_QMLTYPE" }}; + QTest::newRow("Default/ProgressBar.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/RadioButton.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/RadioDelegate.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/RangeSlider.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/RoundButton.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/ScrollBar.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/ScrollIndicator.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/ScrollView.qml") << ignoredNames << StringPairSet {{ "QQuickScrollBarAttached", "ScrollView_QMLTYPE" }}; + QTest::newRow("Default/Slider.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/SpinBox.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/SplitView.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/StackView.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/SwipeDelegate.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/SwipeView.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/Switch.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/SwitchDelegate.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/TabBar.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/TabButton.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/TextArea.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/TextField.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/ToolBar.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/ToolButton.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/ToolSeparator.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/ToolTip.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/Tumbler.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Default/VerticalHeaderView.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/ApplicationWindow.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/BusyIndicator.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/Button.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/CheckBox.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/CheckDelegate.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/ComboBox.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/DelayButton.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/Dial.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/Dialog.qml") << ignoredNames << StringPairSet {{ "QQuickOverlayAttached", "Dialog_QMLTYPE" }}; + QTest::newRow("Fusion/DialogButtonBox.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/Drawer.qml") << ignoredNames << StringPairSet {{ "QQuickOverlayAttached", "Drawer_QMLTYPE" }}; + QTest::newRow("Fusion/Frame.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/GroupBox.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/HorizontalHeaderView.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/ItemDelegate.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/Label.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/Menu.qml") << ignoredNames << StringPairSet { + { "QQuickOverlayAttached", "Menu_QMLTYPE" }, + { "QQuickScrollIndicatorAttached", "QQuickListView" }, + { "QQuickWindowAttached", "QQuickListView" } + }; + QTest::newRow("Fusion/MenuBar.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/MenuBarItem.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/MenuItem.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/MenuSeparator.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/Page.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/PageIndicator.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/Pane.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/Popup.qml") << ignoredNames << StringPairSet {{ "QQuickOverlayAttached", "Popup_QMLTYPE" }}; + QTest::newRow("Fusion/ProgressBar.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/RadioButton.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/RadioDelegate.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/RangeSlider.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/RoundButton.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/ScrollBar.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/ScrollIndicator.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/Slider.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/SpinBox.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/SplitView.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/SwipeDelegate.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/Switch.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/SwitchDelegate.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/TabBar.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/TabButton.qml") << ignoredNames << StringPairSet {{ "QQuickTabBarAttached", "TabButton_QMLTYPE" }}; + QTest::newRow("Fusion/TextArea.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/TextField.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/ToolBar.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/ToolButton.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/ToolSeparator.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/ToolTip.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/Tumbler.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Fusion/VerticalHeaderView.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Material/ApplicationWindow.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "ApplicationWindow_QMLTYPE" }}; + QTest::newRow("Material/BusyIndicator.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "BusyIndicator_QMLTYPE" }}; + QTest::newRow("Material/Button.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "Button_QMLTYPE" }}; + QTest::newRow("Material/CheckBox.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "CheckBox_QMLTYPE" }}; + QTest::newRow("Material/CheckDelegate.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "CheckDelegate_QMLTYPE" }}; + QTest::newRow("Material/ComboBox.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "ComboBox_QMLTYPE" }}; + QTest::newRow("Material/DelayButton.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "DelayButton_QMLTYPE" }}; + QTest::newRow("Material/Dial.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "Dial_QMLTYPE" }}; + QTest::newRow("Material/Dialog.qml") << ignoredNames << StringPairSet { + { "QQuickMaterialStyle", "DialogButtonBox_QMLTYPE" }, + { "QQuickOverlayAttached", "Dialog_QMLTYPE" }, + { "QQuickMaterialStyle", "Dialog_QMLTYPE" }, + { "QQuickMaterialStyle", "Label_QMLTYPE" } + }; + QTest::newRow("Material/DialogButtonBox.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "DialogButtonBox_QMLTYPE" }}; + QTest::newRow("Material/Drawer.qml") << ignoredNames << StringPairSet { + { "QQuickOverlayAttached", "Drawer_QMLTYPE" }, + { "QQuickMaterialStyle", "Drawer_QMLTYPE" } + }; + QTest::newRow("Material/Frame.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "Frame_QMLTYPE" }}; + QTest::newRow("Material/GroupBox.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "GroupBox_QMLTYPE" }}; + QTest::newRow("Material/HorizontalHeaderView.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Material/ItemDelegate.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "ItemDelegate_QMLTYPE" }}; + QTest::newRow("Material/Label.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "Label_QMLTYPE" }}; + QTest::newRow("Material/Menu.qml") << ignoredNames << StringPairSet { + { "QQuickOverlayAttached", "Menu_QMLTYPE" }, + { "QQuickMaterialStyle", "Menu_QMLTYPE" }, + { "QQuickScrollIndicatorAttached", "QQuickListView" }, + { "QQuickWindowAttached", "QQuickListView" }, + { "QQuickMaterialStyle", "ScrollIndicator_QMLTYPE" } + }; + QTest::newRow("Material/MenuBar.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "MenuBar_QMLTYPE" }}; + QTest::newRow("Material/MenuBarItem.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "MenuBarItem_QMLTYPE" }}; + QTest::newRow("Material/MenuItem.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "MenuItem_QMLTYPE" }}; + QTest::newRow("Material/MenuSeparator.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "MenuSeparator_QMLTYPE" }}; + QTest::newRow("Material/Page.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "Page_QMLTYPE" }}; + QTest::newRow("Material/PageIndicator.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Material/Pane.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "Pane_QMLTYPE" }}; + QTest::newRow("Material/Popup.qml") << ignoredNames << StringPairSet { + { "QQuickOverlayAttached", "Popup_QMLTYPE" }, + { "QQuickMaterialStyle", "Popup_QMLTYPE" } + }; + QTest::newRow("Material/ProgressBar.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "ProgressBar_QMLTYPE" }}; + QTest::newRow("Material/RadioButton.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "RadioButton_QMLTYPE" }}; + QTest::newRow("Material/RadioDelegate.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "RadioDelegate_QMLTYPE" }}; + QTest::newRow("Material/RangeSlider.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "RangeSlider_QMLTYPE" }}; + QTest::newRow("Material/RoundButton.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "RoundButton_QMLTYPE" }}; + QTest::newRow("Material/ScrollBar.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "ScrollBar_QMLTYPE" }}; + QTest::newRow("Material/ScrollIndicator.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "ScrollIndicator_QMLTYPE" }}; + QTest::newRow("Material/Slider.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "Slider_QMLTYPE" }}; + QTest::newRow("Material/SpinBox.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "SpinBox_QMLTYPE" }}; + QTest::newRow("Material/SplitView.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Material/StackView.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Material/SwipeDelegate.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "SwipeDelegate_QMLTYPE" }}; + QTest::newRow("Material/SwipeView.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Material/Switch.qml") << ignoredNames << StringPairSet { + { "QQuickMaterialStyle", "SwitchIndicator_QMLTYPE" }, + { "QQuickMaterialStyle", "Switch_QMLTYPE" } + }; + QTest::newRow("Material/SwitchDelegate.qml") << ignoredNames << StringPairSet { + { "QQuickMaterialStyle", "SwitchDelegate_QMLTYPE" }, + { "QQuickMaterialStyle", "SwitchIndicator_QMLTYPE" } + }; + QTest::newRow("Material/TabBar.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "TabBar_QMLTYPE" }}; + QTest::newRow("Material/TabButton.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "TabButton_QMLTYPE" }}; + QTest::newRow("Material/TextArea.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "TextArea_QMLTYPE" }}; + QTest::newRow("Material/TextField.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "TextField_QMLTYPE" }}; + QTest::newRow("Material/ToolBar.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "ToolBar_QMLTYPE" }}; + QTest::newRow("Material/ToolButton.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "ToolButton_QMLTYPE" }}; + QTest::newRow("Material/ToolSeparator.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "ToolSeparator_QMLTYPE" }}; + QTest::newRow("Material/ToolTip.qml") << ignoredNames << StringPairSet {{ "QQuickMaterialStyle", "ToolTip_QMLTYPE" }}; + QTest::newRow("Material/Tumbler.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Material/VerticalHeaderView.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Universal/ApplicationWindow.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "ApplicationWindow_QMLTYPE" }}; + QTest::newRow("Universal/BusyIndicator.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "BusyIndicator_QMLTYPE" }}; + QTest::newRow("Universal/Button.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "Button_QMLTYPE" }}; + QTest::newRow("Universal/CheckBox.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "CheckBox_QMLTYPE" }}; + QTest::newRow("Universal/CheckDelegate.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "CheckDelegate_QMLTYPE" }}; + QTest::newRow("Universal/ComboBox.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "ComboBox_QMLTYPE" }}; + QTest::newRow("Universal/DelayButton.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "DelayButton_QMLTYPE" }}; + QTest::newRow("Universal/Dial.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "Dial_QMLTYPE" }}; + QTest::newRow("Universal/Dialog.qml") << ignoredNames << StringPairSet { + { "QQuickOverlayAttached", "Dialog_QMLTYPE" }, + { "QQuickUniversalStyle", "Label_QMLTYPE" }, + { "QQuickUniversalStyle", "Dialog_QMLTYPE" }, + { "QQuickUniversalStyle", "DialogButtonBox_QMLTYPE" } + }; + QTest::newRow("Universal/DialogButtonBox.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "DialogButtonBox_QMLTYPE" }}; + QTest::newRow("Universal/Drawer.qml") << ignoredNames << StringPairSet { + { "QQuickOverlayAttached", "Drawer_QMLTYPE" }, + { "QQuickUniversalStyle", "Drawer_QMLTYPE" } + }; + QTest::newRow("Universal/Frame.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "Frame_QMLTYPE" }}; + QTest::newRow("Universal/GroupBox.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "GroupBox_QMLTYPE" }}; + QTest::newRow("Universal/HorizontalHeaderView.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Universal/ItemDelegate.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "ItemDelegate_QMLTYPE" }}; + QTest::newRow("Universal/Label.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "Label_QMLTYPE" }}; + QTest::newRow("Universal/Menu.qml") << ignoredNames << StringPairSet { + { "QQuickOverlayAttached", "Menu_QMLTYPE" }, + { "QQuickUniversalStyle", "Menu_QMLTYPE" }, + { "QQuickScrollIndicatorAttached", "QQuickListView" }, + { "QQuickWindowAttached", "QQuickListView" }, + { "QQuickUniversalStyle", "ScrollIndicator_QMLTYPE" } + }; + QTest::newRow("Universal/MenuBar.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "MenuBar_QMLTYPE" }}; + QTest::newRow("Universal/MenuBarItem.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "MenuBarItem_QMLTYPE" }}; + QTest::newRow("Universal/MenuItem.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "MenuItem_QMLTYPE" }}; + QTest::newRow("Universal/MenuSeparator.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "MenuSeparator_QMLTYPE" }}; + QTest::newRow("Universal/Page.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "Page_QMLTYPE" }}; + QTest::newRow("Universal/PageIndicator.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Universal/Pane.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "Pane_QMLTYPE" }}; + QTest::newRow("Universal/Popup.qml") << ignoredNames << StringPairSet { + { "QQuickOverlayAttached", "Popup_QMLTYPE" }, + { "QQuickUniversalStyle", "Popup_QMLTYPE" } + }; + QTest::newRow("Universal/ProgressBar.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "ProgressBar_QMLTYPE" }}; + QTest::newRow("Universal/RadioButton.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "RadioButton_QMLTYPE" }}; + QTest::newRow("Universal/RadioDelegate.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "RadioDelegate_QMLTYPE" }}; + QTest::newRow("Universal/RangeSlider.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "RangeSlider_QMLTYPE" }}; + QTest::newRow("Universal/RoundButton.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "RoundButton_QMLTYPE" }}; + QTest::newRow("Universal/ScrollBar.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "ScrollBar_QMLTYPE" }}; + QTest::newRow("Universal/ScrollIndicator.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "ScrollIndicator_QMLTYPE" }}; + QTest::newRow("Universal/Slider.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "Slider_QMLTYPE" }}; + QTest::newRow("Universal/SpinBox.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "SpinBox_QMLTYPE" }}; + QTest::newRow("Universal/SplitView.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Universal/StackView.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Universal/SwipeDelegate.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "SwipeDelegate_QMLTYPE" }}; + QTest::newRow("Universal/Switch.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "Switch_QMLTYPE" }}; + QTest::newRow("Universal/SwitchDelegate.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "SwitchDelegate_QMLTYPE" }}; + QTest::newRow("Universal/TabBar.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "TabBar_QMLTYPE" }}; + QTest::newRow("Universal/TabButton.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "TabButton_QMLTYPE" }}; + QTest::newRow("Universal/TextArea.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "TextArea_QMLTYPE" }}; + QTest::newRow("Universal/TextField.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "TextField_QMLTYPE" }}; + QTest::newRow("Universal/ToolBar.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "ToolBar_QMLTYPE" }}; + QTest::newRow("Universal/ToolButton.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "ToolButton_QMLTYPE" }}; + QTest::newRow("Universal/ToolSeparator.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "ToolSeparator_QMLTYPE" }}; + QTest::newRow("Universal/ToolTip.qml") << ignoredNames << StringPairSet {{ "QQuickUniversalStyle", "ToolTip_QMLTYPE" }}; + QTest::newRow("Universal/Tumbler.qml") << ignoredNames << StringPairSet {}; + QTest::newRow("Universal/VerticalHeaderView.qml") << ignoredNames << StringPairSet {}; } QTEST_MAIN(tst_Sanity) diff --git a/tests/auto/shared/qtest_quickcontrols.h b/tests/auto/shared/qtest_quickcontrols.h index 4a06c021..17e5a6d2 100644 --- a/tests/auto/shared/qtest_quickcontrols.h +++ b/tests/auto/shared/qtest_quickcontrols.h @@ -42,11 +42,12 @@ #include #include #include +#include static QStringList testStyles() { if (QQuickStyle::name().isEmpty()) - return QQuickStyle::availableStyles(); + return QQuickStylePrivate::builtInStyles(); return QStringList(QQuickStyle::name()); } diff --git a/tests/auto/shared/util.pri b/tests/auto/shared/util.pri index 99607a66..c2eb9f0a 100644 --- a/tests/auto/shared/util.pri +++ b/tests/auto/shared/util.pri @@ -1,4 +1,4 @@ -QT += testlib-private core-private gui-private qml-private quick-private quicktemplates2-private quickcontrols2 +QT += testlib-private core-private gui-private qml-private quick-private quicktemplates2-private quickcontrols2 quickcontrols2-private HEADERS += $$PWD/visualtestutil.h \ $$PWD/util.h \ diff --git a/tests/auto/shared/visualtestutil.cpp b/tests/auto/shared/visualtestutil.cpp index 1740a6a6..bfbf0a17 100644 --- a/tests/auto/shared/visualtestutil.cpp +++ b/tests/auto/shared/visualtestutil.cpp @@ -95,12 +95,12 @@ void QQuickVisualTestUtil::centerOnScreen(QQuickWindow *window) window->setFramePosition(screenGeometry.center() - offset); } -void QQuickVisualTestUtil::addTestRowForEachControl(QQmlEngine *engine, const QString &sourcePath, const QString &targetPath, const QStringList &skiplist) +void QQuickVisualTestUtil::forEachControl(QQmlEngine *engine, const QString &sourcePath, const QString &targetPath, const QStringList &skipList, QQuickVisualTestUtil::ForEachCallback callback) { // We cannot use QQmlComponent to load QML files directly from the source tree. // For styles that use internal QML types (eg. material/Ripple.qml), the source // dir would be added as an "implicit" import path overriding the actual import - // path (qtbase/qml/QtQuick/Controls/Material). => The QML engine fails to load + // path (qtbase/qml/QtQuick/Controls.2/Material). => The QML engine fails to load // the style C++ plugin from the implicit import path (the source dir). // // Therefore we only use the source tree for finding out the set of QML files that @@ -111,7 +111,7 @@ void QQuickVisualTestUtil::addTestRowForEachControl(QQmlEngine *engine, const QS const QFileInfoList entries = QDir(QQC2_IMPORT_PATH "/" + sourcePath).entryInfoList(QStringList("*.qml"), QDir::Files); for (const QFileInfo &entry : entries) { QString name = entry.baseName(); - if (!skiplist.contains(name)) { + if (!skipList.contains(name)) { const auto importPathList = engine->importPathList(); for (const QString &importPath : importPathList) { QString name = entry.dir().dirName() + "/" + entry.fileName(); @@ -119,13 +119,13 @@ void QQuickVisualTestUtil::addTestRowForEachControl(QQmlEngine *engine, const QS if (filePath.startsWith(":")) filePath.prepend("qrc"); if (QFile::exists(filePath)) { - QTest::newRow(qPrintable(name)) << QUrl::fromLocalFile(filePath); + callback(name, QUrl::fromLocalFile(filePath)); break; } else { QUrl url(filePath); filePath = QQmlFile::urlToLocalFileOrQrc(filePath); if (!filePath.isEmpty() && QFile::exists(filePath)) { - QTest::newRow(qPrintable(name)) << url; + callback(name, url); break; } } @@ -133,3 +133,10 @@ void QQuickVisualTestUtil::addTestRowForEachControl(QQmlEngine *engine, const QS } } } + +void QQuickVisualTestUtil::addTestRowForEachControl(QQmlEngine *engine, const QString &sourcePath, const QString &targetPath, const QStringList &skipList) +{ + forEachControl(engine, sourcePath, targetPath, skipList, [&](const QString &relativePath, const QUrl &absoluteUrl) { + QTest::newRow(qPrintable(relativePath)) << absoluteUrl; + }); +} diff --git a/tests/auto/shared/visualtestutil.h b/tests/auto/shared/visualtestutil.h index e3f4075d..726daa27 100644 --- a/tests/auto/shared/visualtestutil.h +++ b/tests/auto/shared/visualtestutil.h @@ -37,11 +37,12 @@ #ifndef QQUICKVISUALTESTUTIL_H #define QQUICKVISUALTESTUTIL_H +#include + #include #include - #include - +#include #include #include "util.h" @@ -162,7 +163,33 @@ namespace QQuickVisualTestUtil QByteArray errorMessage; }; - void addTestRowForEachControl(QQmlEngine *engine, const QString &sourcePath, const QString &targetPath, const QStringList &skiplist = QStringList()); + struct QQuickStyleHelper + { + bool updateStyle(const QString &style) + { + // If it's not the first time a style has been set and the new style is not different, do nothing. + if (!currentStyle.isEmpty() && style == currentStyle) + return false; + + engine.reset(new QQmlEngine); + currentStyle = style; + qmlClearTypeRegistrations(); + QQuickStyle::setStyle(style); + + QQmlComponent component(engine.data()); + component.setData(QString("import QtQuick\nimport QtQuick.Controls\n Control { }").toUtf8(), QUrl()); + + return true; + } + + QString currentStyle; + QScopedPointer engine; + }; + + typedef std::function ForEachCallback; + + void forEachControl(QQmlEngine *engine, const QString &sourcePath, const QString &targetPath, const QStringList &skipList, ForEachCallback callback); + void addTestRowForEachControl(QQmlEngine *engine, const QString &sourcePath, const QString &targetPath, const QStringList &skipList = QStringList()); } #define QQUICK_VERIFY_POLISH(item) \ diff --git a/tests/auto/snippets/snippets.pro b/tests/auto/snippets/snippets.pro index 6f366f23..9fa65055 100644 --- a/tests/auto/snippets/snippets.pro +++ b/tests/auto/snippets/snippets.pro @@ -1,7 +1,7 @@ TEMPLATE = app TARGET = tst_snippets -QT += quick quickcontrols2 testlib +QT += quick quickcontrols2 quickcontrols2-private testlib CONFIG += testcase macos:CONFIG -= app_bundle diff --git a/tests/auto/snippets/tst_snippets.cpp b/tests/auto/snippets/tst_snippets.cpp index 107759ce..1c420edb 100644 --- a/tests/auto/snippets/tst_snippets.cpp +++ b/tests/auto/snippets/tst_snippets.cpp @@ -36,7 +36,8 @@ #include #include -#include +#include +#include typedef QPair QStringPair; @@ -114,14 +115,14 @@ void tst_Snippets::verify() if (takeScreenshots) { const QString currentDataTag = QLatin1String(QTest::currentDataTag()); static const QString applicationStyle = QQuickStyle::name().isEmpty() ? "Default" : QQuickStyle::name(); - static const QStringList availableStyles = QQuickStyle::availableStyles(); + static const QStringList builtInStyles = QQuickStylePrivate::builtInStyles(); bool isStyledSnippet = false; const QString snippetStyle = currentDataTag.section("-", 1, 1); - for (const QString &availableStyle : availableStyles) { - if (!snippetStyle.compare(availableStyle, Qt::CaseInsensitive)) { - if (applicationStyle != availableStyle) - QSKIP(qPrintable(QString("%1 style specific snippet. Running with the %2 style.").arg(availableStyle, applicationStyle))); + for (const QString &style : builtInStyles) { + if (!snippetStyle.compare(style, Qt::CaseInsensitive)) { + if (applicationStyle != style) + QSKIP(qPrintable(QString("%1 style specific snippet. Running with the %2 style.").arg(style, applicationStyle))); isStyledSnippet = true; } } diff --git a/tests/auto/styleimports/ResourceStyle/Button.qml b/tests/auto/styleimports/ResourceStyle/Button.qml new file mode 100644 index 00000000..2b9e5bb7 --- /dev/null +++ b/tests/auto/styleimports/ResourceStyle/Button.qml @@ -0,0 +1,4 @@ +import QtQuick.Templates 2.15 as T +T.Button { + objectName: "ResourceStyle" +} diff --git a/tests/auto/styleimports/ResourceStyle/qmldir b/tests/auto/styleimports/ResourceStyle/qmldir new file mode 100644 index 00000000..4cb1a835 --- /dev/null +++ b/tests/auto/styleimports/ResourceStyle/qmldir @@ -0,0 +1,2 @@ +module ResourceStyle +Button 2.15 Button.qml diff --git a/tests/auto/styleimports/data/Action.qml b/tests/auto/styleimports/data/Action.qml new file mode 100644 index 00000000..62501292 --- /dev/null +++ b/tests/auto/styleimports/data/Action.qml @@ -0,0 +1,4 @@ +import QtQuick.Templates 2.15 as T +T.Action { + objectName: "data" +} diff --git a/tests/auto/styleimports/data/Button.qml b/tests/auto/styleimports/data/Button.qml new file mode 100644 index 00000000..f67719e3 --- /dev/null +++ b/tests/auto/styleimports/data/Button.qml @@ -0,0 +1,4 @@ +import QtQuick.Templates 2.15 as T +T.Button { + objectName: "data" +} diff --git a/tests/auto/styleimports/data/FileSystemStyle/Button.qml b/tests/auto/styleimports/data/FileSystemStyle/Button.qml new file mode 100644 index 00000000..a5719dd8 --- /dev/null +++ b/tests/auto/styleimports/data/FileSystemStyle/Button.qml @@ -0,0 +1,4 @@ +import QtQuick.Templates 2.15 as T +T.Button { + objectName: "FileSystemStyle" +} diff --git a/tests/auto/styleimports/data/FileSystemStyle/qmldir b/tests/auto/styleimports/data/FileSystemStyle/qmldir new file mode 100644 index 00000000..3c401588 --- /dev/null +++ b/tests/auto/styleimports/data/FileSystemStyle/qmldir @@ -0,0 +1,2 @@ +module FileSystemStyle +Button 2.15 Button.qml diff --git a/tests/auto/styleimports/data/Label.qml b/tests/auto/styleimports/data/Label.qml new file mode 100644 index 00000000..b6d434ad --- /dev/null +++ b/tests/auto/styleimports/data/Label.qml @@ -0,0 +1,4 @@ +import QtQuick.Templates 2.15 as T +T.Label { + objectName: "data" +} diff --git a/tests/auto/styleimports/data/PlatformStyle/+linux/Button.qml b/tests/auto/styleimports/data/PlatformStyle/+linux/Button.qml new file mode 100644 index 00000000..74e2a6f4 --- /dev/null +++ b/tests/auto/styleimports/data/PlatformStyle/+linux/Button.qml @@ -0,0 +1,4 @@ +import QtQuick.Templates 2.15 as T +T.Button { + objectName: "PlatformStyle/+linux" +} diff --git a/tests/auto/styleimports/data/PlatformStyle/+macos/Button.qml b/tests/auto/styleimports/data/PlatformStyle/+macos/Button.qml new file mode 100644 index 00000000..47fd788b --- /dev/null +++ b/tests/auto/styleimports/data/PlatformStyle/+macos/Button.qml @@ -0,0 +1,4 @@ +import QtQuick.Templates 2.15 as T +T.Button { + objectName: "PlatformStyle/+macos" +} diff --git a/tests/auto/styleimports/data/PlatformStyle/+windows/Button.qml b/tests/auto/styleimports/data/PlatformStyle/+windows/Button.qml new file mode 100644 index 00000000..e5b56653 --- /dev/null +++ b/tests/auto/styleimports/data/PlatformStyle/+windows/Button.qml @@ -0,0 +1,4 @@ +import QtQuick.Templates 2.15 as T +T.Button { + objectName: "PlatformStyle/+windows" +} diff --git a/tests/auto/styleimports/data/PlatformStyle/Button.qml b/tests/auto/styleimports/data/PlatformStyle/Button.qml new file mode 100644 index 00000000..fd1dc83d --- /dev/null +++ b/tests/auto/styleimports/data/PlatformStyle/Button.qml @@ -0,0 +1,4 @@ +import QtQuick.Templates 2.15 as T +T.Button { + objectName: "PlatformStyle/Button.qml" +} diff --git a/tests/auto/styleimports/data/PlatformStyle/qmldir b/tests/auto/styleimports/data/PlatformStyle/qmldir new file mode 100644 index 00000000..ce254517 --- /dev/null +++ b/tests/auto/styleimports/data/PlatformStyle/qmldir @@ -0,0 +1,2 @@ +module PlatformStyle +Button 2.15 Button.qml diff --git a/tests/auto/styleimports/data/platformSelectors.qml b/tests/auto/styleimports/data/platformSelectors.qml new file mode 100644 index 00000000..ba6af22c --- /dev/null +++ b/tests/auto/styleimports/data/platformSelectors.qml @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick +import QtQuick.Controls + +ApplicationWindow { + title: "Test Application Window" + width: 400 + height: 400 + + property alias button: button + + Button { + id: button + } +} diff --git a/tests/auto/styleimports/data/qmldir b/tests/auto/styleimports/data/qmldir new file mode 100644 index 00000000..9e9df940 --- /dev/null +++ b/tests/auto/styleimports/data/qmldir @@ -0,0 +1,4 @@ +module data +Action 2.15 Action.qml +Button 2.15 Button.qml +Label 2.15 Label.qml diff --git a/tests/auto/styleimports/styleimports.pro b/tests/auto/styleimports/styleimports.pro new file mode 100644 index 00000000..774d93e9 --- /dev/null +++ b/tests/auto/styleimports/styleimports.pro @@ -0,0 +1,29 @@ +CONFIG += testcase +TARGET = tst_styleimports +SOURCES += tst_styleimports.cpp + +macos:CONFIG -= app_bundle + +QT += core-private gui-private qml-private quick-private quickcontrols2-private testlib + +include (../shared/util.pri) + +resourcestyle.prefix = / +resourcestyle.files += \ + $$PWD/ResourceStyle/Button.qml \ + $$PWD/ResourceStyle/qmldir +RESOURCES += resourcestyle + +TESTDATA = data/* + +OTHER_FILES += \ + data/*.qml \ + data/qmldir \ + data/FileSystemStyle/*.qml \ + data/FileSystemStyle/qmldir \ + data/PlatformStyle/*.qml \ + data/PlatformStyle/+linux/*.qml \ + data/PlatformStyle/+macos/*.qml \ + data/PlatformStyle/+windows/*.qml \ + data/PlatformStyle/qmldir + diff --git a/tests/auto/styleimports/tst_styleimports.cpp b/tests/auto/styleimports/tst_styleimports.cpp new file mode 100644 index 00000000..04acbb21 --- /dev/null +++ b/tests/auto/styleimports/tst_styleimports.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../shared/util.h" + +class tst_StyleImports : public QQmlDataTest +{ + Q_OBJECT + +private slots: + void initTestCase(); + + void cleanup(); + + void select_data(); + void select(); + + void platformSelectors(); +}; + +void tst_StyleImports::initTestCase() +{ + QQmlDataTest::initTestCase(); +} + +void tst_StyleImports::cleanup() +{ + qmlClearTypeRegistrations(); +} + +void tst_StyleImports::select_data() +{ + QTest::addColumn("file"); + QTest::addColumn("style"); + QTest::addColumn("fallback"); + QTest::addColumn("expected"); + + // Action.qml exists in the "data" style and the Default style. + QTest::newRow("control=Action,style=empty,fallback=empty") << "Action.qml" << "" << "" << "Default"; + QTest::newRow("control=Action,style=fs,fallback=empty") << "Action.qml" << "FileSystemStyle" << "" << "Default"; + QTest::newRow("control=Action,style=qrc,fallback=empty") << "Action.qml" << "ResourceStyle" << "" << "Default"; + QTest::newRow("control=Action,style=nosuch,fallback=empty") << "Action.qml" << "NoSuchStyle" << "" << "Default"; + QTest::newRow("control=Action,style=data,fallback=empty") << "Action.qml" << "data" << "" << "data"; + + QTest::newRow("control=Action,style=empty,fallback=mat") << "Action.qml" << "" << "Material" << ""; + QTest::newRow("control=Action,style=fs,fallback=mat") << "Action.qml" << "FileSystemStyle" << "Material" << "Default"; + QTest::newRow("control=Action,style=qrc,fallback=mat") << "Action.qml" << "ResourceStyle" << "Material" << "Default"; + QTest::newRow("control=Action,style=nosuch,fallback=mat") << "Action.qml" << "NoSuchStyle" << "Material" << "Default"; + QTest::newRow("control=Action,style=data,fallback=mat") << "Action.qml" << "data" << "Material" << "data"; + + // ScrollView.qml only exists in the Default style. + QTest::newRow("control=ScrollView,style=empty,fallback=empty") << "ScrollView.qml" << "" << "" << "Default"; + QTest::newRow("control=ScrollView,style=fs,fallback=empty") << "ScrollView.qml" << "FileSystemStyle" << "" << "Default"; + QTest::newRow("control=ScrollView,style=qrc,fallback=empty") << "ScrollView.qml" << "ResourceStyle" << "" << "Default"; + QTest::newRow("control=ScrollView,style=nosuch,fallback=empty") << "ScrollView.qml" << "NoSuchStyle" << "" << "Default"; + QTest::newRow("control=ScrollView,style=data,fallback=empty") << "ScrollView.qml" << "data" << "" << "Default"; + + QTest::newRow("control=ScrollView,style=empty,fallback=mat") << "ScrollView.qml" << "" << "Material" << "Default"; + QTest::newRow("control=ScrollView,style=fs,fallback=mat") << "ScrollView.qml" << "FileSystemStyle" << "Material" << "Default"; + QTest::newRow("control=ScrollView,style=qrc,fallback=mat") << "ScrollView.qml" << "ResourceStyle" << "Material" << "Default"; + QTest::newRow("control=ScrollView,style=nosuch,fallback=mat") << "ScrollView.qml" << "NoSuchStyle" << "Material" << "Default"; + QTest::newRow("control=ScrollView,style=data,fallback=mat") << "ScrollView.qml" << "data" << "Material" << "Default"; + + // Label.qml exists in the "data", Default and Material styles. + QTest::newRow("control=Label,style=none,fallback=none") << "Label.qml" << "" << "" << "Default"; + QTest::newRow("control=Label,style=fs,fallback=none") << "Label.qml" << "FileSystemStyle" << "" << "Default"; + QTest::newRow("control=Label,style=qrc,fallback=none") << "Label.qml" << "ResourceStyle" << "" << "Default"; + QTest::newRow("control=Label,style=nosuch,fallback=none") << "Label.qml" << "NoSuchStyle" << "" << "Default"; + QTest::newRow("control=Label,style=data,fallback=none") << "Label.qml" << "data" << "" << "data"; + + QTest::newRow("control=Label,style=none,fallback=mat") << "Label.qml" << "" << "Material" << "Default"; + QTest::newRow("control=Label,style=fs,fallback=mat") << "Label.qml" << "FileSystemStyle" << "Material" << "Default"; + QTest::newRow("control=Label,style=qrc,fallback=mat") << "Label.qml" << "ResourceStyle" << "Material" << "Default"; + QTest::newRow("control=Label,style=nosuch,fallback=mat") << "Label.qml" << "NoSuchStyle" << "Material" << "Default"; + QTest::newRow("control=Label,style=data,fallback=mat") << "Label.qml" << "data" << "Material" << "data"; + + // Button.qml exists in all styles including the fs and qrc styles + QTest::newRow("control=Button,style=none,fallback=none") << "Button.qml" << "" << "" << "Default"; + QTest::newRow("control=Button,style=fs,fallback=none") << "Button.qml" << "FileSystemStyle" << "" << "FileSystemStyle"; + QTest::newRow("control=Button,style=qrc,fallback=none") << "Button.qml" << "ResourceStyle" << "" << "ResourceStyle"; + QTest::newRow("control=Button,style=nosuch,fallback=none") << "Button.qml" << "NoSuchStyle" << "" << "Default"; + QTest::newRow("control=Button,style=data,fallback=none") << "Button.qml" << "data" << "" << "data"; + + QTest::newRow("control=Button,style=none,fallback=mat") << "Button.qml" << "" << "Material" << "Default"; + 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" << "Default"; + QTest::newRow("control=Button,style=data,fallback=mat") << "Button.qml" << "data" << "Material" << "data"; +} + +void tst_StyleImports::select() +{ + QFETCH(QString, file); + QFETCH(QString, style); + QFETCH(QString, fallback); + QFETCH(QString, expected); + + // In Qt 5, there were several accepted forms for style names. + // In Qt 6, the only accepted form is the base name of the style directory. + const bool invalidStyleName = style.contains(QLatin1Char('/')); + if (invalidStyleName) + QTest::ignoreMessage(QtWarningMsg, + "Style names must not contain paths; see the \"Definition of a Style\" documentation for more information"); + QQuickStyle::setStyle(style); + QQuickStyle::setFallbackStyle(fallback); + + QQmlEngine engine; + engine.addImportPath(directory()); + engine.addImportPath(dataDirectory()); + QQmlComponent component(&engine); + const QString controlName = file.mid(0, file.indexOf(QLatin1Char('.'))); + component.setData(QString::fromLatin1("import QtQuick 2.15; import QtQuick.Controls 2.15; %1 { }").arg(controlName).toUtf8(), QUrl()); + + const bool nonExistentStyle = style == QLatin1String("NoSuchStyle"); + if (nonExistentStyle) + QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); + QScopedPointer object(component.create()); + if (nonExistentStyle) { + QVERIFY(object.isNull()); + return; + } + + 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); + } +} + +void tst_StyleImports::platformSelectors() +{ + QQuickStyle::setStyle(QLatin1String("PlatformStyle")); + + QQmlApplicationEngine engine; + engine.addImportPath(dataDirectory()); + engine.load(testFileUrl("platformSelectors.qml")); + QQuickWindow *window = qobject_cast(engine.rootObjects().first()); + QVERIFY(window); + + QObject *button = window->property("button").value(); + QVERIFY(button); + +#if defined(Q_OS_LINUX) + QCOMPARE(button->objectName(), "PlatformStyle/+linux"); +#elif defined(Q_OS_MACOS) + QCOMPARE(button->objectName(), "PlatformStyle/+macos"); +#elif defined(Q_OS_WIN) + QCOMPARE(button->objectName(), "PlatformStyle/+windows"); +#else + QCOMPARE(button->objectName(), "PlatformStyle/Button.qml"); +#endif +} + +QTEST_MAIN(tst_StyleImports) + +#include "tst_styleimports.moc" diff --git a/tests/benchmarks/creationtime/creationtime.pro b/tests/benchmarks/creationtime/creationtime.pro index a594027e..613cf60d 100644 --- a/tests/benchmarks/creationtime/creationtime.pro +++ b/tests/benchmarks/creationtime/creationtime.pro @@ -1,7 +1,7 @@ TEMPLATE = app TARGET = tst_creationtime -QT += qml testlib +QT += qml testlib quickcontrols2 CONFIG += testcase macos:CONFIG -= app_bundle diff --git a/tests/benchmarks/creationtime/tst_creationtime.cpp b/tests/benchmarks/creationtime/tst_creationtime.cpp index f243cc5c..d186dc83 100644 --- a/tests/benchmarks/creationtime/tst_creationtime.cpp +++ b/tests/benchmarks/creationtime/tst_creationtime.cpp @@ -34,8 +34,10 @@ ** ****************************************************************************/ -#include +#include #include +#include +#include #include "../../auto/shared/visualtestutil.h" @@ -46,10 +48,11 @@ class tst_CreationTime : public QObject Q_OBJECT private slots: + void initTestCase(); void init(); - void controls(); - void controls_data(); + void defaultStyle(); + void defaultStyle_data(); void fusion(); void fusion_data(); @@ -64,17 +67,29 @@ private slots: void universal_data(); private: - QQmlEngine engine; + QQuickStyleHelper styleHelper; }; +void tst_CreationTime::initTestCase() +{ + styleHelper.engine.reset(new QQmlEngine); +} + void tst_CreationTime::init() { - engine.clearComponentCache(); + styleHelper.engine->clearComponentCache(); } -static void doBenchmark(QQmlEngine *engine, const QUrl &url) +static void doBenchmark(QQuickStyleHelper &styleHelper, const QUrl &url) { - QQmlComponent component(engine); + const QString tagStr = QString::fromLatin1(QTest::currentDataTag()); + QStringList styleAndFileName = tagStr.split('/'); + QCOMPARE(styleAndFileName.size(), 2); + QString style = styleAndFileName.first(); + style[0] = style.at(0).toUpper(); + styleHelper.updateStyle(style); + + QQmlComponent component(styleHelper.engine.data()); component.loadUrl(url); QObjectList objects; @@ -87,64 +102,64 @@ static void doBenchmark(QQmlEngine *engine, const QUrl &url) qDeleteAll(objects); } -void tst_CreationTime::controls() +void tst_CreationTime::defaultStyle() { QFETCH(QUrl, url); - doBenchmark(&engine, url); + doBenchmark(styleHelper, url); } -void tst_CreationTime::controls_data() +void tst_CreationTime::defaultStyle_data() { QTest::addColumn("url"); - addTestRowForEachControl(&engine, "controls", "QtQuick/Controls", QStringList() << "ApplicationWindow"); + addTestRowForEachControl(styleHelper.engine.data(), "controls/default", "QtQuick/Controls/Default", QStringList() << "ApplicationWindow"); } void tst_CreationTime::fusion() { QFETCH(QUrl, url); - doBenchmark(&engine, url); + doBenchmark(styleHelper, url); } void tst_CreationTime::fusion_data() { QTest::addColumn("url"); - addTestRowForEachControl(&engine, "controls/fusion", "QtQuick/Controls/Fusion", QStringList() << "ApplicationWindow" << "ButtonPanel" << "CheckIndicator" << "RadioIndicator" << "SliderGroove" << "SliderHandle" << "SwitchIndicator"); + addTestRowForEachControl(styleHelper.engine.data(), "controls/fusion", "QtQuick/Controls/Fusion", QStringList() << "ApplicationWindow" << "ButtonPanel" << "CheckIndicator" << "RadioIndicator" << "SliderGroove" << "SliderHandle" << "SwitchIndicator"); } void tst_CreationTime::imagine() { QFETCH(QUrl, url); - doBenchmark(&engine, url); + doBenchmark(styleHelper, url); } void tst_CreationTime::imagine_data() { QTest::addColumn("url"); - addTestRowForEachControl(&engine, "controls/imagine", "QtQuick/Controls/Imagine", QStringList() << "ApplicationWindow"); + addTestRowForEachControl(styleHelper.engine.data(), "controls/imagine", "QtQuick/Controls/Imagine", QStringList() << "ApplicationWindow"); } void tst_CreationTime::material() { QFETCH(QUrl, url); - doBenchmark(&engine, url); + doBenchmark(styleHelper, url); } void tst_CreationTime::material_data() { QTest::addColumn("url"); - addTestRowForEachControl(&engine, "controls/material", "QtQuick/Controls/Material", QStringList() << "ApplicationWindow" << "Ripple" << "SliderHandle" << "CheckIndicator" << "RadioIndicator" << "SwitchIndicator" << "BoxShadow" << "ElevationEffect" << "CursorDelegate"); + addTestRowForEachControl(styleHelper.engine.data(), "controls/material", "QtQuick/Controls/Material", QStringList() << "ApplicationWindow" << "Ripple" << "SliderHandle" << "CheckIndicator" << "RadioIndicator" << "SwitchIndicator" << "BoxShadow" << "ElevationEffect" << "CursorDelegate"); } void tst_CreationTime::universal() { QFETCH(QUrl, url); - doBenchmark(&engine, url); + doBenchmark(styleHelper, url); } void tst_CreationTime::universal_data() { QTest::addColumn("url"); - addTestRowForEachControl(&engine, "controls/universal", "QtQuick/Controls/Universal", QStringList() << "ApplicationWindow" << "CheckIndicator" << "RadioIndicator" << "SwitchIndicator"); + addTestRowForEachControl(styleHelper.engine.data(), "controls/universal", "QtQuick/Controls/Universal", QStringList() << "ApplicationWindow" << "CheckIndicator" << "RadioIndicator" << "SwitchIndicator"); } QTEST_MAIN(tst_CreationTime) diff --git a/tests/benchmarks/objectcount/tst_objectcount.cpp b/tests/benchmarks/objectcount/tst_objectcount.cpp index c1194c10..cb5a0956 100644 --- a/tests/benchmarks/objectcount/tst_objectcount.cpp +++ b/tests/benchmarks/objectcount/tst_objectcount.cpp @@ -93,7 +93,7 @@ void tst_ObjectCount::cleanup() static void initTestRows(QQmlEngine *engine) { - addTestRowForEachControl(engine, "controls", "QtQuick/Controls"); + addTestRowForEachControl(engine, "controls/default", "QtQuick/Controls/Default"); addTestRowForEachControl(engine, "controls/fusion", "QtQuick/Controls/Fusion", QStringList() << "ButtonPanel" << "CheckIndicator" << "RadioIndicator" << "SliderGroove" << "SliderHandle" << "SwitchIndicator"); addTestRowForEachControl(engine, "controls/imagine", "QtQuick/Controls/Imagine"); addTestRowForEachControl(engine, "controls/material", "QtQuick/Controls/Material", QStringList() << "Ripple" << "SliderHandle" << "CheckIndicator" << "RadioIndicator" << "SwitchIndicator" << "BoxShadow" << "ElevationEffect" << "CursorDelegate"); diff --git a/tests/manual/testbench/main.cpp b/tests/manual/testbench/main.cpp index 0287537d..0584bd6d 100644 --- a/tests/manual/testbench/main.cpp +++ b/tests/manual/testbench/main.cpp @@ -55,6 +55,7 @@ #include #include #include +#include #include "assetfixer.h" #include "clipboard.h" @@ -85,7 +86,7 @@ int main(int argc, char *argv[]) qmlRegisterType("App", 1, 0, "Clipboard"); qmlRegisterType("App", 1, 0, "DirectoryValidator"); - engine.rootContext()->setContextProperty("availableStyles", QQuickStyle::availableStyles()); + engine.rootContext()->setContextProperty("availableStyles", QQuickStylePrivate::builtInStyles()); engine.load(QUrl(QStringLiteral("qrc:/testbench.qml"))); diff --git a/tests/manual/testbench/testbench.pro b/tests/manual/testbench/testbench.pro index 829bcbde..4509f496 100644 --- a/tests/manual/testbench/testbench.pro +++ b/tests/manual/testbench/testbench.pro @@ -1,6 +1,6 @@ TEMPLATE = app -QT += qml quick quickcontrols2 +QT += qml quick quickcontrols2 quickcontrols2-private CONFIG += c++11 HEADERS += \ -- cgit v1.2.3