2011-04-27 12:13:26 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
2013-01-02 11:17:46 +00:00
|
|
|
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
2012-09-20 05:21:40 +00:00
|
|
|
** Contact: http://www.qt-project.org/legal
|
2011-04-27 12:13:26 +00:00
|
|
|
**
|
2012-02-16 04:43:03 +00:00
|
|
|
** This file is part of the QtQml module of the Qt Toolkit.
|
2011-04-27 12:13:26 +00:00
|
|
|
**
|
|
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
2012-09-20 05:21:40 +00:00
|
|
|
** 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 Digia. For licensing terms and
|
|
|
|
** conditions see http://qt.digia.com/licensing. For further information
|
|
|
|
** use the contact form at http://qt.digia.com/contact-us.
|
|
|
|
**
|
2011-04-27 12:13:26 +00:00
|
|
|
** GNU Lesser General Public License Usage
|
2012-09-20 05:21:40 +00:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
** General Public License version 2.1 as published by the Free Software
|
|
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
**
|
|
|
|
** In addition, as a special exception, Digia gives you certain additional
|
|
|
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
2011-04-27 12:13:26 +00:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
**
|
2011-07-07 12:52:03 +00:00
|
|
|
** GNU General Public License Usage
|
2012-09-20 05:21:40 +00:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
** General Public License version 3.0 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 3.0 requirements will be
|
|
|
|
** met: http://www.gnu.org/copyleft/gpl.html.
|
2011-04-27 12:13:26 +00:00
|
|
|
**
|
2012-01-24 03:37:23 +00:00
|
|
|
**
|
2011-04-27 12:13:26 +00:00
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
#include "qquickpathview_p.h"
|
|
|
|
#include "qquickpathview_p_p.h"
|
2012-07-11 07:32:16 +00:00
|
|
|
#include "qquickwindow.h"
|
2011-04-27 12:13:26 +00:00
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
#include <QtQuick/private/qquickstate_p.h>
|
|
|
|
#include <private/qqmlglobal_p.h>
|
|
|
|
#include <private/qqmlopenmetaobject_p.h>
|
2013-01-15 22:26:59 +00:00
|
|
|
#include <private/qqmlchangeset_p.h>
|
2011-04-27 12:13:26 +00:00
|
|
|
|
2013-01-15 22:26:59 +00:00
|
|
|
#include <QtQml/qqmlinfo.h>
|
2011-04-27 12:13:26 +00:00
|
|
|
#include <QtGui/qevent.h>
|
2011-08-30 14:48:57 +00:00
|
|
|
#include <QtGui/qevent.h>
|
2011-08-30 11:17:00 +00:00
|
|
|
#include <QtGui/qguiapplication.h>
|
|
|
|
#include <QtGui/qstylehints.h>
|
2011-04-27 12:13:26 +00:00
|
|
|
#include <QtCore/qmath.h>
|
|
|
|
#include <math.h>
|
|
|
|
|
2012-02-16 05:54:09 +00:00
|
|
|
// The number of samples to use in calculating the velocity of a flick
|
|
|
|
#ifndef QML_FLICK_SAMPLEBUFFER
|
2012-03-21 03:18:05 +00:00
|
|
|
#define QML_FLICK_SAMPLEBUFFER 1
|
2012-02-16 05:54:09 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// The number of samples to discard when calculating the flick velocity.
|
|
|
|
// Touch panels often produce inaccurate results as the finger is lifted.
|
|
|
|
#ifndef QML_FLICK_DISCARDSAMPLES
|
2012-03-21 03:18:05 +00:00
|
|
|
#define QML_FLICK_DISCARDSAMPLES 0
|
2012-02-16 05:54:09 +00:00
|
|
|
#endif
|
|
|
|
|
2012-03-25 02:37:08 +00:00
|
|
|
// The default maximum velocity of a flick.
|
|
|
|
#ifndef QML_FLICK_DEFAULTMAXVELOCITY
|
|
|
|
#define QML_FLICK_DEFAULTMAXVELOCITY 2500
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2011-04-27 12:13:26 +00:00
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
2012-03-25 02:37:08 +00:00
|
|
|
const qreal MinimumFlickVelocity = 75.0;
|
|
|
|
|
2011-04-27 12:13:26 +00:00
|
|
|
inline qreal qmlMod(qreal x, qreal y)
|
|
|
|
{
|
|
|
|
#ifdef QT_USE_MATH_H_FLOATS
|
2011-10-17 11:03:58 +00:00
|
|
|
if (sizeof(qreal) == sizeof(float))
|
2011-04-27 12:13:26 +00:00
|
|
|
return fmodf(float(x), float(y));
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
return fmod(x, y);
|
|
|
|
}
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
static QQmlOpenMetaObjectType *qPathViewAttachedType = 0;
|
2011-04-27 12:13:26 +00:00
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickPathViewAttached::QQuickPathViewAttached(QObject *parent)
|
2011-04-27 12:13:26 +00:00
|
|
|
: QObject(parent), m_percent(-1), m_view(0), m_onPath(false), m_isCurrent(false)
|
|
|
|
{
|
|
|
|
if (qPathViewAttachedType) {
|
2012-02-16 04:43:03 +00:00
|
|
|
m_metaobject = new QQmlOpenMetaObject(this, qPathViewAttachedType);
|
2011-04-27 12:13:26 +00:00
|
|
|
m_metaobject->setCached(true);
|
|
|
|
} else {
|
2012-02-16 04:43:03 +00:00
|
|
|
m_metaobject = new QQmlOpenMetaObject(this);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickPathViewAttached::~QQuickPathViewAttached()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
QVariant QQuickPathViewAttached::value(const QByteArray &name) const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
|
|
|
return m_metaobject->value(name);
|
|
|
|
}
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathViewAttached::setValue(const QByteArray &name, const QVariant &val)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
|
|
|
m_metaobject->setValue(name, val);
|
|
|
|
}
|
|
|
|
|
2012-03-25 02:37:08 +00:00
|
|
|
QQuickPathViewPrivate::QQuickPathViewPrivate()
|
2012-03-21 03:18:05 +00:00
|
|
|
: path(0), currentIndex(0), currentItemOffset(0.0), startPc(0)
|
2012-08-21 04:47:48 +00:00
|
|
|
, offset(0.0), offsetAdj(0.0), mappedRange(1.0), mappedCache(0.0)
|
2012-03-25 02:37:08 +00:00
|
|
|
, stealMouse(false), ownModel(false), interactive(true), haveHighlightRange(true)
|
|
|
|
, autoHighlight(true), highlightUp(false), layoutScheduled(false)
|
2013-01-15 22:26:59 +00:00
|
|
|
, moving(false), flicking(false), dragging(false), inRequest(false), delegateValidated(false)
|
2012-03-25 02:37:08 +00:00
|
|
|
, dragMargin(0), deceleration(100), maximumFlickVelocity(QML_FLICK_DEFAULTMAXVELOCITY)
|
|
|
|
, moveOffset(this, &QQuickPathViewPrivate::setAdjustedOffset), flickDuration(0)
|
2012-08-21 04:47:48 +00:00
|
|
|
, firstIndex(-1), pathItems(-1), requestedIndex(-1), cacheSize(0), requestedZ(0)
|
2012-03-25 02:37:08 +00:00
|
|
|
, moveReason(Other), moveDirection(Shortest), attType(0), highlightComponent(0), highlightItem(0)
|
|
|
|
, moveHighlight(this, &QQuickPathViewPrivate::setHighlightPosition)
|
|
|
|
, highlightPosition(0)
|
|
|
|
, highlightRangeStart(0), highlightRangeEnd(0)
|
|
|
|
, highlightRangeMode(QQuickPathView::StrictlyEnforceRange)
|
2012-03-21 03:18:05 +00:00
|
|
|
, highlightMoveDuration(300), modelCount(0), snapMode(QQuickPathView::NoSnap)
|
2012-03-25 02:37:08 +00:00
|
|
|
{
|
|
|
|
}
|
2011-04-27 12:13:26 +00:00
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathViewPrivate::init()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_Q(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
offset = 0;
|
|
|
|
q->setAcceptedMouseButtons(Qt::LeftButton);
|
2011-10-14 08:51:42 +00:00
|
|
|
q->setFlag(QQuickItem::ItemIsFocusScope);
|
2011-04-27 12:13:26 +00:00
|
|
|
q->setFiltersChildMouseEvents(true);
|
2012-05-11 11:01:41 +00:00
|
|
|
qmlobject_connect(&tl, QQuickTimeLine, SIGNAL(updated()),
|
|
|
|
q, QQuickPathView, SLOT(ticked()))
|
2012-03-21 03:18:05 +00:00
|
|
|
timer.invalidate();
|
2012-05-11 11:01:41 +00:00
|
|
|
qmlobject_connect(&tl, QQuickTimeLine, SIGNAL(completed()),
|
|
|
|
q, QQuickPathView, SLOT(movementEnding()))
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
2012-08-21 04:47:48 +00:00
|
|
|
QQuickItem *QQuickPathViewPrivate::getItem(int modelIndex, qreal z, bool async)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_Q(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
requestedIndex = modelIndex;
|
2011-11-03 05:52:13 +00:00
|
|
|
requestedZ = z;
|
|
|
|
inRequest = true;
|
2013-01-15 22:26:59 +00:00
|
|
|
QObject *object = model->object(modelIndex, async);
|
|
|
|
QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
|
|
|
|
if (!item) {
|
|
|
|
if (object) {
|
|
|
|
model->release(object);
|
|
|
|
if (!delegateValidated) {
|
|
|
|
delegateValidated = true;
|
|
|
|
QObject* delegate = q->delegate();
|
|
|
|
qmlInfo(delegate ? delegate : q) << q->tr("Delegate must be of Item type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2011-11-03 05:52:13 +00:00
|
|
|
item->setParentItem(q);
|
|
|
|
requestedIndex = -1;
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
|
|
|
|
itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
2011-11-03 05:52:13 +00:00
|
|
|
inRequest = false;
|
2011-04-27 12:13:26 +00:00
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
2013-01-15 22:26:59 +00:00
|
|
|
void QQuickPathView::createdItem(int index, QObject *object)
|
2011-11-03 05:52:13 +00:00
|
|
|
{
|
|
|
|
Q_D(QQuickPathView);
|
2013-01-15 22:26:59 +00:00
|
|
|
QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
|
2011-11-03 05:52:13 +00:00
|
|
|
if (d->requestedIndex != index) {
|
|
|
|
qPathViewAttachedType = d->attachedType();
|
|
|
|
QQuickPathViewAttached *att = static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item));
|
|
|
|
qPathViewAttachedType = 0;
|
|
|
|
if (att) {
|
|
|
|
att->m_view = this;
|
|
|
|
att->setOnPath(false);
|
|
|
|
}
|
|
|
|
item->setParentItem(this);
|
2012-08-21 04:47:48 +00:00
|
|
|
d->updateItem(item, 1.0);
|
2011-11-03 05:52:13 +00:00
|
|
|
} else {
|
|
|
|
d->requestedIndex = -1;
|
|
|
|
if (!d->inRequest)
|
|
|
|
refill();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-15 22:26:59 +00:00
|
|
|
void QQuickPathView::initItem(int index, QObject *object)
|
2011-11-03 05:52:13 +00:00
|
|
|
{
|
|
|
|
Q_D(QQuickPathView);
|
2013-01-15 22:26:59 +00:00
|
|
|
QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
|
|
|
|
if (item && d->requestedIndex == index) {
|
2012-08-21 04:47:48 +00:00
|
|
|
QQuickItemPrivate::get(item)->setCulled(true);
|
2011-11-03 05:52:13 +00:00
|
|
|
item->setParentItem(this);
|
|
|
|
qPathViewAttachedType = d->attachedType();
|
|
|
|
QQuickPathViewAttached *att = static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item));
|
|
|
|
qPathViewAttachedType = 0;
|
|
|
|
if (att) {
|
|
|
|
att->m_view = this;
|
|
|
|
qreal percent = d->positionOfIndex(index);
|
2013-01-17 09:46:45 +00:00
|
|
|
if (percent < 1.0 && d->path) {
|
2012-08-21 04:47:48 +00:00
|
|
|
foreach (const QString &attr, d->path->attributes())
|
|
|
|
att->setValue(attr.toUtf8(), d->path->attributeAt(attr, percent));
|
|
|
|
item->setZ(d->requestedZ);
|
|
|
|
}
|
|
|
|
att->setOnPath(percent < 1.0);
|
2011-11-03 05:52:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathViewPrivate::releaseItem(QQuickItem *item)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
|
|
|
if (!item || !model)
|
|
|
|
return;
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
|
|
|
|
itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
|
2013-01-15 22:26:59 +00:00
|
|
|
QQmlInstanceModel::ReleaseFlags flags = model->release(item);
|
|
|
|
if (!flags) {
|
2011-04-27 12:13:26 +00:00
|
|
|
// item was not destroyed, and we no longer reference it.
|
2011-10-14 08:51:42 +00:00
|
|
|
if (QQuickPathViewAttached *att = attached(item))
|
2011-04-27 12:13:26 +00:00
|
|
|
att->setOnPath(false);
|
2013-01-15 22:26:59 +00:00
|
|
|
} else if (flags & QQmlInstanceModel::Destroyed) {
|
|
|
|
// but we still reference it
|
|
|
|
item->setParentItem(0);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickPathViewAttached *QQuickPathViewPrivate::attached(QQuickItem *item)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
return static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item, false));
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlOpenMetaObjectType *QQuickPathViewPrivate::attachedType()
|
2011-11-03 05:52:13 +00:00
|
|
|
{
|
|
|
|
Q_Q(QQuickPathView);
|
|
|
|
if (!attType) {
|
|
|
|
// pre-create one metatype to share with all attached objects
|
2012-02-16 04:43:03 +00:00
|
|
|
attType = new QQmlOpenMetaObjectType(&QQuickPathViewAttached::staticMetaObject, qmlEngine(q));
|
2013-01-17 09:46:45 +00:00
|
|
|
if (path) {
|
|
|
|
foreach (const QString &attr, path->attributes())
|
|
|
|
attType->createProperty(attr.toUtf8());
|
|
|
|
}
|
2011-11-03 05:52:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return attType;
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathViewPrivate::clear()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-08-01 03:09:10 +00:00
|
|
|
if (currentItem) {
|
|
|
|
releaseItem(currentItem);
|
|
|
|
currentItem = 0;
|
|
|
|
}
|
2011-04-27 12:13:26 +00:00
|
|
|
for (int i=0; i<items.count(); i++){
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickItem *p = items[i];
|
2011-04-27 12:13:26 +00:00
|
|
|
releaseItem(p);
|
|
|
|
}
|
2012-08-21 04:47:48 +00:00
|
|
|
if (requestedIndex >= 0) {
|
|
|
|
if (model)
|
|
|
|
model->cancel(requestedIndex);
|
|
|
|
requestedIndex = -1;
|
|
|
|
}
|
|
|
|
|
2011-04-27 12:13:26 +00:00
|
|
|
items.clear();
|
2012-07-18 00:36:17 +00:00
|
|
|
tl.clear();
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathViewPrivate::updateMappedRange()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2012-08-21 04:47:48 +00:00
|
|
|
if (model && pathItems != -1 && pathItems < modelCount) {
|
|
|
|
mappedRange = qreal(modelCount)/pathItems;
|
|
|
|
mappedCache = qreal(cacheSize)/pathItems/2; // Half of cache at each end
|
|
|
|
} else {
|
2011-04-27 12:13:26 +00:00
|
|
|
mappedRange = 1.0;
|
2012-08-21 04:47:48 +00:00
|
|
|
mappedCache = 0.0;
|
|
|
|
}
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
qreal QQuickPathViewPrivate::positionOfIndex(qreal index) const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
|
|
|
qreal pos = -1.0;
|
|
|
|
|
|
|
|
if (model && index >= 0 && index < modelCount) {
|
|
|
|
qreal start = 0.0;
|
2012-03-21 03:18:05 +00:00
|
|
|
if (haveHighlightRange && (highlightRangeMode != QQuickPathView::NoHighlightRange
|
|
|
|
|| snapMode != QQuickPathView::NoSnap))
|
2011-04-27 12:13:26 +00:00
|
|
|
start = highlightRangeStart;
|
|
|
|
qreal globalPos = index + offset;
|
|
|
|
globalPos = qmlMod(globalPos, qreal(modelCount)) / modelCount;
|
|
|
|
if (pathItems != -1 && pathItems < modelCount) {
|
2012-08-21 04:47:48 +00:00
|
|
|
globalPos += start / mappedRange;
|
2011-04-27 12:13:26 +00:00
|
|
|
globalPos = qmlMod(globalPos, 1.0);
|
2012-08-21 04:47:48 +00:00
|
|
|
pos = globalPos * mappedRange;
|
2011-04-27 12:13:26 +00:00
|
|
|
} else {
|
|
|
|
pos = qmlMod(globalPos + start, 1.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
2012-08-21 04:47:48 +00:00
|
|
|
// returns true if position is between lower and upper, taking into
|
|
|
|
// account the circular space.
|
|
|
|
bool QQuickPathViewPrivate::isInBound(qreal position, qreal lower, qreal upper) const
|
|
|
|
{
|
|
|
|
if (lower > upper) {
|
|
|
|
if (position > upper && position > lower)
|
|
|
|
position -= mappedRange;
|
|
|
|
lower -= mappedRange;
|
|
|
|
}
|
|
|
|
return position >= lower && position < upper;
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathViewPrivate::createHighlight()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_Q(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (!q->isComponentComplete())
|
|
|
|
return;
|
|
|
|
|
|
|
|
bool changed = false;
|
|
|
|
if (highlightItem) {
|
2011-05-30 06:35:52 +00:00
|
|
|
highlightItem->setParentItem(0);
|
|
|
|
highlightItem->deleteLater();
|
2011-04-27 12:13:26 +00:00
|
|
|
highlightItem = 0;
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickItem *item = 0;
|
2011-04-27 12:13:26 +00:00
|
|
|
if (highlightComponent) {
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlContext *creationContext = highlightComponent->creationContext();
|
|
|
|
QQmlContext *highlightContext = new QQmlContext(
|
2011-10-14 00:20:08 +00:00
|
|
|
creationContext ? creationContext : qmlContext(q));
|
2011-04-27 12:13:26 +00:00
|
|
|
QObject *nobj = highlightComponent->create(highlightContext);
|
|
|
|
if (nobj) {
|
2012-02-16 04:43:03 +00:00
|
|
|
QQml_setParent_noEvent(highlightContext, nobj);
|
2011-10-14 08:51:42 +00:00
|
|
|
item = qobject_cast<QQuickItem *>(nobj);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (!item)
|
|
|
|
delete nobj;
|
|
|
|
} else {
|
|
|
|
delete highlightContext;
|
|
|
|
}
|
|
|
|
} else {
|
2011-10-14 08:51:42 +00:00
|
|
|
item = new QQuickItem;
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
if (item) {
|
2012-02-16 04:43:03 +00:00
|
|
|
QQml_setParent_noEvent(item, q);
|
2011-04-27 12:13:26 +00:00
|
|
|
item->setParentItem(q);
|
|
|
|
highlightItem = item;
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
if (changed)
|
|
|
|
emit q->highlightItemChanged();
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathViewPrivate::updateHighlight()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_Q(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (!q->isComponentComplete() || !isValid())
|
|
|
|
return;
|
|
|
|
if (highlightItem) {
|
2011-10-14 08:51:42 +00:00
|
|
|
if (haveHighlightRange && highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
|
2011-04-27 12:13:26 +00:00
|
|
|
updateItem(highlightItem, highlightRangeStart);
|
|
|
|
} else {
|
|
|
|
qreal target = currentIndex;
|
|
|
|
|
|
|
|
offsetAdj = 0.0;
|
|
|
|
tl.reset(moveHighlight);
|
|
|
|
moveHighlight.setValue(highlightPosition);
|
|
|
|
|
|
|
|
const int duration = highlightMoveDuration;
|
|
|
|
|
|
|
|
if (target - highlightPosition > modelCount/2) {
|
|
|
|
highlightUp = false;
|
|
|
|
qreal distance = modelCount - target + highlightPosition;
|
|
|
|
tl.move(moveHighlight, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * highlightPosition / distance));
|
|
|
|
tl.set(moveHighlight, modelCount-0.01);
|
|
|
|
tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * (modelCount-target) / distance));
|
|
|
|
} else if (target - highlightPosition <= -modelCount/2) {
|
|
|
|
highlightUp = true;
|
|
|
|
qreal distance = modelCount - highlightPosition + target;
|
|
|
|
tl.move(moveHighlight, modelCount-0.01, QEasingCurve(QEasingCurve::InQuad), int(duration * (modelCount-highlightPosition) / distance));
|
|
|
|
tl.set(moveHighlight, 0.0);
|
|
|
|
tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * target / distance));
|
|
|
|
} else {
|
|
|
|
highlightUp = highlightPosition - target < 0;
|
|
|
|
tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::InOutQuad), duration);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathViewPrivate::setHighlightPosition(qreal pos)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
|
|
|
if (pos != highlightPosition) {
|
|
|
|
qreal start = 0.0;
|
|
|
|
qreal end = 1.0;
|
2011-10-14 08:51:42 +00:00
|
|
|
if (haveHighlightRange && highlightRangeMode != QQuickPathView::NoHighlightRange) {
|
2011-04-27 12:13:26 +00:00
|
|
|
start = highlightRangeStart;
|
|
|
|
end = highlightRangeEnd;
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal range = qreal(modelCount);
|
|
|
|
// calc normalized position of highlight relative to offset
|
|
|
|
qreal relativeHighlight = qmlMod(pos + offset, range) / range;
|
|
|
|
|
2012-08-21 04:47:48 +00:00
|
|
|
if (!highlightUp && relativeHighlight > end / mappedRange) {
|
2011-04-27 12:13:26 +00:00
|
|
|
qreal diff = 1.0 - relativeHighlight;
|
|
|
|
setOffset(offset + diff * range);
|
2012-08-21 04:47:48 +00:00
|
|
|
} else if (highlightUp && relativeHighlight >= (end - start) / mappedRange) {
|
|
|
|
qreal diff = relativeHighlight - (end - start) / mappedRange;
|
2011-04-27 12:13:26 +00:00
|
|
|
setOffset(offset - diff * range - 0.00001);
|
|
|
|
}
|
|
|
|
|
|
|
|
highlightPosition = pos;
|
|
|
|
qreal pathPos = positionOfIndex(pos);
|
|
|
|
updateItem(highlightItem, pathPos);
|
2011-10-14 08:51:42 +00:00
|
|
|
if (QQuickPathViewAttached *att = attached(highlightItem))
|
2012-08-21 04:47:48 +00:00
|
|
|
att->setOnPath(pathPos < 1.0);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::pathUpdated()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
|
|
|
QList<QQuickItem*>::iterator it = d->items.begin();
|
2011-04-27 12:13:26 +00:00
|
|
|
while (it != d->items.end()) {
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickItem *item = *it;
|
|
|
|
if (QQuickPathViewAttached *att = d->attached(item))
|
2011-04-27 12:13:26 +00:00
|
|
|
att->m_percent = -1;
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
refill();
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathViewPrivate::updateItem(QQuickItem *item, qreal percent)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2013-01-17 09:46:45 +00:00
|
|
|
if (!path)
|
|
|
|
return;
|
2011-10-14 08:51:42 +00:00
|
|
|
if (QQuickPathViewAttached *att = attached(item)) {
|
2011-04-27 12:13:26 +00:00
|
|
|
if (qFuzzyCompare(att->m_percent, percent))
|
|
|
|
return;
|
|
|
|
att->m_percent = percent;
|
2011-10-17 11:03:58 +00:00
|
|
|
foreach (const QString &attr, path->attributes())
|
2011-04-27 12:13:26 +00:00
|
|
|
att->setValue(attr.toUtf8(), path->attributeAt(attr, percent));
|
2012-08-21 04:47:48 +00:00
|
|
|
att->setOnPath(percent < 1.0);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
2012-08-21 04:47:48 +00:00
|
|
|
QQuickItemPrivate::get(item)->setCulled(percent >= 1.0);
|
|
|
|
QPointF pf = path->pointAt(qMin(percent, qreal(1.0)));
|
2012-02-16 05:54:09 +00:00
|
|
|
item->setX(pf.x() - item->width()/2);
|
|
|
|
item->setY(pf.y() - item->height()/2);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathViewPrivate::regenerate()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_Q(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (!q->isComponentComplete())
|
|
|
|
return;
|
|
|
|
|
|
|
|
clear();
|
|
|
|
|
|
|
|
if (!isValid())
|
|
|
|
return;
|
|
|
|
|
|
|
|
firstIndex = -1;
|
|
|
|
updateMappedRange();
|
|
|
|
q->refill();
|
|
|
|
}
|
|
|
|
|
2012-07-03 03:57:54 +00:00
|
|
|
void QQuickPathViewPrivate::setDragging(bool d)
|
|
|
|
{
|
|
|
|
Q_Q(QQuickPathView);
|
|
|
|
if (dragging == d)
|
|
|
|
return;
|
|
|
|
|
|
|
|
dragging = d;
|
|
|
|
if (dragging)
|
|
|
|
emit q->dragStarted();
|
|
|
|
else
|
|
|
|
emit q->dragEnded();
|
|
|
|
|
|
|
|
emit q->draggingChanged();
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
2012-07-27 15:18:12 +00:00
|
|
|
\qmltype PathView
|
|
|
|
\instantiates QQuickPathView
|
2011-08-08 08:03:31 +00:00
|
|
|
\inqmlmodule QtQuick 2
|
2012-05-28 17:40:02 +00:00
|
|
|
\ingroup qtquick-paths
|
|
|
|
\ingroup qtquick-views
|
2011-08-08 08:03:31 +00:00
|
|
|
\inherits Item
|
2012-05-28 17:40:02 +00:00
|
|
|
\brief Lays out model-provided items on a path
|
2011-08-08 08:03:31 +00:00
|
|
|
|
2012-07-11 06:29:42 +00:00
|
|
|
A PathView displays data from models created from built-in QML types like ListModel
|
2011-08-08 08:03:31 +00:00
|
|
|
and XmlListModel, or custom model classes defined in C++ that inherit from
|
|
|
|
QAbstractListModel.
|
|
|
|
|
|
|
|
The view has a \l model, which defines the data to be displayed, and
|
|
|
|
a \l delegate, which defines how the data should be displayed.
|
|
|
|
The \l delegate is instantiated for each item on the \l path.
|
|
|
|
The items may be flicked to move them along the path.
|
|
|
|
|
|
|
|
For example, if there is a simple list model defined in a file \c ContactModel.qml like this:
|
|
|
|
|
2012-05-28 01:56:24 +00:00
|
|
|
\snippet qml/pathview/ContactModel.qml 0
|
2011-08-08 08:03:31 +00:00
|
|
|
|
|
|
|
This data can be represented as a PathView, like this:
|
|
|
|
|
2012-05-28 01:56:24 +00:00
|
|
|
\snippet qml/pathview/pathview.qml 0
|
2011-08-08 08:03:31 +00:00
|
|
|
|
|
|
|
\image pathview.gif
|
|
|
|
|
|
|
|
(Note the above example uses PathAttribute to scale and modify the
|
|
|
|
opacity of the items as they rotate. This additional code can be seen in the
|
|
|
|
PathAttribute documentation.)
|
|
|
|
|
|
|
|
PathView does not automatically handle keyboard navigation. This is because
|
|
|
|
the keys to use for navigation will depend upon the shape of the path. Navigation
|
|
|
|
can be added quite simply by setting \c focus to \c true and calling
|
|
|
|
\l decrementCurrentIndex() or \l incrementCurrentIndex(), for example to navigate
|
|
|
|
using the left and right arrow keys:
|
|
|
|
|
|
|
|
\qml
|
|
|
|
PathView {
|
|
|
|
// ...
|
|
|
|
focus: true
|
|
|
|
Keys.onLeftPressed: decrementCurrentIndex()
|
|
|
|
Keys.onRightPressed: incrementCurrentIndex()
|
|
|
|
}
|
|
|
|
\endqml
|
|
|
|
|
2012-08-02 03:06:09 +00:00
|
|
|
The path view itself is a focus scope (see \l{Keyboard Focus in Qt Quick} for more details).
|
2011-08-08 08:03:31 +00:00
|
|
|
|
|
|
|
Delegates are instantiated as needed and may be destroyed at any time.
|
|
|
|
State should \e never be stored in a delegate.
|
|
|
|
|
|
|
|
PathView attaches a number of properties to the root item of the delegate, for example
|
|
|
|
\c {PathView.isCurrentItem}. In the following example, the root delegate item can access
|
|
|
|
this attached property directly as \c PathView.isCurrentItem, while the child
|
|
|
|
\c nameText object must refer to this property as \c wrapper.PathView.isCurrentItem.
|
|
|
|
|
2012-05-28 01:56:24 +00:00
|
|
|
\snippet qml/pathview/pathview.qml 1
|
2011-08-08 08:03:31 +00:00
|
|
|
|
2012-03-01 17:05:16 +00:00
|
|
|
\b Note that views do not enable \e clip automatically. If the view
|
2011-08-08 08:03:31 +00:00
|
|
|
is not clipped by another item or the screen, it will be necessary
|
|
|
|
to set \e {clip: true} in order to have the out of view items clipped
|
|
|
|
nicely.
|
|
|
|
|
2012-05-28 07:12:56 +00:00
|
|
|
\sa Path, {quick/modelviews/pathview}{PathView example}
|
2011-08-08 08:03:31 +00:00
|
|
|
*/
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickPathView::QQuickPathView(QQuickItem *parent)
|
|
|
|
: QQuickItem(*(new QQuickPathViewPrivate), parent)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
d->init();
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickPathView::~QQuickPathView()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
d->clear();
|
|
|
|
if (d->attType)
|
|
|
|
d->attType->release();
|
|
|
|
if (d->ownModel)
|
|
|
|
delete d->model;
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
2011-08-16 23:20:22 +00:00
|
|
|
\qmlattachedproperty PathView QtQuick2::PathView::view
|
2011-08-08 08:03:31 +00:00
|
|
|
This attached property holds the view that manages this delegate instance.
|
|
|
|
|
|
|
|
It is attached to each instance of the delegate.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
2011-08-16 23:20:22 +00:00
|
|
|
\qmlattachedproperty bool QtQuick2::PathView::onPath
|
2011-08-08 08:03:31 +00:00
|
|
|
This attached property holds whether the item is currently on the path.
|
|
|
|
|
|
|
|
If a pathItemCount has been set, it is possible that some items may
|
|
|
|
be instantiated, but not considered to be currently on the path.
|
|
|
|
Usually, these items would be set invisible, for example:
|
|
|
|
|
|
|
|
\qml
|
|
|
|
Component {
|
|
|
|
Rectangle {
|
|
|
|
visible: PathView.onPath
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
\endqml
|
|
|
|
|
|
|
|
It is attached to each instance of the delegate.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
2011-08-16 23:20:22 +00:00
|
|
|
\qmlattachedproperty bool QtQuick2::PathView::isCurrentItem
|
2011-08-08 08:03:31 +00:00
|
|
|
This attached property is true if this delegate is the current item; otherwise false.
|
|
|
|
|
|
|
|
It is attached to each instance of the delegate.
|
|
|
|
|
|
|
|
This property may be used to adjust the appearance of the current item.
|
|
|
|
|
2012-05-28 01:56:24 +00:00
|
|
|
\snippet qml/pathview/pathview.qml 1
|
2011-08-08 08:03:31 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\qmlproperty model QtQuick2::PathView::model
|
|
|
|
This property holds the model providing data for the view.
|
|
|
|
|
|
|
|
The model provides a set of data that is used to create the items for the view.
|
|
|
|
For large or dynamic datasets the model is usually provided by a C++ model object.
|
2012-07-11 06:29:42 +00:00
|
|
|
Models can also be created directly in QML, using the ListModel type.
|
2011-08-08 08:03:31 +00:00
|
|
|
|
2012-07-18 00:36:17 +00:00
|
|
|
\note changing the model will reset the offset and currentIndex to 0.
|
|
|
|
|
2012-08-02 03:06:09 +00:00
|
|
|
\sa {qml-data-models}{Data Models}
|
2011-08-08 08:03:31 +00:00
|
|
|
*/
|
2011-10-14 08:51:42 +00:00
|
|
|
QVariant QQuickPathView::model() const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
return d->modelVariant;
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::setModel(const QVariant &model)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->modelVariant == model)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (d->model) {
|
2013-01-15 22:26:59 +00:00
|
|
|
qmlobject_disconnect(d->model, QQmlInstanceModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)),
|
|
|
|
this, QQuickPathView, SLOT(modelUpdated(QQmlChangeSet,bool)));
|
|
|
|
qmlobject_disconnect(d->model, QQmlInstanceModel, SIGNAL(createdItem(int,QObject*)),
|
|
|
|
this, QQuickPathView, SLOT(createdItem(int,QObject*)));
|
|
|
|
qmlobject_disconnect(d->model, QQmlInstanceModel, SIGNAL(initItem(int,QObject*)),
|
|
|
|
this, QQuickPathView, SLOT(initItem(int,QObject*)));
|
2012-07-18 00:36:17 +00:00
|
|
|
d->clear();
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
d->modelVariant = model;
|
|
|
|
QObject *object = qvariant_cast<QObject*>(model);
|
2013-01-15 22:26:59 +00:00
|
|
|
QQmlInstanceModel *vim = 0;
|
|
|
|
if (object && (vim = qobject_cast<QQmlInstanceModel *>(object))) {
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->ownModel) {
|
|
|
|
delete d->model;
|
|
|
|
d->ownModel = false;
|
|
|
|
}
|
|
|
|
d->model = vim;
|
|
|
|
} else {
|
|
|
|
if (!d->ownModel) {
|
2013-01-15 22:26:59 +00:00
|
|
|
d->model = new QQmlDelegateModel(qmlContext(this));
|
2011-04-27 12:13:26 +00:00
|
|
|
d->ownModel = true;
|
2011-10-05 06:03:12 +00:00
|
|
|
if (isComponentComplete())
|
2013-01-15 22:26:59 +00:00
|
|
|
static_cast<QQmlDelegateModel *>(d->model.data())->componentComplete();
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
2013-01-15 22:26:59 +00:00
|
|
|
if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel*>(d->model))
|
2011-04-27 12:13:26 +00:00
|
|
|
dataModel->setModel(model);
|
|
|
|
}
|
2012-07-18 00:36:17 +00:00
|
|
|
int oldModelCount = d->modelCount;
|
2011-04-27 12:13:26 +00:00
|
|
|
d->modelCount = 0;
|
|
|
|
if (d->model) {
|
2013-01-15 22:26:59 +00:00
|
|
|
qmlobject_connect(d->model, QQmlInstanceModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)),
|
|
|
|
this, QQuickPathView, SLOT(modelUpdated(QQmlChangeSet,bool)));
|
|
|
|
qmlobject_connect(d->model, QQmlInstanceModel, SIGNAL(createdItem(int,QObject*)),
|
|
|
|
this, QQuickPathView, SLOT(createdItem(int,QObject*)));
|
|
|
|
qmlobject_connect(d->model, QQmlInstanceModel, SIGNAL(initItem(int,QObject*)),
|
|
|
|
this, QQuickPathView, SLOT(initItem(int,QObject*)));
|
2011-04-27 12:13:26 +00:00
|
|
|
d->modelCount = d->model->count();
|
2012-07-18 00:36:17 +00:00
|
|
|
}
|
|
|
|
if (isComponentComplete()) {
|
|
|
|
if (d->currentIndex != 0) {
|
|
|
|
d->currentIndex = 0;
|
|
|
|
emit currentIndexChanged();
|
|
|
|
}
|
|
|
|
if (d->offset != 0.0) {
|
|
|
|
d->offset = 0;
|
|
|
|
emit offsetChanged();
|
|
|
|
}
|
|
|
|
}
|
2011-04-27 12:13:26 +00:00
|
|
|
d->regenerate();
|
2012-07-18 00:36:17 +00:00
|
|
|
if (d->modelCount != oldModelCount)
|
|
|
|
emit countChanged();
|
2011-04-27 12:13:26 +00:00
|
|
|
emit modelChanged();
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty int QtQuick2::PathView::count
|
|
|
|
This property holds the number of items in the model.
|
|
|
|
*/
|
2011-10-14 08:51:42 +00:00
|
|
|
int QQuickPathView::count() const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
return d->model ? d->modelCount : 0;
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty Path QtQuick2::PathView::path
|
|
|
|
This property holds the path used to lay out the items.
|
|
|
|
For more information see the \l Path documentation.
|
|
|
|
*/
|
2012-02-16 04:43:03 +00:00
|
|
|
QQuickPath *QQuickPathView::path() const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
return d->path;
|
|
|
|
}
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
void QQuickPathView::setPath(QQuickPath *path)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->path == path)
|
|
|
|
return;
|
|
|
|
if (d->path)
|
2012-05-11 11:01:41 +00:00
|
|
|
qmlobject_disconnect(d->path, QQuickPath, SIGNAL(changed()),
|
|
|
|
this, QQuickPathView, SLOT(pathUpdated()));
|
2011-04-27 12:13:26 +00:00
|
|
|
d->path = path;
|
2012-05-11 11:01:41 +00:00
|
|
|
qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()),
|
|
|
|
this, QQuickPathView, SLOT(pathUpdated()));
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->isValid() && isComponentComplete()) {
|
|
|
|
d->clear();
|
|
|
|
if (d->attType) {
|
|
|
|
d->attType->release();
|
|
|
|
d->attType = 0;
|
|
|
|
}
|
|
|
|
d->regenerate();
|
|
|
|
}
|
|
|
|
emit pathChanged();
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty int QtQuick2::PathView::currentIndex
|
|
|
|
This property holds the index of the current item.
|
|
|
|
*/
|
2011-10-14 08:51:42 +00:00
|
|
|
int QQuickPathView::currentIndex() const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
return d->currentIndex;
|
|
|
|
}
|
|
|
|
|
2012-08-08 06:33:05 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty int QtQuick2::PathView::currentItem
|
|
|
|
This property holds the current item in the view.
|
|
|
|
*/
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::setCurrentIndex(int idx)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2012-07-18 00:36:17 +00:00
|
|
|
if (!isComponentComplete()) {
|
|
|
|
if (idx != d->currentIndex) {
|
|
|
|
d->currentIndex = idx;
|
|
|
|
emit currentIndexChanged();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-05-29 05:45:15 +00:00
|
|
|
idx = d->modelCount
|
|
|
|
? ((idx % d->modelCount) + d->modelCount) % d->modelCount
|
|
|
|
: 0;
|
2011-11-03 05:52:13 +00:00
|
|
|
if (d->model && (idx != d->currentIndex || !d->currentItem)) {
|
2011-08-01 03:09:10 +00:00
|
|
|
if (d->currentItem) {
|
2011-10-14 08:51:42 +00:00
|
|
|
if (QQuickPathViewAttached *att = d->attached(d->currentItem))
|
2011-08-01 03:09:10 +00:00
|
|
|
att->setIsCurrentItem(false);
|
|
|
|
d->releaseItem(d->currentItem);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
2011-11-03 05:52:13 +00:00
|
|
|
int oldCurrentIdx = d->currentIndex;
|
|
|
|
QQuickItem *oldCurrentItem = d->currentItem;
|
2011-04-27 12:13:26 +00:00
|
|
|
d->currentItem = 0;
|
2011-10-14 08:51:42 +00:00
|
|
|
d->moveReason = QQuickPathViewPrivate::SetIndex;
|
2011-04-27 12:13:26 +00:00
|
|
|
d->currentIndex = idx;
|
|
|
|
if (d->modelCount) {
|
2011-11-03 05:52:13 +00:00
|
|
|
d->createCurrentItem();
|
2011-10-14 08:51:42 +00:00
|
|
|
if (d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange)
|
2012-03-21 03:18:05 +00:00
|
|
|
d->snapToIndex(d->currentIndex);
|
2011-04-27 12:13:26 +00:00
|
|
|
d->currentItemOffset = d->positionOfIndex(d->currentIndex);
|
|
|
|
d->updateHighlight();
|
|
|
|
}
|
2011-11-03 05:52:13 +00:00
|
|
|
if (oldCurrentIdx != d->currentIndex)
|
|
|
|
emit currentIndexChanged();
|
|
|
|
if (oldCurrentItem != d->currentItem)
|
|
|
|
emit currentItemChanged();
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickItem *QQuickPathView::currentItem() const
|
2011-08-01 03:09:10 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-08-01 03:09:10 +00:00
|
|
|
return d->currentItem;
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
|
|
|
\qmlmethod QtQuick2::PathView::incrementCurrentIndex()
|
|
|
|
|
|
|
|
Increments the current index.
|
|
|
|
|
2012-03-01 17:05:16 +00:00
|
|
|
\b Note: methods should only be called after the Component has completed.
|
2011-08-08 08:03:31 +00:00
|
|
|
*/
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::incrementCurrentIndex()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
|
|
|
d->moveDirection = QQuickPathViewPrivate::Positive;
|
2011-04-27 12:13:26 +00:00
|
|
|
setCurrentIndex(currentIndex()+1);
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
|
|
|
\qmlmethod QtQuick2::PathView::decrementCurrentIndex()
|
|
|
|
|
|
|
|
Decrements the current index.
|
|
|
|
|
2012-03-01 17:05:16 +00:00
|
|
|
\b Note: methods should only be called after the Component has completed.
|
2011-08-08 08:03:31 +00:00
|
|
|
*/
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::decrementCurrentIndex()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2012-05-29 05:45:15 +00:00
|
|
|
d->moveDirection = QQuickPathViewPrivate::Negative;
|
|
|
|
setCurrentIndex(currentIndex()-1);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty real QtQuick2::PathView::offset
|
|
|
|
|
|
|
|
The offset specifies how far along the path the items are from their initial positions.
|
|
|
|
This is a real number that ranges from 0.0 to the count of items in the model.
|
|
|
|
*/
|
2011-10-14 08:51:42 +00:00
|
|
|
qreal QQuickPathView::offset() const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
return d->offset;
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::setOffset(qreal offset)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2012-06-27 04:05:38 +00:00
|
|
|
d->moveReason = QQuickPathViewPrivate::Other;
|
2011-04-27 12:13:26 +00:00
|
|
|
d->setOffset(offset);
|
|
|
|
d->updateCurrent();
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathViewPrivate::setOffset(qreal o)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_Q(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (offset != o) {
|
|
|
|
if (isValid() && q->isComponentComplete()) {
|
|
|
|
offset = qmlMod(o, qreal(modelCount));
|
|
|
|
if (offset < 0)
|
|
|
|
offset += qreal(modelCount);
|
|
|
|
q->refill();
|
|
|
|
} else {
|
|
|
|
offset = o;
|
|
|
|
}
|
|
|
|
emit q->offsetChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathViewPrivate::setAdjustedOffset(qreal o)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
|
|
|
setOffset(o+offsetAdj);
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty Component QtQuick2::PathView::highlight
|
|
|
|
This property holds the component to use as the highlight.
|
|
|
|
|
|
|
|
An instance of the highlight component will be created for each view.
|
|
|
|
The geometry of the resultant component instance will be managed by the view
|
|
|
|
so as to stay with the current item.
|
|
|
|
|
|
|
|
The below example demonstrates how to make a simple highlight. Note the use
|
|
|
|
of the \l{PathView::onPath}{PathView.onPath} attached property to ensure that
|
|
|
|
the highlight is hidden when flicked away from the path.
|
|
|
|
|
|
|
|
\qml
|
|
|
|
Component {
|
|
|
|
Rectangle {
|
|
|
|
visible: PathView.onPath
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
\endqml
|
|
|
|
|
|
|
|
\sa highlightItem, highlightRangeMode
|
|
|
|
*/
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlComponent *QQuickPathView::highlight() const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
return d->highlightComponent;
|
|
|
|
}
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
void QQuickPathView::setHighlight(QQmlComponent *highlight)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (highlight != d->highlightComponent) {
|
|
|
|
d->highlightComponent = highlight;
|
|
|
|
d->createHighlight();
|
|
|
|
d->updateHighlight();
|
|
|
|
emit highlightChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty Item QtQuick2::PathView::highlightItem
|
|
|
|
|
|
|
|
\c highlightItem holds the highlight item, which was created
|
|
|
|
from the \l highlight component.
|
|
|
|
|
|
|
|
\sa highlight
|
|
|
|
*/
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickItem *QQuickPathView::highlightItem()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
return d->highlightItem;
|
|
|
|
}
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty real QtQuick2::PathView::preferredHighlightBegin
|
|
|
|
\qmlproperty real QtQuick2::PathView::preferredHighlightEnd
|
|
|
|
\qmlproperty enumeration QtQuick2::PathView::highlightRangeMode
|
|
|
|
|
|
|
|
These properties set the preferred range of the highlight (current item)
|
|
|
|
within the view. The preferred values must be in the range 0.0-1.0.
|
|
|
|
|
|
|
|
If highlightRangeMode is set to \e PathView.NoHighlightRange
|
|
|
|
|
|
|
|
If highlightRangeMode is set to \e PathView.ApplyRange the view will
|
|
|
|
attempt to maintain the highlight within the range, however
|
|
|
|
the highlight can move outside of the range at the ends of the path
|
|
|
|
or due to a mouse interaction.
|
|
|
|
|
|
|
|
If highlightRangeMode is set to \e PathView.StrictlyEnforceRange the highlight will never
|
|
|
|
move outside of the range. This means that the current item will change
|
|
|
|
if a keyboard or mouse action would cause the highlight to move
|
|
|
|
outside of the range.
|
|
|
|
|
|
|
|
Note that this is the correct way to influence where the
|
|
|
|
current item ends up when the view moves. For example, if you want the
|
|
|
|
currently selected item to be in the middle of the path, then set the
|
|
|
|
highlight range to be 0.5,0.5 and highlightRangeMode to PathView.StrictlyEnforceRange.
|
|
|
|
Then, when the path scrolls,
|
|
|
|
the currently selected item will be the item at that position. This also applies to
|
|
|
|
when the currently selected item changes - it will scroll to within the preferred
|
|
|
|
highlight range. Furthermore, the behaviour of the current item index will occur
|
|
|
|
whether or not a highlight exists.
|
|
|
|
|
|
|
|
The default value is \e PathView.StrictlyEnforceRange.
|
|
|
|
|
|
|
|
Note that a valid range requires preferredHighlightEnd to be greater
|
|
|
|
than or equal to preferredHighlightBegin.
|
|
|
|
*/
|
2011-10-14 08:51:42 +00:00
|
|
|
qreal QQuickPathView::preferredHighlightBegin() const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
return d->highlightRangeStart;
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::setPreferredHighlightBegin(qreal start)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->highlightRangeStart == start || start < 0 || start > 1.0)
|
|
|
|
return;
|
|
|
|
d->highlightRangeStart = start;
|
2012-03-21 03:18:05 +00:00
|
|
|
d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd;
|
2011-04-27 12:13:26 +00:00
|
|
|
refill();
|
|
|
|
emit preferredHighlightBeginChanged();
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
qreal QQuickPathView::preferredHighlightEnd() const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
return d->highlightRangeEnd;
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::setPreferredHighlightEnd(qreal end)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->highlightRangeEnd == end || end < 0 || end > 1.0)
|
|
|
|
return;
|
|
|
|
d->highlightRangeEnd = end;
|
2012-03-21 03:18:05 +00:00
|
|
|
d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd;
|
2011-04-27 12:13:26 +00:00
|
|
|
refill();
|
|
|
|
emit preferredHighlightEndChanged();
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickPathView::HighlightRangeMode QQuickPathView::highlightRangeMode() const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
return d->highlightRangeMode;
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::setHighlightRangeMode(HighlightRangeMode mode)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->highlightRangeMode == mode)
|
|
|
|
return;
|
|
|
|
d->highlightRangeMode = mode;
|
2012-03-21 03:18:05 +00:00
|
|
|
d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd;
|
2011-11-22 07:26:49 +00:00
|
|
|
if (d->haveHighlightRange) {
|
|
|
|
d->regenerate();
|
2012-03-21 03:18:05 +00:00
|
|
|
int index = d->highlightRangeMode != NoHighlightRange ? d->currentIndex : d->calcCurrentIndex();
|
|
|
|
if (index >= 0)
|
|
|
|
d->snapToIndex(index);
|
2011-11-22 07:26:49 +00:00
|
|
|
}
|
2011-04-27 12:13:26 +00:00
|
|
|
emit highlightRangeModeChanged();
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty int QtQuick2::PathView::highlightMoveDuration
|
|
|
|
This property holds the move animation duration of the highlight delegate.
|
|
|
|
|
|
|
|
If the highlightRangeMode is StrictlyEnforceRange then this property
|
|
|
|
determines the speed that the items move along the path.
|
|
|
|
|
|
|
|
The default value for the duration is 300ms.
|
|
|
|
*/
|
2011-10-14 08:51:42 +00:00
|
|
|
int QQuickPathView::highlightMoveDuration() const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
return d->highlightMoveDuration;
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::setHighlightMoveDuration(int duration)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->highlightMoveDuration == duration)
|
|
|
|
return;
|
|
|
|
d->highlightMoveDuration = duration;
|
|
|
|
emit highlightMoveDurationChanged();
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty real QtQuick2::PathView::dragMargin
|
|
|
|
This property holds the maximum distance from the path that initiate mouse dragging.
|
|
|
|
|
|
|
|
By default the path can only be dragged by clicking on an item. If
|
|
|
|
dragMargin is greater than zero, a drag can be initiated by clicking
|
|
|
|
within dragMargin pixels of the path.
|
|
|
|
*/
|
2011-10-14 08:51:42 +00:00
|
|
|
qreal QQuickPathView::dragMargin() const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
return d->dragMargin;
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::setDragMargin(qreal dragMargin)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->dragMargin == dragMargin)
|
|
|
|
return;
|
|
|
|
d->dragMargin = dragMargin;
|
|
|
|
emit dragMarginChanged();
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty real QtQuick2::PathView::flickDeceleration
|
|
|
|
This property holds the rate at which a flick will decelerate.
|
|
|
|
|
|
|
|
The default is 100.
|
|
|
|
*/
|
2011-10-14 08:51:42 +00:00
|
|
|
qreal QQuickPathView::flickDeceleration() const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
return d->deceleration;
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::setFlickDeceleration(qreal dec)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->deceleration == dec)
|
|
|
|
return;
|
|
|
|
d->deceleration = dec;
|
|
|
|
emit flickDecelerationChanged();
|
|
|
|
}
|
|
|
|
|
2012-03-25 02:37:08 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty real QtQuick2::PathView::maximumFlickVelocity
|
|
|
|
This property holds the approximate maximum velocity that the user can flick the view in pixels/second.
|
|
|
|
|
|
|
|
The default value is platform dependent.
|
|
|
|
*/
|
|
|
|
qreal QQuickPathView::maximumFlickVelocity() const
|
|
|
|
{
|
|
|
|
Q_D(const QQuickPathView);
|
|
|
|
return d->maximumFlickVelocity;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQuickPathView::setMaximumFlickVelocity(qreal vel)
|
|
|
|
{
|
|
|
|
Q_D(QQuickPathView);
|
|
|
|
if (vel == d->maximumFlickVelocity)
|
|
|
|
return;
|
|
|
|
d->maximumFlickVelocity = vel;
|
|
|
|
emit maximumFlickVelocityChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty bool QtQuick2::PathView::interactive
|
|
|
|
|
|
|
|
A user cannot drag or flick a PathView that is not interactive.
|
|
|
|
|
|
|
|
This property is useful for temporarily disabling flicking. This allows
|
|
|
|
special interaction with PathView's children.
|
|
|
|
*/
|
2011-10-14 08:51:42 +00:00
|
|
|
bool QQuickPathView::isInteractive() const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
return d->interactive;
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::setInteractive(bool interactive)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (interactive != d->interactive) {
|
|
|
|
d->interactive = interactive;
|
|
|
|
if (!interactive)
|
|
|
|
d->tl.clear();
|
|
|
|
emit interactiveChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty bool QtQuick2::PathView::moving
|
|
|
|
|
|
|
|
This property holds whether the view is currently moving
|
|
|
|
due to the user either dragging or flicking the view.
|
|
|
|
*/
|
2011-10-14 08:51:42 +00:00
|
|
|
bool QQuickPathView::isMoving() const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
return d->moving;
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty bool QtQuick2::PathView::flicking
|
|
|
|
|
|
|
|
This property holds whether the view is currently moving
|
|
|
|
due to the user flicking the view.
|
|
|
|
*/
|
2011-10-14 08:51:42 +00:00
|
|
|
bool QQuickPathView::isFlicking() const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
return d->flicking;
|
|
|
|
}
|
|
|
|
|
2012-07-03 03:57:54 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty bool QtQuick2::PathView::dragging
|
|
|
|
|
|
|
|
This property holds whether the view is currently moving
|
|
|
|
due to the user dragging the view.
|
|
|
|
*/
|
|
|
|
bool QQuickPathView::isDragging() const
|
|
|
|
{
|
|
|
|
Q_D(const QQuickPathView);
|
|
|
|
return d->dragging;
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
|
|
|
\qmlsignal QtQuick2::PathView::onMovementStarted()
|
|
|
|
|
|
|
|
This handler is called when the view begins moving due to user
|
|
|
|
interaction.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\qmlsignal QtQuick2::PathView::onMovementEnded()
|
|
|
|
|
|
|
|
This handler is called when the view stops moving due to user
|
|
|
|
interaction. If a flick was generated, this handler will
|
|
|
|
be triggered once the flick stops. If a flick was not
|
|
|
|
generated, the handler will be triggered when the
|
|
|
|
user stops dragging - i.e. a mouse or touch release.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\qmlsignal QtQuick2::PathView::onFlickStarted()
|
|
|
|
|
|
|
|
This handler is called when the view is flicked. A flick
|
|
|
|
starts from the point that the mouse or touch is released,
|
|
|
|
while still in motion.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\qmlsignal QtQuick2::PathView::onFlickEnded()
|
|
|
|
|
|
|
|
This handler is called when the view stops moving due to a flick.
|
|
|
|
*/
|
|
|
|
|
2012-07-03 03:57:54 +00:00
|
|
|
/*!
|
|
|
|
\qmlsignal QtQuick2::PathView::onDragStarted()
|
|
|
|
|
|
|
|
This handler is called when the view starts to be dragged due to user
|
|
|
|
interaction.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\qmlsignal QtQuick2::PathView::onDragEnded()
|
|
|
|
|
|
|
|
This handler is called when the user stops dragging the view.
|
|
|
|
|
|
|
|
If the velocity of the drag is suffient at the time the
|
|
|
|
touch/mouse button is released then a flick will start.
|
|
|
|
*/
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty Component QtQuick2::PathView::delegate
|
|
|
|
|
|
|
|
The delegate provides a template defining each item instantiated by the view.
|
|
|
|
The index is exposed as an accessible \c index property. Properties of the
|
2012-08-02 03:06:09 +00:00
|
|
|
model are also available depending upon the type of \l {qml-data-models}{Data Model}.
|
2011-08-08 08:03:31 +00:00
|
|
|
|
2012-07-11 06:29:42 +00:00
|
|
|
The number of objects and bindings in the delegate has a direct effect on the
|
2011-08-08 08:03:31 +00:00
|
|
|
flicking performance of the view when pathItemCount is specified. If at all possible, place functionality
|
|
|
|
that is not needed for the normal display of the delegate in a \l Loader which
|
2012-07-11 06:29:42 +00:00
|
|
|
can load additional components when needed.
|
2011-08-08 08:03:31 +00:00
|
|
|
|
|
|
|
Note that the PathView will layout the items based on the size of the root
|
|
|
|
item in the delegate.
|
|
|
|
|
|
|
|
Here is an example delegate:
|
2012-05-28 01:56:24 +00:00
|
|
|
\snippet qml/pathview/pathview.qml 1
|
2011-08-08 08:03:31 +00:00
|
|
|
*/
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlComponent *QQuickPathView::delegate() const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->model) {
|
2013-01-15 22:26:59 +00:00
|
|
|
if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel*>(d->model))
|
2011-04-27 12:13:26 +00:00
|
|
|
return dataModel->delegate();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
void QQuickPathView::setDelegate(QQmlComponent *delegate)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (delegate == this->delegate())
|
|
|
|
return;
|
|
|
|
if (!d->ownModel) {
|
2013-01-15 22:26:59 +00:00
|
|
|
d->model = new QQmlDelegateModel(qmlContext(this));
|
2011-04-27 12:13:26 +00:00
|
|
|
d->ownModel = true;
|
|
|
|
}
|
2013-01-15 22:26:59 +00:00
|
|
|
if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel*>(d->model)) {
|
2011-05-30 06:35:52 +00:00
|
|
|
int oldCount = dataModel->count();
|
2011-04-27 12:13:26 +00:00
|
|
|
dataModel->setDelegate(delegate);
|
|
|
|
d->modelCount = dataModel->count();
|
|
|
|
d->regenerate();
|
2011-05-30 06:35:52 +00:00
|
|
|
if (oldCount != dataModel->count())
|
|
|
|
emit countChanged();
|
2011-04-27 12:13:26 +00:00
|
|
|
emit delegateChanged();
|
2013-01-15 22:26:59 +00:00
|
|
|
d->delegateValidated = false;
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty int QtQuick2::PathView::pathItemCount
|
|
|
|
This property holds the number of items visible on the path at any one time.
|
2012-07-23 01:08:06 +00:00
|
|
|
|
|
|
|
Setting pathItemCount to undefined will show all items on the path.
|
2011-08-08 08:03:31 +00:00
|
|
|
*/
|
2011-10-14 08:51:42 +00:00
|
|
|
int QQuickPathView::pathItemCount() const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(const QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
return d->pathItems;
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::setPathItemCount(int i)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (i == d->pathItems)
|
|
|
|
return;
|
|
|
|
if (i < 1)
|
|
|
|
i = 1;
|
|
|
|
d->pathItems = i;
|
|
|
|
d->updateMappedRange();
|
|
|
|
if (d->isValid() && isComponentComplete()) {
|
|
|
|
d->regenerate();
|
|
|
|
}
|
|
|
|
emit pathItemCountChanged();
|
|
|
|
}
|
|
|
|
|
2012-07-23 01:08:06 +00:00
|
|
|
void QQuickPathView::resetPathItemCount()
|
|
|
|
{
|
|
|
|
Q_D(QQuickPathView);
|
|
|
|
if (-1 == d->pathItems)
|
|
|
|
return;
|
|
|
|
d->pathItems = -1;
|
|
|
|
d->updateMappedRange();
|
|
|
|
if (d->isValid() && isComponentComplete())
|
|
|
|
d->regenerate();
|
|
|
|
emit pathItemCountChanged();
|
|
|
|
}
|
|
|
|
|
2012-08-21 04:47:48 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty int QtQuick2::PathView::cacheItemCount
|
|
|
|
This property holds the maximum number of items to cache off the path.
|
|
|
|
|
|
|
|
For example, a PathView with a model containing 20 items, a pathItemCount
|
|
|
|
of 10, and an cacheItemCount of 4 will create up to 14 items, with 10 visible
|
|
|
|
on the path and 4 invisible cached items.
|
|
|
|
|
|
|
|
The cached delegates are created asynchronously,
|
|
|
|
allowing creation to occur across multiple frames and reducing the
|
|
|
|
likelihood of skipping frames.
|
|
|
|
|
|
|
|
Setting this value can improve the smoothness of scrolling behavior at the expense
|
|
|
|
of additional memory usage. It is not a substitute for creating efficient
|
|
|
|
delegates; the fewer objects and bindings in a delegate, the faster a view can be
|
|
|
|
moved.
|
|
|
|
|
|
|
|
\sa pathItemCount
|
|
|
|
*/
|
|
|
|
int QQuickPathView::cacheItemCount() const
|
|
|
|
{
|
|
|
|
Q_D(const QQuickPathView);
|
|
|
|
return d->cacheSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQuickPathView::setCacheItemCount(int i)
|
|
|
|
{
|
|
|
|
Q_D(QQuickPathView);
|
|
|
|
if (i == d->cacheSize || i < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
d->cacheSize = i;
|
|
|
|
d->updateMappedRange();
|
|
|
|
refill();
|
|
|
|
emit cacheItemCountChanged();
|
|
|
|
}
|
|
|
|
|
2012-03-21 03:18:05 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty enumeration QtQuick2::PathView::snapMode
|
|
|
|
|
|
|
|
This property determines how the items will settle following a drag or flick.
|
|
|
|
The possible values are:
|
|
|
|
|
|
|
|
\list
|
|
|
|
\li PathView.NoSnap (default) - the items stop anywhere along the path.
|
|
|
|
\li PathView.SnapToItem - the items settle with an item aligned with the \l preferredHighlightBegin.
|
|
|
|
\li PathView.SnapOneItem - the items settle no more than one item away from the item nearest
|
|
|
|
\l preferredHighlightBegin at the time the press is released. This mode is particularly
|
|
|
|
useful for moving one page at a time.
|
|
|
|
\endlist
|
|
|
|
|
|
|
|
\c snapMode does not affect the \l currentIndex. To update the
|
|
|
|
\l currentIndex as the view is moved, set \l highlightRangeMode
|
|
|
|
to \c PathView.StrictlyEnforceRange (default for PathView).
|
|
|
|
|
|
|
|
\sa highlightRangeMode
|
|
|
|
*/
|
|
|
|
QQuickPathView::SnapMode QQuickPathView::snapMode() const
|
|
|
|
{
|
|
|
|
Q_D(const QQuickPathView);
|
|
|
|
return d->snapMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQuickPathView::setSnapMode(SnapMode mode)
|
|
|
|
{
|
|
|
|
Q_D(QQuickPathView);
|
|
|
|
if (mode == d->snapMode)
|
|
|
|
return;
|
|
|
|
d->snapMode = mode;
|
|
|
|
emit snapModeChanged();
|
|
|
|
}
|
|
|
|
|
2012-07-23 01:08:06 +00:00
|
|
|
/*!
|
|
|
|
\qmlmethod QtQuick2::PathView::positionViewAtIndex(int index, PositionMode mode)
|
|
|
|
|
|
|
|
Positions the view such that the \a index is at the position specified by
|
|
|
|
\a mode:
|
|
|
|
|
|
|
|
\list
|
|
|
|
\li PathView.Beginning - position item at the beginning of the path.
|
|
|
|
\li PathView.Center - position item in the center of the path.
|
|
|
|
\li PathView.End - position item at the end of the path.
|
|
|
|
\li PathView.Contain - ensure the item is positioned on the path.
|
|
|
|
\li PathView.SnapPosition - position the item at \l preferredHighlightBegin. This mode
|
|
|
|
is only valid if \l highlightRangeMode is StrictlyEnforceRange or snapping is enabled
|
|
|
|
via \l snapMode.
|
|
|
|
\endlist
|
|
|
|
|
|
|
|
\b Note: methods should only be called after the Component has completed. To position
|
|
|
|
the view at startup, this method should be called by Component.onCompleted. For
|
|
|
|
example, to position the view at the end:
|
|
|
|
|
|
|
|
\code
|
|
|
|
Component.onCompleted: positionViewAtIndex(count - 1, PathView.End)
|
|
|
|
\endcode
|
|
|
|
*/
|
|
|
|
void QQuickPathView::positionViewAtIndex(int index, int mode)
|
|
|
|
{
|
|
|
|
Q_D(QQuickPathView);
|
|
|
|
if (!d->isValid())
|
|
|
|
return;
|
|
|
|
if (mode < QQuickPathView::Beginning || mode > QQuickPathView::SnapPosition || mode == 3) // 3 is unused in PathView
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (mode == QQuickPathView::Contain && (d->pathItems < 0 || d->modelCount <= d->pathItems))
|
|
|
|
return;
|
|
|
|
|
|
|
|
int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount);
|
|
|
|
int idx = (index+d->modelCount) % d->modelCount;
|
|
|
|
bool snap = d->haveHighlightRange && (d->highlightRangeMode != QQuickPathView::NoHighlightRange
|
|
|
|
|| d->snapMode != QQuickPathView::NoSnap);
|
|
|
|
|
|
|
|
qreal beginOffset;
|
|
|
|
qreal endOffset;
|
|
|
|
if (snap) {
|
|
|
|
beginOffset = d->modelCount - idx - qFloor(count * d->highlightRangeStart);
|
|
|
|
endOffset = beginOffset + count - 1;
|
|
|
|
} else {
|
|
|
|
beginOffset = d->modelCount - idx;
|
|
|
|
// Small offset since the last point coincides with the first and
|
|
|
|
// this the only "end" position that gives the expected visual result.
|
|
|
|
qreal adj = sizeof(qreal) == sizeof(float) ? 0.00001f : 0.000000000001;
|
|
|
|
endOffset = qmlMod(beginOffset + count, d->modelCount) - adj;
|
|
|
|
}
|
|
|
|
qreal offset = d->offset;
|
|
|
|
switch (mode) {
|
|
|
|
case Beginning:
|
|
|
|
offset = beginOffset;
|
|
|
|
break;
|
|
|
|
case End:
|
|
|
|
offset = endOffset;
|
|
|
|
break;
|
|
|
|
case Center:
|
|
|
|
if (beginOffset < endOffset)
|
|
|
|
offset = (beginOffset + endOffset)/2;
|
|
|
|
else
|
|
|
|
offset = (beginOffset + (endOffset + d->modelCount))/2;
|
|
|
|
if (snap)
|
|
|
|
offset = qRound(offset);
|
|
|
|
break;
|
|
|
|
case Contain:
|
|
|
|
if ((beginOffset < endOffset && (d->offset < beginOffset || d->offset > endOffset))
|
|
|
|
|| (d->offset < beginOffset && d->offset > endOffset)) {
|
|
|
|
qreal diff1 = qmlMod(beginOffset - d->offset + d->modelCount, d->modelCount);
|
|
|
|
qreal diff2 = qmlMod(d->offset - endOffset + d->modelCount, d->modelCount);
|
|
|
|
if (diff1 < diff2)
|
|
|
|
offset = beginOffset;
|
|
|
|
else
|
|
|
|
offset = endOffset;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SnapPosition:
|
|
|
|
offset = d->modelCount - idx;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
d->tl.clear();
|
|
|
|
setOffset(offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\qmlmethod int QtQuick2::PathView::indexAt(int x, int y)
|
|
|
|
|
|
|
|
Returns the index of the item containing the point \a x, \a y in content
|
|
|
|
coordinates. If there is no item at the point specified, -1 is returned.
|
|
|
|
|
|
|
|
\b Note: methods should only be called after the Component has completed.
|
|
|
|
*/
|
|
|
|
int QQuickPathView::indexAt(qreal x, qreal y) const
|
|
|
|
{
|
|
|
|
Q_D(const QQuickPathView);
|
|
|
|
if (!d->isValid())
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
for (int idx = 0; idx < d->items.count(); ++idx) {
|
|
|
|
QQuickItem *item = d->items.at(idx);
|
|
|
|
QPointF p = item->mapFromItem(this, QPointF(x, y));
|
|
|
|
if (item->contains(p))
|
|
|
|
return (d->firstIndex + idx) % d->modelCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\qmlmethod Item QtQuick2::PathView::itemAt(int x, int y)
|
|
|
|
|
|
|
|
Returns the item containing the point \a x, \a y in content
|
|
|
|
coordinates. If there is no item at the point specified, null is returned.
|
|
|
|
|
|
|
|
\b Note: methods should only be called after the Component has completed.
|
|
|
|
*/
|
|
|
|
QQuickItem *QQuickPathView::itemAt(qreal x, qreal y) const
|
|
|
|
{
|
|
|
|
Q_D(const QQuickPathView);
|
|
|
|
if (!d->isValid())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (int idx = 0; idx < d->items.count(); ++idx) {
|
|
|
|
QQuickItem *item = d->items.at(idx);
|
|
|
|
QPointF p = item->mapFromItem(this, QPointF(x, y));
|
|
|
|
if (item->contains(p))
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
QPointF QQuickPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2012-02-16 05:54:09 +00:00
|
|
|
qreal samples = qMin(path->path().length()/5, qreal(500.0));
|
|
|
|
qreal res = path->path().length()/samples;
|
|
|
|
|
2011-04-27 12:13:26 +00:00
|
|
|
qreal mindist = 1e10; // big number
|
|
|
|
QPointF nearPoint = path->pointAt(0);
|
|
|
|
qreal nearPc = 0;
|
2012-02-16 05:54:09 +00:00
|
|
|
|
|
|
|
// get rough pos
|
|
|
|
for (qreal i=1; i < samples; i++) {
|
|
|
|
QPointF pt = path->pointAt(i/samples);
|
|
|
|
QPointF diff = pt - point;
|
|
|
|
qreal dist = diff.x()*diff.x() + diff.y()*diff.y();
|
|
|
|
if (dist < mindist) {
|
|
|
|
nearPoint = pt;
|
|
|
|
nearPc = i;
|
|
|
|
mindist = dist;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now refine
|
|
|
|
qreal approxPc = nearPc;
|
|
|
|
for (qreal i = approxPc-1.0; i < approxPc+1.0; i += 1/(2*res)) {
|
|
|
|
QPointF pt = path->pointAt(i/samples);
|
2011-04-27 12:13:26 +00:00
|
|
|
QPointF diff = pt - point;
|
|
|
|
qreal dist = diff.x()*diff.x() + diff.y()*diff.y();
|
|
|
|
if (dist < mindist) {
|
|
|
|
nearPoint = pt;
|
|
|
|
nearPc = i;
|
|
|
|
mindist = dist;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nearPercent)
|
2012-02-16 05:54:09 +00:00
|
|
|
*nearPercent = nearPc / samples;
|
2011-04-27 12:13:26 +00:00
|
|
|
|
|
|
|
return nearPoint;
|
|
|
|
}
|
|
|
|
|
2012-02-16 05:54:09 +00:00
|
|
|
void QQuickPathViewPrivate::addVelocitySample(qreal v)
|
|
|
|
{
|
|
|
|
velocityBuffer.append(v);
|
|
|
|
if (velocityBuffer.count() > QML_FLICK_SAMPLEBUFFER)
|
|
|
|
velocityBuffer.remove(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal QQuickPathViewPrivate::calcVelocity() const
|
|
|
|
{
|
|
|
|
qreal velocity = 0;
|
|
|
|
if (velocityBuffer.count() > QML_FLICK_DISCARDSAMPLES) {
|
|
|
|
int count = velocityBuffer.count()-QML_FLICK_DISCARDSAMPLES;
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
qreal v = velocityBuffer.at(i);
|
|
|
|
velocity += v;
|
|
|
|
}
|
|
|
|
velocity /= count;
|
|
|
|
}
|
|
|
|
return velocity;
|
|
|
|
}
|
|
|
|
|
2012-03-21 03:18:05 +00:00
|
|
|
qint64 QQuickPathViewPrivate::computeCurrentTime(QInputEvent *event)
|
|
|
|
{
|
|
|
|
if (0 != event->timestamp() && QQuickItemPrivate::consistentTime == -1) {
|
|
|
|
return event->timestamp();
|
|
|
|
}
|
|
|
|
|
|
|
|
return QQuickItemPrivate::elapsed(timer);
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::mousePressEvent(QMouseEvent *event)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->interactive) {
|
|
|
|
d->handleMousePressEvent(event);
|
|
|
|
event->accept();
|
|
|
|
} else {
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickItem::mousePressEvent(event);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathViewPrivate::handleMousePressEvent(QMouseEvent *event)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2012-03-25 02:37:08 +00:00
|
|
|
if (!interactive || !items.count() || !model || !modelCount)
|
2011-04-27 12:13:26 +00:00
|
|
|
return;
|
2012-02-16 05:54:09 +00:00
|
|
|
velocityBuffer.clear();
|
2011-04-27 12:13:26 +00:00
|
|
|
int idx = 0;
|
|
|
|
for (; idx < items.count(); ++idx) {
|
2012-02-26 16:26:53 +00:00
|
|
|
QQuickItem *item = items.at(idx);
|
|
|
|
if (item->contains(item->mapFromScene(event->windowPos())))
|
2011-04-27 12:13:26 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (idx == items.count() && dragMargin == 0.) // didn't click on an item
|
|
|
|
return;
|
|
|
|
|
2011-08-30 14:48:57 +00:00
|
|
|
startPoint = pointNear(event->localPos(), &startPc);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (idx == items.count()) {
|
2011-08-30 14:48:57 +00:00
|
|
|
qreal distance = qAbs(event->localPos().x() - startPoint.x()) + qAbs(event->localPos().y() - startPoint.y());
|
2011-04-27 12:13:26 +00:00
|
|
|
if (distance > dragMargin)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-09 05:40:29 +00:00
|
|
|
|
|
|
|
if (tl.isActive() && flicking && flickDuration && qreal(tl.time())/flickDuration < 0.8)
|
2011-04-27 12:13:26 +00:00
|
|
|
stealMouse = true; // If we've been flicked then steal the click.
|
|
|
|
else
|
|
|
|
stealMouse = false;
|
|
|
|
|
2012-03-21 03:18:05 +00:00
|
|
|
QQuickItemPrivate::start(timer);
|
|
|
|
lastPosTime = computeCurrentTime(event);
|
2011-04-27 12:13:26 +00:00
|
|
|
tl.clear();
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::mouseMoveEvent(QMouseEvent *event)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->interactive) {
|
|
|
|
d->handleMouseMoveEvent(event);
|
|
|
|
if (d->stealMouse)
|
|
|
|
setKeepMouseGrab(true);
|
|
|
|
event->accept();
|
|
|
|
} else {
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickItem::mouseMoveEvent(event);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathViewPrivate::handleMouseMoveEvent(QMouseEvent *event)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_Q(QQuickPathView);
|
2012-03-21 03:18:05 +00:00
|
|
|
if (!interactive || !timer.isValid() || !model || !modelCount)
|
2011-04-27 12:13:26 +00:00
|
|
|
return;
|
|
|
|
|
2012-05-14 02:49:52 +00:00
|
|
|
qint64 currentTimestamp = computeCurrentTime(event);
|
2011-04-27 12:13:26 +00:00
|
|
|
qreal newPc;
|
2011-08-30 14:48:57 +00:00
|
|
|
QPointF pathPoint = pointNear(event->localPos(), &newPc);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (!stealMouse) {
|
|
|
|
QPointF delta = pathPoint - startPoint;
|
2011-08-30 11:17:00 +00:00
|
|
|
if (qAbs(delta.x()) > qApp->styleHints()->startDragDistance() || qAbs(delta.y()) > qApp->styleHints()->startDragDistance()) {
|
2011-04-27 12:13:26 +00:00
|
|
|
stealMouse = true;
|
|
|
|
}
|
2012-05-14 02:49:52 +00:00
|
|
|
} else {
|
2011-10-14 08:51:42 +00:00
|
|
|
moveReason = QQuickPathViewPrivate::Mouse;
|
2012-08-21 04:47:48 +00:00
|
|
|
int count = pathItems == -1 ? modelCount : qMin(pathItems, modelCount);
|
|
|
|
qreal diff = (newPc - startPc)*count;
|
2011-04-27 12:13:26 +00:00
|
|
|
if (diff) {
|
|
|
|
q->setOffset(offset + diff);
|
|
|
|
|
|
|
|
if (diff > modelCount/2)
|
|
|
|
diff -= modelCount;
|
|
|
|
else if (diff < -modelCount/2)
|
|
|
|
diff += modelCount;
|
|
|
|
|
2012-03-21 03:18:05 +00:00
|
|
|
qint64 elapsed = currentTimestamp - lastPosTime;
|
|
|
|
if (elapsed > 0)
|
|
|
|
addVelocitySample(diff / (qreal(elapsed) / 1000.));
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
if (!moving) {
|
|
|
|
moving = true;
|
|
|
|
emit q->movingChanged();
|
|
|
|
emit q->movementStarted();
|
|
|
|
}
|
2012-07-03 03:57:54 +00:00
|
|
|
setDragging(true);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
2012-03-21 03:18:05 +00:00
|
|
|
startPc = newPc;
|
|
|
|
lastPosTime = currentTimestamp;
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::mouseReleaseEvent(QMouseEvent *event)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->interactive) {
|
|
|
|
d->handleMouseReleaseEvent(event);
|
|
|
|
event->accept();
|
|
|
|
ungrabMouse();
|
|
|
|
} else {
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickItem::mouseReleaseEvent(event);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_Q(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
stealMouse = false;
|
|
|
|
q->setKeepMouseGrab(false);
|
2012-07-03 03:57:54 +00:00
|
|
|
setDragging(false);
|
2012-03-21 03:18:05 +00:00
|
|
|
if (!interactive || !timer.isValid() || !model || !modelCount) {
|
|
|
|
timer.invalidate();
|
2012-03-25 02:37:08 +00:00
|
|
|
if (!tl.isActive())
|
|
|
|
q->movementEnding();
|
2011-04-27 12:13:26 +00:00
|
|
|
return;
|
2012-03-25 02:37:08 +00:00
|
|
|
}
|
2011-04-27 12:13:26 +00:00
|
|
|
|
2012-02-16 05:54:09 +00:00
|
|
|
qreal velocity = calcVelocity();
|
2012-08-21 04:47:48 +00:00
|
|
|
qreal count = pathItems == -1 ? modelCount : qMin(pathItems, modelCount);
|
2012-03-25 02:37:08 +00:00
|
|
|
qreal pixelVelocity = (path->path().length()/count) * velocity;
|
|
|
|
if (qAbs(pixelVelocity) > MinimumFlickVelocity) {
|
2012-03-21 03:18:05 +00:00
|
|
|
if (qAbs(pixelVelocity) > maximumFlickVelocity || snapMode == QQuickPathView::SnapOneItem) {
|
2012-03-25 02:37:08 +00:00
|
|
|
// limit velocity
|
|
|
|
qreal maxVel = velocity < 0 ? -maximumFlickVelocity : maximumFlickVelocity;
|
|
|
|
velocity = maxVel / (path->path().length()/count);
|
|
|
|
}
|
2011-04-27 12:13:26 +00:00
|
|
|
// Calculate the distance to be travelled
|
|
|
|
qreal v2 = velocity*velocity;
|
|
|
|
qreal accel = deceleration/10;
|
2012-02-16 05:54:09 +00:00
|
|
|
qreal dist = 0;
|
2012-03-21 03:18:05 +00:00
|
|
|
if (haveHighlightRange && (highlightRangeMode == QQuickPathView::StrictlyEnforceRange
|
|
|
|
|| snapMode != QQuickPathView::NoSnap)) {
|
|
|
|
if (snapMode == QQuickPathView::SnapOneItem) {
|
|
|
|
// encourage snapping one item in direction of motion
|
|
|
|
if (velocity > 0.)
|
|
|
|
dist = qRound(0.5 + offset) - offset;
|
|
|
|
else
|
|
|
|
dist = qRound(0.5 - offset) + offset;
|
|
|
|
} else {
|
|
|
|
// + 0.25 to encourage moving at least one item in the flick direction
|
|
|
|
dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0) + 0.25));
|
|
|
|
|
|
|
|
// round to nearest item.
|
|
|
|
if (velocity > 0.)
|
|
|
|
dist = qRound(dist + offset) - offset;
|
|
|
|
else
|
|
|
|
dist = qRound(dist - offset) + offset;
|
|
|
|
}
|
2011-04-27 12:13:26 +00:00
|
|
|
// Calculate accel required to stop on item boundary
|
|
|
|
if (dist <= 0.) {
|
|
|
|
dist = 0.;
|
|
|
|
accel = 0.;
|
|
|
|
} else {
|
|
|
|
accel = v2 / (2.0f * qAbs(dist));
|
|
|
|
}
|
2012-02-16 05:54:09 +00:00
|
|
|
} else {
|
|
|
|
dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0)));
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
2012-03-09 05:40:29 +00:00
|
|
|
flickDuration = static_cast<int>(1000 * qAbs(velocity) / accel);
|
2011-04-27 12:13:26 +00:00
|
|
|
offsetAdj = 0.0;
|
|
|
|
moveOffset.setValue(offset);
|
|
|
|
tl.accel(moveOffset, velocity, accel, dist);
|
2012-02-16 04:43:03 +00:00
|
|
|
tl.callback(QQuickTimeLineCallback(&moveOffset, fixOffsetCallback, this));
|
2011-04-27 12:13:26 +00:00
|
|
|
if (!flicking) {
|
|
|
|
flicking = true;
|
|
|
|
emit q->flickingChanged();
|
|
|
|
emit q->flickStarted();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fixOffset();
|
|
|
|
}
|
|
|
|
|
2012-03-21 03:18:05 +00:00
|
|
|
timer.invalidate();
|
2011-04-27 12:13:26 +00:00
|
|
|
if (!tl.isActive())
|
|
|
|
q->movementEnding();
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
bool QQuickPathView::sendMouseEvent(QMouseEvent *event)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2012-02-26 16:26:53 +00:00
|
|
|
QPointF localPos = mapFromScene(event->windowPos());
|
|
|
|
|
2012-07-11 07:32:16 +00:00
|
|
|
QQuickWindow *c = window();
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
|
2011-04-27 12:13:26 +00:00
|
|
|
bool stealThisEvent = d->stealMouse;
|
2012-02-26 16:26:53 +00:00
|
|
|
if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab())) {
|
|
|
|
QMouseEvent mouseEvent(event->type(), localPos, event->windowPos(), event->screenPos(),
|
2011-08-30 14:48:57 +00:00
|
|
|
event->button(), event->buttons(), event->modifiers());
|
2011-04-27 12:13:26 +00:00
|
|
|
mouseEvent.setAccepted(false);
|
|
|
|
|
2011-10-17 11:03:58 +00:00
|
|
|
switch (mouseEvent.type()) {
|
2011-08-30 14:48:57 +00:00
|
|
|
case QEvent::MouseMove:
|
2011-04-27 12:13:26 +00:00
|
|
|
d->handleMouseMoveEvent(&mouseEvent);
|
|
|
|
break;
|
2011-08-30 14:48:57 +00:00
|
|
|
case QEvent::MouseButtonPress:
|
2011-04-27 12:13:26 +00:00
|
|
|
d->handleMousePressEvent(&mouseEvent);
|
|
|
|
stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
|
|
|
|
break;
|
2011-08-30 14:48:57 +00:00
|
|
|
case QEvent::MouseButtonRelease:
|
2011-04-27 12:13:26 +00:00
|
|
|
d->handleMouseReleaseEvent(&mouseEvent);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
grabber = c->mouseGrabberItem();
|
|
|
|
if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
|
|
|
|
grabMouse();
|
|
|
|
|
|
|
|
return d->stealMouse;
|
2012-03-21 03:18:05 +00:00
|
|
|
} else if (d->timer.isValid()) {
|
|
|
|
d->timer.invalidate();
|
2011-08-01 05:49:33 +00:00
|
|
|
d->fixOffset();
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
2011-08-30 14:48:57 +00:00
|
|
|
if (event->type() == QEvent::MouseButtonRelease)
|
2011-04-27 12:13:26 +00:00
|
|
|
d->stealMouse = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
bool QQuickPathView::childMouseEventFilter(QQuickItem *i, QEvent *e)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (!isVisible() || !d->interactive)
|
2011-10-14 08:51:42 +00:00
|
|
|
return QQuickItem::childMouseEventFilter(i, e);
|
2011-04-27 12:13:26 +00:00
|
|
|
|
|
|
|
switch (e->type()) {
|
2011-08-30 14:48:57 +00:00
|
|
|
case QEvent::MouseButtonPress:
|
|
|
|
case QEvent::MouseMove:
|
|
|
|
case QEvent::MouseButtonRelease:
|
|
|
|
return sendMouseEvent(static_cast<QMouseEvent *>(e));
|
2011-04-27 12:13:26 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
return QQuickItem::childMouseEventFilter(i, e);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::mouseUngrabEvent()
|
2011-08-01 05:49:33 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-08-01 05:49:33 +00:00
|
|
|
if (d->stealMouse) {
|
|
|
|
// if our mouse grab has been removed (probably by a Flickable),
|
|
|
|
// fix our state
|
|
|
|
d->stealMouse = false;
|
|
|
|
setKeepMouseGrab(false);
|
2012-03-21 03:18:05 +00:00
|
|
|
d->timer.invalidate();
|
2012-03-15 04:58:03 +00:00
|
|
|
d->fixOffset();
|
2012-07-03 03:57:54 +00:00
|
|
|
d->setDragging(false);
|
2012-03-15 04:58:03 +00:00
|
|
|
if (!d->tl.isActive())
|
|
|
|
movementEnding();
|
2011-08-01 05:49:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::updatePolish()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickItem::updatePolish();
|
2011-04-27 12:13:26 +00:00
|
|
|
refill();
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::componentComplete()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-10-05 06:03:12 +00:00
|
|
|
if (d->model && d->ownModel)
|
2013-01-15 22:26:59 +00:00
|
|
|
static_cast<QQmlDelegateModel *>(d->model.data())->componentComplete();
|
2011-10-05 06:03:12 +00:00
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickItem::componentComplete();
|
2011-10-05 06:03:12 +00:00
|
|
|
|
2012-07-18 00:36:17 +00:00
|
|
|
if (d->model) {
|
2011-04-27 12:13:26 +00:00
|
|
|
d->modelCount = d->model->count();
|
2012-07-18 00:36:17 +00:00
|
|
|
if (d->modelCount && d->currentIndex != 0) // an initial value has been provided for currentIndex
|
|
|
|
d->offset = qmlMod(d->modelCount - d->currentIndex, d->modelCount);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
2012-07-18 00:36:17 +00:00
|
|
|
|
|
|
|
d->createHighlight();
|
|
|
|
d->regenerate();
|
2011-04-27 12:13:26 +00:00
|
|
|
d->updateHighlight();
|
2012-06-27 04:05:38 +00:00
|
|
|
d->updateCurrent();
|
2011-10-05 06:03:12 +00:00
|
|
|
|
|
|
|
if (d->modelCount)
|
|
|
|
emit countChanged();
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::refill()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2012-05-25 01:41:17 +00:00
|
|
|
|
|
|
|
d->layoutScheduled = false;
|
|
|
|
|
2011-04-27 12:13:26 +00:00
|
|
|
if (!d->isValid() || !isComponentComplete())
|
|
|
|
return;
|
|
|
|
|
|
|
|
bool currentVisible = false;
|
2012-08-21 04:47:48 +00:00
|
|
|
int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount);
|
2011-04-27 12:13:26 +00:00
|
|
|
|
|
|
|
// first move existing items and remove items off path
|
|
|
|
int idx = d->firstIndex;
|
2011-10-14 08:51:42 +00:00
|
|
|
QList<QQuickItem*>::iterator it = d->items.begin();
|
2011-04-27 12:13:26 +00:00
|
|
|
while (it != d->items.end()) {
|
|
|
|
qreal pos = d->positionOfIndex(idx);
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickItem *item = *it;
|
2012-08-21 04:47:48 +00:00
|
|
|
if (pos < 1.0) {
|
2011-04-27 12:13:26 +00:00
|
|
|
d->updateItem(item, pos);
|
|
|
|
if (idx == d->currentIndex) {
|
|
|
|
currentVisible = true;
|
|
|
|
d->currentItemOffset = pos;
|
|
|
|
}
|
|
|
|
++it;
|
|
|
|
} else {
|
2012-08-21 04:47:48 +00:00
|
|
|
d->updateItem(item, pos);
|
|
|
|
if (QQuickPathViewAttached *att = d->attached(item))
|
|
|
|
att->setOnPath(pos < 1.0);
|
|
|
|
if (!d->isInBound(pos, d->mappedRange - d->mappedCache, 1.0 + d->mappedCache)) {
|
|
|
|
// qDebug() << "release";
|
|
|
|
d->releaseItem(item);
|
|
|
|
if (it == d->items.begin()) {
|
|
|
|
if (++d->firstIndex >= d->modelCount) {
|
|
|
|
d->firstIndex = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
it = d->items.erase(it);
|
|
|
|
} else {
|
|
|
|
++it;
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
++idx;
|
|
|
|
if (idx >= d->modelCount)
|
|
|
|
idx = 0;
|
|
|
|
}
|
2012-08-21 04:47:48 +00:00
|
|
|
|
2011-04-27 12:13:26 +00:00
|
|
|
if (!d->items.count())
|
|
|
|
d->firstIndex = -1;
|
|
|
|
|
2011-11-03 05:52:13 +00:00
|
|
|
bool waiting = false;
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->modelCount) {
|
|
|
|
// add items to beginning and end
|
2012-08-21 04:47:48 +00:00
|
|
|
if (d->items.count() < count+d->cacheSize) {
|
2011-04-27 12:13:26 +00:00
|
|
|
int idx = qRound(d->modelCount - d->offset) % d->modelCount;
|
|
|
|
qreal startPos = 0.0;
|
2012-03-21 03:18:05 +00:00
|
|
|
if (d->haveHighlightRange && (d->highlightRangeMode != QQuickPathView::NoHighlightRange
|
|
|
|
|| d->snapMode != QQuickPathView::NoSnap))
|
2011-04-27 12:13:26 +00:00
|
|
|
startPos = d->highlightRangeStart;
|
|
|
|
if (d->firstIndex >= 0) {
|
|
|
|
startPos = d->positionOfIndex(d->firstIndex);
|
|
|
|
idx = (d->firstIndex + d->items.count()) % d->modelCount;
|
|
|
|
}
|
|
|
|
qreal pos = d->positionOfIndex(idx);
|
2012-08-21 04:47:48 +00:00
|
|
|
while ((d->isInBound(pos, startPos, 1.0 + d->mappedCache) || !d->items.count()) && d->items.count() < count+d->cacheSize) {
|
2011-11-03 05:52:13 +00:00
|
|
|
// qDebug() << "append" << idx;
|
2012-08-21 04:47:48 +00:00
|
|
|
QQuickItem *item = d->getItem(idx, idx+1, pos >= 1.0);
|
2011-11-03 05:52:13 +00:00
|
|
|
if (!item) {
|
|
|
|
waiting = true;
|
|
|
|
break;
|
|
|
|
}
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->currentIndex == idx) {
|
|
|
|
currentVisible = true;
|
|
|
|
d->currentItemOffset = pos;
|
|
|
|
}
|
|
|
|
if (d->items.count() == 0)
|
|
|
|
d->firstIndex = idx;
|
|
|
|
d->items.append(item);
|
|
|
|
d->updateItem(item, pos);
|
|
|
|
++idx;
|
|
|
|
if (idx >= d->modelCount)
|
|
|
|
idx = 0;
|
|
|
|
pos = d->positionOfIndex(idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
idx = d->firstIndex - 1;
|
|
|
|
if (idx < 0)
|
|
|
|
idx = d->modelCount - 1;
|
|
|
|
pos = d->positionOfIndex(idx);
|
2012-08-21 04:47:48 +00:00
|
|
|
while (!waiting && d->isInBound(pos, d->mappedRange - d->mappedCache, startPos) && d->items.count() < count+d->cacheSize) {
|
2011-11-03 05:52:13 +00:00
|
|
|
// qDebug() << "prepend" << idx;
|
2012-08-21 04:47:48 +00:00
|
|
|
QQuickItem *item = d->getItem(idx, idx+1, pos >= 1.0);
|
2011-11-03 05:52:13 +00:00
|
|
|
if (!item) {
|
|
|
|
waiting = true;
|
|
|
|
break;
|
|
|
|
}
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->currentIndex == idx) {
|
|
|
|
currentVisible = true;
|
|
|
|
d->currentItemOffset = pos;
|
|
|
|
}
|
|
|
|
d->items.prepend(item);
|
|
|
|
d->updateItem(item, pos);
|
|
|
|
d->firstIndex = idx;
|
|
|
|
idx = d->firstIndex - 1;
|
|
|
|
if (idx < 0)
|
|
|
|
idx = d->modelCount - 1;
|
|
|
|
pos = d->positionOfIndex(idx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-01 03:09:10 +00:00
|
|
|
if (!currentVisible) {
|
2011-04-27 12:13:26 +00:00
|
|
|
d->currentItemOffset = 1.0;
|
2011-08-01 03:09:10 +00:00
|
|
|
if (d->currentItem) {
|
2012-08-21 04:47:48 +00:00
|
|
|
d->updateItem(d->currentItem, 1.0);
|
2011-11-03 05:52:13 +00:00
|
|
|
} else if (!waiting && d->currentIndex >= 0 && d->currentIndex < d->modelCount) {
|
2012-08-21 04:47:48 +00:00
|
|
|
if ((d->currentItem = d->getItem(d->currentIndex, d->currentIndex))) {
|
|
|
|
d->updateItem(d->currentItem, 1.0);
|
2011-11-03 05:52:13 +00:00
|
|
|
if (QQuickPathViewAttached *att = d->attached(d->currentItem))
|
|
|
|
att->setIsCurrentItem(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (!waiting && !d->currentItem) {
|
2012-08-21 04:47:48 +00:00
|
|
|
if ((d->currentItem = d->getItem(d->currentIndex, d->currentIndex))) {
|
2011-11-03 05:52:13 +00:00
|
|
|
d->currentItem->setFocus(true);
|
2011-10-14 08:51:42 +00:00
|
|
|
if (QQuickPathViewAttached *att = d->attached(d->currentItem))
|
2011-08-01 03:09:10 +00:00
|
|
|
att->setIsCurrentItem(true);
|
|
|
|
}
|
|
|
|
}
|
2011-04-27 12:13:26 +00:00
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
if (d->highlightItem && d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
|
2011-04-27 12:13:26 +00:00
|
|
|
d->updateItem(d->highlightItem, d->highlightRangeStart);
|
2011-10-14 08:51:42 +00:00
|
|
|
if (QQuickPathViewAttached *att = d->attached(d->highlightItem))
|
2011-04-27 12:13:26 +00:00
|
|
|
att->setOnPath(true);
|
2011-10-14 08:51:42 +00:00
|
|
|
} else if (d->highlightItem && d->moveReason != QQuickPathViewPrivate::SetIndex) {
|
2011-04-27 12:13:26 +00:00
|
|
|
d->updateItem(d->highlightItem, d->currentItemOffset);
|
2011-10-14 08:51:42 +00:00
|
|
|
if (QQuickPathViewAttached *att = d->attached(d->highlightItem))
|
2011-04-27 12:13:26 +00:00
|
|
|
att->setOnPath(currentVisible);
|
|
|
|
}
|
|
|
|
while (d->itemCache.count())
|
|
|
|
d->releaseItem(d->itemCache.takeLast());
|
|
|
|
}
|
|
|
|
|
2013-01-15 22:26:59 +00:00
|
|
|
void QQuickPathView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-09-01 05:24:34 +00:00
|
|
|
if (!d->model || !d->model->isValid() || !d->path || !isComponentComplete())
|
2011-04-27 12:13:26 +00:00
|
|
|
return;
|
|
|
|
|
2011-09-01 05:24:34 +00:00
|
|
|
if (reset) {
|
|
|
|
d->modelCount = d->model->count();
|
2011-04-27 12:13:26 +00:00
|
|
|
d->regenerate();
|
2011-09-01 05:24:34 +00:00
|
|
|
emit countChanged();
|
|
|
|
return;
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
2011-09-01 05:24:34 +00:00
|
|
|
if (changeSet.removes().isEmpty() && changeSet.inserts().isEmpty())
|
2011-04-27 12:13:26 +00:00
|
|
|
return;
|
|
|
|
|
2011-09-01 05:24:34 +00:00
|
|
|
const int modelCount = d->modelCount;
|
|
|
|
int moveId = -1;
|
|
|
|
int moveOffset;
|
2011-04-27 12:13:26 +00:00
|
|
|
bool currentChanged = false;
|
2011-09-01 05:24:34 +00:00
|
|
|
bool changedOffset = false;
|
2013-01-15 22:26:59 +00:00
|
|
|
foreach (const QQmlChangeSet::Remove &r, changeSet.removes()) {
|
2011-09-01 05:24:34 +00:00
|
|
|
if (moveId == -1 && d->currentIndex >= r.index + r.count) {
|
|
|
|
d->currentIndex -= r.count;
|
|
|
|
currentChanged = true;
|
|
|
|
} else if (moveId == -1 && d->currentIndex >= r.index && d->currentIndex < r.index + r.count) {
|
|
|
|
// current item has been removed.
|
|
|
|
if (r.isMove()) {
|
|
|
|
moveId = r.moveId;
|
|
|
|
moveOffset = d->currentIndex - r.index;
|
|
|
|
} else if (d->currentItem) {
|
2011-10-14 08:51:42 +00:00
|
|
|
if (QQuickPathViewAttached *att = d->attached(d->currentItem))
|
2011-09-01 05:24:34 +00:00
|
|
|
att->setIsCurrentItem(true);
|
|
|
|
d->releaseItem(d->currentItem);
|
|
|
|
d->currentItem = 0;
|
|
|
|
}
|
2012-05-29 05:45:15 +00:00
|
|
|
d->currentIndex = qMin(r.index, d->modelCount - r.count - 1);
|
2011-09-01 05:24:34 +00:00
|
|
|
currentChanged = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r.index > d->currentIndex) {
|
2011-11-22 07:26:49 +00:00
|
|
|
changedOffset = true;
|
|
|
|
d->offset -= r.count;
|
|
|
|
d->offsetAdj -= r.count;
|
2011-09-01 05:24:34 +00:00
|
|
|
}
|
|
|
|
d->modelCount -= r.count;
|
|
|
|
}
|
2013-01-15 22:26:59 +00:00
|
|
|
foreach (const QQmlChangeSet::Insert &i, changeSet.inserts()) {
|
2011-09-01 05:24:34 +00:00
|
|
|
if (d->modelCount) {
|
|
|
|
if (moveId == -1 && i.index <= d->currentIndex) {
|
|
|
|
d->currentIndex += i.count;
|
2011-11-09 05:34:01 +00:00
|
|
|
currentChanged = true;
|
2011-11-22 07:26:49 +00:00
|
|
|
} else {
|
2011-11-09 05:34:01 +00:00
|
|
|
if (moveId != -1 && moveId == i.moveId) {
|
2011-09-01 05:24:34 +00:00
|
|
|
d->currentIndex = i.index + moveOffset;
|
2011-11-09 05:34:01 +00:00
|
|
|
currentChanged = true;
|
|
|
|
}
|
2011-11-22 07:26:49 +00:00
|
|
|
if (i.index > d->currentIndex) {
|
|
|
|
d->offset += i.count;
|
|
|
|
d->offsetAdj += i.count;
|
|
|
|
changedOffset = true;
|
|
|
|
}
|
2011-09-01 05:24:34 +00:00
|
|
|
}
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
2011-09-01 05:24:34 +00:00
|
|
|
d->modelCount += i.count;
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
2011-11-22 07:26:49 +00:00
|
|
|
d->offset = qmlMod(d->offset, d->modelCount);
|
|
|
|
if (d->offset < 0)
|
|
|
|
d->offset += d->modelCount;
|
2012-05-29 05:45:15 +00:00
|
|
|
if (d->currentIndex == -1)
|
|
|
|
d->currentIndex = d->calcCurrentIndex();
|
2011-11-22 07:26:49 +00:00
|
|
|
|
2011-04-27 12:13:26 +00:00
|
|
|
d->itemCache += d->items;
|
|
|
|
d->items.clear();
|
|
|
|
|
|
|
|
if (!d->modelCount) {
|
|
|
|
while (d->itemCache.count())
|
|
|
|
d->releaseItem(d->itemCache.takeLast());
|
|
|
|
d->offset = 0;
|
|
|
|
changedOffset = true;
|
|
|
|
d->tl.reset(d->moveOffset);
|
2011-11-22 07:26:49 +00:00
|
|
|
} else {
|
2011-11-09 05:34:01 +00:00
|
|
|
if (!d->flicking && !d->moving && d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
|
2011-11-22 07:26:49 +00:00
|
|
|
d->offset = qmlMod(d->modelCount - d->currentIndex, d->modelCount);
|
|
|
|
changedOffset = true;
|
2011-11-09 05:34:01 +00:00
|
|
|
}
|
2011-09-01 05:24:34 +00:00
|
|
|
d->firstIndex = -1;
|
|
|
|
d->updateMappedRange();
|
|
|
|
d->scheduleLayout();
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
if (changedOffset)
|
|
|
|
emit offsetChanged();
|
|
|
|
if (currentChanged)
|
|
|
|
emit currentIndexChanged();
|
2011-09-01 05:24:34 +00:00
|
|
|
if (d->modelCount != modelCount)
|
|
|
|
emit countChanged();
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
2013-01-15 22:26:59 +00:00
|
|
|
void QQuickPathView::destroyingItem(QObject *item)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(item);
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::ticked()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
d->updateCurrent();
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathView::movementEnding()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->flicking) {
|
|
|
|
d->flicking = false;
|
|
|
|
emit flickingChanged();
|
|
|
|
emit flickEnded();
|
|
|
|
}
|
|
|
|
if (d->moving && !d->stealMouse) {
|
|
|
|
d->moving = false;
|
|
|
|
emit movingChanged();
|
|
|
|
emit movementEnded();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// find the item closest to the snap position
|
2011-10-14 08:51:42 +00:00
|
|
|
int QQuickPathViewPrivate::calcCurrentIndex()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2012-05-29 05:45:15 +00:00
|
|
|
int current = 0;
|
2011-04-27 12:13:26 +00:00
|
|
|
if (modelCount && model && items.count()) {
|
|
|
|
offset = qmlMod(offset, modelCount);
|
|
|
|
if (offset < 0)
|
|
|
|
offset += modelCount;
|
|
|
|
current = qRound(qAbs(qmlMod(modelCount - offset, modelCount)));
|
|
|
|
current = current % modelCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
return current;
|
|
|
|
}
|
|
|
|
|
2011-11-03 05:52:13 +00:00
|
|
|
void QQuickPathViewPrivate::createCurrentItem()
|
|
|
|
{
|
|
|
|
if (requestedIndex != -1)
|
|
|
|
return;
|
|
|
|
int itemIndex = (currentIndex - firstIndex + modelCount) % modelCount;
|
|
|
|
if (itemIndex < items.count()) {
|
2012-08-21 04:47:48 +00:00
|
|
|
if ((currentItem = getItem(currentIndex, currentIndex))) {
|
2011-11-03 05:52:13 +00:00
|
|
|
currentItem->setFocus(true);
|
|
|
|
if (QQuickPathViewAttached *att = attached(currentItem))
|
|
|
|
att->setIsCurrentItem(true);
|
|
|
|
}
|
|
|
|
} else if (currentIndex >= 0 && currentIndex < modelCount) {
|
2012-08-21 04:47:48 +00:00
|
|
|
if ((currentItem = getItem(currentIndex, currentIndex))) {
|
|
|
|
updateItem(currentItem, 1.0);
|
2011-11-03 05:52:13 +00:00
|
|
|
if (QQuickPathViewAttached *att = attached(currentItem))
|
|
|
|
att->setIsCurrentItem(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathViewPrivate::updateCurrent()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_Q(QQuickPathView);
|
2012-06-27 04:05:38 +00:00
|
|
|
if (moveReason == SetIndex)
|
2011-04-27 12:13:26 +00:00
|
|
|
return;
|
2011-10-14 08:51:42 +00:00
|
|
|
if (!modelCount || !haveHighlightRange || highlightRangeMode != QQuickPathView::StrictlyEnforceRange)
|
2011-04-27 12:13:26 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
int idx = calcCurrentIndex();
|
2012-07-18 00:36:17 +00:00
|
|
|
if (model && (idx != currentIndex || !currentItem)) {
|
2011-08-01 03:09:10 +00:00
|
|
|
if (currentItem) {
|
2011-10-14 08:51:42 +00:00
|
|
|
if (QQuickPathViewAttached *att = attached(currentItem))
|
2011-08-01 03:09:10 +00:00
|
|
|
att->setIsCurrentItem(false);
|
|
|
|
releaseItem(currentItem);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
2012-07-18 00:36:17 +00:00
|
|
|
int oldCurrentIndex = currentIndex;
|
2011-04-27 12:13:26 +00:00
|
|
|
currentIndex = idx;
|
|
|
|
currentItem = 0;
|
2011-11-03 05:52:13 +00:00
|
|
|
createCurrentItem();
|
2012-07-18 00:36:17 +00:00
|
|
|
if (oldCurrentIndex != currentIndex)
|
|
|
|
emit q->currentIndexChanged();
|
|
|
|
emit q->currentItemChanged();
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathViewPrivate::fixOffsetCallback(void *d)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
((QQuickPathViewPrivate *)d)->fixOffset();
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
void QQuickPathViewPrivate::fixOffset()
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_Q(QQuickPathView);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (model && items.count()) {
|
2012-03-21 03:18:05 +00:00
|
|
|
if (haveHighlightRange && (highlightRangeMode == QQuickPathView::StrictlyEnforceRange
|
|
|
|
|| snapMode != QQuickPathView::NoSnap)) {
|
2011-04-27 12:13:26 +00:00
|
|
|
int curr = calcCurrentIndex();
|
2012-03-21 03:18:05 +00:00
|
|
|
if (curr != currentIndex && highlightRangeMode == QQuickPathView::StrictlyEnforceRange)
|
2011-04-27 12:13:26 +00:00
|
|
|
q->setCurrentIndex(curr);
|
|
|
|
else
|
2012-03-21 03:18:05 +00:00
|
|
|
snapToIndex(curr);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-21 03:18:05 +00:00
|
|
|
void QQuickPathViewPrivate::snapToIndex(int index)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
|
|
|
if (!model || modelCount <= 0)
|
|
|
|
return;
|
|
|
|
|
2012-03-21 03:18:05 +00:00
|
|
|
qreal targetOffset = qmlMod(modelCount - index, modelCount);
|
2011-04-27 12:13:26 +00:00
|
|
|
|
2011-11-09 05:34:01 +00:00
|
|
|
if (offset == targetOffset)
|
|
|
|
return;
|
|
|
|
|
2011-04-27 12:13:26 +00:00
|
|
|
moveReason = Other;
|
|
|
|
offsetAdj = 0.0;
|
|
|
|
tl.reset(moveOffset);
|
|
|
|
moveOffset.setValue(offset);
|
|
|
|
|
|
|
|
const int duration = highlightMoveDuration;
|
|
|
|
|
2011-11-21 23:28:40 +00:00
|
|
|
if (!duration) {
|
|
|
|
tl.set(moveOffset, targetOffset);
|
2012-06-27 05:29:54 +00:00
|
|
|
} else if (moveDirection == Positive || (moveDirection == Shortest && targetOffset - offset > modelCount/2.0)) {
|
2011-04-27 12:13:26 +00:00
|
|
|
qreal distance = modelCount - targetOffset + offset;
|
|
|
|
if (targetOffset > moveOffset) {
|
|
|
|
tl.move(moveOffset, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * offset / distance));
|
|
|
|
tl.set(moveOffset, modelCount);
|
|
|
|
tl.move(moveOffset, targetOffset, QEasingCurve(offset == 0.0 ? QEasingCurve::InOutQuad : QEasingCurve::OutQuad), int(duration * (modelCount-targetOffset) / distance));
|
|
|
|
} else {
|
|
|
|
tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
|
|
|
|
}
|
2012-06-27 05:29:54 +00:00
|
|
|
} else if (moveDirection == Negative || targetOffset - offset <= -modelCount/2.0) {
|
2011-04-27 12:13:26 +00:00
|
|
|
qreal distance = modelCount - offset + targetOffset;
|
|
|
|
if (targetOffset < moveOffset) {
|
|
|
|
tl.move(moveOffset, modelCount, QEasingCurve(targetOffset == 0 ? QEasingCurve::InOutQuad : QEasingCurve::InQuad), int(duration * (modelCount-offset) / distance));
|
|
|
|
tl.set(moveOffset, 0.0);
|
|
|
|
tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * targetOffset / distance));
|
|
|
|
} else {
|
|
|
|
tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
|
|
|
|
}
|
|
|
|
moveDirection = Shortest;
|
|
|
|
}
|
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickPathViewAttached *QQuickPathView::qmlAttachedProperties(QObject *obj)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
return new QQuickPathViewAttached(obj);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
|