aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ-P Nurmi <jpnurmi@qt.io>2018-03-12 16:51:32 +0100
committerJ-P Nurmi <jpnurmi@qt.io>2018-03-13 10:37:59 +0000
commit0cd3f970fff9643129a046a2bab1d79ce2f97c28 (patch)
tree54f9453c55068ba864cb3c212f6476dc7ed71310
parent57aa9580f07307c5e277e52362943e7efd1f9ac9 (diff)
Refactor QQuickStyleSelector
Instead of trying to determine a single base URL based on the import URI of the plugin, and whether built in static mode, provide a list of paths where to lookup styles, in priority order: - requested style path (-style /path/to/style) - QT_QUICK_CONTROLS_STYLE_PATH environment variable - QT_INSTALL_QML/QtQuick/Controls.2/ - qrc://qt-project.org/imports/QtQuick/Controls.2/ Furthermore, provide the requested style name and the fallback style name as a simple list of selectors. The lookup order is: 1) requested style 2) fallback style 3) default style As a result, QQuickStyleSelector implementation is a lot simpler, completely independent of QQuickStyle, and the style lookup works regardless of whether the files are in QRC or on the file system. This allows us to utilize QRC and the Qt Quick Compiler in the future. Note: two data rows in tst_QQuickStyleSelector::select_data() had to be fixed. Not sure how the expected values ended up like that, but as the comments say, "Label.qml exists in the default and fallback styles" and "Button.qml exists in all styles", so it makes no sense to expect that Label.qml or Button.qml is selected from the FallbackStyle when a valid "data" folder is specified. Change-Id: I18dea9fddf8f079e0140b51b567814da0df2802c Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
-rw-r--r--src/imports/controls/qtquickcontrols2plugin.cpp7
-rw-r--r--src/quickcontrols2/qquickstyle.cpp17
-rw-r--r--src/quickcontrols2/qquickstyle_p.h2
-rw-r--r--src/quickcontrols2/qquickstyleselector.cpp122
-rw-r--r--src/quickcontrols2/qquickstyleselector_p.h9
-rw-r--r--src/quickcontrols2/qquickstyleselector_p_p.h9
-rw-r--r--tests/auto/qquickstyleselector/tst_qquickstyleselector.cpp11
7 files changed, 82 insertions, 95 deletions
diff --git a/src/imports/controls/qtquickcontrols2plugin.cpp b/src/imports/controls/qtquickcontrols2plugin.cpp
index c495b6b2..d0018901 100644
--- a/src/imports/controls/qtquickcontrols2plugin.cpp
+++ b/src/imports/controls/qtquickcontrols2plugin.cpp
@@ -98,12 +98,17 @@ QtQuickControls2Plugin::~QtQuickControls2Plugin()
void QtQuickControls2Plugin::registerTypes(const char *uri)
{
QQuickStylePrivate::init(typeUrl());
+
const QString style = QQuickStyle::name();
+ const QString fallback = QQuickStylePrivate::fallbackStyle();
if (!style.isEmpty())
QFileSelectorPrivate::addStatics(QStringList() << style.toLower());
QQuickStyleSelector selector;
- selector.setBaseUrl(typeUrl());
+ selector.addSelector(style);
+ if (!fallback.isEmpty())
+ selector.addSelector(fallback);
+ selector.setPaths(QQuickStylePrivate::stylePaths(true));
qmlRegisterModule(uri, 2, QT_VERSION_MINOR - 7); // Qt 5.7->2.0, 5.8->2.1, 5.9->2.2...
diff --git a/src/quickcontrols2/qquickstyle.cpp b/src/quickcontrols2/qquickstyle.cpp
index 5a295b6c..139e65f1 100644
--- a/src/quickcontrols2/qquickstyle.cpp
+++ b/src/quickcontrols2/qquickstyle.cpp
@@ -117,7 +117,7 @@ static QStringList defaultImportPathList()
importPaths.reserve(3);
importPaths += QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath);
importPaths += envPathList("QML2_IMPORT_PATH");
- importPaths += QStringLiteral("qrc:/qt-project.org/imports");
+ importPaths += QStringLiteral(":/qt-project.org/imports");
importPaths += QCoreApplication::applicationDirPath();
return importPaths;
}
@@ -264,10 +264,20 @@ struct QQuickStyleSpec
Q_GLOBAL_STATIC(QQuickStyleSpec, styleSpec)
-QStringList QQuickStylePrivate::stylePaths()
+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;
+ }
+
// system/custom style paths
- QStringList paths = envPathList("QT_QUICK_CONTROLS_STYLE_PATH");
+ paths += envPathList("QT_QUICK_CONTROLS_STYLE_PATH");
// built-in import paths
const QString targetPath = QStringLiteral("QtQuick/Controls.2");
@@ -278,6 +288,7 @@ QStringList QQuickStylePrivate::stylePaths()
paths += dir.absolutePath();
}
+ paths.removeDuplicates();
return paths;
}
diff --git a/src/quickcontrols2/qquickstyle_p.h b/src/quickcontrols2/qquickstyle_p.h
index b92df3c2..f6034fa0 100644
--- a/src/quickcontrols2/qquickstyle_p.h
+++ b/src/quickcontrols2/qquickstyle_p.h
@@ -59,7 +59,7 @@ class QSettings;
class Q_QUICKCONTROLS2_PRIVATE_EXPORT QQuickStylePrivate
{
public:
- static QStringList stylePaths();
+ static QStringList stylePaths(bool resolve = false);
static QString fallbackStyle();
static bool isCustomStyle();
static void init(const QUrl &baseUrl);
diff --git a/src/quickcontrols2/qquickstyleselector.cpp b/src/quickcontrols2/qquickstyleselector.cpp
index d5543c17..1dee4dcc 100644
--- a/src/quickcontrols2/qquickstyleselector.cpp
+++ b/src/quickcontrols2/qquickstyleselector.cpp
@@ -40,33 +40,16 @@
#include "qquickstyleselector_p.h"
#include "qquickstyleselector_p_p.h"
-#include "qquickstyle.h"
-#include "qquickstyle_p.h"
-#include <QtCore/qdir.h>
-#include <QtCore/qfile.h>
#include <QtCore/qfileinfo.h>
-#include <QtCore/qsysinfo.h>
#include <QtCore/qlocale.h>
-#include <QtQml/qqmlfile.h>
-
#include <QtCore/private/qfileselector_p.h>
-#include <QtGui/private/qguiapplication_p.h>
QT_BEGIN_NAMESPACE
-static bool isLocalScheme(const QString &scheme)
-{
- bool local = scheme == QLatin1String("qrc");
-#ifdef Q_OS_ANDROID
- local |= scheme == QLatin1String("assets");
-#endif
- return local;
-}
-
static QString ensureSlash(const QString &path)
{
- if (path.endsWith(QLatin1Char('/')))
+ if (path.isEmpty() || path.endsWith(QLatin1Char('/')))
return path;
return path + QLatin1Char('/');
}
@@ -79,105 +62,90 @@ static QStringList prefixedPlatformSelectors(const QChar &prefix)
return selectors;
}
-static QStringList allSelectors(const QString &style = QString())
+static QStringList allSelectors()
{
static const QStringList platformSelectors = prefixedPlatformSelectors(QLatin1Char('+'));
QStringList selectors = platformSelectors;
const QString locale = QLocale().name();
if (!locale.isEmpty())
selectors += QLatin1Char('+') + locale;
- if (!style.isEmpty())
- selectors.prepend(style);
return selectors;
}
-QString QQuickStyleSelectorPrivate::select(const QString &filePath) const
+QUrl QQuickStyleSelectorPrivate::select(const QString &filePath) const
{
QFileInfo fi(filePath);
// If file doesn't exist, don't select
if (!fi.exists())
- return filePath;
+ return QUrl();
- const QString path = fi.path();
- const QString ret = QFileSelectorPrivate::selectionHelper(path.isEmpty() ? QString() : path + QLatin1Char('/'),
- fi.fileName(), allSelectors(styleName), QChar());
+ const QString selected = QFileSelectorPrivate::selectionHelper(ensureSlash(fi.canonicalPath()),
+ fi.fileName(), allSelectors(), QChar());
- if (!ret.isEmpty())
- return ret;
- return filePath;
-}
+ if (selected.startsWith(QLatin1Char(':')))
+ return QUrl(QLatin1String("qrc") + selected);
-QString QQuickStyleSelectorPrivate::trySelect(const QString &filePath, const QString &fallback) const
-{
- QFileInfo fi(filePath);
- if (!fi.exists())
- return fallback;
-
- // the path contains the name of the custom/fallback style, so exclude it from
- // the selectors. the rest of the selectors (os, locale) are still valid, though.
- const QString path = fi.path();
- const QString selectedPath = QFileSelectorPrivate::selectionHelper(path.isEmpty() ? QString() : path + QLatin1Char('/'),
- fi.fileName(), allSelectors(), QChar());
- if (selectedPath.startsWith(QLatin1Char(':')))
- return QLatin1String("qrc") + selectedPath;
- return QUrl::fromLocalFile(QFileInfo(selectedPath).absoluteFilePath()).toString();
+ return QUrl::fromLocalFile(selected.isEmpty() ? filePath : selected);
}
QQuickStyleSelector::QQuickStyleSelector() : d_ptr(new QQuickStyleSelectorPrivate)
{
- Q_D(QQuickStyleSelector);
- d->styleName = QQuickStyle::name();
- d->stylePath = QQuickStyle::path();
}
QQuickStyleSelector::~QQuickStyleSelector()
{
}
-QUrl QQuickStyleSelector::baseUrl() const
+QStringList QQuickStyleSelector::selectors() const
{
Q_D(const QQuickStyleSelector);
- return d->baseUrl;
+ return d->selectors;
}
-void QQuickStyleSelector::setBaseUrl(const QUrl &url)
+void QQuickStyleSelector::addSelector(const QString &selector)
{
Q_D(QQuickStyleSelector);
- d->baseUrl = url;
- d->basePath = QQmlFile::urlToLocalFileOrQrc(url.toString(QUrl::StripTrailingSlash) + QLatin1Char('/'));
+ if (d->selectors.contains(selector))
+ return;
+
+ d->selectors += selector;
}
-QString QQuickStyleSelector::select(const QString &fileName) const
+QStringList QQuickStyleSelector::paths() const
{
Q_D(const QQuickStyleSelector);
+ return d->paths;
+}
- // 1) try selecting from a custom style path, for example ":/mystyle"
- if (QQuickStylePrivate::isCustomStyle()) {
- // NOTE: this path may contain a subset of controls
- const QString selectedPath = d->trySelect(ensureSlash(d->stylePath) + d->styleName + QLatin1Char('/') + fileName);
- if (!selectedPath.isEmpty())
- return selectedPath;
- }
+void QQuickStyleSelector::setPaths(const QStringList &paths)
+{
+ Q_D(QQuickStyleSelector);
+ d->paths = paths;
+}
- // 2) try selecting from the fallback style path, for example QT_INSTALL_QML/QtQuick/Controls.2/Material
- const QString fallbackStyle = QQuickStylePrivate::fallbackStyle();
- if (!fallbackStyle.isEmpty()) {
- // NOTE: this path may also contain a subset of controls
- const QString selectedPath = d->trySelect(ensureSlash(d->basePath) + fallbackStyle + QLatin1Char('/') + fileName);
- if (!selectedPath.isEmpty())
- return selectedPath;
+QUrl QQuickStyleSelector::select(const QString &fileName) const
+{
+ Q_D(const QQuickStyleSelector);
+ // The lookup order is
+ // 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)
+
+ int to = d->selectors.count() - 1;
+ if (d->selectors.isEmpty() || !d->selectors.first().isEmpty())
+ ++to; // lookup #3 unless #1 is also empty (redundant)
+
+ // NOTE: last iteration intentionally out of bounds => empty selector
+ for (int i = 0; i <= to; ++i) {
+ const QString selector = d->selectors.value(i);
+ for (const QString &path : d->paths) {
+ const QUrl selectedUrl = d->select(ensureSlash(path) + selector + QLatin1Char('/') + fileName);
+ if (selectedUrl.isValid())
+ return selectedUrl;
+ }
}
- // 3) fallback to the default style that is guaranteed to contain all controls
- QUrl url(ensureSlash(d->baseUrl.toString()) + fileName);
- if (isLocalScheme(url.scheme())) {
- QString equivalentPath = QLatin1Char(':') + url.path();
- QString selectedPath = d->select(equivalentPath);
- url.setPath(selectedPath.remove(0, 1));
- } else if (url.isLocalFile()) {
- url = QUrl::fromLocalFile(d->select(url.toLocalFile()));
- }
- return url.toString(QUrl::NormalizePathSegments);
+ return fileName;
}
QT_END_NAMESPACE
diff --git a/src/quickcontrols2/qquickstyleselector_p.h b/src/quickcontrols2/qquickstyleselector_p.h
index 29dba836..c4c0f540 100644
--- a/src/quickcontrols2/qquickstyleselector_p.h
+++ b/src/quickcontrols2/qquickstyleselector_p.h
@@ -67,10 +67,13 @@ public:
QQuickStyleSelector();
~QQuickStyleSelector();
- QUrl baseUrl() const;
- void setBaseUrl(const QUrl &url);
+ QStringList selectors() const;
+ void addSelector(const QString &selector);
- QString select(const QString &fileName) const;
+ QStringList paths() const;
+ void setPaths(const QStringList &paths);
+
+ QUrl select(const QString &fileName) const;
private:
Q_DISABLE_COPY(QQuickStyleSelector)
diff --git a/src/quickcontrols2/qquickstyleselector_p_p.h b/src/quickcontrols2/qquickstyleselector_p_p.h
index e940cd87..e69e7db2 100644
--- a/src/quickcontrols2/qquickstyleselector_p_p.h
+++ b/src/quickcontrols2/qquickstyleselector_p_p.h
@@ -59,13 +59,10 @@ QT_BEGIN_NAMESPACE
class QQuickStyleSelectorPrivate
{
public:
- QString select(const QString &filePath) const;
- QString trySelect(const QString &filePath, const QString &fallback = QString()) const;
+ QUrl select(const QString &filePath) const;
- QUrl baseUrl;
- QString basePath;
- QString styleName;
- QString stylePath;
+ QStringList paths;
+ QStringList selectors;
};
QT_END_NAMESPACE
diff --git a/tests/auto/qquickstyleselector/tst_qquickstyleselector.cpp b/tests/auto/qquickstyleselector/tst_qquickstyleselector.cpp
index e90a8bd9..ce2e2e99 100644
--- a/tests/auto/qquickstyleselector/tst_qquickstyleselector.cpp
+++ b/tests/auto/qquickstyleselector/tst_qquickstyleselector.cpp
@@ -96,7 +96,7 @@ void tst_QQuickStyleSelector::select_data()
QTest::newRow("nosuch/label") << "Label.qml" << "NoSuchStyle" << "data" << "" << testFileUrl("Label.qml").toString();
QTest::newRow("/nosuch/label") << "Label.qml" << "NoSuchStyle" << dataDirectory() << "" << testFileUrl("Label.qml").toString();
- QTest::newRow("label->base") << "Label.qml" << "" << "data" << "FallbackStyle" << testFileUrl("FallbackStyle/Label.qml").toString();
+ QTest::newRow("label->base") << "Label.qml" << "" << "data" << "FallbackStyle" << testFileUrl("Label.qml").toString();
QTest::newRow("/label->base") << "Label.qml" << "" << dataDirectory() << "FallbackStyle" << testFileUrl("Label.qml").toString();
QTest::newRow("fs/label->base") << "Label.qml" << "FileSystemStyle" << "data" << "FallbackStyle" << testFileUrl("FallbackStyle/Label.qml").toString();
QTest::newRow("/fs/label->base") << "Label.qml" << "FileSystemStyle" << dataDirectory() << "FallbackStyle" << testFileUrl("FallbackStyle/Label.qml").toString();
@@ -115,7 +115,7 @@ void tst_QQuickStyleSelector::select_data()
QTest::newRow("nosuch/button") << "Button.qml" << "NoSuchStyle" << "data" << "" << testFileUrl("Button.qml").toString();
QTest::newRow("/nosuch/button") << "Button.qml" << "NoSuchStyle" << dataDirectory() << "" << testFileUrl("Button.qml").toString();
- QTest::newRow("button->base") << "Button.qml" << "" << "data" << "FallbackStyle" << testFileUrl("FallbackStyle/Button.qml").toString();
+ QTest::newRow("button->base") << "Button.qml" << "" << "data" << "FallbackStyle" << testFileUrl("Button.qml").toString();
QTest::newRow("/button->base") << "Button.qml" << "" << dataDirectory() << "FallbackStyle" << testFileUrl("Button.qml").toString();
QTest::newRow("fs/button->base") << "Button.qml" << "FileSystemStyle" << "data" << "FallbackStyle" << testFileUrl("FileSystemStyle/Button.qml").toString();
QTest::newRow("/fs/button->base") << "Button.qml" << "FileSystemStyle" << dataDirectory() << "FallbackStyle" << testFileUrl("FileSystemStyle/Button.qml").toString();
@@ -137,7 +137,9 @@ void tst_QQuickStyleSelector::select()
QQuickStyle::setFallbackStyle(fallback);
QQuickStyleSelector selector;
- selector.setBaseUrl(dataDirectoryUrl());
+ selector.addSelector(style);
+ selector.addSelector(fallback);
+ selector.setPaths(QStringList() << dataDirectory() << ":/");
QCOMPARE(selector.select(file), expected);
}
@@ -146,7 +148,8 @@ void tst_QQuickStyleSelector::platformSelectors()
QQuickStyle::setStyle(QDir(dataDirectory()).filePath("PlatformStyle"));
QQuickStyleSelector selector;
- selector.setBaseUrl(dataDirectoryUrl());
+ selector.addSelector("PlatformStyle");
+ selector.setPaths(QStringList() << dataDirectory());
#if defined(Q_OS_LINUX)
QCOMPARE(selector.select("Button.qml"), testFileUrl("PlatformStyle/+linux/Button.qml").toString());