aboutsummaryrefslogtreecommitdiffstats
path: root/src/quickcontrols2
diff options
context:
space:
mode:
Diffstat (limited to 'src/quickcontrols2')
-rw-r--r--src/quickcontrols2/qquickpaddedrectangle.cpp2
-rw-r--r--src/quickcontrols2/qquickproxytheme.cpp6
-rw-r--r--src/quickcontrols2/qquickproxytheme_p.h3
-rw-r--r--src/quickcontrols2/qquickstyle.cpp138
-rw-r--r--src/quickcontrols2/qquickstyle.h1
-rw-r--r--src/quickcontrols2/qquickstyle_p.h67
-rw-r--r--src/quickcontrols2/qquickstyleattached.cpp21
-rw-r--r--src/quickcontrols2/qquickstyleselector.cpp92
-rw-r--r--src/quickcontrols2/qquickstyleselector_p_p.h5
-rw-r--r--src/quickcontrols2/qquicktumblerview.cpp273
-rw-r--r--src/quickcontrols2/qquicktumblerview_p.h109
-rw-r--r--src/quickcontrols2/quickcontrols2.pri7
12 files changed, 647 insertions, 77 deletions
diff --git a/src/quickcontrols2/qquickpaddedrectangle.cpp b/src/quickcontrols2/qquickpaddedrectangle.cpp
index 08d74dc6..28423f51 100644
--- a/src/quickcontrols2/qquickpaddedrectangle.cpp
+++ b/src/quickcontrols2/qquickpaddedrectangle.cpp
@@ -184,7 +184,7 @@ QSGNode *QQuickPaddedRectangle::updatePaintNode(QSGNode *node, UpdatePaintNodeDa
if (!transformNode)
transformNode = new QSGTransformNode;
- QSGRectangleNode *rectNode = static_cast<QSGRectangleNode *>(QQuickRectangle::updatePaintNode(transformNode->firstChild(), data));
+ QSGInternalRectangleNode *rectNode = static_cast<QSGInternalRectangleNode *>(QQuickRectangle::updatePaintNode(transformNode->firstChild(), data));
if (rectNode) {
if (!transformNode->firstChild())
diff --git a/src/quickcontrols2/qquickproxytheme.cpp b/src/quickcontrols2/qquickproxytheme.cpp
index da4f2a2e..3791bb06 100644
--- a/src/quickcontrols2/qquickproxytheme.cpp
+++ b/src/quickcontrols2/qquickproxytheme.cpp
@@ -137,11 +137,11 @@ QPixmap QQuickProxyTheme::standardPixmap(QPlatformTheme::StandardPixmap sp, cons
return QPlatformTheme::standardPixmap(sp, size);
}
-QPixmap QQuickProxyTheme::fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &size, QPlatformTheme::IconOptions iconOptions) const
+QIcon QQuickProxyTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions iconOptions) const
{
if (m_theme)
- return m_theme->fileIconPixmap(fileInfo, size, iconOptions);
- return QPlatformTheme::fileIconPixmap(fileInfo, size, iconOptions);
+ return m_theme->fileIcon(fileInfo, iconOptions);
+ return QPlatformTheme::fileIcon(fileInfo, iconOptions);
}
QIconEngine *QQuickProxyTheme::createIconEngine(const QString &iconName) const
diff --git a/src/quickcontrols2/qquickproxytheme_p.h b/src/quickcontrols2/qquickproxytheme_p.h
index c94417dc..c2cec4f7 100644
--- a/src/quickcontrols2/qquickproxytheme_p.h
+++ b/src/quickcontrols2/qquickproxytheme_p.h
@@ -80,8 +80,7 @@ public:
QVariant themeHint(ThemeHint hint) const override;
QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const override;
- QPixmap fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &size,
- QPlatformTheme::IconOptions iconOptions = 0) const override;
+ QIcon fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions iconOptions = 0) const override;
QIconEngine *createIconEngine(const QString &iconName) const override;
diff --git a/src/quickcontrols2/qquickstyle.cpp b/src/quickcontrols2/qquickstyle.cpp
index 471fcec2..80a7abef 100644
--- a/src/quickcontrols2/qquickstyle.cpp
+++ b/src/quickcontrols2/qquickstyle.cpp
@@ -35,6 +35,7 @@
****************************************************************************/
#include "qquickstyle.h"
+#include "qquickstyle_p.h"
#include "qquickstyleattached_p.h"
#include <QtCore/qdir.h>
@@ -84,7 +85,7 @@ QT_BEGIN_NAMESPACE
struct QQuickStyleSpec
{
- QQuickStyleSpec() : resolved(false) { }
+ QQuickStyleSpec() : custom(false), resolved(false) { }
QString name()
{
@@ -110,54 +111,126 @@ struct QQuickStyleSpec
resolve();
}
- void resolve()
+ void setFallbackStyle(const QString &fallback, const QByteArray &method)
+ {
+ fallbackStyle = fallback;
+ fallbackMethod = method;
+ }
+
+ static QString findStyle(const QString &path, const QString &name)
+ {
+ 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())
{
if (style.isEmpty())
style = QGuiApplicationPrivate::styleOverride;
if (style.isEmpty())
style = QString::fromLatin1(qgetenv("QT_QUICK_CONTROLS_STYLE"));
- if (style.isEmpty()) {
+ if (fallbackStyle.isEmpty())
+ setFallbackStyle(QString::fromLatin1(qgetenv("QT_QUICK_CONTROLS_FALLBACK_STYLE")), "QT_QUICK_CONTROLS_FALLBACK_STYLE");
+ if (style.isEmpty() || fallbackStyle.isEmpty()) {
QSharedPointer<QSettings> settings = QQuickStyleAttached::settings(QStringLiteral("Controls"));
- if (settings)
- style = settings->value(QStringLiteral("Style")).toString();
+ if (settings) {
+ if (style.isEmpty())
+ style = settings->value(QStringLiteral("Style")).toString();
+ if (fallbackStyle.isEmpty())
+ setFallbackStyle(settings->value(QStringLiteral("FallbackStyle")).toString(), ":/qtquickcontrols2.conf");
+ }
+ }
+ custom = style.contains(QLatin1Char('/'));
+
+ if (baseUrl.isValid()) {
+ QString path = QQmlFile::urlToLocalFileOrQrc(baseUrl);
+ QString stylePath = findStyle(path, style);
+ if (!stylePath.isEmpty()) {
+ style = stylePath;
+ resolved = true;
+ }
}
if (QGuiApplication::instance()) {
- if (!style.contains(QLatin1Char('/'))) {
+ if (!custom) {
const QString targetPath = QStringLiteral("QtQuick/Controls.2");
const QStringList importPaths = QQmlEngine().importPathList();
for (const QString &importPath : importPaths) {
- QDir importDir(importPath);
- if (importDir.cd(targetPath)) {
- if (style.isEmpty()) {
- style = importDir.absolutePath() + QLatin1Char('/');
- resolved = true;
- break;
- }
- const QStringList entries = importDir.entryList(QStringList(), QDir::Dirs | QDir::NoDotAndDotDot);
- for (const QString &entry : entries) {
- if (entry.compare(style, Qt::CaseInsensitive) == 0) {
- style = importDir.absoluteFilePath(entry);
- resolved = true;
- break;
- }
- }
- }
- if (resolved)
+ QString stylePath = findStyle(importPath + QLatin1Char('/') + targetPath, style);
+ if (!stylePath.isEmpty()) {
+ style = stylePath;
+ resolved = true;
break;
+ }
}
}
resolved = true;
}
}
+ void reset()
+ {
+ custom = false;
+ resolved = false;
+ style.clear();
+ fallbackStyle.clear();
+ fallbackMethod.clear();
+ }
+
+ bool custom;
bool resolved;
QString style;
+ QString fallbackStyle;
+ QByteArray fallbackMethod;
};
Q_GLOBAL_STATIC(QQuickStyleSpec, styleSpec)
+QString QQuickStylePrivate::fallbackStyle()
+{
+ return styleSpec()->fallbackStyle;
+}
+
+bool QQuickStylePrivate::isCustomStyle()
+{
+ return styleSpec()->custom;
+}
+
+void QQuickStylePrivate::init(const QUrl &baseUrl)
+{
+ QQuickStyleSpec *spec = styleSpec();
+ spec->resolve(baseUrl);
+
+ if (!spec->fallbackStyle.isEmpty()) {
+ QString fallbackStyle = spec->findStyle(baseUrl.toLocalFile(), spec->fallbackStyle);
+ 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();
+ }
+ }
+}
+
+void QQuickStylePrivate::reset()
+{
+ styleSpec()->reset();
+}
+
/*!
Returns the name of the application style.
@@ -199,4 +272,23 @@ void QQuickStyle::setStyle(const QString &style)
styleSpec()->setStyle(style);
}
+/*!
+ \since 5.9
+ Sets the application fallback style to \a style.
+
+ \note The fallback style must be the name of one of the built-in Qt Quick Controls 2 styles, e.g. "Material".
+
+ \note The style must be configured \b before loading QML that imports Qt Quick Controls 2.
+ It is not possible to change the style after the QML types have been registered.
+*/
+void QQuickStyle::setFallbackStyle(const QString &style)
+{
+ if (QQmlMetaType::isModule(QStringLiteral("QtQuick.Controls"), 2, 0)) {
+ qWarning() << "ERROR: QQuickStyle::setFallbackStyle() must be called before loading QML that imports Qt Quick Controls 2.";
+ return;
+ }
+
+ styleSpec()->setFallbackStyle(style, "QQuickStyle::setFallbackStyle()");
+}
+
QT_END_NAMESPACE
diff --git a/src/quickcontrols2/qquickstyle.h b/src/quickcontrols2/qquickstyle.h
index 4476a29a..d2e7faf1 100644
--- a/src/quickcontrols2/qquickstyle.h
+++ b/src/quickcontrols2/qquickstyle.h
@@ -49,6 +49,7 @@ public:
static QString name();
static QString path();
static void setStyle(const QString &style);
+ static void setFallbackStyle(const QString &style);
};
QT_END_NAMESPACE
diff --git a/src/quickcontrols2/qquickstyle_p.h b/src/quickcontrols2/qquickstyle_p.h
new file mode 100644
index 00000000..cfe87fbb
--- /dev/null
+++ b/src/quickcontrols2/qquickstyle_p.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module 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$
+**
+****************************************************************************/
+
+#ifndef QQUICKSTYLE_P_H
+#define QQUICKSTYLE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qurl.h>
+#include <QtQuickControls2/private/qtquickcontrols2global_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICKCONTROLS2_PRIVATE_EXPORT QQuickStylePrivate
+{
+public:
+ static QString fallbackStyle();
+ static bool isCustomStyle();
+ static void init(const QUrl &baseUrl);
+ static void reset();
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKSTYLE_P_H
diff --git a/src/quickcontrols2/qquickstyleattached.cpp b/src/quickcontrols2/qquickstyleattached.cpp
index e72abde0..2ef07fd6 100644
--- a/src/quickcontrols2/qquickstyleattached.cpp
+++ b/src/quickcontrols2/qquickstyleattached.cpp
@@ -39,12 +39,33 @@
#include <QtCore/qfile.h>
#include <QtCore/qsettings.h>
#include <QtCore/qfileselector.h>
+#include <QtGui/qcolor.h>
+#include <QtGui/qpalette.h>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qplatformtheme.h>
#include <QtQuick/qquickwindow.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuickTemplates2/private/qquickpopup_p.h>
QT_BEGIN_NAMESPACE
+static bool isDarkSystemTheme()
+{
+ if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
+ if (const QPalette *systemPalette = theme->palette(QPlatformTheme::SystemPalette)) {
+ const QColor textColor = systemPalette->color(QPalette::WindowText);
+ return textColor.red() > 128 && textColor.blue() > 128 && textColor.green() > 128;
+ }
+ }
+ return false;
+}
+
+Q_QUICKCONTROLS2_PRIVATE_EXPORT bool qt_is_dark_system_theme()
+{
+ static bool dark = isDarkSystemTheme();
+ return dark;
+}
+
static QQuickStyleAttached *attachedStyle(const QMetaObject *type, QObject *object, bool create = false)
{
if (!object)
diff --git a/src/quickcontrols2/qquickstyleselector.cpp b/src/quickcontrols2/qquickstyleselector.cpp
index 5210a684..8dbbc064 100644
--- a/src/quickcontrols2/qquickstyleselector.cpp
+++ b/src/quickcontrols2/qquickstyleselector.cpp
@@ -35,12 +35,14 @@
#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>
@@ -56,6 +58,13 @@ static bool isLocalScheme(const QString &scheme)
return local;
}
+static QString ensureSlash(const QString &path)
+{
+ if (path.endsWith(QLatin1Char('/')))
+ return path;
+ return path + QLatin1Char('/');
+}
+
static QStringList allSelectors(const QString &style = QString())
{
static const QStringList platformSelectors = QFileSelectorPrivate::platformSelectors();
@@ -66,33 +75,6 @@ static QStringList allSelectors(const QString &style = QString())
return selectors;
}
-static QString selectionHelper(const QString &path, const QString &fileName, const QStringList &selectors)
-{
- /* selectionHelper does a depth-first search of possible selected files. Because there is strict
- selector ordering in the API, we can stop checking as soon as we find the file in a directory
- which does not contain any other valid selector directories.
- */
- Q_ASSERT(path.isEmpty() || path.endsWith(QLatin1Char('/')));
-
- for (const QString &s : selectors) {
- QString prospectiveBase = path + s + QLatin1Char('/');
- QStringList remainingSelectors = selectors;
- remainingSelectors.removeAll(s);
- if (!QDir(prospectiveBase).exists())
- continue;
- QString prospectiveFile = selectionHelper(prospectiveBase, fileName, remainingSelectors);
- if (!prospectiveFile.isEmpty())
- return prospectiveFile;
- }
-
- // If we reach here there were no successful files found at a lower level in this branch, so we
- // should check this level as a potential result.
- const QString result = path + fileName;
- if (!QFileInfo::exists(result))
- return QString();
- return result;
-}
-
QString QQuickStyleSelectorPrivate::select(const QString &filePath) const
{
QFileInfo fi(filePath);
@@ -101,18 +83,35 @@ QString QQuickStyleSelectorPrivate::select(const QString &filePath) const
return filePath;
const QString path = fi.path();
- const QString ret = selectionHelper(path.isEmpty() ? QString() : path + QLatin1Char('/'),
- fi.fileName(), allSelectors(style));
+ const QString ret = QFileSelectorPrivate::selectionHelper(path.isEmpty() ? QString() : path + QLatin1Char('/'),
+ fi.fileName(), allSelectors(styleName), QChar());
if (!ret.isEmpty())
return ret;
return filePath;
}
+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();
+}
+
QQuickStyleSelector::QQuickStyleSelector() : d_ptr(new QQuickStyleSelectorPrivate)
{
Q_D(QQuickStyleSelector);
- d->style = QQuickStyle::name();
+ d->styleName = QQuickStyle::name();
+ d->stylePath = QQuickStyle::path();
}
QQuickStyleSelector::~QQuickStyleSelector()
@@ -129,29 +128,32 @@ void QQuickStyleSelector::setBaseUrl(const QUrl &url)
{
Q_D(QQuickStyleSelector);
d->baseUrl = url;
+ d->basePath = QQmlFile::urlToLocalFileOrQrc(url.toString(QUrl::StripTrailingSlash) + QLatin1Char('/'));
}
QString QQuickStyleSelector::select(const QString &fileName) const
{
Q_D(const QQuickStyleSelector);
- const QString overridePath = QQuickStyle::path();
- if (!overridePath.isEmpty()) {
- const QString stylePath = overridePath + d->style + QLatin1Char('/');
- if (QFile::exists(stylePath + fileName)) {
- // the style name is included to the path, so exclude it from the selectors.
- // the rest of the selectors (os, locale) are still valid, though.
- const QString selectedPath = selectionHelper(stylePath, fileName, allSelectors());
- if (selectedPath.startsWith(QLatin1Char(':')))
- return QLatin1String("qrc") + selectedPath;
- return QUrl::fromLocalFile(QFileInfo(selectedPath).absoluteFilePath()).toString();
- }
+
+ // 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;
}
- QString base = d->baseUrl.toString();
- if (!base.isEmpty() && !base.endsWith(QLatin1Char('/')))
- base += QLatin1Char('/');
+ // 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 url(base + fileName);
+ // 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);
diff --git a/src/quickcontrols2/qquickstyleselector_p_p.h b/src/quickcontrols2/qquickstyleselector_p_p.h
index cc3f0a58..4ff28d5d 100644
--- a/src/quickcontrols2/qquickstyleselector_p_p.h
+++ b/src/quickcontrols2/qquickstyleselector_p_p.h
@@ -54,9 +54,12 @@ class QQuickStyleSelectorPrivate
{
public:
QString select(const QString &filePath) const;
+ QString trySelect(const QString &filePath, const QString &fallback = QString()) const;
QUrl baseUrl;
- QString style;
+ QString basePath;
+ QString styleName;
+ QString stylePath;
};
QT_END_NAMESPACE
diff --git a/src/quickcontrols2/qquicktumblerview.cpp b/src/quickcontrols2/qquicktumblerview.cpp
new file mode 100644
index 00000000..540a8dd1
--- /dev/null
+++ b/src/quickcontrols2/qquicktumblerview.cpp
@@ -0,0 +1,273 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module 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 "qquicktumblerview_p.h"
+
+#include <QtQml/private/qqmldelegatemodel_p.h>
+#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquicklistview_p.h>
+#include <QtQuick/private/qquickpathview_p.h>
+
+#include <QtQuickTemplates2/private/qquicktumbler_p.h>
+#include <QtQuickTemplates2/private/qquicktumbler_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQuickTumblerView::QQuickTumblerView(QQuickItem *parent) :
+ QQuickItem(parent),
+ m_tumbler(nullptr),
+ m_delegate(nullptr),
+ m_pathView(nullptr),
+ m_listView(nullptr),
+ m_path(nullptr)
+{
+ // We don't call createView() here because we don't know what the wrap flag is set to
+ // yet, and we don't want to create a view that might never get used.
+}
+
+QVariant QQuickTumblerView::model() const
+{
+ return m_model;
+}
+
+void QQuickTumblerView::setModel(const QVariant &model)
+{
+ if (model == m_model)
+ return;
+
+ m_model = model;
+
+ if (m_pathView) {
+ m_pathView->setModel(m_model);
+ } else if (m_listView) {
+ // QQuickItemView::setModel() resets the current index,
+ // but if we're still creating the Tumbler, it should be maintained.
+ const int oldCurrentIndex = m_listView->currentIndex();
+ m_listView->setModel(m_model);
+ if (!isComponentComplete())
+ m_listView->setCurrentIndex(oldCurrentIndex);
+ }
+
+ emit modelChanged();
+}
+
+QQmlComponent *QQuickTumblerView::delegate() const
+{
+ return m_delegate;
+}
+
+void QQuickTumblerView::setDelegate(QQmlComponent *delegate)
+{
+ if (delegate == m_delegate)
+ return;
+
+ m_delegate = delegate;
+
+ if (m_pathView)
+ m_pathView->setDelegate(m_delegate);
+ else if (m_listView)
+ m_listView->setDelegate(m_delegate);
+
+ emit delegateChanged();
+}
+
+QQuickPath *QQuickTumblerView::path() const
+{
+ return m_path;
+}
+
+void QQuickTumblerView::setPath(QQuickPath *path)
+{
+ if (path == m_path)
+ return;
+
+ m_path = path;
+ emit pathChanged();
+}
+
+void QQuickTumblerView::createView()
+{
+ Q_ASSERT(m_tumbler);
+
+ // We create a view regardless of whether or not we know
+ // the count yet, because we rely on the view to tell us the count.
+ if (m_tumbler->wrap()) {
+ if (m_listView) {
+ delete m_listView;
+ m_listView = nullptr;
+ }
+
+ if (!m_pathView) {
+ m_pathView = new QQuickPathView;
+ QQmlEngine::setContextForObject(m_pathView, qmlContext(this));
+ QQml_setParent_noEvent(m_pathView, this);
+ m_pathView->setParentItem(this);
+ m_pathView->setPath(m_path);
+ m_pathView->setDelegate(m_delegate);
+ m_pathView->setPreferredHighlightBegin(0.5);
+ m_pathView->setPreferredHighlightEnd(0.5);
+ m_pathView->setClip(true);
+
+ // Give the view a size.
+ updateView();
+ // Ensure that the model is set eventually.
+ polish();
+ }
+ } else {
+ if (m_pathView) {
+ delete m_pathView;
+ m_pathView = nullptr;
+ }
+
+ if (!m_listView) {
+ m_listView = new QQuickListView;
+ QQmlEngine::setContextForObject(m_listView, qmlContext(this));
+ QQml_setParent_noEvent(m_listView, this);
+ m_listView->setParentItem(this);
+ m_listView->setSnapMode(QQuickListView::SnapToItem);
+ m_listView->setHighlightRangeMode(QQuickListView::StrictlyEnforceRange);
+ m_listView->setClip(true);
+ m_listView->setDelegate(m_delegate);
+
+ // Give the view a size.
+ updateView();
+ // Ensure that the model is set eventually.
+ polish();
+ }
+ }
+}
+
+// Called whever the size or visibleItemCount changes.
+void QQuickTumblerView::updateView()
+{
+ QQuickItem *theView = view();
+ if (!theView)
+ return;
+
+ theView->setSize(QSizeF(width(), height()));
+
+ // Can be called in geometryChanged when it might not have a parent item yet.
+ if (!m_tumbler)
+ return;
+
+ // Set view-specific properties that have a dependency on the size, etc.
+ if (m_pathView) {
+ m_pathView->setPathItemCount(m_tumbler->visibleItemCount() + 1);
+ m_pathView->setDragMargin(width() / 2);
+ } else {
+ m_listView->setPreferredHighlightBegin(height() / 2 - (height() / m_tumbler->visibleItemCount() / 2));
+ m_listView->setPreferredHighlightEnd(height() / 2 + (height() / m_tumbler->visibleItemCount() / 2));
+ }
+}
+
+void QQuickTumblerView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ QQuickItem::geometryChanged(newGeometry, oldGeometry);
+ updateView();
+}
+
+void QQuickTumblerView::componentComplete()
+{
+ QQuickItem::componentComplete();
+ updateView();
+}
+
+void QQuickTumblerView::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
+{
+ QQuickItem::itemChange(change, data);
+
+ if (change == QQuickItem::ItemParentHasChanged && data.item) {
+ if (m_tumbler)
+ m_tumbler->disconnect(this);
+
+ m_tumbler = qobject_cast<QQuickTumbler*>(parentItem());
+
+ if (m_tumbler) {
+ // We assume that the parentChanged() signal of the tumbler will be emitted before its wrap property is set...
+ connect(m_tumbler, &QQuickTumbler::wrapChanged, this, &QQuickTumblerView::createView);
+ connect(m_tumbler, &QQuickTumbler::visibleItemCountChanged, this, &QQuickTumblerView::updateView);
+ }
+ }
+}
+
+void QQuickTumblerView::updatePolish()
+{
+ // There are certain cases where model count changes can potentially cause problems.
+ // An example of this is a ListModel that appends items in a for loop in Component.onCompleted.
+ // If we didn't delay assignment of the model, the PathView/ListView would be deleted in
+ // response to it emitting countChanged(), causing a crash. To avoid this issue,
+ // and to avoid the overhead of count affecting the wrap property, which in turn may
+ // unnecessarily create delegates that are never seen, we delay setting the model. This ensures that
+ // Component.onCompleted would have been finished, for example.
+ if (m_pathView && !m_pathView->model().isValid() && m_model.isValid()) {
+ // QQuickPathView::setPathItemCount() resets the offset animation,
+ // so we just skip the animation while constructing the view.
+ const int oldHighlightMoveDuration = m_pathView->highlightMoveDuration();
+ m_pathView->setHighlightMoveDuration(0);
+
+ // Setting model can change the count, which can affect the wrap, which can cause
+ // the current view to be deleted before setModel() is finished, which causes a crash.
+ // Since QQuickTumbler can't know about QQuickTumblerView, we use its private API to
+ // inform it that it should delay setting wrap.
+ QQuickTumblerPrivate *tumblerPrivate = QQuickTumblerPrivate::get(m_tumbler);
+ tumblerPrivate->lockWrap();
+ m_pathView->setModel(m_model);
+ tumblerPrivate->unlockWrap();
+
+ // The count-depends-on-wrap behavior could cause wrap to change after
+ // the call above, so we must check that we're still using a PathView.
+ if (m_pathView)
+ m_pathView->setHighlightMoveDuration(oldHighlightMoveDuration);
+ } else if (m_listView && !m_listView->model().isValid() && m_model.isValid()) {
+ // Usually we'd do this in QQuickTumbler::setWrap(), but that will be too early for polishes.
+ const int currentIndex = m_tumbler->currentIndex();
+ m_listView->setModel(m_model);
+ m_listView->setCurrentIndex(currentIndex);
+ }
+}
+
+QQuickItem *QQuickTumblerView::view()
+{
+ if (!m_tumbler)
+ return nullptr;
+
+ if (m_tumbler->wrap())
+ return m_pathView;
+
+ return m_listView;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickcontrols2/qquicktumblerview_p.h b/src/quickcontrols2/qquicktumblerview_p.h
new file mode 100644
index 00000000..f3ba3721
--- /dev/null
+++ b/src/quickcontrols2/qquicktumblerview_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module 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$
+**
+****************************************************************************/
+
+#ifndef QQUICKTUMBLERVIEW_P_H
+#define QQUICKTUMBLERVIEW_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QQuickItem>
+#include <QtQuickControls2/private/qtquickcontrols2global_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickListView;
+class QQuickPath;
+class QQuickPathView;
+
+class QQuickTumbler;
+
+class Q_QUICKCONTROLS2_PRIVATE_EXPORT QQuickTumblerView : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged)
+ Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
+ Q_PROPERTY(QQuickPath *path READ path WRITE setPath NOTIFY pathChanged)
+
+public:
+ QQuickTumblerView(QQuickItem *parent = nullptr);
+
+ QVariant model() const;
+ void setModel(const QVariant &model);
+
+ QQmlComponent *delegate() const;
+ void setDelegate(QQmlComponent *delegate);
+
+ QQuickPath *path() const;
+ void setPath(QQuickPath *path);
+
+Q_SIGNALS:
+ void modelChanged();
+ void delegateChanged();
+ void pathChanged();
+
+protected:
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+ void componentComplete() override;
+ void itemChange(ItemChange change, const ItemChangeData &data) override;
+ void updatePolish() override;
+
+private:
+ QQuickItem *view();
+ void createView();
+ void updateView();
+
+ void wrapChange();
+
+ QQuickTumbler *m_tumbler;
+ QVariant m_model;
+ QQmlComponent *m_delegate;
+ QQuickPathView *m_pathView;
+ QQuickListView *m_listView;
+ QQuickPath *m_path;
+};
+
+QT_END_NAMESPACE
+
+#endif // TUMBLERVIEW_H
diff --git a/src/quickcontrols2/quickcontrols2.pri b/src/quickcontrols2/quickcontrols2.pri
index aaf01ed6..8e4e0046 100644
--- a/src/quickcontrols2/quickcontrols2.pri
+++ b/src/quickcontrols2/quickcontrols2.pri
@@ -2,11 +2,13 @@ HEADERS += \
$$PWD/qquickcolorimageprovider_p.h \
$$PWD/qquickproxytheme_p.h \
$$PWD/qquickstyle.h \
+ $$PWD/qquickstyle_p.h \
$$PWD/qquickstyleattached_p.h \
$$PWD/qquickstyleplugin_p.h \
$$PWD/qquickstyleselector_p.h \
$$PWD/qquickstyleselector_p_p.h \
- $$PWD/qquickpaddedrectangle_p.h
+ $$PWD/qquickpaddedrectangle_p.h \
+ $$PWD/qquicktumblerview_p.h
SOURCES += \
$$PWD/qquickcolorimageprovider.cpp \
@@ -15,4 +17,5 @@ SOURCES += \
$$PWD/qquickstyleattached.cpp \
$$PWD/qquickstyleplugin.cpp \
$$PWD/qquickstyleselector.cpp \
- $$PWD/qquickpaddedrectangle.cpp
+ $$PWD/qquickpaddedrectangle.cpp \
+ $$PWD/qquicktumblerview.cpp