Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qquickpathview.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquickpathview_p.h"
6#include "qquickflickablebehavior_p.h" //Contains flicking behavior defines
7#include "qquicktext_p.h"
8
9#include <QtQuick/private/qquickstate_p.h>
10#include <private/qqmlglobal_p.h>
11#include <private/qqmlopenmetaobject_p.h>
12#include <private/qqmlchangeset_p.h>
13#include <qpa/qplatformintegration.h>
14
15#include <QtQml/qqmlinfo.h>
16
17#include <QtGui/private/qeventpoint_p.h>
18#include <QtGui/qevent.h>
19#include <QtGui/qguiapplication.h>
20#include <QtGui/private/qguiapplication_p.h>
21#include <QtGui/qstylehints.h>
22#include <QtCore/qmath.h>
23
24#include <cmath>
25
27
28Q_DECLARE_LOGGING_CATEGORY(lcItemViewDelegateLifecycle)
29#if !QT_CONFIG(quick_itemview)
30Q_LOGGING_CATEGORY(lcItemViewDelegateLifecycle, "qt.quick.itemview.lifecycle")
31#endif
32Q_LOGGING_CATEGORY(lcPathView, "qt.quick.pathview")
33
35
37: QObject(parent), m_percent(-1), m_view(nullptr), m_onPath(false), m_isCurrent(false)
38{
40 m_metaobject = new QQmlOpenMetaObject(this, qPathViewAttachedType);
41 m_metaobject->setCached(true);
42 } else {
43 m_metaobject = new QQmlOpenMetaObject(this);
44 }
45}
46
48{
49}
50
52{
53 return m_metaobject->value(name);
54}
56{
57 m_metaobject->setValue(name, val);
58}
59
61 : path(nullptr), currentIndex(0), currentItemOffset(0), startPc(0)
62 , offset(0), offsetAdj(0), mappedRange(1), mappedCache(0)
63 , stealMouse(false), ownModel(false), interactive(true), haveHighlightRange(true)
64 , autoHighlight(true), highlightUp(false), layoutScheduled(false)
65 , moving(false), flicking(false), dragging(false), inRequest(false), delegateValidated(false)
66 , inRefill(false)
67 , dragMargin(0), deceleration(100)
68 , maximumFlickVelocity(QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FlickMaximumVelocity).toReal())
69 , moveOffset(this, &QQuickPathViewPrivate::setAdjustedOffset), flickDuration(0)
70 , pathItems(-1), requestedIndex(-1), cacheSize(0), requestedZ(0)
71 , moveReason(Other), movementDirection(QQuickPathView::Shortest), moveDirection(QQuickPathView::Shortest)
72 , attType(nullptr), highlightComponent(nullptr), highlightItem(nullptr)
73 , moveHighlight(this, &QQuickPathViewPrivate::setHighlightPosition)
74 , highlightPosition(0)
75 , highlightRangeStart(0), highlightRangeEnd(0)
76 , highlightRangeMode(QQuickPathView::StrictlyEnforceRange)
77 , highlightMoveDuration(300), modelCount(0), snapMode(QQuickPathView::NoSnap)
78{
79}
80
82{
83 Q_Q(QQuickPathView);
84 offset = 0;
85 q->setAcceptedMouseButtons(Qt::LeftButton);
87 q->setFiltersChildMouseEvents(true);
89 q, QQuickPathView, SLOT(ticked()));
92 q, QQuickPathView, SLOT(movementEnding()));
93}
94
95QQuickItem *QQuickPathViewPrivate::getItem(int modelIndex, qreal z, bool async)
96{
97 Q_Q(QQuickPathView);
98 requestedIndex = modelIndex;
99 requestedZ = z;
100 inRequest = true;
103 if (!item) {
104 if (object) {
105 model->release(object);
106 if (!delegateValidated) {
107 delegateValidated = true;
108 QObject* delegate = q->delegate();
109 qmlWarning(delegate ? delegate : q) << QQuickPathView::tr("Delegate must be of Item type");
110 }
111 }
112 } else {
114 requestedIndex = -1;
116 itemPrivate->addItemChangeListener(
118 }
119 inRequest = false;
120 return item;
121}
122
123void QQuickPathView::createdItem(int index, QObject *object)
124{
125 Q_D(QQuickPathView);
127 if (d->requestedIndex != index) {
128 qPathViewAttachedType = d->attachedType();
129 QQuickPathViewAttached *att = static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item));
130 qPathViewAttachedType = nullptr;
131 if (att) {
132 att->m_view = this;
133 att->setOnPath(false);
134 }
135 item->setParentItem(this);
136 d->updateItem(item, 1);
137 } else {
138 d->requestedIndex = -1;
139 if (!d->inRequest)
140 refill();
141 }
142}
143
144void QQuickPathView::initItem(int index, QObject *object)
145{
146 Q_D(QQuickPathView);
148 if (item && d->requestedIndex == index) {
150 item->setParentItem(this);
151 qPathViewAttachedType = d->attachedType();
152 QQuickPathViewAttached *att = static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item));
153 qPathViewAttachedType = nullptr;
154 if (att) {
155 att->m_view = this;
156 qreal percent = d->positionOfIndex(index);
157 if (percent < 1 && d->path) {
158 const auto attributes = d->path->attributes();
159 for (const QString &attr : attributes)
160 att->setValue(attr.toUtf8(), d->path->attributeAt(attr, percent));
161 item->setZ(d->requestedZ);
162 }
163 att->setOnPath(percent < 1);
164 }
165 }
166}
167
169{
170 if (!item)
171 return;
172 qCDebug(lcItemViewDelegateLifecycle) << "release" << item;
174 itemPrivate->removeItemChangeListener(
176 if (!model)
177 return;
178 QQmlInstanceModel::ReleaseFlags flags = model->release(item);
179 if (!flags) {
180 // item was not destroyed, and we no longer reference it.
182 att->setOnPath(false);
183 } else if (flags & QQmlInstanceModel::Destroyed) {
184 // but we still reference it
185 item->setParentItem(nullptr);
186 }
187}
188
190{
191 return static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item, false));
192}
193
195{
196 if (!attType) {
197 // pre-create one metatype to share with all attached objects
198 attType = new QQmlOpenMetaObjectType(&QQuickPathViewAttached::staticMetaObject);
199 if (path) {
200 const auto attributes = path->attributes();
201 for (const QString &attr : attributes)
202 attType->createProperty(attr.toUtf8());
203 }
204 }
205
206 return attType;
207}
208
210{
211 if (currentItem) {
213 currentItem = nullptr;
214 }
215
216 for (QQuickItem *p : std::as_const(items))
217 releaseItem(p);
218
219 for (QQuickItem *p : std::as_const(itemCache))
220 releaseItem(p);
221
222 if (requestedIndex >= 0) {
223 if (model)
225 requestedIndex = -1;
226 }
227
228 items.clear();
230 tl.clear();
231}
232
234{
235 if (model && pathItems != -1 && pathItems < modelCount) {
237 mappedCache = qreal(cacheSize)/pathItems/2; // Half of cache at each end
238 } else {
239 mappedRange = 1;
240 mappedCache = 0;
241 }
242}
243
245{
246 qreal pos = -1;
247
248 if (model && index >= 0 && index < modelCount) {
249 qreal start = 0;
253 qreal globalPos = index + offset;
254 globalPos = std::fmod(globalPos, qreal(modelCount)) / modelCount;
255 if (pathItems != -1 && pathItems < modelCount) {
256 globalPos += start / mappedRange;
257 globalPos = std::fmod(globalPos, qreal(1));
258 pos = globalPos * mappedRange;
259 } else {
260 pos = std::fmod(globalPos + start, qreal(1));
261 }
262 }
263
264 return pos;
265}
266
267// returns true if position is between lower and upper, taking into
268// account the circular space.
270{
271 if (qFuzzyCompare(lower, upper))
272 return true;
273 if (lower > upper) {
274 if (position > upper && position > lower)
276 lower -= mappedRange;
277 }
278 return position >= lower && position < upper;
279}
280
282{
283 Q_Q(QQuickPathView);
284 if (!q->isComponentComplete())
285 return;
286
287 bool changed = false;
288 if (highlightItem) {
291 highlightItem = nullptr;
292 changed = true;
293 }
294
295 QQuickItem *item = nullptr;
296 if (highlightComponent) {
297 QQmlContext *creationContext = highlightComponent->creationContext();
298 QQmlContext *highlightContext = new QQmlContext(
299 creationContext ? creationContext : qmlContext(q));
300 QObject *nobj = highlightComponent->create(highlightContext);
301 if (nobj) {
302 QQml_setParent_noEvent(highlightContext, nobj);
304 if (!item)
305 delete nobj;
306 } else {
307 delete highlightContext;
308 }
309 } else {
310 item = new QQuickItem;
311 }
312 if (item) {
316 changed = true;
317 }
318 if (changed)
319 emit q->highlightItemChanged();
320}
321
323{
324 Q_Q(QQuickPathView);
325 if (!q->isComponentComplete() || !isValid())
326 return;
327 if (highlightItem) {
330 } else {
332
333 offsetAdj = 0;
336
337 const int duration = highlightMoveDuration;
338
340 highlightUp = false;
345 } else if (target - highlightPosition <= -modelCount/2) {
346 highlightUp = true;
349 tl.set(moveHighlight, 0);
351 } else {
354 }
355 }
356 }
357}
358
360{
362 qreal start = 0;
363 qreal end = 1;
367 }
368
370 // calc normalized position of highlight relative to offset
371 qreal relativeHighlight = std::fmod(pos + offset, range) / range;
372
373 if (!highlightUp && relativeHighlight > end / mappedRange) {
374 qreal diff = 1 - relativeHighlight;
375 setOffset(offset + diff * range);
376 } else if (highlightUp && relativeHighlight >= (end - start) / mappedRange) {
377 qreal diff = relativeHighlight - (end - start) / mappedRange;
378 setOffset(offset - diff * range - 0.00001);
379 }
380
382 qreal pathPos = positionOfIndex(pos);
383 updateItem(highlightItem, pathPos);
385 att->setOnPath(pathPos < 1);
386 }
387}
388
389void QQuickPathView::pathUpdated()
390{
391 Q_D(QQuickPathView);
392 for (QQuickItem *item : std::as_const(d->items)) {
393 if (QQuickPathViewAttached *att = d->attached(item))
394 att->m_percent = -1;
395 }
396 refill();
397}
398
400{
401 if (!path)
402 return;
404 if (qFuzzyCompare(att->m_percent, percent))
405 return;
406 att->m_percent = percent;
407 const auto attributes = path->attributes();
408 for (const QString &attr : attributes)
409 att->setValue(attr.toUtf8(), path->attributeAt(attr, percent));
410 att->setOnPath(percent < 1);
411 }
412 QQuickItemPrivate::get(item)->setCulled(percent >= 1);
413 QPointF pf = path->pointAtPercent(qMin(percent, qreal(1)));
414 item->setX(pf.x() - item->width()/2);
415 item->setY(pf.y() - item->height()/2);
416}
417
419{
420 Q_Q(QQuickPathView);
421 if (!q->isComponentComplete())
422 return;
423
424 clear();
425
426 if (!isValid())
427 return;
428
430 q->refill();
431}
432
434{
435 Q_Q(QQuickPathView);
436 if (dragging == d)
437 return;
438
439 dragging = d;
440 if (dragging)
441 emit q->dragStarted();
442 else
443 emit q->dragEnded();
444
445 emit q->draggingChanged();
446}
447
517{
518 Q_D(QQuickPathView);
519 d->init();
520}
521
523{
524 Q_D(QQuickPathView);
525 d->clear();
526 if (d->attType)
527 d->attType->release();
528 if (d->ownModel)
529 delete d->model;
530}
531
589{
590 Q_D(const QQuickPathView);
591 return d->modelVariant;
592}
593
595{
596 Q_D(QQuickPathView);
597 QVariant model = m;
598 if (model.userType() == qMetaTypeId<QJSValue>())
600
601 if (d->modelVariant == model)
602 return;
603
604 if (d->model) {
605 qmlobject_disconnect(d->model, QQmlInstanceModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)),
606 this, QQuickPathView, SLOT(modelUpdated(QQmlChangeSet,bool)));
607 qmlobject_disconnect(d->model, QQmlInstanceModel, SIGNAL(createdItem(int,QObject*)),
608 this, QQuickPathView, SLOT(createdItem(int,QObject*)));
609 qmlobject_disconnect(d->model, QQmlInstanceModel, SIGNAL(initItem(int,QObject*)),
610 this, QQuickPathView, SLOT(initItem(int,QObject*)));
611 d->clear();
612 }
613
614 d->modelVariant = model;
615 QObject *object = qvariant_cast<QObject*>(model);
616 QQmlInstanceModel *vim = nullptr;
617 if (object && (vim = qobject_cast<QQmlInstanceModel *>(object))) {
618 if (d->ownModel) {
619 delete d->model;
620 d->ownModel = false;
621 }
622 d->model = vim;
623 } else {
624 if (!d->ownModel) {
625 d->model = new QQmlDelegateModel(qmlContext(this));
626 d->ownModel = true;
628 static_cast<QQmlDelegateModel *>(d->model.data())->componentComplete();
629 }
630 if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel*>(d->model))
631 dataModel->setModel(model);
632 }
633 int oldModelCount = d->modelCount;
634 d->modelCount = 0;
635 if (d->model) {
636 qmlobject_connect(d->model, QQmlInstanceModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)),
637 this, QQuickPathView, SLOT(modelUpdated(QQmlChangeSet,bool)));
638 qmlobject_connect(d->model, QQmlInstanceModel, SIGNAL(createdItem(int,QObject*)),
639 this, QQuickPathView, SLOT(createdItem(int,QObject*)));
640 qmlobject_connect(d->model, QQmlInstanceModel, SIGNAL(initItem(int,QObject*)),
641 this, QQuickPathView, SLOT(initItem(int,QObject*)));
642 d->modelCount = d->model->count();
643 }
644 if (isComponentComplete()) {
645 if (d->currentIndex != 0) {
646 d->currentIndex = 0;
648 }
649 if (!(qFuzzyIsNull(d->offset))) {
650 d->offset = 0;
652 }
653 }
654 d->regenerate();
655 if (d->modelCount != oldModelCount)
658}
659
665{
666 Q_D(const QQuickPathView);
667 return d->model ? d->modelCount : 0;
668}
669
676{
677 Q_D(const QQuickPathView);
678 return d->path;
679}
680
682{
683 Q_D(QQuickPathView);
684 if (d->path == path)
685 return;
686 if (d->path)
687 qmlobject_disconnect(d->path, QQuickPath, SIGNAL(changed()),
688 this, QQuickPathView, SLOT(pathUpdated()));
689 d->path = path;
690
691 if (path) {
692 qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()),
693 this, QQuickPathView, SLOT(pathUpdated()));
694 }
695
696 if (isComponentComplete()) {
697 d->clear();
698 if (d->isValid()) {
699 if (d->attType) {
700 d->attType->release();
701 d->attType = nullptr;
702 }
703 d->regenerate();
704 }
705 }
706
708}
709
715{
716 Q_D(const QQuickPathView);
717 return d->currentIndex;
718}
719
721{
722 Q_D(QQuickPathView);
723 if (!isComponentComplete()) {
724 if (idx != d->currentIndex) {
725 d->currentIndex = idx;
727 }
728 return;
729 }
730
731 idx = d->modelCount
732 ? ((idx % d->modelCount) + d->modelCount) % d->modelCount
733 : 0;
734 if (d->model && (idx != d->currentIndex || !d->currentItem)) {
735 if (d->currentItem) {
736 if (QQuickPathViewAttached *att = d->attached(d->currentItem))
737 att->setIsCurrentItem(false);
738 d->releaseItem(d->currentItem);
739 }
740 int oldCurrentIdx = d->currentIndex;
741 QQuickItem *oldCurrentItem = d->currentItem;
742 d->currentItem = nullptr;
744 d->currentIndex = idx;
745 if (d->modelCount) {
746 d->createCurrentItem();
747 if (d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange)
748 d->snapToIndex(d->currentIndex, QQuickPathViewPrivate::SetIndex);
749 d->currentItemOffset = d->positionOfIndex(d->currentIndex);
750 d->updateHighlight();
751 }
752 if (oldCurrentIdx != d->currentIndex)
754 if (oldCurrentItem != d->currentItem)
756 }
757}
758
764{
765 Q_D(const QQuickPathView);
766 return d->currentItem;
767}
768
777{
778 Q_D(QQuickPathView);
779 d->moveDirection = QQuickPathView::Positive;
781}
782
791{
792 Q_D(QQuickPathView);
793 d->moveDirection = QQuickPathView::Negative;
795}
796
804{
805 Q_D(const QQuickPathView);
806 return d->offset;
807}
808
810{
811 Q_D(QQuickPathView);
812 d->moveReason = QQuickPathViewPrivate::Other;
813 d->setOffset(offset);
814 d->updateCurrent();
815}
816
818{
819 Q_Q(QQuickPathView);
820 if (!qFuzzyCompare(offset, o)) {
821 if (isValid() && q->isComponentComplete()) {
822 qreal oldOffset = offset;
823 offset = std::fmod(o, qreal(modelCount));
824 if (offset < 0)
826 qCDebug(lcItemViewDelegateLifecycle) << o << "was" << oldOffset << "now" << offset;
827 q->refill();
828 } else {
829 offset = o;
830 }
831 emit q->offsetChanged();
832 }
833}
834
836{
838}
839
864{
865 Q_D(const QQuickPathView);
866 return d->highlightComponent;
867}
868
870{
871 Q_D(QQuickPathView);
872 if (highlight != d->highlightComponent) {
873 d->highlightComponent = highlight;
874 d->createHighlight();
875 d->updateHighlight();
877 }
878}
879
889{
890 Q_D(const QQuickPathView);
891 return d->highlightItem;
892}
893
931{
932 Q_D(const QQuickPathView);
933 return d->highlightRangeStart;
934}
935
937{
938 Q_D(QQuickPathView);
939 if (qFuzzyCompare(d->highlightRangeStart, start) || start < 0 || start > 1)
940 return;
941 d->highlightRangeStart = start;
942 d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd;
943 refill();
945}
946
948{
949 Q_D(const QQuickPathView);
950 return d->highlightRangeEnd;
951}
952
954{
955 Q_D(QQuickPathView);
956 if (qFuzzyCompare(d->highlightRangeEnd, end) || end < 0 || end > 1)
957 return;
958 d->highlightRangeEnd = end;
959 d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd;
960 refill();
962}
963
965{
966 Q_D(const QQuickPathView);
967 return d->highlightRangeMode;
968}
969
971{
972 Q_D(QQuickPathView);
973 if (d->highlightRangeMode == mode)
974 return;
975 d->highlightRangeMode = mode;
976 d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd;
977 if (d->haveHighlightRange) {
978 d->regenerate();
979 int index = d->highlightRangeMode != NoHighlightRange ? d->currentIndex : d->calcCurrentIndex();
980 if (index >= 0)
981 d->snapToIndex(index, QQuickPathViewPrivate::Other);
982 }
984}
985
996{
997 Q_D(const QQuickPathView);
998 return d->highlightMoveDuration;
999}
1000
1002{
1003 Q_D(QQuickPathView);
1004 if (d->highlightMoveDuration == duration)
1005 return;
1006 d->highlightMoveDuration = duration;
1008}
1009
1019{
1020 Q_D(const QQuickPathView);
1021 return d->dragMargin;
1022}
1023
1025{
1026 Q_D(QQuickPathView);
1027 if (qFuzzyCompare(d->dragMargin, dragMargin))
1028 return;
1029 d->dragMargin = dragMargin;
1031}
1032
1040{
1041 Q_D(const QQuickPathView);
1042 return d->deceleration;
1043}
1044
1046{
1047 Q_D(QQuickPathView);
1048 if (qFuzzyCompare(d->deceleration, dec))
1049 return;
1050 d->deceleration = dec;
1052}
1053
1061{
1062 Q_D(const QQuickPathView);
1063 return d->maximumFlickVelocity;
1064}
1065
1067{
1068 Q_D(QQuickPathView);
1069 if (qFuzzyCompare(vel, d->maximumFlickVelocity))
1070 return;
1071 d->maximumFlickVelocity = vel;
1073}
1074
1075
1085{
1086 Q_D(const QQuickPathView);
1087 return d->interactive;
1088}
1089
1091{
1092 Q_D(QQuickPathView);
1093 if (interactive != d->interactive) {
1094 d->interactive = interactive;
1095 if (!interactive)
1096 d->tl.clear();
1098 }
1099}
1100
1108{
1109 Q_D(const QQuickPathView);
1110 return d->moving;
1111}
1112
1120{
1121 Q_D(const QQuickPathView);
1122 return d->flicking;
1123}
1124
1132{
1133 Q_D(const QQuickPathView);
1134 return d->dragging;
1135}
1136
1203{
1204 Q_D(const QQuickPathView);
1205 if (d->model) {
1206 if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel*>(d->model))
1207 return dataModel->delegate();
1208 }
1209
1210 return nullptr;
1211}
1212
1214{
1215 Q_D(QQuickPathView);
1216 if (delegate == this->delegate())
1217 return;
1218 if (!d->ownModel) {
1219 d->model = new QQmlDelegateModel(qmlContext(this));
1220 d->ownModel = true;
1221 if (isComponentComplete())
1222 static_cast<QQmlDelegateModel *>(d->model.data())->componentComplete();
1223 }
1224 if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel*>(d->model)) {
1225 int oldCount = dataModel->count();
1226 dataModel->setDelegate(delegate);
1227 d->modelCount = dataModel->count();
1228 d->regenerate();
1229 if (oldCount != dataModel->count())
1232 d->delegateValidated = false;
1233 }
1234}
1235
1243{
1244 Q_D(const QQuickPathView);
1245 return d->pathItems;
1246}
1247
1249{
1250 Q_D(QQuickPathView);
1251 if (i == d->pathItems)
1252 return;
1253 if (i < 1)
1254 i = 1;
1255 d->pathItems = i;
1256 d->updateMappedRange();
1257 if (d->isValid() && isComponentComplete()) {
1258 d->regenerate();
1259 }
1261}
1262
1264{
1265 Q_D(QQuickPathView);
1266 if (-1 == d->pathItems)
1267 return;
1268 d->pathItems = -1;
1269 d->updateMappedRange();
1270 if (d->isValid() && isComponentComplete())
1271 d->regenerate();
1273}
1274
1297{
1298 Q_D(const QQuickPathView);
1299 return d->cacheSize;
1300}
1301
1303{
1304 Q_D(QQuickPathView);
1305 if (i == d->cacheSize || i < 0)
1306 return;
1307
1308 d->cacheSize = i;
1309 d->updateMappedRange();
1310 refill();
1312}
1313
1333{
1334 Q_D(const QQuickPathView);
1335 return d->snapMode;
1336}
1337
1339{
1340 Q_D(QQuickPathView);
1341 if (mode == d->snapMode)
1342 return;
1343 d->snapMode = mode;
1345}
1346
1371{
1372 Q_D(const QQuickPathView);
1373 return d->movementDirection;
1374}
1375
1377{
1378 Q_D(QQuickPathView);
1379 if (dir == d->movementDirection)
1380 return;
1381 d->movementDirection = dir;
1382 if (!d->tl.isActive())
1383 d->moveDirection = d->movementDirection;
1384 emit movementDirectionChanged();
1385}
1386
1410{
1411 Q_D(QQuickPathView);
1412 if (!d->isValid())
1413 return;
1414 if (mode < QQuickPathView::Beginning || mode > QQuickPathView::SnapPosition || mode == 3) // 3 is unused in PathView
1415 return;
1416
1417 if (mode == QQuickPathView::Contain && (d->pathItems < 0 || d->modelCount <= d->pathItems))
1418 return;
1419
1420 int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount);
1421 int idx = (index+d->modelCount) % d->modelCount;
1422 bool snap = d->haveHighlightRange && (d->highlightRangeMode != QQuickPathView::NoHighlightRange
1423 || d->snapMode != QQuickPathView::NoSnap);
1424
1425 qreal beginOffset;
1426 qreal endOffset;
1427 if (snap) {
1428 beginOffset = d->modelCount - idx - qFloor(count * d->highlightRangeStart);
1429 endOffset = beginOffset + count - 1;
1430 } else {
1431 beginOffset = d->modelCount - idx;
1432 // Small offset since the last point coincides with the first and
1433 // this the only "end" position that gives the expected visual result.
1434 qreal adj = sizeof(qreal) == sizeof(float) ? 0.00001f : 0.000000000001;
1435 endOffset = std::fmod(beginOffset + count, qreal(d->modelCount)) - adj;
1436 }
1437 qreal offset = d->offset;
1438 switch (mode) {
1439 case Beginning:
1440 offset = beginOffset;
1441 break;
1442 case End:
1443 offset = endOffset;
1444 break;
1445 case Center:
1446 if (beginOffset < endOffset)
1447 offset = (beginOffset + endOffset)/2;
1448 else
1449 offset = (beginOffset + (endOffset + d->modelCount))/2;
1450 if (snap)
1451 offset = qRound(offset);
1452 break;
1453 case Contain:
1454 if ((beginOffset < endOffset && (d->offset < beginOffset || d->offset > endOffset))
1455 || (d->offset < beginOffset && d->offset > endOffset)) {
1456 qreal diff1 = std::fmod(beginOffset - d->offset + d->modelCount, qreal(d->modelCount));
1457 qreal diff2 = std::fmod(d->offset - endOffset + d->modelCount, qreal(d->modelCount));
1458 if (diff1 < diff2)
1459 offset = beginOffset;
1460 else
1461 offset = endOffset;
1462 }
1463 break;
1464 case SnapPosition:
1465 offset = d->modelCount - idx;
1466 break;
1467 }
1468
1469 d->tl.clear();
1471}
1472
1482{
1483 Q_D(const QQuickPathView);
1484 QQuickItem *item = itemAt(x, y);
1485 return item ? d->model->indexOf(item, nullptr) : -1;
1486}
1487
1497{
1498 Q_D(const QQuickPathView);
1499 if (!d->isValid())
1500 return nullptr;
1501
1502 for (QQuickItem *item : d->items) {
1503 QPointF p = item->mapFromItem(this, QPointF(x, y));
1504 if (item->contains(p))
1505 return item;
1506 }
1507
1508 return nullptr;
1509}
1510
1524QQuickItem *QQuickPathView::itemAtIndex(int index) const
1525{
1526 Q_D(const QQuickPathView);
1527 if (!d->isValid())
1528 return nullptr;
1529
1530 for (QQuickItem *item : d->items) {
1531 if (index == d->model->indexOf(item, nullptr))
1532 return item;
1533 }
1534
1535 return nullptr;
1536}
1537
1539{
1540 const auto pathLength = path->path().length();
1541 qreal samples = qMin(pathLength / 5, qreal(500));
1542 qreal res = pathLength / samples;
1543
1544 qreal mindist = 1e10; // big number
1545 QPointF nearPoint = path->pointAtPercent(0);
1546 qreal nearPc = 0;
1547
1548 // get rough pos
1549 for (qreal i=1; i < samples; i++) {
1550 QPointF pt = path->pointAtPercent(i/samples);
1551 QPointF diff = pt - point;
1552 qreal dist = diff.x()*diff.x() + diff.y()*diff.y();
1553 if (dist < mindist) {
1554 nearPoint = pt;
1555 nearPc = i;
1556 mindist = dist;
1557 }
1558 }
1559
1560 // now refine
1561 qreal approxPc = nearPc;
1562 for (qreal i = approxPc-1; i < approxPc+1; i += 1/(2*res)) {
1563 QPointF pt = path->pointAtPercent(i/samples);
1564 QPointF diff = pt - point;
1565 qreal dist = diff.x()*diff.x() + diff.y()*diff.y();
1566 if (dist < mindist) {
1567 nearPoint = pt;
1568 nearPc = i;
1569 mindist = dist;
1570 }
1571 }
1572
1573 if (nearPercent)
1574 *nearPercent = nearPc / samples;
1575
1576 return nearPoint;
1577}
1578
1580{
1584 qCDebug(lcPathView) << "instantaneous velocity" << v;
1585}
1586
1588{
1589 qreal velocity = 0;
1592 for (int i = 0; i < count; ++i) {
1594 velocity += v;
1595 }
1596 velocity /= count;
1597 qCDebug(lcPathView) << "average velocity" << velocity << "based on" << count << "samples";
1598 }
1599 return velocity;
1600}
1601
1603{
1604 if (0 != event->timestamp())
1605 return qint64(event->timestamp());
1606 return timer.elapsed();
1607}
1608
1610{
1611 Q_D(QQuickPathView);
1612 if (d->interactive) {
1613 d->handleMousePressEvent(event);
1614 event->accept();
1615 } else {
1617 }
1618}
1619
1621{
1622 Q_Q(QQuickPathView);
1623 if (!interactive || !items.size() || !model || !modelCount)
1624 return;
1626 int idx = 0;
1627 for (; idx < items.size(); ++idx) {
1628 QQuickItem *item = items.at(idx);
1629 if (item->contains(item->mapFromScene(event->scenePosition())))
1630 break;
1631 }
1632 if (idx == items.size() && qFuzzyIsNull(dragMargin)) // didn't click on an item
1633 return;
1634
1635 startPoint = pointNear(event->position(), &startPc);
1636 startPos = event->position();
1637 if (idx == items.size()) {
1638 qreal distance = qAbs(event->position().x() - startPoint.x()) + qAbs(event->position().y() - startPoint.y());
1639 if (distance > dragMargin)
1640 return;
1641 }
1642
1643 if (tl.isActive() && flicking && flickDuration && qreal(tl.time()) / flickDuration < 0.8) {
1644 stealMouse = true; // If we've been flicked then steal the click.
1645 q->grabMouse(); // grab it right now too, just to be sure (QTBUG-77173)
1646 } else {
1647 stealMouse = false;
1648 }
1649 q->setKeepMouseGrab(stealMouse);
1650
1651 timer.start();
1653 tl.clear();
1654}
1655
1657{
1658 Q_D(QQuickPathView);
1659 if (d->interactive) {
1660 d->handleMouseMoveEvent(event);
1661 event->accept();
1662 } else {
1664 }
1665}
1666
1668{
1669 Q_Q(QQuickPathView);
1670 if (!interactive || !timer.isValid() || !model || !modelCount)
1671 return;
1672
1673 qint64 currentTimestamp = computeCurrentTime(event);
1674 qreal newPc;
1675 QPointF pathPoint = pointNear(event->position(), &newPc);
1676 if (!stealMouse) {
1677 QPointF posDelta = event->position() - startPos;
1680 // The touch has exceeded the threshold. If the movement along the path is close to the drag threshold
1681 // then we'll assume that this gesture targets the PathView. This ensures PathView gesture grabbing
1682 // is in sync with other items.
1683 QPointF pathDelta = pathPoint - startPoint;
1684 const int startDragDistance = QGuiApplication::styleHints()->startDragDistance();
1685 if (qAbs(pathDelta.x()) > startDragDistance * 0.8
1686 || qAbs(pathDelta.y()) > startDragDistance * 0.8) {
1687 stealMouse = true;
1688 q->setKeepMouseGrab(true);
1689 }
1690 }
1691 } else {
1694 qreal diff = (newPc - startPc)*count;
1695 if (!qFuzzyIsNull(diff)) {
1696 q->setOffset(offset + diff);
1697
1698 if (diff > modelCount/2)
1699 diff -= modelCount;
1700 else if (diff < -modelCount/2)
1701 diff += modelCount;
1702
1703 qint64 elapsed = currentTimestamp - lastPosTime;
1704 if (elapsed > 0)
1705 addVelocitySample(diff / (qreal(elapsed) / 1000));
1706 }
1707 if (!moving) {
1708 moving = true;
1709 emit q->movingChanged();
1710 emit q->movementStarted();
1711 }
1712 setDragging(true);
1713 }
1714 startPc = newPc;
1715 lastPosTime = currentTimestamp;
1716}
1717
1719{
1720 Q_D(QQuickPathView);
1721 if (d->interactive) {
1722 d->handleMouseReleaseEvent(event);
1723 event->accept();
1724 ungrabMouse();
1725 } else {
1727 }
1728}
1729
1731{
1732 Q_Q(QQuickPathView);
1733 stealMouse = false;
1734 q->setKeepMouseGrab(false);
1735 setDragging(false);
1736 if (!interactive || !timer.isValid() || !model || !modelCount) {
1737 timer.invalidate();
1738 if (!tl.isActive())
1739 q->movementEnding();
1740 return;
1741 }
1742
1743 qreal velocity = calcVelocity();
1745 // Let the velocity linearly decay such that it becomes 0 if elapsed time > QML_FLICK_VELOCITY_DECAY_TIME
1746 // The intention is that if you are flicking at some speed, then stop in one place for some time before releasing,
1747 // the previous velocity is lost. (QTBUG-77173, QTBUG-59052)
1749 qCDebug(lcPathView) << "after elapsed time" << elapsed << "velocity decayed to" << velocity;
1751 const auto averageItemLength = path->path().length() / count;
1752 qreal pixelVelocity = averageItemLength * velocity;
1753 if (qAbs(pixelVelocity) > _q_MinimumFlickVelocity) {
1755 // limit velocity
1756 qreal maxVel = velocity < 0 ? -maximumFlickVelocity : maximumFlickVelocity;
1757 velocity = maxVel / averageItemLength;
1758 }
1759 // Calculate the distance to be travelled
1760 qreal v2 = velocity*velocity;
1761 qreal accel = deceleration/10;
1762 qreal dist = 0;
1766 // encourage snapping one item in direction of motion
1767 if (velocity > 0)
1768 dist = qRound(0.5 + offset) - offset;
1769 else
1770 dist = qRound(0.5 - offset) + offset;
1771 } else {
1772 // + 0.25 to encourage moving at least one item in the flick direction
1773 dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2) + 0.25));
1774
1775 // round to nearest item.
1776 if (velocity > 0)
1777 dist = qRound(dist + offset) - offset;
1778 else
1779 dist = qRound(dist - offset) + offset;
1780 }
1781 // Calculate accel required to stop on item boundary
1782 if (dist <= 0) {
1783 dist = 0;
1784 accel = 0;
1785 } else {
1786 accel = v2 / (2 * qAbs(dist));
1787 }
1788 } else {
1789 dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2)));
1790 }
1791 flickDuration = int(1000 * qAbs(velocity) / accel);
1792 offsetAdj = 0;
1794 tl.accel(moveOffset, velocity, accel, dist);
1796 if (!flicking) {
1797 flicking = true;
1798 emit q->flickingChanged();
1799 emit q->flickStarted();
1800 }
1801 } else {
1802 fixOffset();
1803 }
1804
1805 timer.invalidate();
1806 if (!tl.isActive())
1807 q->movementEnding();
1808}
1809
1811{
1812 Q_D(QQuickPathView);
1813 if (!isVisible() || !d->interactive || !e->isPointerEvent())
1815
1816 QPointerEvent *pe = static_cast<QPointerEvent *>(e);
1818 // The event is localized for the intended receiver (in the delegate, probably),
1819 // but we need to look at position relative to the PathView itself.
1820 const auto &point = pe->points().first();
1821 QPointF localPos = mapFromScene(point.scenePosition());
1823 if (grabber == this && d->stealMouse) {
1824 // we are already the grabber and we do want the mouse event to ourselves.
1825 return true;
1826 }
1827
1828 bool grabberDisabled = grabber && !grabber->isEnabled();
1829 bool stealThisEvent = d->stealMouse;
1830 if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab() || grabberDisabled)) {
1831 // Make a localized copy of the QMouseEvent.
1832 QMutableSinglePointEvent localizedEvent(*static_cast<QMouseEvent *>(pe));
1833 QMutableEventPoint::setPosition(localizedEvent.point(0), localPos);
1834 localizedEvent.setAccepted(false);
1835
1836 switch (localizedEvent.type()) {
1837 case QEvent::MouseMove:
1838 d->handleMouseMoveEvent(static_cast<QMouseEvent *>(static_cast<QSinglePointEvent *>(&localizedEvent)));
1839 break;
1841 d->handleMousePressEvent(static_cast<QMouseEvent *>(static_cast<QSinglePointEvent *>(&localizedEvent)));
1842 stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
1843 break;
1845 d->handleMouseReleaseEvent(static_cast<QMouseEvent *>(static_cast<QSinglePointEvent *>(&localizedEvent)));
1846 break;
1847 default:
1848 break;
1849 }
1850
1851 grabber = qmlobject_cast<QQuickItem *>(localizedEvent.exclusiveGrabber(localizedEvent.points().first()));
1852 if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || grabberDisabled)
1853 pe->setExclusiveGrabber(point, this);
1854
1855 const bool filtered = stealThisEvent || grabberDisabled;
1856 if (filtered)
1857 pe->setAccepted(false);
1858 return filtered;
1859 } else if (d->timer.isValid()) {
1860 d->timer.invalidate();
1861 d->fixOffset();
1862 }
1863 if (pe->type() == QEvent::MouseButtonRelease || (grabber && grabber->keepMouseGrab() && !grabberDisabled))
1864 d->stealMouse = false;
1865 return false;
1866 }
1867
1869}
1870
1872{
1873 Q_D(QQuickPathView);
1874 if (d->stealMouse ||
1875 (!d->flicking && d->snapMode != NoSnap && !qFuzzyCompare(qRound(d->offset), d->offset))) {
1876 // if our mouse grab has been removed (probably by a Flickable),
1877 // or if we should snap but haven't done it, fix our state
1878 d->stealMouse = false;
1879 setKeepMouseGrab(false);
1880 d->timer.invalidate();
1881 d->fixOffset();
1882 d->setDragging(false);
1883 if (!d->tl.isActive())
1884 movementEnding();
1885 }
1886}
1887
1889{
1891 refill();
1892}
1893
1894static inline int currentIndexRemainder(int currentIndex, int modelCount) noexcept
1895{
1896 if (currentIndex < 0)
1897 return modelCount + currentIndex % modelCount;
1898 else
1899 return currentIndex % modelCount;
1900}
1901
1903{
1904 Q_D(QQuickPathView);
1905 if (d->model && d->ownModel)
1906 static_cast<QQmlDelegateModel *>(d->model.data())->componentComplete();
1907
1909
1910 if (d->model) {
1911 d->modelCount = d->model->count();
1912 if (d->modelCount && d->currentIndex != 0) // an initial value has been provided for currentIndex
1913 d->offset = std::fmod(qreal(d->modelCount - currentIndexRemainder(d->currentIndex, d->modelCount)), qreal(d->modelCount));
1914 }
1915
1916 d->createHighlight();
1917 d->regenerate();
1918 d->updateHighlight();
1919 d->updateCurrent();
1920
1921 if (d->modelCount)
1923}
1924
1925void QQuickPathView::refill()
1926{
1927 Q_D(QQuickPathView);
1928
1929 if (d->inRefill) {
1930 d->scheduleLayout();
1931 return;
1932 }
1933
1934 d->layoutScheduled = false;
1935
1936 if (!d->isValid() || !isComponentComplete())
1937 return;
1938
1939 d->inRefill = true;
1940
1941 bool currentVisible = false;
1942 int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount);
1943
1944 // first move existing items and remove items off path
1945 qCDebug(lcItemViewDelegateLifecycle) << "currentIndex" << d->currentIndex << "offset" << d->offset;
1946 QList<QQuickItem*>::iterator it = d->items.begin();
1947 while (it != d->items.end()) {
1948 QQuickItem *item = *it;
1949 int idx = d->model->indexOf(item, nullptr);
1950 qreal pos = d->positionOfIndex(idx);
1951 if (lcItemViewDelegateLifecycle().isDebugEnabled()) {
1952 QQuickText *text = qmlobject_cast<QQuickText*>(item);
1953 if (text)
1954 qCDebug(lcItemViewDelegateLifecycle) << "idx" << idx << "@" << pos << ": QQuickText" << text->objectName() << QStringView{text->text()}.left(40);
1955 else
1956 qCDebug(lcItemViewDelegateLifecycle) << "idx" << idx << "@" << pos << ":" << item;
1957 }
1958 if (pos < 1) {
1959 d->updateItem(item, pos);
1960 if (idx == d->currentIndex) {
1961 currentVisible = true;
1962 d->currentItemOffset = pos;
1963 }
1964 ++it;
1965 } else {
1966 d->updateItem(item, pos);
1967 if (QQuickPathViewAttached *att = d->attached(item))
1968 att->setOnPath(pos < 1);
1969 if (!d->isInBound(pos, d->mappedRange - d->mappedCache, 1 + d->mappedCache)) {
1970 qCDebug(lcItemViewDelegateLifecycle) << "release" << idx << "@" << pos << ", !isInBound: lower" << (d->mappedRange - d->mappedCache) << "upper" << (1 + d->mappedCache);
1971 d->releaseItem(item);
1972 it = d->items.erase(it);
1973 } else {
1974 ++it;
1975 }
1976 }
1977 }
1978
1979 bool waiting = false;
1980 if (d->modelCount) {
1981 // add items as needed
1982 if (d->items.size() < count+d->cacheSize) {
1983 int endIdx = 0;
1984 qreal endPos;
1985 int startIdx = 0;
1986 qreal startPos = 0;
1987 const bool wasEmpty = d->items.isEmpty();
1988 if (!wasEmpty) {
1989 //Find the beginning and end, items may not be in sorted order
1990 endPos = -1;
1991 startPos = 2;
1992
1993 for (QQuickItem * item : std::as_const(d->items)) {
1994 int idx = d->model->indexOf(item, nullptr);
1995 qreal curPos = d->positionOfIndex(idx);
1996 if (curPos > endPos) {
1997 endPos = curPos;
1998 endIdx = idx;
1999 }
2000
2001 if (curPos < startPos) {
2002 startPos = curPos;
2003 startIdx = idx;
2004 }
2005 }
2006 } else {
2007 if (d->haveHighlightRange
2008 && (d->highlightRangeMode != QQuickPathView::NoHighlightRange
2009 || d->snapMode != QQuickPathView::NoSnap))
2010 startPos = d->highlightRangeStart;
2011 // With no items, then "end" is just off the top so we populate via append
2012 endIdx = (qRound(d->modelCount - d->offset) - 1) % d->modelCount;
2013 endPos = d->positionOfIndex(endIdx);
2014 }
2015 //Append
2016 int idx = endIdx + 1;
2017 if (idx >= d->modelCount)
2018 idx = 0;
2019 qreal nextPos = d->positionOfIndex(idx);
2020 while ((d->isInBound(nextPos, endPos, 1 + d->mappedCache) || !d->items.size())
2021 && d->items.size() < count+d->cacheSize) {
2022 qCDebug(lcItemViewDelegateLifecycle) << "append" << idx << "@" << nextPos << (d->currentIndex == idx ? "current" : "") << "items count was" << d->items.size();
2023 QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1);
2024 if (!item) {
2025 waiting = true;
2026 break;
2027 }
2028 if (d->items.contains(item)) {
2029 d->releaseItem(item);
2030 break; //Otherwise we'd "re-add" it, and get confused
2031 }
2032 if (d->currentIndex == idx) {
2033 currentVisible = true;
2034 d->currentItemOffset = nextPos;
2035 }
2036 d->items.append(item);
2037 d->updateItem(item, nextPos);
2038 endIdx = idx;
2039 endPos = nextPos;
2040 ++idx;
2041 if (idx >= d->modelCount)
2042 idx = 0;
2043 nextPos = d->positionOfIndex(idx);
2044 }
2045
2046 //Prepend
2047 idx = (wasEmpty ? d->calcCurrentIndex() : startIdx) - 1;
2048
2049 if (idx < 0)
2050 idx = d->modelCount - 1;
2051 nextPos = d->positionOfIndex(idx);
2052 while (!waiting && d->isInBound(nextPos, d->mappedRange - d->mappedCache, startPos)
2053 && d->items.size() < count+d->cacheSize) {
2054 qCDebug(lcItemViewDelegateLifecycle) << "prepend" << idx << "@" << nextPos << (d->currentIndex == idx ? "current" : "") << "items count was" << d->items.size();
2055 QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1);
2056 if (!item) {
2057 waiting = true;
2058 break;
2059 }
2060 if (d->items.contains(item)) {
2061 d->releaseItem(item);
2062 break; //Otherwise we'd "re-add" it, and get confused
2063 }
2064 if (d->currentIndex == idx) {
2065 currentVisible = true;
2066 d->currentItemOffset = nextPos;
2067 }
2068 d->items.prepend(item);
2069 d->updateItem(item, nextPos);
2070 startIdx = idx;
2071 startPos = nextPos;
2072 --idx;
2073 if (idx < 0)
2074 idx = d->modelCount - 1;
2075 nextPos = d->positionOfIndex(idx);
2076 }
2077
2078 // In rare cases, when jumping around with pathCount close to modelCount,
2079 // new items appear in the middle. This more generic addition iteration handles this
2080 // Since this is the rare case, we try append/prepend first and only do this if
2081 // there are gaps still left to fill.
2082 if (!waiting && d->items.size() < count+d->cacheSize) {
2083 qCDebug(lcItemViewDelegateLifecycle) << "Checking for pathview middle inserts, items count was" << d->items.size();
2084 idx = startIdx;
2085 QQuickItem *lastItem = d->items.at(0);
2086 while (idx != endIdx) {
2087 nextPos = d->positionOfIndex(idx);
2088 if (d->isInBound(nextPos, d->mappedRange - d->mappedCache, 1 + d->mappedCache)) {
2089 //This gets the reference from the delegate model, and will not re-create
2090 QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1);
2091 if (!item) {
2092 waiting = true;
2093 break;
2094 }
2095
2096 if (!d->items.contains(item)) { //We found a hole
2097 qCDebug(lcItemViewDelegateLifecycle) << "middle insert" << idx << "@" << nextPos
2098 << (d->currentIndex == idx ? "current" : "")
2099 << "items count was" << d->items.size();
2100 if (d->currentIndex == idx) {
2101 currentVisible = true;
2102 d->currentItemOffset = nextPos;
2103 }
2104 int lastListIdx = d->items.indexOf(lastItem);
2105 d->items.insert(lastListIdx + 1, item);
2106 d->updateItem(item, nextPos);
2107 } else {
2108 d->releaseItem(item);
2109 }
2110
2111 lastItem = item;
2112 }
2113
2114 ++idx;
2115 if (idx >= d->modelCount)
2116 idx = 0;
2117 }
2118 }
2119 }
2120 }
2121
2122 bool currentChanged = false;
2123 if (!currentVisible) {
2124 d->currentItemOffset = 1;
2125 if (d->currentItem) {
2126 d->updateItem(d->currentItem, 1);
2127 } else if (!waiting && d->currentIndex >= 0 && d->currentIndex < d->modelCount) {
2128 if ((d->currentItem = d->getItem(d->currentIndex, d->currentIndex))) {
2129 currentChanged = true;
2130 d->updateItem(d->currentItem, 1);
2131 if (QQuickPathViewAttached *att = d->attached(d->currentItem))
2132 att->setIsCurrentItem(true);
2133 }
2134 }
2135 } else if (!waiting && !d->currentItem) {
2136 if ((d->currentItem = d->getItem(d->currentIndex, d->currentIndex))) {
2137 currentChanged = true;
2138 d->currentItem->setFocus(true);
2139 if (QQuickPathViewAttached *att = d->attached(d->currentItem))
2140 att->setIsCurrentItem(true);
2141 }
2142 }
2143
2144 if (d->highlightItem && d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
2145 d->updateItem(d->highlightItem, d->highlightRangeStart);
2146 if (QQuickPathViewAttached *att = d->attached(d->highlightItem))
2147 att->setOnPath(true);
2148 } else if (d->highlightItem && d->moveReason != QQuickPathViewPrivate::SetIndex) {
2149 d->updateItem(d->highlightItem, d->currentItemOffset);
2150 if (QQuickPathViewAttached *att = d->attached(d->highlightItem))
2151 att->setOnPath(currentVisible);
2152 }
2153 for (QQuickItem *item : std::as_const(d->itemCache))
2154 d->releaseItem(item);
2155 d->itemCache.clear();
2156
2157 d->inRefill = false;
2158 if (currentChanged)
2160}
2161
2162void QQuickPathView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
2163{
2164 Q_D(QQuickPathView);
2165 if (!d->model || !d->model->isValid() || !d->path || !isComponentComplete())
2166 return;
2167
2168 if (reset) {
2169 d->modelCount = d->model->count();
2170 d->regenerate();
2172 return;
2173 }
2174
2175 if (changeSet.removes().isEmpty() && changeSet.inserts().isEmpty())
2176 return;
2177
2178 const int modelCount = d->modelCount;
2179 int moveId = -1;
2180 int moveOffset = 0;
2181 bool currentChanged = false;
2182 bool changedOffset = false;
2183 for (const QQmlChangeSet::Change &r : changeSet.removes()) {
2184 if (moveId == -1 && d->currentIndex >= r.index + r.count) {
2185 d->currentIndex -= r.count;
2186 currentChanged = true;
2187 } else if (moveId == -1 && d->currentIndex >= r.index && d->currentIndex < r.index + r.count) {
2188 // current item has been removed.
2189 if (r.isMove()) {
2190 moveId = r.moveId;
2191 moveOffset = d->currentIndex - r.index;
2192 } else if (d->currentItem) {
2193 if (QQuickPathViewAttached *att = d->attached(d->currentItem))
2194 att->setIsCurrentItem(true);
2195 d->releaseItem(d->currentItem);
2196 d->currentItem = nullptr;
2197 }
2198 d->currentIndex = qMin(r.index, d->modelCount - r.count - 1);
2199 currentChanged = true;
2200 }
2201
2202 if (r.index > d->currentIndex) {
2203 changedOffset = true;
2204 d->offset -= r.count;
2205 d->offsetAdj -= r.count;
2206 }
2207 d->modelCount -= r.count;
2208 }
2209 for (const QQmlChangeSet::Change &i : changeSet.inserts()) {
2210 if (d->modelCount) {
2211 if (moveId == -1 && i.index <= d->currentIndex) {
2212 d->currentIndex += i.count;
2213 currentChanged = true;
2214 } else {
2215 if (moveId != -1 && moveId == i.moveId) {
2216 d->currentIndex = i.index + moveOffset;
2217 currentChanged = true;
2218 }
2219 if (i.index > d->currentIndex) {
2220 d->offset += i.count;
2221 d->offsetAdj += i.count;
2222 changedOffset = true;
2223 }
2224 }
2225 }
2226 d->modelCount += i.count;
2227 }
2228
2229 d->offset = std::fmod(d->offset, qreal(d->modelCount));
2230 if (d->offset < 0)
2231 d->offset += d->modelCount;
2232 if (d->currentIndex == -1)
2233 d->currentIndex = d->calcCurrentIndex();
2234
2235 d->itemCache += d->items;
2236 d->items.clear();
2237
2238 if (!d->modelCount) {
2239 for (QQuickItem * item : std::as_const(d->itemCache))
2240 d->releaseItem(item);
2241 d->itemCache.clear();
2242 d->offset = 0;
2243 changedOffset = true;
2244 d->tl.reset(d->moveOffset);
2245 } else {
2246 if (!d->flicking && !d->moving && d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
2247 d->offset = std::fmod(qreal(d->modelCount - d->currentIndex), qreal(d->modelCount));
2248 changedOffset = true;
2249 }
2250 d->updateMappedRange();
2251 d->scheduleLayout();
2252 }
2253 if (changedOffset)
2255 if (currentChanged)
2257 if (d->modelCount != modelCount)
2259}
2260
2261void QQuickPathView::destroyingItem(QObject *item)
2262{
2263 Q_UNUSED(item);
2264}
2265
2266void QQuickPathView::ticked()
2267{
2268 Q_D(QQuickPathView);
2269 d->updateCurrent();
2270}
2271
2272void QQuickPathView::movementEnding()
2273{
2274 Q_D(QQuickPathView);
2275 if (d->flicking) {
2276 d->flicking = false;
2278 emit flickEnded();
2279 }
2280 if (d->moving && !d->stealMouse) {
2281 d->moving = false;
2284 }
2285 d->moveDirection = d->movementDirection;
2286}
2287
2288// find the item closest to the snap position
2290{
2291 int current = 0;
2292 if (modelCount && model && items.size()) {
2293 offset = std::fmod(offset, qreal(modelCount));
2294 if (offset < 0)
2295 offset += modelCount;
2296 current = qRound(qAbs(std::fmod(modelCount - offset, qreal(modelCount))));
2297 current = current % modelCount;
2298 }
2299
2300 return current;
2301}
2302
2304{
2305 if (requestedIndex != -1)
2306 return;
2307
2308 bool inItems = false;
2309 for (QQuickItem *item : std::as_const(items)) {
2310 if (model->indexOf(item, nullptr) == currentIndex) {
2311 inItems = true;
2312 break;
2313 }
2314 }
2315
2316 if (inItems) {
2318 currentItem->setFocus(true);
2320 att->setIsCurrentItem(true);
2321 }
2322 } else if (currentIndex >= 0 && currentIndex < modelCount) {
2326 att->setIsCurrentItem(true);
2327 }
2328 }
2329}
2330
2332{
2333 Q_Q(QQuickPathView);
2334 if (moveReason == SetIndex)
2335 return;
2337 return;
2338
2339 int idx = calcCurrentIndex();
2340 if (model && (idx != currentIndex || !currentItem)) {
2341 if (currentItem) {
2343 att->setIsCurrentItem(false);
2345 }
2346 int oldCurrentIndex = currentIndex;
2347 currentIndex = idx;
2348 currentItem = nullptr;
2350 if (oldCurrentIndex != currentIndex)
2351 emit q->currentIndexChanged();
2352 emit q->currentItemChanged();
2353 }
2354}
2355
2357{
2358 static_cast<QQuickPathViewPrivate *>(d)->fixOffset();
2359}
2360
2362{
2363 Q_Q(QQuickPathView);
2364 if (model && items.size()) {
2367 int curr = calcCurrentIndex();
2369 q->setCurrentIndex(curr);
2370 else
2371 snapToIndex(curr, Other);
2372 }
2373 }
2374}
2375
2377{
2378 if (!model || modelCount <= 0)
2379 return;
2380
2381 qreal targetOffset = std::fmod(qreal(modelCount - index), qreal(modelCount));
2382 moveReason = reason;
2383 offsetAdj = 0;
2386
2387 const int duration = highlightMoveDuration;
2388
2390 const qreal averageItemLength = path->path().length() / count;
2391 const qreal threshold = 0.5 / averageItemLength; // if we are within .5 px, we want to immediately assign rather than animate
2392
2393 if (!duration || qAbs(offset - targetOffset) < threshold || (qFuzzyIsNull(targetOffset) && qAbs(modelCount - offset) < threshold)) {
2394 tl.set(moveOffset, targetOffset);
2396 qreal distance = modelCount - targetOffset + offset;
2397 if (targetOffset > moveOffset) {
2400 tl.move(moveOffset, targetOffset, QEasingCurve(qFuzzyIsNull(offset) ? QEasingCurve::InOutQuad : QEasingCurve::OutQuad), int(duration * (modelCount-targetOffset) / distance));
2401 } else {
2402 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
2403 }
2404 } else if (moveDirection == QQuickPathView::Negative || targetOffset - offset <= -modelCount/2) {
2405 qreal distance = modelCount - offset + targetOffset;
2406 if (targetOffset < moveOffset) {
2408 tl.set(moveOffset, 0);
2409 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * targetOffset / distance));
2410 } else {
2411 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
2412 }
2413 } else {
2414 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
2415 }
2416}
2417
2419{
2420 return new QQuickPathViewAttached(obj);
2421}
2422
2424
2425#include "moc_qquickpathview_p.cpp"
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore
void invalidate() noexcept
Marks this QElapsedTimer object as invalid.
qint64 elapsed() const noexcept
Returns the number of milliseconds since this QElapsedTimer was last started.
void start() noexcept
Starts this timer.
bool isValid() const noexcept
Returns false if the timer has never been started or invalidated by a call to invalidate().
\inmodule QtCore
Definition qcoreevent.h:45
@ MouseMove
Definition qcoreevent.h:63
@ MouseButtonPress
Definition qcoreevent.h:60
@ MouseButtonRelease
Definition qcoreevent.h:61
Type type() const
Returns the event type.
Definition qcoreevent.h:299
virtual bool contains(const QPointF &point) const
Returns true if this item contains point, which is in local coordinates; otherwise,...
void setX(qreal x)
QPointF mapFromItem(const QGraphicsItem *item, const QPointF &point) const
Maps the point point, which is in item's coordinate system, to this item's coordinate system,...
void setParentItem(QGraphicsItem *parent)
Sets this item's parent item to newParent.
void setY(qreal y)
QPointF mapFromScene(const QPointF &point) const
Maps the point point, which is in this item's scene's coordinate system, to this item's coordinate sy...
static QStyleHints * styleHints()
Returns the application's style hints.
\inmodule QtGui
Definition qevent.h:49
The QJSValue class acts as a container for Qt/JavaScript data types.
Definition qjsvalue.h:31
qsizetype size() const noexcept
Definition qlist.h:386
T & first()
Definition qlist.h:628
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
void clear()
Definition qlist.h:417
\inmodule QtGui
Definition qevent.h:195
\inmodule QtCore
Definition qobject.h:90
void deleteLater()
\threadsafe
Definition qobject.cpp:2352
void clear()
void remove(int idx, int count=1)
void append(const T &v)
const T & at(int idx) const
int count() const
The QPlatformIntegration class is the entry for WindowSystem specific functionality.
\inmodule QtCore\reentrant
Definition qpoint.h:214
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:333
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:338
A base class for pointer events.
Definition qevent.h:73
void setExclusiveGrabber(const QEventPoint &point, QObject *exclusiveGrabber)
Informs the delivery logic that the given exclusiveGrabber is to receive all future update events and...
Definition qevent.cpp:366
QEventPoint & point(qsizetype i)
Returns a QEventPoint reference for the point at index i.
Definition qevent.cpp:237
QObject * exclusiveGrabber(const QEventPoint &point) const
Returns the object which has been set to receive all future update events and the release event conta...
Definition qevent.cpp:348
virtual void setAccepted(bool accepted) override
\reimp
Definition qevent.cpp:315
const QList< QEventPoint > & points() const
Returns a list of points in this pointer event.
Definition qevent.h:86
The QQmlChangeSet class stores an ordered list of notifications about changes to a linear data set.
const QVector< Change > & removes() const
const QVector< Change > & inserts() const
The QQmlComponent class encapsulates a QML component definition.
QQmlContext * creationContext() const
Returns the QQmlContext the component was created in.
virtual QObject * create(QQmlContext *context=nullptr)
Create an object instance from this component, within the specified context.
The QQmlContext class defines a context within a QML engine.
Definition qqmlcontext.h:25
virtual void cancel(int)
virtual int indexOf(QObject *object, QObject *objectContext) const =0
virtual ReleaseFlags release(QObject *object, ReusableFlag reusableFlag=NotReusable)=0
int createProperty(const QByteArray &name)
bool setValue(const QByteArray &, const QVariant &, bool force=false)
QVariant value(const QByteArray &) const
static bool isMouseEvent(const QPointerEvent *ev)
static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold=-1)
void removeItemChangeListener(QQuickItemChangeListener *, ChangeTypes types)
qreal z() const
void addItemChangeListener(QQuickItemChangeListener *listener, ChangeTypes types)
static QQuickItemPrivate * get(QQuickItem *item)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:64
virtual void mouseReleaseEvent(QMouseEvent *event)
This event handler can be reimplemented in a subclass to receive mouse release events for an item.
void setFocus(bool)
void setParentItem(QQuickItem *parent)
void componentComplete() override
\reimp Derived classes should call the base class method before adding their own actions to perform a...
QPointF mapFromScene(const QPointF &point) const
Maps the given point in the scene's coordinate system to the equivalent point within this item's coor...
bool isVisible() const
virtual Q_INVOKABLE bool contains(const QPointF &point) const
\qmlmethod bool QtQuick::Item::contains(point point)
void ungrabMouse()
virtual void mousePressEvent(QMouseEvent *event)
This event handler can be reimplemented in a subclass to receive mouse press events for an item.
bool isComponentComplete() const
Returns true if construction of the QML component is complete; otherwise returns false.
bool keepMouseGrab() const
Returns whether mouse input should exclusively remain with this item.
bool isEnabled() const
void setKeepMouseGrab(bool)
Sets whether the mouse input should remain exclusively with this item.
virtual bool childMouseEventFilter(QQuickItem *, QEvent *)
Reimplement this method to filter the pointer events that are received by this item's children.
virtual void updatePolish()
This function should perform any layout as required for this item.
virtual void mouseMoveEvent(QMouseEvent *event)
This event handler can be reimplemented in a subclass to receive mouse move events for an item.
void setValue(const QByteArray &name, const QVariant &val)
QVariant value(const QByteArray &name) const
QPointer< QQmlInstanceModel > model
void releaseItem(QQuickItem *item)
qint64 computeCurrentTime(QInputEvent *event) const
void updateItem(QQuickItem *, qreal)
void snapToIndex(int index, MovementReason reason)
QPODVector< qreal, 10 > velocityBuffer
void handleMousePressEvent(QMouseEvent *event)
QQmlOpenMetaObjectType * attType
void handleMouseReleaseEvent(QMouseEvent *)
QPointer< QQuickItem > currentItem
QQmlComponent * highlightComponent
void setAdjustedOffset(qreal offset)
bool isInBound(qreal position, qreal lower, qreal upper) const
QQuickTimeLineValueProxy< QQuickPathViewPrivate > moveHighlight
void setHighlightPosition(qreal pos)
QQuickTimeLineValueProxy< QQuickPathViewPrivate > moveOffset
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)
QList< QQuickItem * > itemCache
QQuickPathView::SnapMode snapMode
QQuickPathView::HighlightRangeMode highlightRangeMode
void addVelocitySample(qreal v)
QQuickPathView::MovementDirection moveDirection
QQuickPathViewAttached * attached(QQuickItem *item)
QList< QQuickItem * > items
static void fixOffsetCallback(void *)
QQuickPath * path
void dragMarginChanged()
void setPreferredHighlightBegin(qreal)
void setInteractive(bool)
void countChanged()
MovementDirection movementDirection
void decrementCurrentIndex()
\qmlmethod QtQuick::PathView::decrementCurrentIndex()
void delegateChanged()
void pathChanged()
virtual ~QQuickPathView()
void setCacheItemCount(int)
void interactiveChanged()
void setModel(const QVariant &)
void updatePolish() override
This function should perform any layout as required for this item.
void setHighlightRangeMode(HighlightRangeMode mode)
QQuickPathView(QQuickItem *parent=nullptr)
void cacheItemCountChanged()
void setFlickDeceleration(qreal dec)
void setSnapMode(SnapMode mode)
QQuickItem * highlightItem
void highlightMoveDurationChanged()
void setCurrentIndex(int idx)
bool isMoving() const
\qmlproperty bool QtQuick::PathView::moving
void pathItemCountChanged()
void flickDecelerationChanged()
void setOffset(qreal offset)
void currentItemChanged()
friend class QQuickPathViewAttached
bool childMouseEventFilter(QQuickItem *, QEvent *) override
Reimplement this method to filter the pointer events that are received by this item's children.
QQmlComponent * highlight
bool isInteractive() const
\qmlproperty bool QtQuick::PathView::interactive
void incrementCurrentIndex()
\qmlmethod QtQuick::PathView::incrementCurrentIndex()
void movementEnded()
void preferredHighlightEndChanged()
void currentIndexChanged()
void setMovementDirection(MovementDirection dir)
void setMaximumFlickVelocity(qreal)
void modelChanged()
bool isFlicking() const
\qmlproperty bool QtQuick::PathView::flicking
void setHighlight(QQmlComponent *highlight)
void snapModeChanged()
Q_INVOKABLE int indexAt(qreal x, qreal y) const
\qmlmethod int QtQuick::PathView::indexAt(real x, real y)
void flickingChanged()
void setDelegate(QQmlComponent *)
FINALint cacheItemCount
void maximumFlickVelocityChanged()
void setHighlightMoveDuration(int)
void movingChanged()
void highlightChanged()
void mouseMoveEvent(QMouseEvent *event) override
This event handler can be reimplemented in a subclass to receive mouse move events for an item.
void setPath(QQuickPath *)
void mousePressEvent(QMouseEvent *event) override
This event handler can be reimplemented in a subclass to receive mouse press events for an item.
Q_INVOKABLE QQuickItem * itemAt(qreal x, qreal y) const
\qmlmethod Item QtQuick::PathView::itemAt(real x, real y)
qreal preferredHighlightBegin
void preferredHighlightBeginChanged()
void setDragMargin(qreal margin)
void offsetChanged()
bool isDragging() const
\qmlproperty bool QtQuick::PathView::dragging
void mouseReleaseEvent(QMouseEvent *) override
This event handler can be reimplemented in a subclass to receive mouse release events for an item.
void componentComplete() override
\reimp Derived classes should call the base class method before adding their own actions to perform a...
QQuickItem * currentItem
void setPathItemCount(int)
void mouseUngrabEvent() override
This event handler can be reimplemented in a subclass to be notified when a mouse ungrab event has oc...
HighlightRangeMode highlightRangeMode
Q_INVOKABLE void positionViewAtIndex(int index, int mode)
\qmlmethod QtQuick::PathView::positionViewAtIndex(int index, PositionMode mode)
static QQuickPathViewAttached * qmlAttachedProperties(QObject *)
void highlightRangeModeChanged()
void setPreferredHighlightEnd(qreal)
QQmlComponent * delegate
void setValue(qreal v) override
Set the current value.
The QQuickTimeLine class provides a timeline for controlling animations.
void reset(QQuickTimeLineValue &)
Cancel (but don't complete) all scheduled actions for timeLineValue.
int accel(QQuickTimeLineValue &, qreal velocity, qreal accel)
Decelerate timeLineValue from the starting velocity to zero at the given acceleration rate.
void callback(const QQuickTimeLineCallback &)
Execute the event.
bool isActive() const
Returns true if the timeline is active.
void clear()
Resets the timeline.
void set(QQuickTimeLineValue &, qreal)
Set the value of timeLineValue.
void move(QQuickTimeLineValue &, qreal destination, int time=500)
Linearly change the timeLineValue from its current value to the given destination value over time mil...
A base class for pointer events containing a single point, such as mouse events.
Definition qevent.h:108
\inmodule QtCore
Definition qstringview.h:76
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QString left(qsizetype n) const
Returns a substring that contains the n leftmost characters of the string.
Definition qstring.cpp:5161
int startDragDistance
the distance, in pixels, that the mouse must be moved with a button held down before a drag and drop ...
Definition qstylehints.h:39
\inmodule QtCore
Definition qvariant.h:64
T value() const &
Definition qvariant.h:511
int userType() const
Definition qvariant.h:336
#define this
Definition dialogs.cpp:9
QString text
double e
QSet< QString >::iterator it
Combined button and popup list for selecting options.
int toUtf8(char16_t u, OutputPtr &dst, InputPtr &src, InputPtr end)
@ LeftButton
Definition qnamespace.h:57
@ XAxis
@ YAxis
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:303
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:281
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
int qFloor(T v)
Definition qmath.h:42
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
#define SLOT(a)
Definition qobjectdefs.h:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
GLint GLfloat GLfloat GLfloat v2
GLsizei const GLfloat * v
[13]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLint GLint GLint GLint GLint x
[0]
GLsizei samples
GLenum mode
const GLfloat * m
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLenum GLenum GLsizei count
GLsizei range
GLsizei GLsizei GLfloat distance
GLenum target
GLbitfield flags
GLuint start
GLenum GLuint GLintptr offset
GLuint name
GLint y
struct _cl_event * event
GLhandleARB obj
[2]
GLboolean reset
GLuint res
GLuint GLfloat * val
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLsizei const GLchar *const * path
GLfloat GLfloat p
[1]
static quint64 cacheSize()
QQmlContext * qmlContext(const QObject *obj)
Definition qqml.cpp:71
#define qmlobject_disconnect(Sender, SenderType, Signal, Receiver, ReceiverType, Method)
Disconnect Signal of Sender from Method of Receiver.
#define qmlobject_connect(Sender, SenderType, Signal, Receiver, ReceiverType, Method)
Connect Signal of Sender to Method of Receiver.
void QQml_setParent_noEvent(QObject *object, QObject *parent)
Makes the object a child of parent.
QQuickItem * qmlobject_cast< QQuickItem * >(QObject *object)
Q_QML_EXPORT QQmlInfo qmlWarning(const QObject *me)
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define QML_FLICK_SAMPLEBUFFER
const qreal _q_MinimumFlickVelocity
#define QML_FLICK_DISCARDSAMPLES
#define QML_FLICK_VELOCITY_DECAY_TIME
QQuickItem * qobject_cast< QQuickItem * >(QObject *o)
Definition qquickitem.h:483
static QT_BEGIN_NAMESPACE QQmlOpenMetaObjectType * qPathViewAttachedType
static int currentIndexRemainder(int currentIndex, int modelCount) noexcept
static double elapsed(qint64 after, qint64 before)
#define emit
#define Q_UNUSED(x)
long long qint64
Definition qtypes.h:55
double qreal
Definition qtypes.h:92
static QVariant toVariant(const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols, V4ObjectSet *visitedObjects)
std::uniform_real_distribution dist(1, 2.5)
[2]
settings setValue("DataPump/bgcolor", color)
QObject::connect nullptr
QString dir
[11]
QGraphicsItem * item
QList< QTreeWidgetItem * > items
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent