aboutsummaryrefslogtreecommitdiffstats
path: root/src/quickcontrols2
diff options
context:
space:
mode:
authorMitch Curtis <mitch.curtis@qt.io>2020-04-17 14:32:55 +0200
committerMitch Curtis <mitch.curtis@qt.io>2020-08-26 11:46:07 +0200
commit501bc44bb006ee8021cbaaa7a696e7c9263395d3 (patch)
tree6e266d07f3ef0b57dd19b4a7c505ed064ad01636 /src/quickcontrols2
parent92879d8f6b8b840328813f29f758865a412af270 (diff)
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 <fabian.kosmale@qt.io>
Diffstat (limited to 'src/quickcontrols2')
-rw-r--r--src/quickcontrols2/qquickstyle.cpp350
-rw-r--r--src/quickcontrols2/qquickstyle.h4
-rw-r--r--src/quickcontrols2/qquickstyle_p.h8
-rw-r--r--src/quickcontrols2/qquickstyleplugin.cpp16
-rw-r--r--src/quickcontrols2/qquickstyleplugin_p.h2
5 files changed, 70 insertions, 310 deletions
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.
@@ -554,19 +397,6 @@ QString QQuickStyle::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.
\note The style must be configured \b before loading QML that imports Qt Quick Controls.
@@ -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<QFileInfo> 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 <QtCore/qurl.h>
#include <QtCore/qsharedpointer.h>
#include <QtQuickControls2/qtquickcontrols2global.h>
@@ -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<QSettings> settings(const QString &group = QString());
static const QFont *readFont(const QSharedPointer<QSettings> &settings);
static const QPalette *readPalette(const QSharedPointer<QSettings> &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 <QtCore/qloggingcategory.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlfile.h>
+#include <QtQuickTemplates2/private/qquicktheme_p_p.h>
+
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)