diff options
Diffstat (limited to 'src/quickcontrols2')
-rw-r--r-- | src/quickcontrols2/qquickpaddedrectangle.cpp | 2 | ||||
-rw-r--r-- | src/quickcontrols2/qquickproxytheme.cpp | 6 | ||||
-rw-r--r-- | src/quickcontrols2/qquickproxytheme_p.h | 3 | ||||
-rw-r--r-- | src/quickcontrols2/qquickstyle.cpp | 138 | ||||
-rw-r--r-- | src/quickcontrols2/qquickstyle.h | 1 | ||||
-rw-r--r-- | src/quickcontrols2/qquickstyle_p.h | 67 | ||||
-rw-r--r-- | src/quickcontrols2/qquickstyleattached.cpp | 21 | ||||
-rw-r--r-- | src/quickcontrols2/qquickstyleselector.cpp | 92 | ||||
-rw-r--r-- | src/quickcontrols2/qquickstyleselector_p_p.h | 5 | ||||
-rw-r--r-- | src/quickcontrols2/qquicktumblerview.cpp | 273 | ||||
-rw-r--r-- | src/quickcontrols2/qquicktumblerview_p.h | 109 | ||||
-rw-r--r-- | src/quickcontrols2/quickcontrols2.pri | 7 |
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 |