10#include <QtQml/qqmlcomponent.h>
11#include <QtQuick/private/qquickpointerhandler_p.h>
12#include <QtQuick/private/qquickstate_p.h>
13#include <private/qqmlglobal_p.h>
14#include <private/qqmlopenmetaobject_p.h>
15#include <private/qqmlchangeset_p.h>
16#include <qpa/qplatformtheme.h>
18#include <QtQml/qqmlinfo.h>
20#include <QtGui/private/qeventpoint_p.h>
21#include <QtGui/qevent.h>
22#include <QtGui/qguiapplication.h>
23#include <QtGui/private/qguiapplication_p.h>
24#include <QtGui/qstylehints.h>
25#include <QtCore/qmath.h>
29#if QT_CONFIG(quick_itemview)
30#include <private/qquickitemview_p.h>
35#if !QT_CONFIG(quick_itemview)
43: QObject(parent), m_percent(-1), m_view(
nullptr), m_onPath(
false), m_isCurrent(
false)
46 m_metaobject =
new QQmlOpenMetaObject(
this, qPathViewAttachedType);
47 m_metaobject->setCached(
true);
49 m_metaobject =
new QQmlOpenMetaObject(
this);
59 return m_metaobject->value(name);
63 m_metaobject->setValue(name, val);
67 : path(
nullptr),
currentIndex(0), currentItemOffset(0), startPc(0)
68 , offset(0), offsetAdj(0), mappedRange(1), mappedCache(0)
73 , dragMargin(0), deceleration(100)
74 , maximumFlickVelocity(QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickMaximumVelocity).toReal())
75 , moveOffset(
this, &QQuickPathViewPrivate::setAdjustedOffset),
flickDuration(0)
77 ,
moveReason(
Other), movementDirection(QQuickPathView::Shortest), moveDirection(QQuickPathView::Shortest)
79 , moveHighlight(
this, &QQuickPathViewPrivate::setHighlightPosition)
80 , highlightPosition(0)
81 , highlightRangeStart(0), highlightRangeEnd(0)
82 , highlightRangeMode(QQuickPathView::StrictlyEnforceRange)
86 setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
93 q->setAcceptedMouseButtons(Qt::LeftButton);
94 q->setFlag(QQuickItem::ItemIsFocusScope);
95 q->setFiltersChildMouseEvents(
true);
96 qmlobject_connect(&tl, QQuickTimeLine, SIGNAL(updated()),
97 q, QQuickPathView, SLOT(ticked()));
99 qmlobject_connect(&tl, QQuickTimeLine, SIGNAL(completed()),
100 q, QQuickPathView, SLOT(movementEnding()));
109 QObject *object = model->object(modelIndex, async ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested);
110 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
113 model->release(object);
116 QObject* delegate = q->delegate();
117 qmlWarning(delegate ? delegate : q) << QQuickPathView::tr(
"Delegate must be of Item type");
121 item->setParentItem(q);
123 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
124 itemPrivate->addItemChangeListener(
125 this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed);
131void QQuickPathView::createdItem(
int index, QObject *object)
134 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
135 if (d->requestedIndex != index) {
136 qPathViewAttachedType = d->attachedType();
137 QQuickPathViewAttached *att =
static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item));
138 qPathViewAttachedType =
nullptr;
141 att->setOnPath(
false);
143 item->setParentItem(
this);
144 d->updateItem(item, 1);
146 d->requestedIndex = -1;
152void QQuickPathView::initItem(
int index, QObject *object)
155 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
156 if (item && d->requestedIndex == index) {
157 QQuickItemPrivate::get(item)->setCulled(
true);
158 item->setParentItem(
this);
159 qPathViewAttachedType = d->attachedType();
160 QQuickPathViewAttached *att =
static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item));
161 qPathViewAttachedType =
nullptr;
164 qreal percent = d->positionOfIndex(index);
165 if (percent < 1 && d->path) {
166 const auto attributes = d->path->attributes();
167 for (
const QString &attr : attributes)
168 att->setValue(attr.toUtf8(), d->path->attributeAt(attr, percent));
169 item->setZ(d->requestedZ);
171 att->setOnPath(percent < 1);
180 qCDebug(lcItemViewDelegateLifecycle) <<
"release" << item;
181 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
182 itemPrivate->removeItemChangeListener(
183 this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed);
186 QQmlInstanceModel::ReleaseFlags flags = model->release(item);
191 }
else if (flags & QQmlInstanceModel::Destroyed) {
193 item->setParentItem(
nullptr);
199 return static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item,
false));
206 attType =
new QQmlOpenMetaObjectType(&QQuickPathViewAttached::staticMetaObject);
208 const auto attributes = path->attributes();
209 for (
const QString &attr : attributes)
210 attType->createProperty(attr.toUtf8());
221 for (QQuickItem *p : std::as_const(items))
224 for (QQuickItem *p : std::as_const(itemCache))
229 model->cancel(requestedIndex);
244 if (model && pathItems != -1 && pathItems < modelCount) {
245 mappedRange = qreal(modelCount)/pathItems;
246 mappedCache = qreal(cacheSize)/pathItems/2;
257 if (model && index >= 0 && index < modelCount) {
259 if (haveHighlightRange && (highlightRangeMode != QQuickPathView::NoHighlightRange
260 || snapMode != QQuickPathView::NoSnap))
261 start = highlightRangeStart;
262 qreal globalPos = index + offset;
263 globalPos = std::fmod(globalPos, qreal(modelCount)) / modelCount;
265 globalPos += start / mappedRange;
266 globalPos = std::fmod(globalPos, qreal(1));
267 pos = globalPos * mappedRange;
269 pos = std::fmod(globalPos + start, qreal(1));
279 qreal upper,
bool emptyRangeCheck)
const
281 if (emptyRangeCheck && qFuzzyCompare(lower, upper))
284 if (position > upper && position > lower)
285 position -= mappedRange;
286 lower -= mappedRange;
288 return position >= lower && position < upper;
294 if (!q->isComponentComplete())
297 bool changed =
false;
305 QQuickItem *item =
nullptr;
308 QQmlContext *highlightContext =
new QQmlContext(
309 creationContext ? creationContext : qmlContext(q));
312 QQml_setParent_noEvent(highlightContext, nobj);
313 item = qobject_cast<QQuickItem *>(nobj);
317 delete highlightContext;
320 item =
new QQuickItem;
323 QQml_setParent_noEvent(item, q);
324 item->setParentItem(q);
329 emit q->highlightItemChanged();
335 if (!q->isComponentComplete() || !isValid())
338 if (haveHighlightRange && highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
339 updateItem(highlightItem, highlightRangeStart);
341 qreal target = currentIndex;
344 tl.reset(moveHighlight);
345 moveHighlight.setValue(highlightPosition);
349 if (target - highlightPosition > modelCount/2) {
351 qreal distance = modelCount - target + highlightPosition;
352 tl.move(moveHighlight, 0, QEasingCurve(QEasingCurve::InQuad),
int(duration * highlightPosition / distance));
353 tl.set(moveHighlight, modelCount-0.01);
354 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad),
int(duration * (modelCount-target) / distance));
355 }
else if (target - highlightPosition <= -modelCount/2) {
357 qreal distance = modelCount - highlightPosition + target;
358 tl.move(moveHighlight, modelCount-0.01, QEasingCurve(QEasingCurve::InQuad),
int(duration * (modelCount-highlightPosition) / distance));
359 tl.set(moveHighlight, 0);
360 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad),
int(duration * target / distance));
362 highlightUp = highlightPosition - target < 0;
363 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::InOutQuad), duration);
371 if (!(qFuzzyCompare(pos, highlightPosition))) {
374 if (haveHighlightRange && highlightRangeMode != QQuickPathView::NoHighlightRange) {
375 start = highlightRangeStart;
376 end = highlightRangeEnd;
379 qreal range = qreal(modelCount);
381 qreal relativeHighlight = std::fmod(pos + offset, range) / range;
383 if (!highlightUp && relativeHighlight > end / mappedRange) {
384 qreal diff = 1 - relativeHighlight;
385 setOffset(offset + diff * range);
386 }
else if (highlightUp && relativeHighlight >= (end - start) / mappedRange) {
387 qreal diff = relativeHighlight - (end - start) / mappedRange;
388 setOffset(offset - diff * range - 0.00001);
391 highlightPosition = pos;
392 qreal pathPos = positionOfIndex(pos);
393 updateItem(highlightItem, pathPos);
399void QQuickPathView::pathUpdated()
402 for (QQuickItem *item : std::as_const(d->items)) {
403 if (QQuickPathViewAttached *att = d->attached(item))
414 if (qFuzzyCompare(att->m_percent, percent))
416 att->m_percent = percent;
417 const auto attributes = path->attributes();
418 for (
const QString &attr : attributes)
419 att->setValue(attr.toUtf8(), path->attributeAt(attr, percent));
422 QQuickItemPrivate::get(item)->setCulled(percent >= 1);
423 QPointF pf = path->pointAtPercent(qMin(percent, qreal(1)));
424 item->setX(pf.x() - item->width()/2);
425 item->setY(pf.y() - item->height()/2);
431 if (!q->isComponentComplete())
451 emit q->dragStarted();
455 emit q->draggingChanged();
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
526QQuickPathView::QQuickPathView(QQuickItem *parent)
527 : QQuickItem(*(
new QQuickPathViewPrivate), parent)
533QQuickPathView::~QQuickPathView()
538 d->attType->release();
544
545
546
547
548
549
550
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
575
576
577
578
579
580
581
582
583
584
585
588
589
590
591
592
593
594
595
596
597
598
599QVariant QQuickPathView::model()
const
601 Q_D(
const QQuickPathView);
602 return d->modelVariant;
605void QQuickPathView::setModel(
const QVariant &m)
609 if (model.userType() == qMetaTypeId<QJSValue>())
610 model = model.value<QJSValue>().toVariant();
612 if (d->modelVariant == model)
616 qmlobject_disconnect(d->model, QQmlInstanceModel, SIGNAL(modelUpdated(QQmlChangeSet,
bool)),
617 this, QQuickPathView, SLOT(modelUpdated(QQmlChangeSet,
bool)));
618 qmlobject_disconnect(d->model, QQmlInstanceModel, SIGNAL(createdItem(
int,QObject*)),
619 this, QQuickPathView, SLOT(createdItem(
int,QObject*)));
620 qmlobject_disconnect(d->model, QQmlInstanceModel, SIGNAL(initItem(
int,QObject*)),
621 this, QQuickPathView, SLOT(initItem(
int,QObject*)));
625 d->modelVariant = model;
626 QObject *object = qvariant_cast<QObject*>(model);
627 QQmlInstanceModel *vim =
nullptr;
628 if (object && (vim = qobject_cast<QQmlInstanceModel *>(object))) {
636 d->model =
new QQmlDelegateModel(qmlContext(
this));
638 if (isComponentComplete())
639 static_cast<QQmlDelegateModel *>(d->model.data())->componentComplete();
641 if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel*>(d->model))
642 dataModel->setModel(model);
644 int oldModelCount = d->modelCount;
647 qmlobject_connect(d->model, QQmlInstanceModel, SIGNAL(modelUpdated(QQmlChangeSet,
bool)),
648 this, QQuickPathView, SLOT(modelUpdated(QQmlChangeSet,
bool)));
649 qmlobject_connect(d->model, QQmlInstanceModel, SIGNAL(createdItem(
int,QObject*)),
650 this, QQuickPathView, SLOT(createdItem(
int,QObject*)));
651 qmlobject_connect(d->model, QQmlInstanceModel, SIGNAL(initItem(
int,QObject*)),
652 this, QQuickPathView, SLOT(initItem(
int,QObject*)));
653 d->modelCount = d->model->count();
655 if (isComponentComplete()) {
656 if (d->currentIndex != 0) {
658 emit currentIndexChanged();
660 if (!(qFuzzyIsNull(d->offset))) {
662 emit offsetChanged();
666 if (d->modelCount != oldModelCount)
672
673
674
675int QQuickPathView::count()
const
677 Q_D(
const QQuickPathView);
678 return d->model ? d->modelCount : 0;
682
683
684
685
686QQuickPath *QQuickPathView::path()
const
688 Q_D(
const QQuickPathView);
692void QQuickPathView::setPath(QQuickPath *path)
698 qmlobject_disconnect(d->path, QQuickPath, SIGNAL(changed()),
699 this, QQuickPathView, SLOT(pathUpdated()));
703 qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()),
704 this, QQuickPathView, SLOT(pathUpdated()));
707 if (isComponentComplete()) {
711 d->attType->release();
712 d->attType =
nullptr;
722
723
724
725int QQuickPathView::currentIndex()
const
727 Q_D(
const QQuickPathView);
728 return d->currentIndex;
731void QQuickPathView::setCurrentIndex(
int idx)
734 if (!isComponentComplete()) {
735 if (idx != d->currentIndex) {
736 d->currentIndex = idx;
737 emit currentIndexChanged();
743 ? ((idx % d->modelCount) + d->modelCount) % d->modelCount
745 if (d->model && (idx != d->currentIndex || !d->currentItem)) {
746 const bool hadCurrentItem = d->currentItem !=
nullptr;
747 const int oldCurrentIdx = d->currentIndex;
748 if (hadCurrentItem) {
749 if (QQuickPathViewAttached *att = d->attached(d->currentItem))
750 att->setIsCurrentItem(
false);
751 d->releaseCurrentItem();
753 d->moveReason = QQuickPathViewPrivate::SetIndex;
754 d->currentIndex = idx;
756 d->createCurrentItem();
757 if (d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange)
758 d->snapToIndex(d->currentIndex, QQuickPathViewPrivate::SetIndex);
759 d->currentItemOffset = d->positionOfIndex(d->currentIndex);
760 d->updateHighlight();
762 if (oldCurrentIdx != d->currentIndex)
763 emit currentIndexChanged();
765 emit currentItemChanged();
770
771
772
773QQuickItem *QQuickPathView::currentItem()
const
775 Q_D(
const QQuickPathView);
776 return d->currentItem;
780
781
782
783
784
785
786void QQuickPathView::incrementCurrentIndex()
789 d->moveDirection = QQuickPathView::Positive;
790 setCurrentIndex(currentIndex()+1);
794
795
796
797
798
799
800void QQuickPathView::decrementCurrentIndex()
803 d->moveDirection = QQuickPathView::Negative;
804 setCurrentIndex(currentIndex()-1);
808
809
810
811
812
813qreal QQuickPathView::offset()
const
815 Q_D(
const QQuickPathView);
819void QQuickPathView::setOffset(qreal offset)
822 d->moveReason = QQuickPathViewPrivate::Other;
823 d->setOffset(offset);
830 if (!qFuzzyCompare(offset, o)) {
831 if (isValid() && q->isComponentComplete()) {
832 qreal oldOffset = offset;
833 offset = std::fmod(o, qreal(modelCount));
835 offset += qreal(modelCount);
836 qCDebug(lcItemViewDelegateLifecycle) << o <<
"was" << oldOffset <<
"now" << offset;
841 emit q->offsetChanged();
847 setOffset(o+offsetAdj);
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873QQmlComponent *QQuickPathView::highlight()
const
875 Q_D(
const QQuickPathView);
876 return d->highlightComponent;
879void QQuickPathView::setHighlight(QQmlComponent *highlight)
882 if (highlight != d->highlightComponent) {
883 d->highlightComponent = highlight;
884 d->createHighlight();
885 d->updateHighlight();
886 emit highlightChanged();
891
892
893
894
895
896
897
898QQuickItem *QQuickPathView::highlightItem()
const
900 Q_D(
const QQuickPathView);
901 return d->highlightItem;
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940qreal QQuickPathView::preferredHighlightBegin()
const
942 Q_D(
const QQuickPathView);
943 return d->highlightRangeStart;
946void QQuickPathView::setPreferredHighlightBegin(qreal start)
949 if (qFuzzyCompare(d->highlightRangeStart, start) || start < 0 || start > 1)
951 d->highlightRangeStart = start;
952 d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd;
954 emit preferredHighlightBeginChanged();
957qreal QQuickPathView::preferredHighlightEnd()
const
959 Q_D(
const QQuickPathView);
960 return d->highlightRangeEnd;
963void QQuickPathView::setPreferredHighlightEnd(qreal end)
966 if (qFuzzyCompare(d->highlightRangeEnd, end) || end < 0 || end > 1)
968 d->highlightRangeEnd = end;
969 d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd;
971 emit preferredHighlightEndChanged();
974QQuickPathView::HighlightRangeMode QQuickPathView::highlightRangeMode()
const
976 Q_D(
const QQuickPathView);
977 return d->highlightRangeMode;
980void QQuickPathView::setHighlightRangeMode(HighlightRangeMode mode)
983 if (d->highlightRangeMode == mode)
985 d->highlightRangeMode = mode;
986 d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd;
987 if (d->haveHighlightRange) {
989 int index = d->highlightRangeMode != NoHighlightRange ? d->currentIndex : d->calcCurrentIndex();
991 d->snapToIndex(index, QQuickPathViewPrivate::Other);
993 emit highlightRangeModeChanged();
997
998
999
1000
1001
1002
1003
1004
1005int QQuickPathView::highlightMoveDuration()
const
1007 Q_D(
const QQuickPathView);
1008 return d->highlightMoveDuration;
1011void QQuickPathView::setHighlightMoveDuration(
int duration)
1013 Q_D(QQuickPathView);
1014 if (d->highlightMoveDuration == duration)
1016 d->highlightMoveDuration = duration;
1017 emit highlightMoveDurationChanged();
1021
1022
1023
1024
1025
1026
1027
1028qreal QQuickPathView::dragMargin()
const
1030 Q_D(
const QQuickPathView);
1031 return d->dragMargin;
1034void QQuickPathView::setDragMargin(qreal dragMargin)
1036 Q_D(QQuickPathView);
1037 if (qFuzzyCompare(d->dragMargin, dragMargin))
1039 d->dragMargin = dragMargin;
1040 emit dragMarginChanged();
1044
1045
1046
1047
1048
1049qreal QQuickPathView::flickDeceleration()
const
1051 Q_D(
const QQuickPathView);
1052 return d->deceleration;
1055void QQuickPathView::setFlickDeceleration(qreal dec)
1057 Q_D(QQuickPathView);
1058 if (qFuzzyCompare(d->deceleration, dec))
1060 d->deceleration = dec;
1061 emit flickDecelerationChanged();
1065
1066
1067
1068
1069
1070qreal QQuickPathView::maximumFlickVelocity()
const
1072 Q_D(
const QQuickPathView);
1073 return d->maximumFlickVelocity;
1076void QQuickPathView::setMaximumFlickVelocity(qreal vel)
1078 Q_D(QQuickPathView);
1079 if (qFuzzyCompare(vel, d->maximumFlickVelocity))
1081 d->maximumFlickVelocity = vel;
1082 emit maximumFlickVelocityChanged();
1087
1088
1089
1090
1091
1092
1093
1094bool QQuickPathView::isInteractive()
const
1096 Q_D(
const QQuickPathView);
1097 return d->interactive;
1100void QQuickPathView::setInteractive(
bool interactive)
1102 Q_D(QQuickPathView);
1103 if (interactive != d->interactive) {
1104 d->interactive = interactive;
1107 emit interactiveChanged();
1112
1113
1114
1115
1116
1117bool QQuickPathView::isMoving()
const
1119 Q_D(
const QQuickPathView);
1124
1125
1126
1127
1128
1129bool QQuickPathView::isFlicking()
const
1131 Q_D(
const QQuickPathView);
1136
1137
1138
1139
1140
1141bool QQuickPathView::isDragging()
const
1143 Q_D(
const QQuickPathView);
1148
1149
1150
1151
1152
1155
1156
1157
1158
1159
1160
1161
1162
1165
1166
1167
1168
1169
1170
1173
1174
1175
1176
1179
1180
1181
1182
1183
1186
1187
1188
1189
1190
1191
1192
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212QQmlComponent *QQuickPathView::delegate()
const
1214 Q_D(
const QQuickPathView);
1216 if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel*>(d->model))
1217 return dataModel->delegate();
1223void QQuickPathView::setDelegate(QQmlComponent *delegate)
1225 Q_D(QQuickPathView);
1226 if (delegate ==
this->delegate())
1229 d->model =
new QQmlDelegateModel(qmlContext(
this));
1231 if (isComponentComplete())
1232 static_cast<QQmlDelegateModel *>(d->model.data())->componentComplete();
1234 if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel*>(d->model)) {
1235 int oldCount = dataModel->count();
1236 dataModel->setDelegate(delegate);
1237 d->modelCount = dataModel->count();
1239 if (oldCount != dataModel->count())
1240 emit countChanged();
1241 emit delegateChanged();
1242 d->delegateValidated =
false;
1247
1248
1249
1250
1251
1252int QQuickPathView::pathItemCount()
const
1254 Q_D(
const QQuickPathView);
1255 return d->pathItems;
1258void QQuickPathView::setPathItemCount(
int i)
1260 Q_D(QQuickPathView);
1261 if (i == d->pathItems)
1266 d->updateMappedRange();
1267 if (d->isValid() && isComponentComplete()) {
1270 emit pathItemCountChanged();
1273void QQuickPathView::resetPathItemCount()
1275 Q_D(QQuickPathView);
1276 if (-1 == d->pathItems)
1279 d->updateMappedRange();
1280 if (d->isValid() && isComponentComplete())
1282 emit pathItemCountChanged();
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306int QQuickPathView::cacheItemCount()
const
1308 Q_D(
const QQuickPathView);
1309 return d->requestedCacheSize;
1312void QQuickPathView::setCacheItemCount(
int i)
1314 Q_D(QQuickPathView);
1315 if (i == d->requestedCacheSize || i < 0)
1318 d->requestedCacheSize = i;
1319 d->updateMappedRange();
1321 emit cacheItemCountChanged();
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342QQuickPathView::SnapMode QQuickPathView::snapMode()
const
1344 Q_D(
const QQuickPathView);
1348void QQuickPathView::setSnapMode(SnapMode mode)
1350 Q_D(QQuickPathView);
1351 if (mode == d->snapMode)
1354 emit snapModeChanged();
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380QQuickPathView::MovementDirection QQuickPathView::movementDirection()
const
1382 Q_D(
const QQuickPathView);
1383 return d->movementDirection;
1386void QQuickPathView::setMovementDirection(QQuickPathView::MovementDirection dir)
1388 Q_D(QQuickPathView);
1389 if (dir == d->movementDirection)
1391 d->movementDirection = dir;
1392 if (!d->tl.isActive())
1393 d->moveDirection = d->movementDirection;
1394 emit movementDirectionChanged();
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419void QQuickPathView::positionViewAtIndex(
int index,
int mode)
1421 Q_D(QQuickPathView);
1424 if (mode < QQuickPathView::Beginning || mode > QQuickPathView::SnapPosition || mode == 3)
1427 if (mode == QQuickPathView::Contain && (d->pathItems < 0 || d->modelCount <= d->pathItems))
1430 int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount);
1431 int idx = (index+d->modelCount) % d->modelCount;
1432 bool snap = d->haveHighlightRange && (d->highlightRangeMode != QQuickPathView::NoHighlightRange
1433 || d->snapMode != QQuickPathView::NoSnap);
1438 beginOffset = d->modelCount - idx - qFloor(count * d->highlightRangeStart);
1439 endOffset = beginOffset + count - 1;
1441 beginOffset = d->modelCount - idx;
1444 qreal adj =
sizeof(qreal) ==
sizeof(
float) ? 0.00001f : 0.000000000001;
1445 endOffset = std::fmod(beginOffset + count, qreal(d->modelCount)) - adj;
1447 qreal offset = d->offset;
1450 offset = beginOffset;
1456 if (beginOffset < endOffset)
1457 offset = (beginOffset + endOffset)/2;
1459 offset = (beginOffset + (endOffset + d->modelCount))/2;
1461 offset = qRound(offset);
1464 if ((beginOffset < endOffset && (d->offset < beginOffset || d->offset > endOffset))
1465 || (d->offset < beginOffset && d->offset > endOffset)) {
1466 qreal diff1 = std::fmod(beginOffset - d->offset + d->modelCount, qreal(d->modelCount));
1467 qreal diff2 = std::fmod(d->offset - endOffset + d->modelCount, qreal(d->modelCount));
1469 offset = beginOffset;
1475 offset = d->modelCount - idx;
1484
1485
1486
1487
1488
1489
1490
1491int QQuickPathView::indexAt(qreal x, qreal y)
const
1493 Q_D(
const QQuickPathView);
1494 QQuickItem *item = itemAt(x, y);
1495 return item ? d->model->indexOf(item,
nullptr) : -1;
1499
1500
1501
1502
1503
1504
1505
1506QQuickItem *QQuickPathView::itemAt(qreal x, qreal y)
const
1508 Q_D(
const QQuickPathView);
1512 for (QQuickItem *item : d->items) {
1513 QPointF p = item->mapFromItem(
this, QPointF(x, y));
1514 if (item->contains(p))
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534QQuickItem *QQuickPathView::itemAtIndex(
int index)
const
1536 Q_D(
const QQuickPathView);
1540 for (QQuickItem *item : d->items) {
1541 if (index == d->model->indexOf(item,
nullptr))
1549
1550
1551
1552
1553
1554
1555
1558 const auto pathLength = path->path().length();
1559 qreal samples = qMin(pathLength / 5, qreal(500));
1560 qreal res = pathLength / samples;
1562 qreal mindist = 1e10;
1563 QPointF nearPoint = path->pointAtPercent(0);
1567 for (qreal i=1; i < samples; i++) {
1568 QPointF pt = path->pointAtPercent(i/samples);
1569 QPointF diff = pt - point;
1570 qreal dist = diff.x()*diff.x() + diff.y()*diff.y();
1571 if (dist < mindist) {
1579 qreal approxPc = nearPc;
1580 for (qreal i = approxPc-1; i < approxPc+1; i += 1/(2*res)) {
1581 QPointF pt = path->pointAtPercent(i/samples);
1582 QPointF diff = pt - point;
1583 qreal dist = diff.x()*diff.x() + diff.y()*diff.y();
1584 if (dist < mindist) {
1592 *nearPercent = nearPc / samples;
1599 velocityBuffer[velocityWritePos] = v;
1603 qCDebug(lcPathView) <<
"instantaneous velocity" << v;
1615 for (
int i = 0; i < count; ++i) {
1620 qCDebug(lcPathView) <<
"average velocity" << velocity <<
"based on" << count <<
"samples";
1627 if (0 != event->timestamp())
1628 return qint64(event->timestamp());
1629 return timer.elapsed();
1632void QQuickPathView::mousePressEvent(QMouseEvent *event)
1634 Q_D(QQuickPathView);
1635 if (d->interactive) {
1636 d->handleMousePressEvent(event);
1639 QQuickItem::mousePressEvent(event);
1645 Q_Q(QQuickPathView);
1646 if (!interactive || !items.size() || !model || !modelCount)
1651 for (; idx < items.size(); ++idx) {
1652 QQuickItem *item = items.at(idx);
1653 if (item->contains(item->mapFromScene(event->scenePosition())))
1656 if (idx == items.size() && qFuzzyIsNull(dragMargin))
1659 startPoint = pointNear(event->position(), &startPc);
1660 startPos = event->position();
1661 if (idx == items.size()) {
1662 qreal distance = qAbs(event->position().x() - startPoint.x()) + qAbs(event->position().y() - startPoint.y());
1663 if (distance > dragMargin)
1667 if (tl.isActive() && flicking && flickDuration && qreal(tl.time()) / flickDuration < 0.8) {
1673 q->setKeepMouseGrab(stealMouse);
1676 lastPosTime = computeCurrentTime(event);
1680void QQuickPathView::mouseMoveEvent(QMouseEvent *event)
1682 Q_D(QQuickPathView);
1683 if (d->interactive) {
1684 d->handleMouseMoveEvent(event);
1687 QQuickItem::mouseMoveEvent(event);
1693 Q_Q(QQuickPathView);
1694 if (!interactive || !timer.isValid() || !model || !modelCount)
1697 qint64 currentTimestamp = computeCurrentTime(event);
1699 QPointF pathPoint = pointNear(event->position(), &newPc);
1701 QPointF posDelta = event->position() - startPos;
1702 if (QQuickDeliveryAgentPrivate::dragOverThreshold(posDelta.y(), Qt::YAxis, event) ||
1703 QQuickDeliveryAgentPrivate::dragOverThreshold(posDelta.x(), Qt::XAxis, event)) {
1707 QPointF pathDelta = pathPoint - startPoint;
1708 const int startDragDistance = QGuiApplication::styleHints()->startDragDistance();
1709 if (qAbs(pathDelta.x()) > startDragDistance * 0.8
1710 || qAbs(pathDelta.y()) > startDragDistance * 0.8) {
1712 q->setKeepMouseGrab(
true);
1718 qreal diff = (newPc - startPc)*count;
1719 if (!qFuzzyIsNull(diff)) {
1720 q->setOffset(offset + diff);
1722 if (diff > modelCount/2)
1724 else if (diff < -modelCount/2)
1727 qint64 elapsed = currentTimestamp - lastPosTime;
1729 addVelocitySample(diff / (qreal(elapsed) / 1000));
1733 emit q->movingChanged();
1734 emit q->movementStarted();
1739 lastPosTime = currentTimestamp;
1742void QQuickPathView::mouseReleaseEvent(QMouseEvent *event)
1744 Q_D(QQuickPathView);
1745 if (d->interactive) {
1746 d->handleMouseReleaseEvent(event);
1750 QQuickItem::mouseReleaseEvent(event);
1756 Q_Q(QQuickPathView);
1758 q->setKeepMouseGrab(
false);
1760 if (!interactive || !timer.isValid() || !model || !modelCount) {
1763 q->movementEnding();
1767 qreal velocity = calcVelocity();
1768 qint64 elapsed = computeCurrentTime(event) - lastPosTime;
1773 qCDebug(lcPathView) <<
"after elapsed time" << elapsed <<
"velocity decayed to" << velocity;
1774 qreal count = pathItems == -1 ? modelCount : qMin(pathItems, modelCount);
1775 const auto averageItemLength = path->path().length() / count;
1776 qreal pixelVelocity = averageItemLength * velocity;
1777 if (qAbs(pixelVelocity) > _q_MinimumFlickVelocity) {
1778 if (qAbs(pixelVelocity) > maximumFlickVelocity || snapMode == QQuickPathView::SnapOneItem) {
1780 qreal maxVel = velocity < 0 ? -maximumFlickVelocity : maximumFlickVelocity;
1781 velocity = maxVel / averageItemLength;
1784 qreal v2 = velocity*velocity;
1785 qreal accel = deceleration/10;
1787 if (haveHighlightRange && (highlightRangeMode == QQuickPathView::StrictlyEnforceRange
1788 || snapMode != QQuickPathView::NoSnap)) {
1789 if (snapMode == QQuickPathView::SnapOneItem) {
1792 dist = qRound(0.5 + offset) - offset;
1794 dist = qRound(0.5 - offset) + offset;
1797 dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2) + 0.25));
1801 dist = qRound(dist + offset) - offset;
1803 dist = qRound(dist - offset) + offset;
1810 accel = v2 / (2 * qAbs(dist));
1813 dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2)));
1815 flickDuration =
int(1000 * qAbs(velocity) / accel);
1817 moveOffset.setValue(offset);
1818 tl.accel(moveOffset, velocity, accel, dist);
1819 tl.callback(QQuickTimeLineCallback(&moveOffset, fixOffsetCallback,
this));
1822 emit q->flickingChanged();
1823 emit q->flickStarted();
1831 q->movementEnding();
1834bool QQuickPathView::childMouseEventFilter(QQuickItem *i, QEvent *e)
1836 Q_D(QQuickPathView);
1837 if (!isVisible() || !d->interactive || !e->isPointerEvent())
1838 return QQuickItem::childMouseEventFilter(i, e);
1840 QPointerEvent *pe =
static_cast<QPointerEvent *>(e);
1841 if (QQuickDeliveryAgentPrivate::isMouseEvent(pe)) {
1844 const auto &point = pe->points().first();
1845 QPointF localPos = mapFromScene(point.scenePosition());
1846 QObject *grabber = pe->exclusiveGrabber(point);
1847 QQuickPointerHandler *grabberHandler = qmlobject_cast<QQuickPointerHandler *>(grabber);
1848 QQuickItem *grabberItem = grabberHandler ? grabberHandler->parentItem() : qmlobject_cast<QQuickItem *>(grabber);
1849 if (grabberItem ==
this && d->stealMouse) {
1854 bool grabberDisabled = (grabberItem && !grabberItem->isEnabled()) || (grabberHandler && !grabberHandler->enabled());
1855 bool stealThisEvent = d->stealMouse;
1856 const bool mouseFromTouch = pe->pointingDevice()->type() == QPointingDevice::DeviceType::TouchScreen;
1857 if ((stealThisEvent || contains(localPos)) && (!grabberItem ||
1858 (!grabberItem->keepMouseGrab() && (!mouseFromTouch || !grabberItem->keepTouchGrab())) || grabberDisabled)) {
1860 QMutableSinglePointEvent localizedEvent(*
static_cast<QMouseEvent *>(pe));
1861 QMutableEventPoint::setPosition(localizedEvent.point(0), localPos);
1862 localizedEvent.setAccepted(
false);
1864 switch (localizedEvent.type()) {
1865 case QEvent::MouseMove:
1866 d->handleMouseMoveEvent(
static_cast<QMouseEvent *>(
static_cast<QSinglePointEvent *>(&localizedEvent)));
1868 case QEvent::MouseButtonPress:
1869 d->handleMousePressEvent(
static_cast<QMouseEvent *>(
static_cast<QSinglePointEvent *>(&localizedEvent)));
1870 stealThisEvent = d->stealMouse;
1872 case QEvent::MouseButtonRelease:
1873 d->handleMouseReleaseEvent(
static_cast<QMouseEvent *>(
static_cast<QSinglePointEvent *>(&localizedEvent)));
1879 grabber = localizedEvent.exclusiveGrabber(localizedEvent.points().first());
1880 grabberHandler = qmlobject_cast<QQuickPointerHandler *>(grabber);
1881 grabberItem = grabberHandler ? grabberHandler->parentItem() : qmlobject_cast<QQuickItem *>(grabber);
1882 grabberDisabled = (grabberItem && !grabberItem->isEnabled()) || (grabberHandler && !grabberHandler->enabled());
1883 if ((grabberItem && stealThisEvent && !grabberItem->keepMouseGrab() && grabber !=
this) || grabberDisabled)
1884 pe->setExclusiveGrabber(point,
this);
1886 const bool filtered = stealThisEvent || grabberDisabled;
1888 pe->setAccepted(stealThisEvent && grabberItem ==
this && !grabberDisabled);
1891 }
else if (d->timer.isValid()) {
1892 d->timer.invalidate();
1895 if (pe->type() == QEvent::MouseButtonRelease || (grabberItem && grabberItem->keepMouseGrab() && !grabberDisabled))
1896 d->stealMouse =
false;
1900 return QQuickItem::childMouseEventFilter(i, e);
1903void QQuickPathView::mouseUngrabEvent()
1905 Q_D(QQuickPathView);
1906 if (d->stealMouse ||
1907 (!d->flicking && d->snapMode != NoSnap && !qFuzzyCompare(qRound(d->offset), d->offset))) {
1910 d->stealMouse =
false;
1911 setKeepMouseGrab(
false);
1912 d->timer.invalidate();
1914 d->setDragging(
false);
1915 if (!d->tl.isActive())
1920void QQuickPathView::updatePolish()
1922 QQuickItem::updatePolish();
1928 if (currentIndex < 0)
1929 return modelCount + currentIndex % modelCount;
1931 return currentIndex % modelCount;
1934void QQuickPathView::componentComplete()
1936 Q_D(QQuickPathView);
1937 if (d->model && d->ownModel)
1938 static_cast<QQmlDelegateModel *>(d->model.data())->componentComplete();
1940 QQuickItem::componentComplete();
1943 d->modelCount = d->model->count();
1944 if (d->modelCount && d->currentIndex != 0)
1945 d->offset = std::fmod(qreal(d->modelCount - currentIndexRemainder(d->currentIndex, d->modelCount)), qreal(d->modelCount));
1948 d->createHighlight();
1950 d->updateHighlight();
1954 emit countChanged();
1957void QQuickPathView::refill()
1959 Q_D(QQuickPathView);
1962 d->scheduleLayout();
1966 d->layoutScheduled =
false;
1968 if (!d->isValid() || !isComponentComplete())
1973 bool currentVisible =
false;
1974 int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount);
1977 qCDebug(lcItemViewDelegateLifecycle) <<
"currentIndex" << d->currentIndex <<
"offset" << d->offset;
1978 QList<QQuickItem*>::iterator it = d->items.begin();
1979 while (it != d->items.end()) {
1980 QQuickItem *item = *it;
1981 int idx = d->model->indexOf(item,
nullptr);
1982 qreal pos = d->positionOfIndex(idx);
1983 if (lcItemViewDelegateLifecycle().isDebugEnabled()) {
1984 QQuickText *text = qmlobject_cast<QQuickText*>(item);
1986 qCDebug(lcItemViewDelegateLifecycle) <<
"idx" << idx <<
"@" << pos <<
": QQuickText" << text->objectName() << QStringView{text->text()}.left(40);
1988 qCDebug(lcItemViewDelegateLifecycle) <<
"idx" << idx <<
"@" << pos <<
":" << item;
1991 d->updateItem(item, pos);
1992 if (idx == d->currentIndex) {
1993 currentVisible =
true;
1994 d->currentItemOffset = pos;
1998 d->updateItem(item, pos);
1999 if (QQuickPathViewAttached *att = d->attached(item))
2000 att->setOnPath(pos < 1);
2001 if (!d->isInBound(pos, d->mappedRange - d->mappedCache, 1 + d->mappedCache)) {
2002 qCDebug(lcItemViewDelegateLifecycle) <<
"release" << idx <<
"@" << pos <<
", !isInBound: lower" << (d->mappedRange - d->mappedCache) <<
"upper" << (1 + d->mappedCache);
2003 d->releaseItem(item);
2004 it = d->items.erase(it);
2011 bool waiting =
false;
2012 if (d->modelCount) {
2014 if (d->items.size() < count+d->cacheSize) {
2019 const bool wasEmpty = d->items.isEmpty();
2025 for (QQuickItem * item : std::as_const(d->items)) {
2026 int idx = d->model->indexOf(item,
nullptr);
2027 qreal curPos = d->positionOfIndex(idx);
2028 if (curPos > endPos) {
2033 if (curPos < startPos) {
2039 if (d->haveHighlightRange
2040 && (d->highlightRangeMode != QQuickPathView::NoHighlightRange
2041 || d->snapMode != QQuickPathView::NoSnap))
2042 startPos = d->highlightRangeStart;
2044 endIdx = (qRound(d->modelCount - d->offset) - 1) % d->modelCount;
2045 endIdx = qMax(-1, endIdx);
2046 endPos = d->positionOfIndex(endIdx);
2049 int idx = endIdx + 1;
2050 if (idx >= d->modelCount)
2052 qreal nextPos = d->positionOfIndex(idx);
2053 while ((d->isInBound(nextPos, endPos, 1 + d->mappedCache,
false) || !d->items.size())
2054 && d->items.size() < count + d->cacheSize) {
2055 qCDebug(lcItemViewDelegateLifecycle) <<
"append" << idx <<
"@" << nextPos << (d->currentIndex == idx ?
"current" :
"") <<
"items count was" << d->items.size();
2056 QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1);
2061 if (d->items.contains(item)) {
2062 d->releaseItem(item);
2065 if (d->currentIndex == idx) {
2066 currentVisible =
true;
2067 d->currentItemOffset = nextPos;
2069 d->items.append(item);
2070 d->updateItem(item, nextPos);
2074 if (idx >= d->modelCount)
2076 nextPos = d->positionOfIndex(idx);
2080 idx = (wasEmpty ? d->calcCurrentIndex() : startIdx) - 1;
2083 idx = d->modelCount - 1;
2084 nextPos = d->positionOfIndex(idx);
2085 while (!waiting && d->isInBound(nextPos, d->mappedRange - d->mappedCache, startPos)
2086 && d->items.size() < count+d->cacheSize) {
2087 qCDebug(lcItemViewDelegateLifecycle) <<
"prepend" << idx <<
"@" << nextPos << (d->currentIndex == idx ?
"current" :
"") <<
"items count was" << d->items.size();
2088 QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1);
2093 if (d->items.contains(item)) {
2094 d->releaseItem(item);
2097 if (d->currentIndex == idx) {
2098 currentVisible =
true;
2099 d->currentItemOffset = nextPos;
2101 d->items.prepend(item);
2102 d->updateItem(item, nextPos);
2107 idx = d->modelCount - 1;
2108 nextPos = d->positionOfIndex(idx);
2115 if (!waiting && d->items.size() < count+d->cacheSize) {
2116 qCDebug(lcItemViewDelegateLifecycle) <<
"Checking for pathview middle inserts, items count was" << d->items.size();
2118 QQuickItem *lastItem = d->items.at(0);
2119 while (idx != endIdx) {
2120 nextPos = d->positionOfIndex(idx);
2121 if (d->isInBound(nextPos, d->mappedRange - d->mappedCache, 1 + d->mappedCache)) {
2123 QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1);
2129 if (!d->items.contains(item)) {
2130 qCDebug(lcItemViewDelegateLifecycle) <<
"middle insert" << idx <<
"@" << nextPos
2131 << (d->currentIndex == idx ?
"current" :
"")
2132 <<
"items count was" << d->items.size();
2133 if (d->currentIndex == idx) {
2134 currentVisible =
true;
2135 d->currentItemOffset = nextPos;
2137 int lastListIdx = d->items.indexOf(lastItem);
2138 d->items.insert(lastListIdx + 1, item);
2139 d->updateItem(item, nextPos);
2141 d->releaseItem(item);
2148 if (idx >= d->modelCount)
2155 bool currentChanged =
false;
2156 if (!currentVisible) {
2157 d->currentItemOffset = 1;
2158 if (d->currentItem) {
2159 d->updateItem(d->currentItem, 1);
2160 }
else if (!waiting && d->currentIndex >= 0 && d->currentIndex < d->modelCount) {
2161 if ((d->currentItem = d->getItem(d->currentIndex, d->currentIndex))) {
2162 currentChanged =
true;
2163 d->updateItem(d->currentItem, 1);
2164 if (QQuickPathViewAttached *att = d->attached(d->currentItem))
2165 att->setIsCurrentItem(
true);
2168 }
else if (!waiting && !d->currentItem) {
2169 if ((d->currentItem = d->getItem(d->currentIndex, d->currentIndex))) {
2170 currentChanged =
true;
2171 d->currentItem->setFocus(
true);
2172 if (QQuickPathViewAttached *att = d->attached(d->currentItem))
2173 att->setIsCurrentItem(
true);
2177 if (d->highlightItem && d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
2178 d->updateItem(d->highlightItem, d->highlightRangeStart);
2179 if (QQuickPathViewAttached *att = d->attached(d->highlightItem))
2180 att->setOnPath(
true);
2181 }
else if (d->highlightItem && d->moveReason != QQuickPathViewPrivate::SetIndex) {
2182 d->updateItem(d->highlightItem, d->currentItemOffset);
2183 if (QQuickPathViewAttached *att = d->attached(d->highlightItem))
2184 att->setOnPath(currentVisible);
2186 for (QQuickItem *item : std::as_const(d->itemCache))
2187 d->releaseItem(item);
2188 d->itemCache.clear();
2190 d->inRefill =
false;
2192 emit currentItemChanged();
2195void QQuickPathView::modelUpdated(
const QQmlChangeSet &changeSet,
bool reset)
2197 Q_D(QQuickPathView);
2198 if (!d->model || !d->model->isValid() || !d->path || !isComponentComplete())
2202 d->modelCount = d->model->count();
2204 emit countChanged();
2208 if (changeSet.removes().isEmpty() && changeSet.inserts().isEmpty())
2211 const int modelCount = d->modelCount;
2214 bool currentChanged =
false;
2215 bool changedOffset =
false;
2216 for (
const QQmlChangeSet::Change &r : changeSet.removes()) {
2217 if (moveId == -1 && d->currentIndex >= r.index + r.count) {
2218 d->currentIndex -= r.count;
2219 currentChanged =
true;
2220 }
else if (moveId == -1 && d->currentIndex >= r.index && d->currentIndex < r.index + r.count) {
2224 moveOffset = d->currentIndex - r.index;
2225 }
else if (d->currentItem) {
2226 if (QQuickPathViewAttached *att = d->attached(d->currentItem))
2227 att->setIsCurrentItem(
true);
2228 d->releaseCurrentItem();
2230 d->currentIndex = qMin(r.index, d->modelCount - r.count - 1);
2231 currentChanged =
true;
2234 if (r.index > d->currentIndex) {
2235 changedOffset =
true;
2236 d->offset -= r.count;
2237 d->offsetAdj -= r.count;
2239 d->modelCount -= r.count;
2241 for (
const QQmlChangeSet::Change &i : changeSet.inserts()) {
2242 if (d->modelCount) {
2243 if (moveId == -1 && i.index <= d->currentIndex) {
2244 d->currentIndex += i.count;
2245 currentChanged =
true;
2247 if (moveId != -1 && moveId == i.moveId) {
2248 d->currentIndex = i.index + moveOffset;
2249 currentChanged =
true;
2251 if (i.index > d->currentIndex) {
2252 d->offset += i.count;
2253 d->offsetAdj += i.count;
2254 changedOffset =
true;
2258 d->modelCount += i.count;
2261 d->offset = std::fmod(d->offset, qreal(d->modelCount));
2263 d->offset += d->modelCount;
2264 if (d->currentIndex == -1)
2265 d->currentIndex = d->calcCurrentIndex();
2267 d->itemCache += d->items;
2270 if (!d->modelCount) {
2271 for (QQuickItem * item : std::as_const(d->itemCache))
2272 d->releaseItem(item);
2273 d->itemCache.clear();
2275 changedOffset =
true;
2276 d->tl.reset(d->moveOffset);
2278 if (!d->flicking && !d->moving && d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
2279 d->offset = std::fmod(qreal(d->modelCount - d->currentIndex), qreal(d->modelCount));
2280 changedOffset =
true;
2282 d->updateMappedRange();
2283 d->scheduleLayout();
2286 emit offsetChanged();
2288 emit currentIndexChanged();
2289 if (d->modelCount != modelCount)
2290 emit countChanged();
2293void QQuickPathView::destroyingItem(QObject *item)
2298void QQuickPathView::ticked()
2300 Q_D(QQuickPathView);
2304void QQuickPathView::movementEnding()
2306 Q_D(QQuickPathView);
2308 d->flicking =
false;
2309 emit flickingChanged();
2312 if (d->moving && !d->stealMouse) {
2314 emit movingChanged();
2315 emit movementEnded();
2317 d->moveDirection = d->movementDirection;
2324 if (modelCount && model && items.size()) {
2325 offset = std::fmod(offset, qreal(modelCount));
2327 offset += modelCount;
2328 current = qRound(qAbs(
std::fmod(modelCount - offset, qreal(
modelCount))));
2340 bool inItems =
false;
2341 for (QQuickItem *item : std::as_const(items)) {
2342 if (model->indexOf(item,
nullptr) == currentIndex) {
2349 if ((currentItem = getItem(currentIndex, currentIndex))) {
2350 currentItem->setFocus(
true);
2355 if ((currentItem = getItem(currentIndex, currentIndex))) {
2356 updateItem(currentItem, 1);
2365 Q_Q(QQuickPathView);
2368 if (!modelCount || !haveHighlightRange || highlightRangeMode != QQuickPathView::StrictlyEnforceRange)
2372 if (model && (idx != currentIndex || !currentItem)) {
2380 currentItem =
nullptr;
2383 emit q->currentIndexChanged();
2384 emit q->currentItemChanged();
2395 Q_Q(QQuickPathView);
2396 if (model && items.size()) {
2397 if (haveHighlightRange && (highlightRangeMode == QQuickPathView::StrictlyEnforceRange
2398 || snapMode != QQuickPathView::NoSnap)) {
2400 if (curr != currentIndex && highlightRangeMode == QQuickPathView::StrictlyEnforceRange)
2401 q->setCurrentIndex(curr);
2410 if (!model || modelCount <= 0)
2413 qreal targetOffset = std::fmod(qreal(modelCount - index), qreal(modelCount));
2416 tl.reset(moveOffset);
2417 moveOffset.setValue(offset);
2422 const qreal averageItemLength = path->path().length() / count;
2423 const qreal threshold = 0.5 / averageItemLength;
2425 if (!duration || qAbs(offset - targetOffset) < threshold || (qFuzzyIsNull(targetOffset) && qAbs(modelCount - offset) < threshold)) {
2426 tl.set(moveOffset, targetOffset);
2427 }
else if (moveDirection == QQuickPathView::Positive || (moveDirection == QQuickPathView::Shortest && targetOffset - offset > modelCount/2)) {
2428 qreal distance = modelCount - targetOffset + offset;
2429 if (targetOffset > moveOffset) {
2430 tl.move(moveOffset, 0, QEasingCurve(QEasingCurve::InQuad),
int(duration * offset / distance));
2431 tl.set(moveOffset, modelCount);
2432 tl.move(moveOffset, targetOffset, QEasingCurve(qFuzzyIsNull(offset) ? QEasingCurve::InOutQuad : QEasingCurve::OutQuad),
int(duration * (modelCount-targetOffset) / distance));
2434 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
2436 }
else if (moveDirection == QQuickPathView::Negative || targetOffset - offset <= -modelCount/2) {
2437 qreal distance = modelCount - offset + targetOffset;
2438 if (targetOffset < moveOffset) {
2439 tl.move(moveOffset, modelCount, QEasingCurve(qFuzzyIsNull(targetOffset) ? QEasingCurve::InOutQuad : QEasingCurve::InQuad),
int(duration * (modelCount-offset) / distance));
2440 tl.set(moveOffset, 0);
2441 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad),
int(duration * targetOffset / distance));
2443 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
2446 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
2450QQuickPathViewAttached *QQuickPathView::qmlAttachedProperties(QObject *obj)
2452 return new QQuickPathViewAttached(obj);
2457#include "moc_qquickpathview_p.cpp"
void setIsCurrentItem(bool c)
void setValue(const QByteArray &name, const QVariant &val)
QVariant value(const QByteArray &name) const
~QQuickPathViewAttached()
void releaseItem(QQuickItem *item)
qint64 computeCurrentTime(QInputEvent *event) const
void updateItem(QQuickItem *, qreal)
void snapToIndex(int index, MovementReason reason)
void handleMousePressEvent(QMouseEvent *event)
QQmlOpenMetaObjectType * attType
QQuickItem * highlightItem
void handleMouseReleaseEvent(QMouseEvent *)
void releaseCurrentItem()
QQmlComponent * highlightComponent
void setAdjustedOffset(qreal offset)
int highlightMoveDuration
void setHighlightPosition(qreal pos)
void handleMouseMoveEvent(QMouseEvent *event)
QPointF pointNear(const QPointF &point, qreal *nearPercent=0) const
void setOffset(qreal offset)
QQmlOpenMetaObjectType * attachedType()
qreal positionOfIndex(qreal index) const
QQuickItem * getItem(int modelIndex, qreal z=0, bool async=false)
void addVelocitySample(qreal v)
qreal calcVelocity() const
QQuickPathViewAttached * attached(QQuickItem *item)
bool isInBound(qreal position, qreal lower, qreal upper, bool emptyRangeCheck=true) const
static void fixOffsetCallback(void *)
MovementReason moveReason
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
#define QML_FLICK_SAMPLEBUFFER
#define QML_FLICK_DISCARDSAMPLES
#define QML_FLICK_VELOCITY_DECAY_TIME
static QT_BEGIN_NAMESPACE QQmlOpenMetaObjectType * qPathViewAttachedType
static int currentIndexRemainder(int currentIndex, int modelCount) noexcept