2011-04-27 12:13:26 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
2019-09-17 09:46:04 +00:00
|
|
|
** Copyright (C) 2019 The Qt Company Ltd.
|
2016-01-19 09:38:36 +00:00
|
|
|
** Contact: https://www.qt.io/licensing/
|
2011-04-27 12:13:26 +00:00
|
|
|
**
|
2013-09-30 05:28:31 +00:00
|
|
|
** This file is part of the QtQuick module of the Qt Toolkit.
|
2011-04-27 12:13:26 +00:00
|
|
|
**
|
2016-01-19 09:38:36 +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
|
2015-01-28 11:55:39 +00:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
2016-01-19 09:38:36 +00:00
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
2012-09-20 05:21:40 +00:00
|
|
|
**
|
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
|
2016-01-19 09:38:36 +00:00
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
** ensure the GNU Lesser General Public License version 3 requirements
|
|
|
|
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
2012-09-20 05:21:40 +00:00
|
|
|
**
|
2016-01-19 09:38:36 +00:00
|
|
|
** GNU General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
** General Public License version 2.0 or (at your option) the GNU General
|
|
|
|
** Public license version 3 or any later version approved by the KDE Free
|
|
|
|
** Qt Foundation. The licenses are as published by the Free Software
|
|
|
|
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
|
|
|
** https://www.gnu.org/licenses/gpl-3.0.html.
|
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"
|
2013-01-28 21:12:59 +00:00
|
|
|
#include "qquickflickablebehavior_p.h" //Contains flicking behavior defines
|
2015-03-11 15:02:33 +00:00
|
|
|
#include "qquicktext_p.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>
|
|
|
|
|
2015-02-13 11:02:19 +00:00
|
|
|
#include <cmath>
|
2012-03-25 02:37:08 +00:00
|
|
|
|
2011-04-27 12:13:26 +00:00
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
2015-03-11 15:02:33 +00:00
|
|
|
Q_DECLARE_LOGGING_CATEGORY(lcItemViewDelegateLifecycle)
|
PathView: reduce velocity by linear decay model if release is delayed
That is, from the time between the last mouse move event to the mouse
release, the velocity will be linearly discounted/depreciated until it
reaches 0 at QML_FLICK_VELOCITY_DECAY_TIME, which is currently 50 ms.
50 ms seems like a long time if the user meant to flick and release
immediately (in practice it might be more like 4 ms), and also a
short time if the user meant to "dwell" before releasing.
If we try to translate the fake physics to real physics, this would be
approximately equivalent to saying that if you slide a flat plate on an
air hockey table with one finger, and then stop suddenly, its momentum
_would_ cause it to keep moving under your finger for up to 50ms (except
that it doesn't, because our timeline doesn't "tick" until after the
release); and yet if you hold it for longer than 50ms, it will stop
right on the spot. That's not quite realistic, but feels OK for fake
physics (like the rest of the physics in Qt Quick).
Also add the qt.quick.pathview logging category, which will just log
the velocity calculations for now (but is intended for anything else
in PathView that seems worth logging later on).
Task-number: QTBUG-77173
Task-number: QTBUG-59052
Change-Id: Ie86f18d3b3305874b698c848290e0fd3beda94de
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
2019-09-10 13:26:56 +00:00
|
|
|
Q_LOGGING_CATEGORY(lcPathView, "qt.quick.pathview")
|
2015-03-11 15:02:33 +00:00
|
|
|
|
2019-09-17 09:46:04 +00:00
|
|
|
static const qreal MinimumFlickVelocity = 75;
|
2012-03-25 02:37:08 +00:00
|
|
|
|
2016-09-13 13:01:08 +00:00
|
|
|
static QQmlOpenMetaObjectType *qPathViewAttachedType = nullptr;
|
2011-04-27 12:13:26 +00:00
|
|
|
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickPathViewAttached::QQuickPathViewAttached(QObject *parent)
|
2018-02-21 09:41:54 +00:00
|
|
|
: QObject(parent), m_percent(-1), m_view(nullptr), m_onPath(false), m_isCurrent(false)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
|
|
|
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()
|
2019-09-17 09:46:04 +00:00
|
|
|
: path(nullptr), currentIndex(0), currentItemOffset(0), startPc(0)
|
|
|
|
, offset(0), offsetAdj(0), mappedRange(1), mappedCache(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)
|
2014-07-21 12:55:30 +00:00
|
|
|
, inRefill(false)
|
2012-03-25 02:37:08 +00:00
|
|
|
, dragMargin(0), deceleration(100), maximumFlickVelocity(QML_FLICK_DEFAULTMAXVELOCITY)
|
|
|
|
, moveOffset(this, &QQuickPathViewPrivate::setAdjustedOffset), flickDuration(0)
|
2015-05-07 05:56:03 +00:00
|
|
|
, pathItems(-1), requestedIndex(-1), cacheSize(0), requestedZ(0)
|
2016-04-26 12:12:14 +00:00
|
|
|
, moveReason(Other), movementDirection(QQuickPathView::Shortest), moveDirection(QQuickPathView::Shortest)
|
2016-09-13 13:01:08 +00:00
|
|
|
, attType(nullptr), highlightComponent(nullptr), highlightItem(nullptr)
|
2012-03-25 02:37:08 +00:00
|
|
|
, 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()),
|
2019-09-16 12:12:50 +00:00
|
|
|
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()),
|
2019-09-16 12:12:50 +00:00
|
|
|
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;
|
2017-11-06 15:13:13 +00:00
|
|
|
QObject *object = model->object(modelIndex, async ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested);
|
2013-01-15 22:26:59 +00:00
|
|
|
QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
|
|
|
|
if (!item) {
|
|
|
|
if (object) {
|
|
|
|
model->release(object);
|
|
|
|
if (!delegateValidated) {
|
|
|
|
delegateValidated = true;
|
|
|
|
QObject* delegate = q->delegate();
|
2017-01-05 19:27:53 +00:00
|
|
|
qmlWarning(delegate ? delegate : q) << QQuickPathView::tr("Delegate must be of Item type");
|
2013-01-15 22:26:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} 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));
|
2016-09-13 13:01:08 +00:00
|
|
|
qPathViewAttachedType = nullptr;
|
2011-11-03 05:52:13 +00:00
|
|
|
if (att) {
|
|
|
|
att->m_view = this;
|
|
|
|
att->setOnPath(false);
|
|
|
|
}
|
|
|
|
item->setParentItem(this);
|
2019-09-17 09:46:04 +00:00
|
|
|
d->updateItem(item, 1);
|
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));
|
2016-09-13 13:01:08 +00:00
|
|
|
qPathViewAttachedType = nullptr;
|
2011-11-03 05:52:13 +00:00
|
|
|
if (att) {
|
|
|
|
att->m_view = this;
|
|
|
|
qreal percent = d->positionOfIndex(index);
|
2019-09-17 09:46:04 +00:00
|
|
|
if (percent < 1 && d->path) {
|
2016-08-09 13:41:33 +00:00
|
|
|
const auto attributes = d->path->attributes();
|
|
|
|
for (const QString &attr : attributes)
|
2012-08-21 04:47:48 +00:00
|
|
|
att->setValue(attr.toUtf8(), d->path->attributeAt(attr, percent));
|
|
|
|
item->setZ(d->requestedZ);
|
|
|
|
}
|
2019-09-17 09:46:04 +00:00
|
|
|
att->setOnPath(percent < 1);
|
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;
|
2015-03-11 15:02:33 +00:00
|
|
|
qCDebug(lcItemViewDelegateLifecycle) << "release" << item;
|
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
|
2016-09-13 13:01:08 +00:00
|
|
|
item->setParentItem(nullptr);
|
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) {
|
2016-08-09 13:41:33 +00:00
|
|
|
const auto attributes = path->attributes();
|
|
|
|
for (const QString &attr : attributes)
|
2013-01-17 09:46:45 +00:00
|
|
|
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);
|
2016-09-13 13:01:08 +00:00
|
|
|
currentItem = nullptr;
|
2011-08-01 03:09:10 +00:00
|
|
|
}
|
2018-06-19 14:04:24 +00:00
|
|
|
|
2016-08-16 14:19:29 +00:00
|
|
|
for (QQuickItem *p : qAsConst(items))
|
2011-04-27 12:13:26 +00:00
|
|
|
releaseItem(p);
|
2016-08-16 14:19:29 +00:00
|
|
|
|
2018-06-19 14:04:24 +00:00
|
|
|
for (QQuickItem *p : qAsConst(itemCache))
|
|
|
|
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();
|
2018-06-19 14:04:24 +00:00
|
|
|
itemCache.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 {
|
2019-09-17 09:46:04 +00:00
|
|
|
mappedRange = 1;
|
|
|
|
mappedCache = 0;
|
2012-08-21 04:47:48 +00:00
|
|
|
}
|
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
|
|
|
{
|
2019-09-17 09:46:04 +00:00
|
|
|
qreal pos = -1;
|
2011-04-27 12:13:26 +00:00
|
|
|
|
|
|
|
if (model && index >= 0 && index < modelCount) {
|
2019-09-17 09:46:04 +00:00
|
|
|
qreal start = 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;
|
2016-10-12 08:12:25 +00:00
|
|
|
globalPos = std::fmod(globalPos, qreal(modelCount)) / modelCount;
|
2011-04-27 12:13:26 +00:00
|
|
|
if (pathItems != -1 && pathItems < modelCount) {
|
2012-08-21 04:47:48 +00:00
|
|
|
globalPos += start / mappedRange;
|
2019-09-17 09:46:04 +00:00
|
|
|
globalPos = std::fmod(globalPos, qreal(1));
|
2012-08-21 04:47:48 +00:00
|
|
|
pos = globalPos * mappedRange;
|
2011-04-27 12:13:26 +00:00
|
|
|
} else {
|
2019-09-17 09:46:04 +00:00
|
|
|
pos = std::fmod(globalPos + start, qreal(1));
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
{
|
2019-09-17 09:46:04 +00:00
|
|
|
if (qFuzzyCompare(lower, upper))
|
2016-05-26 11:45:31 +00:00
|
|
|
return true;
|
2012-08-21 04:47:48 +00:00
|
|
|
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) {
|
2016-09-13 13:01:08 +00:00
|
|
|
highlightItem->setParentItem(nullptr);
|
2011-05-30 06:35:52 +00:00
|
|
|
highlightItem->deleteLater();
|
2016-09-13 13:01:08 +00:00
|
|
|
highlightItem = nullptr;
|
2011-04-27 12:13:26 +00:00
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
2016-09-13 13:01:08 +00:00
|
|
|
QQuickItem *item = nullptr;
|
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;
|
|
|
|
|
2019-09-17 09:46:04 +00:00
|
|
|
offsetAdj = 0;
|
2011-04-27 12:13:26 +00:00
|
|
|
tl.reset(moveHighlight);
|
|
|
|
moveHighlight.setValue(highlightPosition);
|
|
|
|
|
|
|
|
const int duration = highlightMoveDuration;
|
|
|
|
|
|
|
|
if (target - highlightPosition > modelCount/2) {
|
|
|
|
highlightUp = false;
|
|
|
|
qreal distance = modelCount - target + highlightPosition;
|
2019-09-17 09:46:04 +00:00
|
|
|
tl.move(moveHighlight, 0, QEasingCurve(QEasingCurve::InQuad), int(duration * highlightPosition / distance));
|
2011-04-27 12:13:26 +00:00
|
|
|
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));
|
2019-09-17 09:46:04 +00:00
|
|
|
tl.set(moveHighlight, 0);
|
2011-04-27 12:13:26 +00:00
|
|
|
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
|
|
|
{
|
2019-09-17 09:46:04 +00:00
|
|
|
if (!(qFuzzyCompare(pos, highlightPosition))) {
|
|
|
|
qreal start = 0;
|
|
|
|
qreal end = 1;
|
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
|
2016-10-12 08:12:25 +00:00
|
|
|
qreal relativeHighlight = std::fmod(pos + offset, range) / range;
|
2011-04-27 12:13:26 +00:00
|
|
|
|
2012-08-21 04:47:48 +00:00
|
|
|
if (!highlightUp && relativeHighlight > end / mappedRange) {
|
2019-09-17 09:46:04 +00:00
|
|
|
qreal diff = 1 - relativeHighlight;
|
2011-04-27 12:13:26 +00:00
|
|
|
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))
|
2019-09-17 09:46:04 +00:00
|
|
|
att->setOnPath(pathPos < 1);
|
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);
|
2016-08-16 14:19:29 +00:00
|
|
|
for (QQuickItem *item : qAsConst(d->items)) {
|
2011-10-14 08:51:42 +00:00
|
|
|
if (QQuickPathViewAttached *att = d->attached(item))
|
2011-04-27 12:13:26 +00:00
|
|
|
att->m_percent = -1;
|
|
|
|
}
|
|
|
|
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;
|
2016-08-09 13:41:33 +00:00
|
|
|
const auto attributes = path->attributes();
|
|
|
|
for (const QString &attr : attributes)
|
2011-04-27 12:13:26 +00:00
|
|
|
att->setValue(attr.toUtf8(), path->attributeAt(attr, percent));
|
2019-09-17 09:46:04 +00:00
|
|
|
att->setOnPath(percent < 1);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
2019-09-17 09:46:04 +00:00
|
|
|
QQuickItemPrivate::get(item)->setCulled(percent >= 1);
|
|
|
|
QPointF pf = path->pointAtPercent(qMin(percent, qreal(1)));
|
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;
|
|
|
|
|
|
|
|
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
|
2013-09-24 14:41:12 +00:00
|
|
|
\inqmlmodule QtQuick
|
2012-05-28 17:40:02 +00:00
|
|
|
\ingroup qtquick-paths
|
|
|
|
\ingroup qtquick-views
|
2011-08-08 08:03:31 +00:00
|
|
|
\inherits Item
|
2018-06-18 13:09:56 +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.
|
|
|
|
|
2013-11-05 12:17:06 +00:00
|
|
|
\sa Path, {QML Data Models}, ListView, GridView, {Qt Quick Examples - Views}
|
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
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlattachedproperty PathView QtQuick::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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlattachedproperty bool QtQuick::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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlattachedproperty bool QtQuick::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
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty model QtQuick::PathView::model
|
2011-08-08 08:03:31 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-10-29 12:59:46 +00:00
|
|
|
void QQuickPathView::setModel(const QVariant &m)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
2011-10-14 08:51:42 +00:00
|
|
|
Q_D(QQuickPathView);
|
2014-10-29 12:59:46 +00:00
|
|
|
QVariant model = m;
|
|
|
|
if (model.userType() == qMetaTypeId<QJSValue>())
|
|
|
|
model = model.value<QJSValue>().toVariant();
|
|
|
|
|
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);
|
2016-09-13 13:01:08 +00:00
|
|
|
QQmlInstanceModel *vim = nullptr;
|
2013-01-15 22:26:59 +00:00
|
|
|
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();
|
|
|
|
}
|
2019-09-17 09:46:04 +00:00
|
|
|
if (!(qFuzzyIsNull(d->offset))) {
|
2012-07-18 00:36:17 +00:00
|
|
|
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
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty int QtQuick::PathView::count
|
2011-08-08 08:03:31 +00:00
|
|
|
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
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty Path QtQuick::PathView::path
|
2011-08-08 08:03:31 +00:00
|
|
|
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;
|
2016-12-28 22:40:11 +00:00
|
|
|
|
|
|
|
if (path) {
|
|
|
|
qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()),
|
|
|
|
this, QQuickPathView, SLOT(pathUpdated()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isComponentComplete()) {
|
2011-04-27 12:13:26 +00:00
|
|
|
d->clear();
|
2016-12-28 22:40:11 +00:00
|
|
|
if (d->isValid()) {
|
|
|
|
if (d->attType) {
|
|
|
|
d->attType->release();
|
|
|
|
d->attType = nullptr;
|
|
|
|
}
|
|
|
|
d->regenerate();
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
2016-12-28 22:40:11 +00:00
|
|
|
|
2011-04-27 12:13:26 +00:00
|
|
|
emit pathChanged();
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty int QtQuick::PathView::currentIndex
|
2011-08-08 08:03:31 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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;
|
2016-09-13 13:01:08 +00:00
|
|
|
d->currentItem = nullptr;
|
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)
|
2015-10-21 15:04:16 +00:00
|
|
|
d->snapToIndex(d->currentIndex, QQuickPathViewPrivate::SetIndex);
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-13 13:35:44 +00:00
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty Item QtQuick::PathView::currentItem
|
2013-09-13 13:35:44 +00:00
|
|
|
This property holds the current item in the view.
|
|
|
|
*/
|
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
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlmethod QtQuick::PathView::incrementCurrentIndex()
|
2011-08-08 08:03:31 +00:00
|
|
|
|
|
|
|
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);
|
2016-04-26 12:12:14 +00:00
|
|
|
d->moveDirection = QQuickPathView::Positive;
|
2011-04-27 12:13:26 +00:00
|
|
|
setCurrentIndex(currentIndex()+1);
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlmethod QtQuick::PathView::decrementCurrentIndex()
|
2011-08-08 08:03:31 +00:00
|
|
|
|
|
|
|
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);
|
2016-04-26 12:12:14 +00:00
|
|
|
d->moveDirection = QQuickPathView::Negative;
|
2012-05-29 05:45:15 +00:00
|
|
|
setCurrentIndex(currentIndex()-1);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty real QtQuick::PathView::offset
|
2011-08-08 08:03:31 +00:00
|
|
|
|
|
|
|
The offset specifies how far along the path the items are from their initial positions.
|
2019-09-17 09:46:04 +00:00
|
|
|
This is a real number that ranges from \c 0 to the count of items in the model.
|
2011-08-08 08:03:31 +00:00
|
|
|
*/
|
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);
|
2019-09-17 09:46:04 +00:00
|
|
|
if (!qFuzzyCompare(offset, o)) {
|
2011-04-27 12:13:26 +00:00
|
|
|
if (isValid() && q->isComponentComplete()) {
|
2015-03-11 15:02:33 +00:00
|
|
|
qreal oldOffset = offset;
|
2016-10-12 08:12:25 +00:00
|
|
|
offset = std::fmod(o, qreal(modelCount));
|
2011-04-27 12:13:26 +00:00
|
|
|
if (offset < 0)
|
|
|
|
offset += qreal(modelCount);
|
2015-03-11 15:02:33 +00:00
|
|
|
qCDebug(lcItemViewDelegateLifecycle) << o << "was" << oldOffset << "now" << offset;
|
2011-04-27 12:13:26 +00:00
|
|
|
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
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty Component QtQuick::PathView::highlight
|
2011-08-08 08:03:31 +00:00
|
|
|
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
|
|
|
/*!
|
2019-09-17 09:46:04 +00:00
|
|
|
\qmlproperty Item QtQuick::PathView::highlightItem
|
2011-08-08 08:03:31 +00:00
|
|
|
|
2019-09-17 09:46:04 +00:00
|
|
|
\c highlightItem holds the highlight item, which was created
|
|
|
|
from the \l highlight component.
|
2011-08-08 08:03:31 +00:00
|
|
|
|
2019-09-17 09:46:04 +00:00
|
|
|
\sa highlight
|
2011-08-08 08:03:31 +00:00
|
|
|
*/
|
2016-10-11 14:10:07 +00:00
|
|
|
QQuickItem *QQuickPathView::highlightItem() 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->highlightItem;
|
|
|
|
}
|
2019-09-17 09:46:04 +00:00
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty real QtQuick::PathView::preferredHighlightBegin
|
|
|
|
\qmlproperty real QtQuick::PathView::preferredHighlightEnd
|
|
|
|
\qmlproperty enumeration QtQuick::PathView::highlightRangeMode
|
2011-08-08 08:03:31 +00:00
|
|
|
|
|
|
|
These properties set the preferred range of the highlight (current item)
|
2019-09-17 09:46:04 +00:00
|
|
|
within the view. The preferred values must be in the range from \c 0 to \c 1.
|
2013-07-16 13:01:57 +00:00
|
|
|
|
|
|
|
Valid values for \c highlightRangeMode are:
|
|
|
|
|
|
|
|
\list
|
|
|
|
\li \e PathView.NoHighlightRange - no range is applied and the
|
|
|
|
highlight will move freely within the view.
|
|
|
|
\li \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.
|
|
|
|
\li \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.
|
|
|
|
\endlist
|
2011-08-08 08:03:31 +00:00
|
|
|
|
2013-07-16 13:01:57 +00:00
|
|
|
The default value is \e PathView.StrictlyEnforceRange.
|
2011-08-08 08:03:31 +00:00
|
|
|
|
2013-07-16 13:01:57 +00:00
|
|
|
Defining a highlight range is the correct way to influence where the
|
2011-08-08 08:03:31 +00:00
|
|
|
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
|
2013-07-16 13:01:57 +00:00
|
|
|
highlight range to be 0.5,0.5 and highlightRangeMode to \e PathView.StrictlyEnforceRange.
|
2011-08-08 08:03:31 +00:00
|
|
|
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.
|
|
|
|
|
2013-07-16 13:01:57 +00:00
|
|
|
\note A valid range requires \c preferredHighlightEnd to be greater
|
|
|
|
than or equal to \c preferredHighlightBegin.
|
2011-08-08 08:03:31 +00:00
|
|
|
*/
|
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);
|
2019-09-17 09:46:04 +00:00
|
|
|
if (qFuzzyCompare(d->highlightRangeStart, start) || start < 0 || start > 1)
|
2011-04-27 12:13:26 +00:00
|
|
|
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);
|
2019-09-17 09:46:04 +00:00
|
|
|
if (qFuzzyCompare(d->highlightRangeEnd, end) || end < 0 || end > 1)
|
2011-04-27 12:13:26 +00:00
|
|
|
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)
|
2015-10-21 15:04:16 +00:00
|
|
|
d->snapToIndex(index, QQuickPathViewPrivate::Other);
|
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
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty int QtQuick::PathView::highlightMoveDuration
|
2011-08-08 08:03:31 +00:00
|
|
|
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
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty real QtQuick::PathView::dragMargin
|
2016-05-20 14:18:46 +00:00
|
|
|
This property holds the maximum distance from the path that initiates mouse dragging.
|
2011-08-08 08:03:31 +00:00
|
|
|
|
|
|
|
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);
|
2019-09-17 09:46:04 +00:00
|
|
|
if (qFuzzyCompare(d->dragMargin, dragMargin))
|
2011-04-27 12:13:26 +00:00
|
|
|
return;
|
|
|
|
d->dragMargin = dragMargin;
|
|
|
|
emit dragMarginChanged();
|
|
|
|
}
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty real QtQuick::PathView::flickDeceleration
|
2011-08-08 08:03:31 +00:00
|
|
|
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);
|
2019-09-17 09:46:04 +00:00
|
|
|
if (qFuzzyCompare(d->deceleration, dec))
|
2011-04-27 12:13:26 +00:00
|
|
|
return;
|
|
|
|
d->deceleration = dec;
|
|
|
|
emit flickDecelerationChanged();
|
|
|
|
}
|
|
|
|
|
2012-03-25 02:37:08 +00:00
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty real QtQuick::PathView::maximumFlickVelocity
|
2012-03-25 02:37:08 +00:00
|
|
|
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);
|
2019-09-17 09:46:04 +00:00
|
|
|
if (qFuzzyCompare(vel, d->maximumFlickVelocity))
|
2012-03-25 02:37:08 +00:00
|
|
|
return;
|
|
|
|
d->maximumFlickVelocity = vel;
|
|
|
|
emit maximumFlickVelocityChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-08-08 08:03:31 +00:00
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty bool QtQuick::PathView::interactive
|
2011-08-08 08:03:31 +00:00
|
|
|
|
|
|
|
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
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty bool QtQuick::PathView::moving
|
2011-08-08 08:03:31 +00:00
|
|
|
|
|
|
|
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
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty bool QtQuick::PathView::flicking
|
2011-08-08 08:03:31 +00:00
|
|
|
|
|
|
|
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
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty bool QtQuick::PathView::dragging
|
2012-07-03 03:57:54 +00:00
|
|
|
|
|
|
|
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
|
|
|
/*!
|
2014-03-14 15:41:01 +00:00
|
|
|
\qmlsignal QtQuick::PathView::movementStarted()
|
2011-08-08 08:03:31 +00:00
|
|
|
|
2014-03-14 15:41:01 +00:00
|
|
|
This signal is emitted when the view begins moving due to user
|
2011-08-08 08:03:31 +00:00
|
|
|
interaction.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
2014-03-14 15:41:01 +00:00
|
|
|
\qmlsignal QtQuick::PathView::movementEnded()
|
2011-08-08 08:03:31 +00:00
|
|
|
|
2014-03-14 15:41:01 +00:00
|
|
|
This signal is emitted when the view stops moving due to user
|
|
|
|
interaction. If a flick was generated, this signal will
|
|
|
|
be emitted once the flick stops. If a flick was not
|
|
|
|
generated, this signal will be emitted when the
|
2011-08-08 08:03:31 +00:00
|
|
|
user stops dragging - i.e. a mouse or touch release.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
2014-03-14 15:41:01 +00:00
|
|
|
\qmlsignal QtQuick::PathView::flickStarted()
|
2011-08-08 08:03:31 +00:00
|
|
|
|
2014-03-14 15:41:01 +00:00
|
|
|
This signal is emitted when the view is flicked. A flick
|
2011-08-08 08:03:31 +00:00
|
|
|
starts from the point that the mouse or touch is released,
|
|
|
|
while still in motion.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
2014-03-14 15:41:01 +00:00
|
|
|
\qmlsignal QtQuick::PathView::flickEnded()
|
2011-08-08 08:03:31 +00:00
|
|
|
|
2014-03-14 15:41:01 +00:00
|
|
|
This signal is emitted when the view stops moving due to a flick.
|
2011-08-08 08:03:31 +00:00
|
|
|
*/
|
|
|
|
|
2012-07-03 03:57:54 +00:00
|
|
|
/*!
|
2014-03-14 15:41:01 +00:00
|
|
|
\qmlsignal QtQuick::PathView::dragStarted()
|
2012-07-03 03:57:54 +00:00
|
|
|
|
2014-03-14 15:41:01 +00:00
|
|
|
This signal is emitted when the view starts to be dragged due to user
|
2012-07-03 03:57:54 +00:00
|
|
|
interaction.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
2014-03-14 15:41:01 +00:00
|
|
|
\qmlsignal QtQuick::PathView::dragEnded()
|
2012-07-03 03:57:54 +00:00
|
|
|
|
2014-03-14 15:41:01 +00:00
|
|
|
This signal is emitted when the user stops dragging the view.
|
2012-07-03 03:57:54 +00:00
|
|
|
|
|
|
|
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
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty Component QtQuick::PathView::delegate
|
2011-08-08 08:03:31 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2016-09-13 13:01:08 +00:00
|
|
|
return nullptr;
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
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;
|
2014-04-15 13:27:29 +00:00
|
|
|
if (isComponentComplete())
|
|
|
|
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-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
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty int QtQuick::PathView::pathItemCount
|
2011-08-08 08:03:31 +00:00
|
|
|
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
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty int QtQuick::PathView::cacheItemCount
|
2012-08-21 04:47:48 +00:00
|
|
|
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.
|
|
|
|
|
2016-06-03 18:18:35 +00:00
|
|
|
\note Setting this property is not a replacement for creating efficient delegates.
|
|
|
|
It can improve the smoothness of scrolling behavior at the expense of additional
|
|
|
|
memory usage. The fewer objects and bindings in a delegate, the faster a
|
|
|
|
view can be scrolled. It is important to realize that setting cacheItemCount
|
|
|
|
will only postpone issues caused by slow-loading delegates, it is not a
|
|
|
|
solution for this scenario.
|
2012-08-21 04:47:48 +00:00
|
|
|
|
|
|
|
\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
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlproperty enumeration QtQuick::PathView::snapMode
|
2012-03-21 03:18:05 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2016-04-26 12:12:14 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty enumeration QtQuick::PathView::movementDirection
|
|
|
|
\since 5.7
|
|
|
|
|
|
|
|
This property determines the direction in which items move when setting the current index.
|
|
|
|
The possible values are:
|
|
|
|
|
|
|
|
\list
|
|
|
|
\li PathView.Shortest (default) - the items move in the direction that requires the least
|
|
|
|
movement, which could be either \c Negative or \c Positive.
|
|
|
|
\li PathView.Negative - the items move backwards towards their destination.
|
|
|
|
\li PathView.Positive - the items move forwards towards their destination.
|
|
|
|
\endlist
|
|
|
|
|
|
|
|
For example, suppose that there are 5 items in the model, and \l currentIndex is \c 0.
|
|
|
|
If currentIndex is set to \c 2,
|
|
|
|
|
|
|
|
\list
|
|
|
|
\li a \c Positive movement direction will result in the following order: 0, 1, 2
|
|
|
|
\li a \c Negative movement direction will result in the following order: 0, 5, 4, 3, 2
|
|
|
|
\li a \c Shortest movement direction will result in same order with \c Positive .
|
|
|
|
\endlist
|
|
|
|
|
|
|
|
\note this property doesn't affect the movement of \l incrementCurrentIndex() and \l decrementCurrentIndex().
|
|
|
|
*/
|
|
|
|
QQuickPathView::MovementDirection QQuickPathView::movementDirection() const
|
|
|
|
{
|
|
|
|
Q_D(const QQuickPathView);
|
|
|
|
return d->movementDirection;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQuickPathView::setMovementDirection(QQuickPathView::MovementDirection dir)
|
|
|
|
{
|
|
|
|
Q_D(QQuickPathView);
|
|
|
|
if (dir == d->movementDirection)
|
|
|
|
return;
|
|
|
|
d->movementDirection = dir;
|
|
|
|
if (!d->tl.isActive())
|
|
|
|
d->moveDirection = d->movementDirection;
|
|
|
|
emit movementDirectionChanged();
|
|
|
|
}
|
|
|
|
|
2012-07-23 01:08:06 +00:00
|
|
|
/*!
|
2013-10-01 11:03:28 +00:00
|
|
|
\qmlmethod QtQuick::PathView::positionViewAtIndex(int index, PositionMode mode)
|
2012-07-23 01:08:06 +00:00
|
|
|
|
|
|
|
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;
|
2016-10-12 08:12:25 +00:00
|
|
|
endOffset = std::fmod(beginOffset + count, qreal(d->modelCount)) - adj;
|
2012-07-23 01:08:06 +00:00
|
|
|
}
|
|
|
|
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)) {
|
2016-10-12 08:12:25 +00:00
|
|
|
qreal diff1 = std::fmod(beginOffset - d->offset + d->modelCount, qreal(d->modelCount));
|
|
|
|
qreal diff2 = std::fmod(d->offset - endOffset + d->modelCount, qreal(d->modelCount));
|
2012-07-23 01:08:06 +00:00
|
|
|
if (diff1 < diff2)
|
|
|
|
offset = beginOffset;
|
|
|
|
else
|
|
|
|
offset = endOffset;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SnapPosition:
|
|
|
|
offset = d->modelCount - idx;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
d->tl.clear();
|
|
|
|
setOffset(offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
2016-08-17 10:13:04 +00:00
|
|
|
\qmlmethod int QtQuick::PathView::indexAt(real x, real y)
|
2012-07-23 01:08:06 +00:00
|
|
|
|
|
|
|
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);
|
2016-08-16 14:41:23 +00:00
|
|
|
QQuickItem *item = itemAt(x, y);
|
2016-09-13 13:01:08 +00:00
|
|
|
return item ? d->model->indexOf(item, nullptr) : -1;
|
2012-07-23 01:08:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
2016-08-17 10:13:04 +00:00
|
|
|
\qmlmethod Item QtQuick::PathView::itemAt(real x, real y)
|
2012-07-23 01:08:06 +00:00
|
|
|
|
|
|
|
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())
|
2016-09-13 13:01:08 +00:00
|
|
|
return nullptr;
|
2012-07-23 01:08:06 +00:00
|
|
|
|
2016-08-16 14:19:29 +00:00
|
|
|
for (QQuickItem *item : d->items) {
|
2012-07-23 01:08:06 +00:00
|
|
|
QPointF p = item->mapFromItem(this, QPointF(x, y));
|
|
|
|
if (item->contains(p))
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
2019-01-11 14:33:58 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\qmlmethod Item QtQuick::QQuickPathView::itemAtIndex(int index)
|
|
|
|
|
|
|
|
Returns the item for \a index. If there is no item for that index, for example
|
|
|
|
because it has not been created yet, or because it has been panned out of
|
|
|
|
the visible area and removed from the cache, null is returned.
|
|
|
|
|
|
|
|
\b Note: this method should only be called after the Component has completed.
|
|
|
|
The returned value should also not be stored since it can turn to null
|
|
|
|
as soon as control goes out of the calling scope, if the view releases that item.
|
|
|
|
|
|
|
|
\since 5.13
|
|
|
|
*/
|
|
|
|
QQuickItem *QQuickPathView::itemAtIndex(int index) const
|
|
|
|
{
|
|
|
|
Q_D(const QQuickPathView);
|
|
|
|
if (!d->isValid())
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
for (QQuickItem *item : d->items) {
|
|
|
|
if (index == d->model->indexOf(item, nullptr))
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
2016-09-13 13:01:08 +00:00
|
|
|
return nullptr;
|
2012-07-23 01:08:06 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
2016-08-16 15:41:48 +00:00
|
|
|
const auto pathLength = path->path().length();
|
2019-09-17 09:46:04 +00:00
|
|
|
qreal samples = qMin(pathLength / 5, qreal(500));
|
2016-08-16 15:41:48 +00:00
|
|
|
qreal res = pathLength / samples;
|
2012-02-16 05:54:09 +00:00
|
|
|
|
2011-04-27 12:13:26 +00:00
|
|
|
qreal mindist = 1e10; // big number
|
2019-05-27 21:36:31 +00:00
|
|
|
QPointF nearPoint = path->pointAtPercent(0);
|
2011-04-27 12:13:26 +00:00
|
|
|
qreal nearPc = 0;
|
2012-02-16 05:54:09 +00:00
|
|
|
|
|
|
|
// get rough pos
|
|
|
|
for (qreal i=1; i < samples; i++) {
|
2019-05-27 21:36:31 +00:00
|
|
|
QPointF pt = path->pointAtPercent(i/samples);
|
2012-02-16 05:54:09 +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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now refine
|
|
|
|
qreal approxPc = nearPc;
|
2019-09-17 09:46:04 +00:00
|
|
|
for (qreal i = approxPc-1; i < approxPc+1; i += 1/(2*res)) {
|
2019-05-27 21:36:31 +00:00
|
|
|
QPointF pt = path->pointAtPercent(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);
|
PathView: reduce velocity by linear decay model if release is delayed
That is, from the time between the last mouse move event to the mouse
release, the velocity will be linearly discounted/depreciated until it
reaches 0 at QML_FLICK_VELOCITY_DECAY_TIME, which is currently 50 ms.
50 ms seems like a long time if the user meant to flick and release
immediately (in practice it might be more like 4 ms), and also a
short time if the user meant to "dwell" before releasing.
If we try to translate the fake physics to real physics, this would be
approximately equivalent to saying that if you slide a flat plate on an
air hockey table with one finger, and then stop suddenly, its momentum
_would_ cause it to keep moving under your finger for up to 50ms (except
that it doesn't, because our timeline doesn't "tick" until after the
release); and yet if you hold it for longer than 50ms, it will stop
right on the spot. That's not quite realistic, but feels OK for fake
physics (like the rest of the physics in Qt Quick).
Also add the qt.quick.pathview logging category, which will just log
the velocity calculations for now (but is intended for anything else
in PathView that seems worth logging later on).
Task-number: QTBUG-77173
Task-number: QTBUG-59052
Change-Id: Ie86f18d3b3305874b698c848290e0fd3beda94de
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
2019-09-10 13:26:56 +00:00
|
|
|
qCDebug(lcPathView) << "instantaneous velocity" << v;
|
2012-02-16 05:54:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
PathView: reduce velocity by linear decay model if release is delayed
That is, from the time between the last mouse move event to the mouse
release, the velocity will be linearly discounted/depreciated until it
reaches 0 at QML_FLICK_VELOCITY_DECAY_TIME, which is currently 50 ms.
50 ms seems like a long time if the user meant to flick and release
immediately (in practice it might be more like 4 ms), and also a
short time if the user meant to "dwell" before releasing.
If we try to translate the fake physics to real physics, this would be
approximately equivalent to saying that if you slide a flat plate on an
air hockey table with one finger, and then stop suddenly, its momentum
_would_ cause it to keep moving under your finger for up to 50ms (except
that it doesn't, because our timeline doesn't "tick" until after the
release); and yet if you hold it for longer than 50ms, it will stop
right on the spot. That's not quite realistic, but feels OK for fake
physics (like the rest of the physics in Qt Quick).
Also add the qt.quick.pathview logging category, which will just log
the velocity calculations for now (but is intended for anything else
in PathView that seems worth logging later on).
Task-number: QTBUG-77173
Task-number: QTBUG-59052
Change-Id: Ie86f18d3b3305874b698c848290e0fd3beda94de
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
2019-09-10 13:26:56 +00:00
|
|
|
qCDebug(lcPathView) << "average velocity" << velocity << "based on" << count << "samples";
|
2012-02-16 05:54:09 +00:00
|
|
|
}
|
|
|
|
return velocity;
|
|
|
|
}
|
|
|
|
|
2016-10-11 14:10:07 +00:00
|
|
|
qint64 QQuickPathViewPrivate::computeCurrentTime(QInputEvent *event) const
|
2012-03-21 03:18:05 +00:00
|
|
|
{
|
2013-10-21 13:22:23 +00:00
|
|
|
if (0 != event->timestamp())
|
2019-09-17 09:46:04 +00:00
|
|
|
return qint64(event->timestamp());
|
2013-10-21 13:22:23 +00:00
|
|
|
return timer.elapsed();
|
2012-03-21 03:18:05 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
2017-05-02 06:25:37 +00:00
|
|
|
Q_Q(QQuickPathView);
|
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);
|
2020-06-08 07:21:58 +00:00
|
|
|
if (item->contains(item->mapFromScene(event->scenePosition())))
|
2011-04-27 12:13:26 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-09-17 09:46:04 +00:00
|
|
|
if (idx == items.count() && qFuzzyIsNull(dragMargin)) // didn't click on an item
|
2011-04-27 12:13:26 +00:00
|
|
|
return;
|
|
|
|
|
2020-06-08 07:21:58 +00:00
|
|
|
startPoint = pointNear(event->position(), &startPc);
|
|
|
|
startPos = event->position();
|
2011-04-27 12:13:26 +00:00
|
|
|
if (idx == items.count()) {
|
2020-06-08 07:21:58 +00:00
|
|
|
qreal distance = qAbs(event->position().x() - startPoint.x()) + qAbs(event->position().y() - startPoint.y());
|
2011-04-27 12:13:26 +00:00
|
|
|
if (distance > dragMargin)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-09-05 11:57:19 +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.
|
2019-09-05 11:57:19 +00:00
|
|
|
q->grabMouse(); // grab it right now too, just to be sure (QTBUG-77173)
|
|
|
|
} else {
|
2011-04-27 12:13:26 +00:00
|
|
|
stealMouse = false;
|
2019-09-05 11:57:19 +00:00
|
|
|
}
|
2017-05-02 06:25:37 +00:00
|
|
|
q->setKeepMouseGrab(stealMouse);
|
2011-04-27 12:13:26 +00:00
|
|
|
|
2013-10-21 13:22:23 +00:00
|
|
|
timer.start();
|
2012-03-21 03:18:05 +00:00
|
|
|
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);
|
|
|
|
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;
|
2020-06-08 07:21:58 +00:00
|
|
|
QPointF pathPoint = pointNear(event->position(), &newPc);
|
2011-04-27 12:13:26 +00:00
|
|
|
if (!stealMouse) {
|
2020-06-08 07:21:58 +00:00
|
|
|
QPointF posDelta = event->position() - startPos;
|
2014-07-23 06:07:31 +00:00
|
|
|
if (QQuickWindowPrivate::dragOverThreshold(posDelta.y(), Qt::YAxis, event) || QQuickWindowPrivate::dragOverThreshold(posDelta.x(), Qt::XAxis, event)) {
|
|
|
|
// The touch has exceeded the threshold. If the movement along the path is close to the drag threshold
|
|
|
|
// then we'll assume that this gesture targets the PathView. This ensures PathView gesture grabbing
|
|
|
|
// is in sync with other items.
|
|
|
|
QPointF pathDelta = pathPoint - startPoint;
|
2015-03-04 10:27:27 +00:00
|
|
|
const int startDragDistance = QGuiApplication::styleHints()->startDragDistance();
|
|
|
|
if (qAbs(pathDelta.x()) > startDragDistance * 0.8
|
|
|
|
|| qAbs(pathDelta.y()) > startDragDistance * 0.8) {
|
2014-07-23 06:07:31 +00:00
|
|
|
stealMouse = true;
|
|
|
|
q->setKeepMouseGrab(true);
|
|
|
|
}
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
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;
|
2019-09-17 09:46:04 +00:00
|
|
|
if (!qFuzzyIsNull(diff)) {
|
2011-04-27 12:13:26 +00:00
|
|
|
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)
|
2019-09-17 09:46:04 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
PathView: reduce velocity by linear decay model if release is delayed
That is, from the time between the last mouse move event to the mouse
release, the velocity will be linearly discounted/depreciated until it
reaches 0 at QML_FLICK_VELOCITY_DECAY_TIME, which is currently 50 ms.
50 ms seems like a long time if the user meant to flick and release
immediately (in practice it might be more like 4 ms), and also a
short time if the user meant to "dwell" before releasing.
If we try to translate the fake physics to real physics, this would be
approximately equivalent to saying that if you slide a flat plate on an
air hockey table with one finger, and then stop suddenly, its momentum
_would_ cause it to keep moving under your finger for up to 50ms (except
that it doesn't, because our timeline doesn't "tick" until after the
release); and yet if you hold it for longer than 50ms, it will stop
right on the spot. That's not quite realistic, but feels OK for fake
physics (like the rest of the physics in Qt Quick).
Also add the qt.quick.pathview logging category, which will just log
the velocity calculations for now (but is intended for anything else
in PathView that seems worth logging later on).
Task-number: QTBUG-77173
Task-number: QTBUG-59052
Change-Id: Ie86f18d3b3305874b698c848290e0fd3beda94de
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
2019-09-10 13:26:56 +00:00
|
|
|
void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *event)
|
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();
|
PathView: reduce velocity by linear decay model if release is delayed
That is, from the time between the last mouse move event to the mouse
release, the velocity will be linearly discounted/depreciated until it
reaches 0 at QML_FLICK_VELOCITY_DECAY_TIME, which is currently 50 ms.
50 ms seems like a long time if the user meant to flick and release
immediately (in practice it might be more like 4 ms), and also a
short time if the user meant to "dwell" before releasing.
If we try to translate the fake physics to real physics, this would be
approximately equivalent to saying that if you slide a flat plate on an
air hockey table with one finger, and then stop suddenly, its momentum
_would_ cause it to keep moving under your finger for up to 50ms (except
that it doesn't, because our timeline doesn't "tick" until after the
release); and yet if you hold it for longer than 50ms, it will stop
right on the spot. That's not quite realistic, but feels OK for fake
physics (like the rest of the physics in Qt Quick).
Also add the qt.quick.pathview logging category, which will just log
the velocity calculations for now (but is intended for anything else
in PathView that seems worth logging later on).
Task-number: QTBUG-77173
Task-number: QTBUG-59052
Change-Id: Ie86f18d3b3305874b698c848290e0fd3beda94de
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
2019-09-10 13:26:56 +00:00
|
|
|
qint64 elapsed = computeCurrentTime(event) - lastPosTime;
|
|
|
|
// Let the velocity linearly decay such that it becomes 0 if elapsed time > QML_FLICK_VELOCITY_DECAY_TIME
|
|
|
|
// The intention is that if you are flicking at some speed, then stop in one place for some time before releasing,
|
|
|
|
// the previous velocity is lost. (QTBUG-77173, QTBUG-59052)
|
|
|
|
velocity *= qreal(qMax(0LL, QML_FLICK_VELOCITY_DECAY_TIME - elapsed)) / QML_FLICK_VELOCITY_DECAY_TIME;
|
|
|
|
qCDebug(lcPathView) << "after elapsed time" << elapsed << "velocity decayed to" << velocity;
|
2012-08-21 04:47:48 +00:00
|
|
|
qreal count = pathItems == -1 ? modelCount : qMin(pathItems, modelCount);
|
2016-08-16 15:41:48 +00:00
|
|
|
const auto averageItemLength = path->path().length() / count;
|
|
|
|
qreal pixelVelocity = averageItemLength * velocity;
|
2012-03-25 02:37:08 +00:00
|
|
|
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;
|
2016-08-16 15:41:48 +00:00
|
|
|
velocity = maxVel / averageItemLength;
|
2012-03-25 02:37:08 +00:00
|
|
|
}
|
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
|
2019-09-17 09:46:04 +00:00
|
|
|
if (velocity > 0)
|
2012-03-21 03:18:05 +00:00
|
|
|
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
|
2019-09-17 09:46:04 +00:00
|
|
|
dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2) + 0.25));
|
2012-03-21 03:18:05 +00:00
|
|
|
|
|
|
|
// round to nearest item.
|
2019-09-17 09:46:04 +00:00
|
|
|
if (velocity > 0)
|
2012-03-21 03:18:05 +00:00
|
|
|
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
|
2019-09-17 09:46:04 +00:00
|
|
|
if (dist <= 0) {
|
|
|
|
dist = 0;
|
|
|
|
accel = 0;
|
2011-04-27 12:13:26 +00:00
|
|
|
} else {
|
2019-09-17 09:46:04 +00:00
|
|
|
accel = v2 / (2 * qAbs(dist));
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
2012-02-16 05:54:09 +00:00
|
|
|
} else {
|
2019-09-17 09:46:04 +00:00
|
|
|
dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2)));
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
2019-09-17 09:46:04 +00:00
|
|
|
flickDuration = int(1000 * qAbs(velocity) / accel);
|
|
|
|
offsetAdj = 0;
|
2011-04-27 12:13:26 +00:00
|
|
|
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);
|
2020-06-08 07:21:58 +00:00
|
|
|
QPointF localPos = mapFromScene(event->scenePosition());
|
2012-02-26 16:26:53 +00:00
|
|
|
|
2012-07-11 07:32:16 +00:00
|
|
|
QQuickWindow *c = window();
|
2016-09-13 13:01:08 +00:00
|
|
|
QQuickItem *grabber = c ? c->mouseGrabberItem() : nullptr;
|
2013-09-27 04:26:48 +00:00
|
|
|
if (grabber == this && d->stealMouse) {
|
|
|
|
// we are already the grabber and we do want the mouse event to ourselves.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool grabberDisabled = grabber && !grabber->isEnabled();
|
2011-04-27 12:13:26 +00:00
|
|
|
bool stealThisEvent = d->stealMouse;
|
2013-09-27 04:26:48 +00:00
|
|
|
if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab() || grabberDisabled)) {
|
|
|
|
QScopedPointer<QMouseEvent> mouseEvent(QQuickWindowPrivate::cloneMouseEvent(event, &localPos));
|
|
|
|
mouseEvent->setAccepted(false);
|
2011-04-27 12:13:26 +00:00
|
|
|
|
2013-09-27 04:26:48 +00:00
|
|
|
switch (mouseEvent->type()) {
|
2011-08-30 14:48:57 +00:00
|
|
|
case QEvent::MouseMove:
|
2013-09-27 04:26:48 +00:00
|
|
|
d->handleMouseMoveEvent(mouseEvent.data());
|
2011-04-27 12:13:26 +00:00
|
|
|
break;
|
2011-08-30 14:48:57 +00:00
|
|
|
case QEvent::MouseButtonPress:
|
2013-09-27 04:26:48 +00:00
|
|
|
d->handleMousePressEvent(mouseEvent.data());
|
2011-04-27 12:13:26 +00:00
|
|
|
stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
|
|
|
|
break;
|
2011-08-30 14:48:57 +00:00
|
|
|
case QEvent::MouseButtonRelease:
|
2013-09-27 04:26:48 +00:00
|
|
|
d->handleMouseReleaseEvent(mouseEvent.data());
|
2011-04-27 12:13:26 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2016-09-13 13:01:08 +00:00
|
|
|
grabber = c ? c->mouseGrabberItem() : nullptr;
|
2013-09-27 04:26:48 +00:00
|
|
|
if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || grabberDisabled) {
|
2011-04-27 12:13:26 +00:00
|
|
|
grabMouse();
|
2013-09-27 04:26:48 +00:00
|
|
|
}
|
2011-04-27 12:13:26 +00:00
|
|
|
|
2013-09-27 04:26:48 +00:00
|
|
|
const bool filtered = stealThisEvent || grabberDisabled;
|
|
|
|
if (filtered) {
|
|
|
|
event->setAccepted(false);
|
|
|
|
}
|
|
|
|
return filtered;
|
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
|
|
|
}
|
2013-09-27 04:26:48 +00:00
|
|
|
if (event->type() == QEvent::MouseButtonRelease || (grabber && grabber->keepMouseGrab() && !grabberDisabled)) {
|
2011-04-27 12:13:26 +00:00
|
|
|
d->stealMouse = false;
|
2013-09-27 04:26:48 +00:00
|
|
|
}
|
2011-04-27 12:13:26 +00:00
|
|
|
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);
|
2019-11-14 07:16:39 +00:00
|
|
|
if (d->stealMouse ||
|
|
|
|
(!d->flicking && d->snapMode != NoSnap && !qFuzzyCompare(qRound(d->offset), d->offset))) {
|
2011-08-01 05:49:33 +00:00
|
|
|
// if our mouse grab has been removed (probably by a Flickable),
|
2019-11-14 07:16:39 +00:00
|
|
|
// or if we should snap but haven't done it, fix our state
|
2011-08-01 05:49:33 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2016-05-24 09:21:39 +00:00
|
|
|
static inline int currentIndexRemainder(int currentIndex, int modelCount) Q_DECL_NOTHROW
|
|
|
|
{
|
|
|
|
if (currentIndex < 0)
|
|
|
|
return modelCount + currentIndex % modelCount;
|
|
|
|
else
|
|
|
|
return currentIndex % modelCount;
|
|
|
|
}
|
|
|
|
|
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
|
2016-10-12 08:12:25 +00:00
|
|
|
d->offset = std::fmod(qreal(d->modelCount - currentIndexRemainder(d->currentIndex, d->modelCount)), qreal(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
|
|
|
|
2014-07-21 12:55:30 +00:00
|
|
|
if (d->inRefill) {
|
|
|
|
d->scheduleLayout();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-05-25 01:41:17 +00:00
|
|
|
d->layoutScheduled = false;
|
|
|
|
|
2011-04-27 12:13:26 +00:00
|
|
|
if (!d->isValid() || !isComponentComplete())
|
|
|
|
return;
|
|
|
|
|
2014-07-21 12:55:30 +00:00
|
|
|
d->inRefill = true;
|
|
|
|
|
2011-04-27 12:13:26 +00:00
|
|
|
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
|
2015-05-07 05:56:03 +00:00
|
|
|
qCDebug(lcItemViewDelegateLifecycle) << "currentIndex" << d->currentIndex << "offset" << d->offset;
|
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()) {
|
2011-10-14 08:51:42 +00:00
|
|
|
QQuickItem *item = *it;
|
2016-09-13 13:01:08 +00:00
|
|
|
int idx = d->model->indexOf(item, nullptr);
|
2015-05-07 05:56:03 +00:00
|
|
|
qreal pos = d->positionOfIndex(idx);
|
2015-03-11 15:02:33 +00:00
|
|
|
if (lcItemViewDelegateLifecycle().isDebugEnabled()) {
|
|
|
|
QQuickText *text = qmlobject_cast<QQuickText*>(item);
|
|
|
|
if (text)
|
2020-06-16 08:23:19 +00:00
|
|
|
qCDebug(lcItemViewDelegateLifecycle) << "idx" << idx << "@" << pos << ": QQuickText" << text->objectName() << QStringView{text->text()}.left(40);
|
2015-03-11 15:02:33 +00:00
|
|
|
else
|
|
|
|
qCDebug(lcItemViewDelegateLifecycle) << "idx" << idx << "@" << pos << ":" << item;
|
|
|
|
}
|
2019-09-17 09:46:04 +00:00
|
|
|
if (pos < 1) {
|
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))
|
2019-09-17 09:46:04 +00:00
|
|
|
att->setOnPath(pos < 1);
|
|
|
|
if (!d->isInBound(pos, d->mappedRange - d->mappedCache, 1 + d->mappedCache)) {
|
|
|
|
qCDebug(lcItemViewDelegateLifecycle) << "release" << idx << "@" << pos << ", !isInBound: lower" << (d->mappedRange - d->mappedCache) << "upper" << (1 + d->mappedCache);
|
2012-08-21 04:47:48 +00:00
|
|
|
d->releaseItem(item);
|
|
|
|
it = d->items.erase(it);
|
|
|
|
} else {
|
|
|
|
++it;
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-08-21 04:47:48 +00:00
|
|
|
|
2011-11-03 05:52:13 +00:00
|
|
|
bool waiting = false;
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->modelCount) {
|
2015-05-07 05:56:03 +00:00
|
|
|
// add items as needed
|
2012-08-21 04:47:48 +00:00
|
|
|
if (d->items.count() < count+d->cacheSize) {
|
2015-05-07 05:56:03 +00:00
|
|
|
int endIdx = 0;
|
|
|
|
qreal endPos;
|
|
|
|
int startIdx = 0;
|
2019-09-17 09:46:04 +00:00
|
|
|
qreal startPos = 0;
|
2016-05-24 09:21:39 +00:00
|
|
|
const bool wasEmpty = d->items.isEmpty();
|
|
|
|
if (!wasEmpty) {
|
2015-05-07 05:56:03 +00:00
|
|
|
//Find the beginning and end, items may not be in sorted order
|
2019-09-17 09:46:04 +00:00
|
|
|
endPos = -1;
|
|
|
|
startPos = 2;
|
2015-05-07 05:56:03 +00:00
|
|
|
|
2016-08-09 15:32:48 +00:00
|
|
|
for (QQuickItem * item : qAsConst(d->items)) {
|
2016-09-13 13:01:08 +00:00
|
|
|
int idx = d->model->indexOf(item, nullptr);
|
2015-05-07 05:56:03 +00:00
|
|
|
qreal curPos = d->positionOfIndex(idx);
|
|
|
|
if (curPos > endPos) {
|
|
|
|
endPos = curPos;
|
|
|
|
endIdx = idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (curPos < startPos) {
|
|
|
|
startPos = curPos;
|
|
|
|
startIdx = idx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (d->haveHighlightRange
|
|
|
|
&& (d->highlightRangeMode != QQuickPathView::NoHighlightRange
|
|
|
|
|| d->snapMode != QQuickPathView::NoSnap))
|
|
|
|
startPos = d->highlightRangeStart;
|
|
|
|
// With no items, then "end" is just off the top so we populate via append
|
|
|
|
endIdx = (qRound(d->modelCount - d->offset) - 1) % d->modelCount;
|
|
|
|
endPos = d->positionOfIndex(endIdx);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
2015-05-07 05:56:03 +00:00
|
|
|
//Append
|
|
|
|
int idx = endIdx + 1;
|
|
|
|
if (idx >= d->modelCount)
|
|
|
|
idx = 0;
|
|
|
|
qreal nextPos = d->positionOfIndex(idx);
|
2019-09-17 09:46:04 +00:00
|
|
|
while ((d->isInBound(nextPos, endPos, 1 + d->mappedCache) || !d->items.count())
|
2015-05-07 05:56:03 +00:00
|
|
|
&& d->items.count() < count+d->cacheSize) {
|
|
|
|
qCDebug(lcItemViewDelegateLifecycle) << "append" << idx << "@" << nextPos << (d->currentIndex == idx ? "current" : "") << "items count was" << d->items.count();
|
2019-09-17 09:46:04 +00:00
|
|
|
QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1);
|
2011-11-03 05:52:13 +00:00
|
|
|
if (!item) {
|
|
|
|
waiting = true;
|
|
|
|
break;
|
|
|
|
}
|
2015-05-07 05:56:03 +00:00
|
|
|
if (d->items.contains(item)) {
|
2016-01-26 11:47:02 +00:00
|
|
|
d->releaseItem(item);
|
2015-05-07 05:56:03 +00:00
|
|
|
break; //Otherwise we'd "re-add" it, and get confused
|
|
|
|
}
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->currentIndex == idx) {
|
|
|
|
currentVisible = true;
|
2015-05-07 05:56:03 +00:00
|
|
|
d->currentItemOffset = nextPos;
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
d->items.append(item);
|
2015-05-07 05:56:03 +00:00
|
|
|
d->updateItem(item, nextPos);
|
|
|
|
endIdx = idx;
|
|
|
|
endPos = nextPos;
|
2011-04-27 12:13:26 +00:00
|
|
|
++idx;
|
|
|
|
if (idx >= d->modelCount)
|
|
|
|
idx = 0;
|
2015-05-07 05:56:03 +00:00
|
|
|
nextPos = d->positionOfIndex(idx);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
2015-05-07 05:56:03 +00:00
|
|
|
//Prepend
|
2016-05-24 09:21:39 +00:00
|
|
|
idx = (wasEmpty ? d->calcCurrentIndex() : startIdx) - 1;
|
|
|
|
|
2011-04-27 12:13:26 +00:00
|
|
|
if (idx < 0)
|
|
|
|
idx = d->modelCount - 1;
|
2015-05-07 05:56:03 +00:00
|
|
|
nextPos = d->positionOfIndex(idx);
|
|
|
|
while (!waiting && d->isInBound(nextPos, d->mappedRange - d->mappedCache, startPos)
|
|
|
|
&& d->items.count() < count+d->cacheSize) {
|
|
|
|
qCDebug(lcItemViewDelegateLifecycle) << "prepend" << idx << "@" << nextPos << (d->currentIndex == idx ? "current" : "") << "items count was" << d->items.count();
|
2019-09-17 09:46:04 +00:00
|
|
|
QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1);
|
2011-11-03 05:52:13 +00:00
|
|
|
if (!item) {
|
|
|
|
waiting = true;
|
|
|
|
break;
|
|
|
|
}
|
2015-05-07 05:56:03 +00:00
|
|
|
if (d->items.contains(item)) {
|
2016-01-26 11:47:02 +00:00
|
|
|
d->releaseItem(item);
|
2015-05-07 05:56:03 +00:00
|
|
|
break; //Otherwise we'd "re-add" it, and get confused
|
|
|
|
}
|
2011-04-27 12:13:26 +00:00
|
|
|
if (d->currentIndex == idx) {
|
|
|
|
currentVisible = true;
|
2015-05-07 05:56:03 +00:00
|
|
|
d->currentItemOffset = nextPos;
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
d->items.prepend(item);
|
2015-05-07 05:56:03 +00:00
|
|
|
d->updateItem(item, nextPos);
|
|
|
|
startIdx = idx;
|
|
|
|
startPos = nextPos;
|
|
|
|
--idx;
|
2011-04-27 12:13:26 +00:00
|
|
|
if (idx < 0)
|
|
|
|
idx = d->modelCount - 1;
|
2015-05-07 05:56:03 +00:00
|
|
|
nextPos = d->positionOfIndex(idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
// In rare cases, when jumping around with pathCount close to modelCount,
|
|
|
|
// new items appear in the middle. This more generic addition iteration handles this
|
|
|
|
// Since this is the rare case, we try append/prepend first and only do this if
|
|
|
|
// there are gaps still left to fill.
|
|
|
|
if (!waiting && d->items.count() < count+d->cacheSize) {
|
|
|
|
qCDebug(lcItemViewDelegateLifecycle) << "Checking for pathview middle inserts, items count was" << d->items.count();
|
|
|
|
idx = startIdx;
|
2016-08-09 15:32:48 +00:00
|
|
|
QQuickItem *lastItem = d->items.at(0);
|
2015-05-07 05:56:03 +00:00
|
|
|
while (idx != endIdx) {
|
2016-05-26 11:45:31 +00:00
|
|
|
nextPos = d->positionOfIndex(idx);
|
2019-09-17 09:46:04 +00:00
|
|
|
if (d->isInBound(nextPos, d->mappedRange - d->mappedCache, 1 + d->mappedCache)) {
|
2016-05-26 11:45:31 +00:00
|
|
|
//This gets the reference from the delegate model, and will not re-create
|
2019-09-17 09:46:04 +00:00
|
|
|
QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1);
|
2016-05-26 11:45:31 +00:00
|
|
|
if (!item) {
|
|
|
|
waiting = true;
|
|
|
|
break;
|
2015-05-07 05:56:03 +00:00
|
|
|
}
|
2016-05-26 11:45:31 +00:00
|
|
|
|
|
|
|
if (!d->items.contains(item)) { //We found a hole
|
|
|
|
qCDebug(lcItemViewDelegateLifecycle) << "middle insert" << idx << "@" << nextPos
|
|
|
|
<< (d->currentIndex == idx ? "current" : "")
|
|
|
|
<< "items count was" << d->items.count();
|
|
|
|
if (d->currentIndex == idx) {
|
|
|
|
currentVisible = true;
|
|
|
|
d->currentItemOffset = nextPos;
|
|
|
|
}
|
|
|
|
int lastListIdx = d->items.indexOf(lastItem);
|
|
|
|
d->items.insert(lastListIdx + 1, item);
|
|
|
|
d->updateItem(item, nextPos);
|
|
|
|
} else {
|
|
|
|
d->releaseItem(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
lastItem = item;
|
2015-05-07 05:56:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
++idx;
|
|
|
|
if (idx >= d->modelCount)
|
|
|
|
idx = 0;
|
|
|
|
}
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-30 12:23:56 +00:00
|
|
|
bool currentChanged = false;
|
2011-08-01 03:09:10 +00:00
|
|
|
if (!currentVisible) {
|
2019-09-17 09:46:04 +00:00
|
|
|
d->currentItemOffset = 1;
|
2011-08-01 03:09:10 +00:00
|
|
|
if (d->currentItem) {
|
2019-09-17 09:46:04 +00:00
|
|
|
d->updateItem(d->currentItem, 1);
|
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))) {
|
2014-09-30 12:23:56 +00:00
|
|
|
currentChanged = true;
|
2019-09-17 09:46:04 +00:00
|
|
|
d->updateItem(d->currentItem, 1);
|
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))) {
|
2014-09-30 12:23:56 +00:00
|
|
|
currentChanged = true;
|
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);
|
|
|
|
}
|
2016-08-16 15:27:44 +00:00
|
|
|
for (QQuickItem *item : qAsConst(d->itemCache))
|
|
|
|
d->releaseItem(item);
|
|
|
|
d->itemCache.clear();
|
2014-07-21 12:55:30 +00:00
|
|
|
|
|
|
|
d->inRefill = false;
|
2014-09-30 12:23:56 +00:00
|
|
|
if (currentChanged)
|
|
|
|
emit currentItemChanged();
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
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;
|
2013-11-06 12:00:12 +00:00
|
|
|
int moveOffset = 0;
|
2011-04-27 12:13:26 +00:00
|
|
|
bool currentChanged = false;
|
2011-09-01 05:24:34 +00:00
|
|
|
bool changedOffset = false;
|
2016-08-09 13:41:33 +00:00
|
|
|
for (const QQmlChangeSet::Change &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);
|
2016-09-13 13:01:08 +00:00
|
|
|
d->currentItem = nullptr;
|
2011-09-01 05:24:34 +00:00
|
|
|
}
|
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;
|
|
|
|
}
|
2016-08-09 13:41:33 +00:00
|
|
|
for (const QQmlChangeSet::Change &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
|
|
|
}
|
|
|
|
|
2016-10-12 08:12:25 +00:00
|
|
|
d->offset = std::fmod(d->offset, qreal(d->modelCount));
|
2011-11-22 07:26:49 +00:00
|
|
|
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) {
|
2016-08-16 15:27:44 +00:00
|
|
|
for (QQuickItem * item : qAsConst(d->itemCache))
|
|
|
|
d->releaseItem(item);
|
|
|
|
d->itemCache.clear();
|
2011-04-27 12:13:26 +00:00
|
|
|
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) {
|
2016-10-12 08:12:25 +00:00
|
|
|
d->offset = std::fmod(qreal(d->modelCount - d->currentIndex), qreal(d->modelCount));
|
2011-11-22 07:26:49 +00:00
|
|
|
changedOffset = true;
|
2011-11-09 05:34:01 +00:00
|
|
|
}
|
2011-09-01 05:24:34 +00:00
|
|
|
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();
|
|
|
|
}
|
2016-04-26 12:12:14 +00:00
|
|
|
d->moveDirection = d->movementDirection;
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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()) {
|
2016-10-12 08:12:25 +00:00
|
|
|
offset = std::fmod(offset, qreal(modelCount));
|
2011-04-27 12:13:26 +00:00
|
|
|
if (offset < 0)
|
|
|
|
offset += modelCount;
|
2016-10-12 08:12:25 +00:00
|
|
|
current = qRound(qAbs(std::fmod(modelCount - offset, qreal(modelCount))));
|
2011-04-27 12:13:26 +00:00
|
|
|
current = current % modelCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
return current;
|
|
|
|
}
|
|
|
|
|
2011-11-03 05:52:13 +00:00
|
|
|
void QQuickPathViewPrivate::createCurrentItem()
|
|
|
|
{
|
|
|
|
if (requestedIndex != -1)
|
|
|
|
return;
|
2015-05-07 05:56:03 +00:00
|
|
|
|
|
|
|
bool inItems = false;
|
2016-08-09 15:32:48 +00:00
|
|
|
for (QQuickItem *item : qAsConst(items)) {
|
2016-09-13 13:01:08 +00:00
|
|
|
if (model->indexOf(item, nullptr) == currentIndex) {
|
2015-05-07 05:56:03 +00:00
|
|
|
inItems = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (inItems) {
|
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))) {
|
2019-09-17 09:46:04 +00:00
|
|
|
updateItem(currentItem, 1);
|
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;
|
2016-09-13 13:01:08 +00:00
|
|
|
currentItem = nullptr;
|
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
|
|
|
{
|
2019-09-17 09:46:04 +00:00
|
|
|
static_cast<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
|
2015-10-21 15:04:16 +00:00
|
|
|
snapToIndex(curr, Other);
|
2011-04-27 12:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-21 15:04:16 +00:00
|
|
|
void QQuickPathViewPrivate::snapToIndex(int index, MovementReason reason)
|
2011-04-27 12:13:26 +00:00
|
|
|
{
|
|
|
|
if (!model || modelCount <= 0)
|
|
|
|
return;
|
|
|
|
|
2016-10-12 08:12:25 +00:00
|
|
|
qreal targetOffset = std::fmod(qreal(modelCount - index), qreal(modelCount));
|
2015-10-21 15:04:16 +00:00
|
|
|
moveReason = reason;
|
2019-09-17 09:46:04 +00:00
|
|
|
offsetAdj = 0;
|
2011-04-27 12:13:26 +00:00
|
|
|
tl.reset(moveOffset);
|
|
|
|
moveOffset.setValue(offset);
|
|
|
|
|
|
|
|
const int duration = highlightMoveDuration;
|
|
|
|
|
2018-11-19 16:27:29 +00:00
|
|
|
const qreal count = pathItems == -1 ? modelCount : qMin(pathItems, modelCount);
|
|
|
|
const qreal averageItemLength = path->path().length() / count;
|
|
|
|
const qreal threshold = 0.5 / averageItemLength; // if we are within .5 px, we want to immediately assign rather than animate
|
|
|
|
|
|
|
|
if (!duration || qAbs(offset - targetOffset) < threshold || (qFuzzyIsNull(targetOffset) && qAbs(modelCount - offset) < threshold)) {
|
2011-11-21 23:28:40 +00:00
|
|
|
tl.set(moveOffset, targetOffset);
|
2019-09-17 09:46:04 +00:00
|
|
|
} else if (moveDirection == QQuickPathView::Positive || (moveDirection == QQuickPathView::Shortest && targetOffset - offset > modelCount/2)) {
|
2011-04-27 12:13:26 +00:00
|
|
|
qreal distance = modelCount - targetOffset + offset;
|
|
|
|
if (targetOffset > moveOffset) {
|
2019-09-17 09:46:04 +00:00
|
|
|
tl.move(moveOffset, 0, QEasingCurve(QEasingCurve::InQuad), int(duration * offset / distance));
|
2011-04-27 12:13:26 +00:00
|
|
|
tl.set(moveOffset, modelCount);
|
2019-09-17 09:46:04 +00:00
|
|
|
tl.move(moveOffset, targetOffset, QEasingCurve(qFuzzyIsNull(offset) ? QEasingCurve::InOutQuad : QEasingCurve::OutQuad), int(duration * (modelCount-targetOffset) / distance));
|
2011-04-27 12:13:26 +00:00
|
|
|
} else {
|
|
|
|
tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
|
|
|
|
}
|
2019-09-17 09:46:04 +00:00
|
|
|
} else if (moveDirection == QQuickPathView::Negative || targetOffset - offset <= -modelCount/2) {
|
2011-04-27 12:13:26 +00:00
|
|
|
qreal distance = modelCount - offset + targetOffset;
|
|
|
|
if (targetOffset < moveOffset) {
|
2019-09-17 09:46:04 +00:00
|
|
|
tl.move(moveOffset, modelCount, QEasingCurve(qFuzzyIsNull(targetOffset) ? QEasingCurve::InOutQuad : QEasingCurve::InQuad), int(duration * (modelCount-offset) / distance));
|
|
|
|
tl.set(moveOffset, 0);
|
2011-04-27 12:13:26 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
2017-04-25 21:08:46 +00:00
|
|
|
#include "moc_qquickpathview_p.cpp"
|