aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickpathview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items/qquickpathview.cpp')
-rw-r--r--src/quick/items/qquickpathview.cpp208
1 files changed, 97 insertions, 111 deletions
diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp
index 4cde344dce..ce61543f42 100644
--- a/src/quick/items/qquickpathview.cpp
+++ b/src/quick/items/qquickpathview.cpp
@@ -1,45 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQuick module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.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.
-**
-** 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.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickpathview_p.h"
#include "qquickpathview_p_p.h"
-#include "qquickwindow.h"
#include "qquickflickablebehavior_p.h" //Contains flicking behavior defines
#include "qquicktext_p.h"
@@ -47,11 +10,14 @@
#include <private/qqmlglobal_p.h>
#include <private/qqmlopenmetaobject_p.h>
#include <private/qqmlchangeset_p.h>
+#include <qpa/qplatformtheme.h>
#include <QtQml/qqmlinfo.h>
-#include <QtGui/qevent.h>
+
+#include <QtGui/private/qeventpoint_p.h>
#include <QtGui/qevent.h>
#include <QtGui/qguiapplication.h>
+#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qstylehints.h>
#include <QtCore/qmath.h>
@@ -60,10 +26,11 @@
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcItemViewDelegateLifecycle)
+#if !QT_CONFIG(quick_itemview)
+Q_LOGGING_CATEGORY(lcItemViewDelegateLifecycle, "qt.quick.itemview.lifecycle")
+#endif
Q_LOGGING_CATEGORY(lcPathView, "qt.quick.pathview")
-static const qreal MinimumFlickVelocity = 75;
-
static QQmlOpenMetaObjectType *qPathViewAttachedType = nullptr;
QQuickPathViewAttached::QQuickPathViewAttached(QObject *parent)
@@ -97,9 +64,10 @@ QQuickPathViewPrivate::QQuickPathViewPrivate()
, autoHighlight(true), highlightUp(false), layoutScheduled(false)
, moving(false), flicking(false), dragging(false), inRequest(false), delegateValidated(false)
, inRefill(false)
- , dragMargin(0), deceleration(100), maximumFlickVelocity(QML_FLICK_DEFAULTMAXVELOCITY)
+ , dragMargin(0), deceleration(100)
+ , maximumFlickVelocity(QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickMaximumVelocity).toReal())
, moveOffset(this, &QQuickPathViewPrivate::setAdjustedOffset), flickDuration(0)
- , pathItems(-1), requestedIndex(-1), cacheSize(0), requestedZ(0)
+ , pathItems(-1), requestedIndex(-1), cacheSize(0), requestedCacheSize(0), requestedZ(0)
, moveReason(Other), movementDirection(QQuickPathView::Shortest), moveDirection(QQuickPathView::Shortest)
, attType(nullptr), highlightComponent(nullptr), highlightItem(nullptr)
, moveHighlight(this, &QQuickPathViewPrivate::setHighlightPosition)
@@ -108,6 +76,7 @@ QQuickPathViewPrivate::QQuickPathViewPrivate()
, highlightRangeMode(QQuickPathView::StrictlyEnforceRange)
, highlightMoveDuration(300), modelCount(0), snapMode(QQuickPathView::NoSnap)
{
+ setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
}
void QQuickPathViewPrivate::init()
@@ -145,7 +114,8 @@ QQuickItem *QQuickPathViewPrivate::getItem(int modelIndex, qreal z, bool async)
item->setParentItem(q);
requestedIndex = -1;
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
- itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
+ itemPrivate->addItemChangeListener(
+ this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed);
}
inRequest = false;
return item;
@@ -198,11 +168,14 @@ void QQuickPathView::initItem(int index, QObject *object)
void QQuickPathViewPrivate::releaseItem(QQuickItem *item)
{
- if (!item || !model)
+ if (!item)
return;
qCDebug(lcItemViewDelegateLifecycle) << "release" << item;
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
- itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
+ itemPrivate->removeItemChangeListener(
+ this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed);
+ if (!model)
+ return;
QQmlInstanceModel::ReleaseFlags flags = model->release(item);
if (!flags) {
// item was not destroyed, and we no longer reference it.
@@ -241,10 +214,10 @@ void QQuickPathViewPrivate::clear()
currentItem = nullptr;
}
- for (QQuickItem *p : qAsConst(items))
+ for (QQuickItem *p : std::as_const(items))
releaseItem(p);
- for (QQuickItem *p : qAsConst(itemCache))
+ for (QQuickItem *p : std::as_const(itemCache))
releaseItem(p);
if (requestedIndex >= 0) {
@@ -260,6 +233,10 @@ void QQuickPathViewPrivate::clear()
void QQuickPathViewPrivate::updateMappedRange()
{
+ // Update the actual cache size to be at max
+ // the available non-visible items.
+ cacheSize = qMax(0, qMin(requestedCacheSize, modelCount - pathItems));
+
if (model && pathItems != -1 && pathItems < modelCount) {
mappedRange = qreal(modelCount)/pathItems;
mappedCache = qreal(cacheSize)/pathItems/2; // Half of cache at each end
@@ -294,9 +271,10 @@ qreal QQuickPathViewPrivate::positionOfIndex(qreal index) const
// 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
+bool QQuickPathViewPrivate::isInBound(qreal position, qreal lower,
+ qreal upper, bool emptyRangeCheck) const
{
- if (qFuzzyCompare(lower, upper))
+ if (emptyRangeCheck && qFuzzyCompare(lower, upper))
return true;
if (lower > upper) {
if (position > upper && position > lower)
@@ -417,7 +395,7 @@ void QQuickPathViewPrivate::setHighlightPosition(qreal pos)
void QQuickPathView::pathUpdated()
{
Q_D(QQuickPathView);
- for (QQuickItem *item : qAsConst(d->items)) {
+ for (QQuickItem *item : std::as_const(d->items)) {
if (QQuickPathViewAttached *att = d->attached(item))
att->m_percent = -1;
}
@@ -559,6 +537,8 @@ QQuickPathView::~QQuickPathView()
/*!
\qmlattachedproperty PathView QtQuick::PathView::view
+ \readonly
+
This attached property holds the view that manages this delegate instance.
It is attached to each instance of the delegate.
@@ -566,6 +546,8 @@ QQuickPathView::~QQuickPathView()
/*!
\qmlattachedproperty bool QtQuick::PathView::onPath
+ \readonly
+
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
@@ -586,6 +568,8 @@ QQuickPathView::~QQuickPathView()
/*!
\qmlattachedproperty bool QtQuick::PathView::isCurrentItem
+ \readonly
+
This attached property is true if this delegate is the current item; otherwise false.
It is attached to each instance of the delegate.
@@ -923,18 +907,16 @@ QQuickItem *QQuickPathView::highlightItem() const
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
+ \value PathView.NoHighlightRange no range is applied: the highlight
+ will move freely within the view.
+ \value 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.
+ \value 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.
The default value is \e PathView.StrictlyEnforceRange.
@@ -1320,16 +1302,16 @@ void QQuickPathView::resetPathItemCount()
int QQuickPathView::cacheItemCount() const
{
Q_D(const QQuickPathView);
- return d->cacheSize;
+ return d->requestedCacheSize;
}
void QQuickPathView::setCacheItemCount(int i)
{
Q_D(QQuickPathView);
- if (i == d->cacheSize || i < 0)
+ if (i == d->requestedCacheSize || i < 0)
return;
- d->cacheSize = i;
+ d->requestedCacheSize = i;
d->updateMappedRange();
refill();
emit cacheItemCountChanged();
@@ -1341,13 +1323,11 @@ void QQuickPathView::setCacheItemCount(int i)
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
+ \value PathView.NoSnap (default) the items stop anywhere along the path.
+ \value PathView.SnapToItem the items settle with an item aligned with the \l preferredHighlightBegin.
+ \value 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
@@ -1377,12 +1357,10 @@ void QQuickPathView::setSnapMode(SnapMode mode)
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
+ \value PathView.Shortest (default) the items move in the direction that requires the least
+ movement, which could be either \c Negative or \c Positive.
+ \value PathView.Negative the items move backwards towards their destination.
+ \value PathView.Positive the items move forwards towards their destination.
For example, suppose that there are 5 items in the model, and \l currentIndex is \c 0.
If currentIndex is set to \c 2,
@@ -1418,15 +1396,13 @@ void QQuickPathView::setMovementDirection(QQuickPathView::MovementDirection dir)
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
+ \value PathView.Beginning position item at the beginning of the path.
+ \value PathView.Center position item in the center of the path.
+ \value PathView.End position item at the end of the path.
+ \value PathView.Contain ensure the item is positioned on the path.
+ \value 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.
\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
@@ -1539,7 +1515,7 @@ QQuickItem *QQuickPathView::itemAt(qreal x, qreal y) const
}
/*!
- \qmlmethod Item QtQuick::QQuickPathView::itemAtIndex(int index)
+ \qmlmethod Item QtQuick::PathView::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
@@ -1565,6 +1541,14 @@ QQuickItem *QQuickPathView::itemAtIndex(int index) const
return nullptr;
}
+/*!
+ \internal
+
+ Returns a point in the path, that has the closest distance from \a point.
+ A value in the range 0-1 will be written to \a nearPercent if given, which
+ represents where on the path the \a point is closest to. \c 0 means the very
+ beginning of the path, and \c 1 means the very end.
+*/
QPointF QQuickPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const
{
const auto pathLength = path->path().length();
@@ -1650,21 +1634,21 @@ void QQuickPathView::mousePressEvent(QMouseEvent *event)
void QQuickPathViewPrivate::handleMousePressEvent(QMouseEvent *event)
{
Q_Q(QQuickPathView);
- if (!interactive || !items.count() || !model || !modelCount)
+ if (!interactive || !items.size() || !model || !modelCount)
return;
velocityBuffer.clear();
int idx = 0;
- for (; idx < items.count(); ++idx) {
+ for (; idx < items.size(); ++idx) {
QQuickItem *item = items.at(idx);
if (item->contains(item->mapFromScene(event->scenePosition())))
break;
}
- if (idx == items.count() && qFuzzyIsNull(dragMargin)) // didn't click on an item
+ if (idx == items.size() && qFuzzyIsNull(dragMargin)) // didn't click on an item
return;
startPoint = pointNear(event->position(), &startPc);
startPos = event->position();
- if (idx == items.count()) {
+ if (idx == items.size()) {
qreal distance = qAbs(event->position().x() - startPoint.x()) + qAbs(event->position().y() - startPoint.y());
if (distance > dragMargin)
return;
@@ -1780,7 +1764,7 @@ void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *event)
qreal count = pathItems == -1 ? modelCount : qMin(pathItems, modelCount);
const auto averageItemLength = path->path().length() / count;
qreal pixelVelocity = averageItemLength * velocity;
- if (qAbs(pixelVelocity) > MinimumFlickVelocity) {
+ if (qAbs(pixelVelocity) > _q_MinimumFlickVelocity) {
if (qAbs(pixelVelocity) > maximumFlickVelocity || snapMode == QQuickPathView::SnapOneItem) {
// limit velocity
qreal maxVel = velocity < 0 ? -maximumFlickVelocity : maximumFlickVelocity;
@@ -1844,7 +1828,7 @@ bool QQuickPathView::childMouseEventFilter(QQuickItem *i, QEvent *e)
return QQuickItem::childMouseEventFilter(i, e);
QPointerEvent *pe = static_cast<QPointerEvent *>(e);
- if (QQuickWindowPrivate::isMouseEvent(pe)) {
+ if (QQuickDeliveryAgentPrivate::isMouseEvent(pe)) {
// The event is localized for the intended receiver (in the delegate, probably),
// but we need to look at position relative to the PathView itself.
const auto &point = pe->points().first();
@@ -1860,7 +1844,7 @@ bool QQuickPathView::childMouseEventFilter(QQuickItem *i, QEvent *e)
if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab() || grabberDisabled)) {
// Make a localized copy of the QMouseEvent.
QMutableSinglePointEvent localizedEvent(*static_cast<QMouseEvent *>(pe));
- QMutableEventPoint::from(localizedEvent.point(0)).setPosition(localPos);
+ QMutableEventPoint::setPosition(localizedEvent.point(0), localPos);
localizedEvent.setAccepted(false);
switch (localizedEvent.type()) {
@@ -1884,7 +1868,8 @@ bool QQuickPathView::childMouseEventFilter(QQuickItem *i, QEvent *e)
const bool filtered = stealThisEvent || grabberDisabled;
if (filtered)
- pe->setAccepted(false);
+ pe->setAccepted(stealThisEvent && grabber == this && grabber->isEnabled());
+
return filtered;
} else if (d->timer.isValid()) {
d->timer.invalidate();
@@ -2009,7 +1994,7 @@ void QQuickPathView::refill()
bool waiting = false;
if (d->modelCount) {
// add items as needed
- if (d->items.count() < count+d->cacheSize) {
+ if (d->items.size() < count+d->cacheSize) {
int endIdx = 0;
qreal endPos;
int startIdx = 0;
@@ -2020,7 +2005,7 @@ void QQuickPathView::refill()
endPos = -1;
startPos = 2;
- for (QQuickItem * item : qAsConst(d->items)) {
+ for (QQuickItem * item : std::as_const(d->items)) {
int idx = d->model->indexOf(item, nullptr);
qreal curPos = d->positionOfIndex(idx);
if (curPos > endPos) {
@@ -2040,6 +2025,7 @@ void QQuickPathView::refill()
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;
+ endIdx = qMax(-1, endIdx); // endIdx shouldn't be smaller than -1
endPos = d->positionOfIndex(endIdx);
}
//Append
@@ -2047,9 +2033,9 @@ void QQuickPathView::refill()
if (idx >= d->modelCount)
idx = 0;
qreal nextPos = d->positionOfIndex(idx);
- while ((d->isInBound(nextPos, endPos, 1 + d->mappedCache) || !d->items.count())
- && d->items.count() < count+d->cacheSize) {
- qCDebug(lcItemViewDelegateLifecycle) << "append" << idx << "@" << nextPos << (d->currentIndex == idx ? "current" : "") << "items count was" << d->items.count();
+ while ((d->isInBound(nextPos, endPos, 1 + d->mappedCache, false) || !d->items.size())
+ && d->items.size() < count + d->cacheSize) {
+ qCDebug(lcItemViewDelegateLifecycle) << "append" << idx << "@" << nextPos << (d->currentIndex == idx ? "current" : "") << "items count was" << d->items.size();
QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1);
if (!item) {
waiting = true;
@@ -2080,8 +2066,8 @@ void QQuickPathView::refill()
idx = d->modelCount - 1;
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();
+ && d->items.size() < count+d->cacheSize) {
+ qCDebug(lcItemViewDelegateLifecycle) << "prepend" << idx << "@" << nextPos << (d->currentIndex == idx ? "current" : "") << "items count was" << d->items.size();
QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1);
if (!item) {
waiting = true;
@@ -2109,8 +2095,8 @@ void QQuickPathView::refill()
// 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();
+ if (!waiting && d->items.size() < count+d->cacheSize) {
+ qCDebug(lcItemViewDelegateLifecycle) << "Checking for pathview middle inserts, items count was" << d->items.size();
idx = startIdx;
QQuickItem *lastItem = d->items.at(0);
while (idx != endIdx) {
@@ -2126,7 +2112,7 @@ void QQuickPathView::refill()
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();
+ << "items count was" << d->items.size();
if (d->currentIndex == idx) {
currentVisible = true;
d->currentItemOffset = nextPos;
@@ -2180,7 +2166,7 @@ void QQuickPathView::refill()
if (QQuickPathViewAttached *att = d->attached(d->highlightItem))
att->setOnPath(currentVisible);
}
- for (QQuickItem *item : qAsConst(d->itemCache))
+ for (QQuickItem *item : std::as_const(d->itemCache))
d->releaseItem(item);
d->itemCache.clear();
@@ -2266,7 +2252,7 @@ void QQuickPathView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
d->items.clear();
if (!d->modelCount) {
- for (QQuickItem * item : qAsConst(d->itemCache))
+ for (QQuickItem * item : std::as_const(d->itemCache))
d->releaseItem(item);
d->itemCache.clear();
d->offset = 0;
@@ -2319,7 +2305,7 @@ void QQuickPathView::movementEnding()
int QQuickPathViewPrivate::calcCurrentIndex()
{
int current = 0;
- if (modelCount && model && items.count()) {
+ if (modelCount && model && items.size()) {
offset = std::fmod(offset, qreal(modelCount));
if (offset < 0)
offset += modelCount;
@@ -2336,7 +2322,7 @@ void QQuickPathViewPrivate::createCurrentItem()
return;
bool inItems = false;
- for (QQuickItem *item : qAsConst(items)) {
+ for (QQuickItem *item : std::as_const(items)) {
if (model->indexOf(item, nullptr) == currentIndex) {
inItems = true;
break;
@@ -2391,7 +2377,7 @@ void QQuickPathViewPrivate::fixOffsetCallback(void *d)
void QQuickPathViewPrivate::fixOffset()
{
Q_Q(QQuickPathView);
- if (model && items.count()) {
+ if (model && items.size()) {
if (haveHighlightRange && (highlightRangeMode == QQuickPathView::StrictlyEnforceRange
|| snapMode != QQuickPathView::NoSnap)) {
int curr = calcCurrentIndex();