diff options
Diffstat (limited to 'src/templates/qquicktumbler.cpp')
-rw-r--r-- | src/templates/qquicktumbler.cpp | 542 |
1 files changed, 0 insertions, 542 deletions
diff --git a/src/templates/qquicktumbler.cpp b/src/templates/qquicktumbler.cpp deleted file mode 100644 index c5b3f676..00000000 --- a/src/templates/qquicktumbler.cpp +++ /dev/null @@ -1,542 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the Qt Labs Templates 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 "qquicktumbler_p.h" - -#include <QtQuick/private/qquickflickable_p.h> -#include <QtLabsTemplates/private/qquickcontrol_p_p.h> - -QT_BEGIN_NAMESPACE - -/*! - \qmltype Tumbler - \inherits Control - \instantiates QQuickTumbler - \inqmlmodule Qt.labs.controls - \ingroup qtlabscontrols-input - \brief A spinnable wheel of items that can be selected. - - \code - Tumbler { - model: 5 - // ... - } - \endcode - - \section1 Non-wrapping Tumbler - - The default contentItem of Tumbler is a \l PathView, which wraps when it - reaches the top and bottom. To achieve a non-wrapping Tumbler, use ListView - as the contentItem: - - \snippet tst_tumbler.qml contentItem - - \image qtlabscontrols-tumbler-wrap.gif - - \labs - - \sa {Customizing Tumbler}, {Input Controls} -*/ - -class QQuickTumblerPrivate : public QQuickControlPrivate, public QQuickItemChangeListener -{ - Q_DECLARE_PUBLIC(QQuickTumbler) - -public: - QQuickTumblerPrivate() : - delegate(Q_NULLPTR), - visibleItemCount(3) - { - } - - ~QQuickTumblerPrivate() - { - } - - QVariant model; - QQmlComponent *delegate; - int visibleItemCount; - - void _q_updateItemHeights(); - void _q_updateItemWidths(); - - void itemChildAdded(QQuickItem *, QQuickItem *) Q_DECL_OVERRIDE; - void itemChildRemoved(QQuickItem *, QQuickItem *) Q_DECL_OVERRIDE; -}; - -static QList<QQuickItem *> contentItemChildItems(QQuickItem *contentItem) -{ - if (!contentItem) - return QList<QQuickItem *>(); - - // PathView has no contentItem property, but ListView does. - QQuickFlickable *flickable = qobject_cast<QQuickFlickable *>(contentItem); - return flickable ? flickable->contentItem()->childItems() : contentItem->childItems(); -} - -namespace { - static inline qreal delegateHeight(const QQuickTumbler *tumbler) - { - return tumbler->availableHeight() / tumbler->visibleItemCount(); - } - - enum ContentItemType { - UnsupportedContentItemType, - PathViewContentItem, - ListViewContentItem - }; - - static inline QQuickItem *actualContentItem(QQuickItem *rootContentItem, ContentItemType contentType) - { - if (contentType == PathViewContentItem) - return rootContentItem; - else if (contentType == ListViewContentItem) - return qobject_cast<QQuickFlickable*>(rootContentItem)->contentItem(); - - return Q_NULLPTR; - } - - static inline ContentItemType contentItemType(QQuickItem *rootContentItem) - { - if (rootContentItem->inherits("QQuickPathView")) - return PathViewContentItem; - else if (rootContentItem->inherits("QQuickListView")) - return ListViewContentItem; - - return UnsupportedContentItemType; - } - - static inline ContentItemType contentItemTypeFromDelegate(QQuickItem *delegateItem) - { - if (delegateItem->parentItem()->inherits("QQuickPathView")) { - return PathViewContentItem; - } else if (delegateItem->parentItem()->parentItem() - && delegateItem->parentItem()->parentItem()->inherits("QQuickListView")) { - return ListViewContentItem; - } - - return UnsupportedContentItemType; - } -} - -void QQuickTumblerPrivate::_q_updateItemHeights() -{ - // Can't use our own private padding members here, as the padding property might be set, - // which doesn't affect them, only their getters. - Q_Q(const QQuickTumbler); - const qreal itemHeight = delegateHeight(q); - foreach (QQuickItem *childItem, contentItemChildItems(contentItem)) - childItem->setHeight(itemHeight); -} - -void QQuickTumblerPrivate::_q_updateItemWidths() -{ - Q_Q(const QQuickTumbler); - const qreal availableWidth = q->availableWidth(); - foreach (QQuickItem *childItem, contentItemChildItems(contentItem)) - childItem->setWidth(availableWidth); -} - -void QQuickTumblerPrivate::itemChildAdded(QQuickItem *, QQuickItem *) -{ - _q_updateItemWidths(); - _q_updateItemHeights(); -} - -void QQuickTumblerPrivate::itemChildRemoved(QQuickItem *, QQuickItem *) -{ - _q_updateItemWidths(); - _q_updateItemHeights(); -} - -QQuickTumbler::QQuickTumbler(QQuickItem *parent) : - QQuickControl(*(new QQuickTumblerPrivate), parent) -{ - setActiveFocusOnTab(true); - - connect(this, SIGNAL(leftPaddingChanged()), this, SLOT(_q_updateItemWidths())); - connect(this, SIGNAL(rightPaddingChanged()), this, SLOT(_q_updateItemWidths())); - connect(this, SIGNAL(topPaddingChanged()), this, SLOT(_q_updateItemHeights())); - connect(this, SIGNAL(bottomPaddingChanged()), this, SLOT(_q_updateItemHeights())); -} - -QQuickTumbler::~QQuickTumbler() -{ -} - -/*! - \qmlproperty variant Qt.labs.controls::Tumbler::model - - This property holds the model that provides data for this tumbler. -*/ -QVariant QQuickTumbler::model() const -{ - Q_D(const QQuickTumbler); - return d->model; -} - -void QQuickTumbler::setModel(const QVariant &model) -{ - Q_D(QQuickTumbler); - if (model != d->model) { - d->model = model; - emit modelChanged(); - } -} - -/*! - \qmlproperty int Qt.labs.controls::Tumbler::count - \readonly - - This property holds the number of items in the model. -*/ -int QQuickTumbler::count() const -{ - Q_D(const QQuickTumbler); - return d->contentItem->property("count").toInt(); -} - -/*! - \qmlproperty int Qt.labs.controls::Tumbler::currentIndex - - This property holds the index of the current item. -*/ -int QQuickTumbler::currentIndex() const -{ - Q_D(const QQuickTumbler); - return d->contentItem ? d->contentItem->property("currentIndex").toInt() : -1; -} - -void QQuickTumbler::setCurrentIndex(int currentIndex) -{ - Q_D(QQuickTumbler); - d->contentItem->setProperty("currentIndex", currentIndex); -} - -/*! - \qmlproperty Item Qt.labs.controls::Tumbler::currentItem - \readonly - - This property holds the item at the current index. -*/ -QQuickItem *QQuickTumbler::currentItem() const -{ - Q_D(const QQuickTumbler); - return d->contentItem ? d->contentItem->property("currentItem").value<QQuickItem*>() : Q_NULLPTR; -} - -/*! - \qmlproperty component Qt.labs.controls::Tumbler::delegate - - This property holds the delegate used to display each item. -*/ -QQmlComponent *QQuickTumbler::delegate() const -{ - Q_D(const QQuickTumbler); - return d->delegate; -} - -void QQuickTumbler::setDelegate(QQmlComponent *delegate) -{ - Q_D(QQuickTumbler); - if (delegate != d->delegate) { - d->delegate = delegate; - emit delegateChanged(); - } -} - -/*! - \qmlproperty int Qt.labs.controls::Tumbler::visibleItemCount - - This property holds the number of items visible in the tumbler. It must be - an odd number, as the current item is always vertically centered. -*/ -int QQuickTumbler::visibleItemCount() const -{ - Q_D(const QQuickTumbler); - return d->visibleItemCount; -} - -void QQuickTumbler::setVisibleItemCount(int visibleItemCount) -{ - Q_D(QQuickTumbler); - if (visibleItemCount != d->visibleItemCount) { - d->visibleItemCount = visibleItemCount; - d->_q_updateItemHeights(); - emit visibleItemCountChanged(); - } -} - -QQuickTumblerAttached *QQuickTumbler::qmlAttachedProperties(QObject *object) -{ - QQuickItem *delegateItem = qobject_cast<QQuickItem *>(object); - if (!delegateItem) { - qWarning() << "Tumbler: attached properties of Tumbler must be accessed from within a delegate item"; - return Q_NULLPTR; - } - - return new QQuickTumblerAttached(delegateItem); -} - -void QQuickTumbler::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) -{ - Q_D(QQuickTumbler); - - QQuickControl::geometryChanged(newGeometry, oldGeometry); - - d->_q_updateItemHeights(); - - if (newGeometry.width() != oldGeometry.width()) - d->_q_updateItemWidths(); -} - -void QQuickTumbler::componentComplete() -{ - Q_D(QQuickTumbler); - QQuickControl::componentComplete(); - d->_q_updateItemHeights(); - d->_q_updateItemWidths(); -} - -void QQuickTumbler::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) -{ - Q_D(QQuickTumbler); - - QQuickControl::contentItemChange(newItem, oldItem); - - // Since we use the currentIndex of the contentItem directly, we must - // ensure that we keep track of the currentIndex so it doesn't get lost - // between contentItem changes. - const int previousCurrentIndex = currentIndex(); - - if (oldItem) { - disconnect(oldItem, SIGNAL(currentIndexChanged()), this, SIGNAL(currentIndexChanged())); - disconnect(oldItem, SIGNAL(currentItemChanged()), this, SIGNAL(currentItemChanged())); - disconnect(oldItem, SIGNAL(countChanged()), this, SIGNAL(countChanged())); - - ContentItemType oldContentItemType = contentItemType(oldItem); - QQuickItem *actualOldContentItem = actualContentItem(oldItem, oldContentItemType); - QQuickItemPrivate *actualContentItemPrivate = QQuickItemPrivate::get(actualOldContentItem); - actualContentItemPrivate->removeItemChangeListener(d, QQuickItemPrivate::Children); - } - - if (newItem) { - ContentItemType contentType = contentItemType(newItem); - if (contentType == UnsupportedContentItemType) { - qWarning() << "Tumbler: contentItems other than PathView and ListView are not supported"; - return; - } - - connect(newItem, SIGNAL(currentIndexChanged()), this, SIGNAL(currentIndexChanged())); - connect(newItem, SIGNAL(currentItemChanged()), this, SIGNAL(currentItemChanged())); - connect(newItem, SIGNAL(countChanged()), this, SIGNAL(countChanged())); - - QQuickItem *actualNewContentItem = actualContentItem(newItem, contentType); - QQuickItemPrivate *actualContentItemPrivate = QQuickItemPrivate::get(actualNewContentItem); - actualContentItemPrivate->addItemChangeListener(d, QQuickItemPrivate::Children); - - // If the previous currentIndex is -1, it means we had no contentItem previously. - if (previousCurrentIndex != -1) { - // Can't call setCurrentIndex here, as contentItemChange() is - // called *before* the contentItem is set. - newItem->setProperty("currentIndex", previousCurrentIndex); - } - } -} - -void QQuickTumbler::keyPressEvent(QKeyEvent *event) -{ - Q_D(QQuickTumbler); - - QQuickControl::keyPressEvent(event); - - if (event->isAutoRepeat()) - return; - - if (event->key() == Qt::Key_Up) { - QMetaObject::invokeMethod(d->contentItem, "decrementCurrentIndex"); - } else if (event->key() == Qt::Key_Down) { - QMetaObject::invokeMethod(d->contentItem, "incrementCurrentIndex"); - } -} - -class QQuickTumblerAttachedPrivate : public QObjectPrivate, public QQuickItemChangeListener -{ - Q_DECLARE_PUBLIC(QQuickTumblerAttached) -public: - QQuickTumblerAttachedPrivate(QQuickItem *delegateItem) : - tumbler(Q_NULLPTR), - index(-1), - displacement(1) - { - if (!delegateItem->parentItem()) { - qWarning() << "Tumbler: attached properties must be accessed from within a delegate item that has a parent"; - return; - } - - QVariant indexContextProperty = qmlContext(delegateItem)->contextProperty(QStringLiteral("index")); - if (!indexContextProperty.isValid()) { - qWarning() << "Tumbler: attempting to access attached property on item without an \"index\" property"; - return; - } - - index = indexContextProperty.toInt(); - const ContentItemType contentItemType = contentItemTypeFromDelegate(delegateItem); - if (contentItemType == UnsupportedContentItemType) - return; - - // ListView has an "additional" content item. - tumbler = qobject_cast<QQuickTumbler* >(contentItemType == PathViewContentItem - ? delegateItem->parentItem()->parentItem() : delegateItem->parentItem()->parentItem()->parentItem()); - Q_ASSERT(tumbler); - } - - ~QQuickTumblerAttachedPrivate() { - } - - void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; - void itemChildAdded(QQuickItem *, QQuickItem *) Q_DECL_OVERRIDE; - void itemChildRemoved(QQuickItem *, QQuickItem *) Q_DECL_OVERRIDE; - - void _q_calculateDisplacement(); - - // The Tumbler that contains the delegate. Required to calculated the displacement. - QQuickTumbler *tumbler; - // The index of the delegate. Used to calculate the displacement. - int index; - // The displacement for our delegate. - qreal displacement; -}; - -void QQuickTumblerAttachedPrivate::itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) -{ - _q_calculateDisplacement(); -} - -void QQuickTumblerAttachedPrivate::itemChildAdded(QQuickItem *, QQuickItem *) -{ - _q_calculateDisplacement(); -} - -void QQuickTumblerAttachedPrivate::itemChildRemoved(QQuickItem *item, QQuickItem *child) -{ - _q_calculateDisplacement(); - - if (parent == child) { - // The child that was removed from the contentItem was the delegate - // that our properties are attached to. If we don't remove the change - // listener, the contentItem will attempt to notify a destroyed - // listener, causing a crash. - - // item is the "actual content item" of Tumbler's contentItem, i.e. a PathView or ListView.contentItem - QQuickItemPrivate *p = QQuickItemPrivate::get(item); - p->removeItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Children); - } -} - -void QQuickTumblerAttachedPrivate::_q_calculateDisplacement() -{ - const int previousDisplacement = displacement; - displacement = 0; - - // This can happen in tests, so it may happen in normal usage too. - if (tumbler->count() == 0) - return; - - ContentItemType contentType = contentItemType(tumbler->contentItem()); - if (contentType == UnsupportedContentItemType) - return; - - qreal offset = 0; - - if (contentType == PathViewContentItem) { - offset = tumbler->contentItem()->property("offset").toReal(); - - displacement = tumbler->count() - index - offset; - int halfVisibleItems = tumbler->visibleItemCount() / 2 + 1; - if (displacement > halfVisibleItems) - displacement -= tumbler->count(); - else if (displacement < -halfVisibleItems) - displacement += tumbler->count(); - } else { - const qreal contentY = tumbler->contentItem()->property("contentY").toReal(); - const qreal delegateH = delegateHeight(tumbler); - const qreal preferredHighlightBegin = tumbler->contentItem()->property("preferredHighlightBegin").toReal(); - // Tumbler's displacement goes from negative at the top to positive towards the bottom, so we must switch this around. - const qreal reverseDisplacement = (contentY + preferredHighlightBegin) / delegateH; - displacement = reverseDisplacement - index; - } - - Q_Q(QQuickTumblerAttached); - if (displacement != previousDisplacement) - emit q->displacementChanged(); -} - -QQuickTumblerAttached::QQuickTumblerAttached(QQuickItem *delegateItem) : - QObject(*(new QQuickTumblerAttachedPrivate(delegateItem)), delegateItem) -{ - Q_D(QQuickTumblerAttached); - if (d->tumbler) { - QQuickItem *rootContentItem = d->tumbler->contentItem(); - const ContentItemType contentType = contentItemType(rootContentItem); - QQuickItemPrivate *p = QQuickItemPrivate::get(actualContentItem(rootContentItem, contentType)); - p->addItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Children); - - const char *contentItemSignal = contentType == PathViewContentItem - ? SIGNAL(offsetChanged()) : SIGNAL(contentYChanged()); - connect(d->tumbler->contentItem(), contentItemSignal, this, SLOT(_q_calculateDisplacement())); - } -} - -QQuickTumblerAttached::~QQuickTumblerAttached() -{ -} - -QQuickTumbler *QQuickTumblerAttached::tumbler() const -{ - Q_D(const QQuickTumblerAttached); - return d->tumbler; -} - -qreal QQuickTumblerAttached::displacement() const -{ - Q_D(const QQuickTumblerAttached); - return d->displacement; -} - -QT_END_NAMESPACE - -#include "moc_qquicktumbler_p.cpp" |