Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qquickflickable.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 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 "qquickflickable_p.h"
7#include "qquickwindow.h"
8#include "qquickwindow_p.h"
9#include "qquickmousearea_p.h"
10#if QT_CONFIG(quick_draganddrop)
11#include "qquickdrag_p.h"
12#endif
13
14#include <QtQuick/private/qquickpointerhandler_p.h>
15#include <QtQuick/private/qquicktransition_p.h>
16#include <private/qqmlglobal_p.h>
17
18#include <QtQml/qqmlinfo.h>
19#include <QtGui/qevent.h>
20#include <QtGui/qguiapplication.h>
21#include <QtGui/private/qguiapplication_p.h>
22#include <QtGui/private/qeventpoint_p.h>
23#include <QtGui/qstylehints.h>
24#include <QtCore/qmath.h>
25#include <qpa/qplatformtheme.h>
26
27#include <math.h>
28#include <cmath>
29
31
32Q_DECLARE_LOGGING_CATEGORY(lcHandlerParent)
33Q_LOGGING_CATEGORY(lcFlickable, "qt.quick.flickable")
34Q_LOGGING_CATEGORY(lcFilter, "qt.quick.flickable.filter")
35Q_LOGGING_CATEGORY(lcReplay, "qt.quick.flickable.replay")
36Q_LOGGING_CATEGORY(lcWheel, "qt.quick.flickable.wheel")
37Q_LOGGING_CATEGORY(lcVel, "qt.quick.flickable.velocity")
38
39// RetainGrabVelocity is the maxmimum instantaneous velocity that
40// will ensure the Flickable retains the grab on consecutive flicks.
41static const int RetainGrabVelocity = 100;
42
44 return qAtan(t);
45}
46
48 : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.)
49 , m_yPosition(0.), m_heightRatio(0.)
50{
51}
52
54{
55 return m_widthRatio;
56}
57
59{
60 return m_xPosition;
61}
62
64{
65 return m_heightRatio;
66}
67
69{
70 return m_yPosition;
71}
72
74{
76
77 bool changeX = false;
78 bool changeY = false;
79 bool changeWidth = false;
80 bool changeHeight = false;
81
82 // Vertical
83 const qreal viewheight = flickable->height();
84 const qreal maxyextent = -flickable->maxYExtent() + flickable->minYExtent();
85 const qreal maxYBounds = maxyextent + viewheight;
86 qreal pagePos = 0;
87 qreal pageSize = 0;
88 if (!qFuzzyIsNull(maxYBounds)) {
89 qreal y = p->pixelAligned ? std::round(p->vData.move.value()) : p->vData.move.value();
90 pagePos = (-y + flickable->minYExtent()) / maxYBounds;
91 pageSize = viewheight / maxYBounds;
92 }
93
94 if (pageSize != m_heightRatio) {
95 m_heightRatio = pageSize;
96 changeHeight = true;
97 }
98 if (pagePos != m_yPosition) {
99 m_yPosition = pagePos;
100 changeY = true;
101 }
102
103 // Horizontal
104 const qreal viewwidth = flickable->width();
105 const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent();
106 const qreal maxXBounds = maxxextent + viewwidth;
107 if (!qFuzzyIsNull(maxXBounds)) {
108 qreal x = p->pixelAligned ? std::round(p->hData.move.value()) : p->hData.move.value();
109 pagePos = (-x + flickable->minXExtent()) / maxXBounds;
110 pageSize = viewwidth / maxXBounds;
111 } else {
112 pagePos = 0;
113 pageSize = 0;
114 }
115
116 if (pageSize != m_widthRatio) {
117 m_widthRatio = pageSize;
118 changeWidth = true;
119 }
120 if (pagePos != m_xPosition) {
121 m_xPosition = pagePos;
122 changeX = true;
123 }
124
125 if (changeX)
126 emit xPositionChanged(m_xPosition);
127 if (changeY)
128 emit yPositionChanged(m_yPosition);
129 if (changeWidth)
130 emit widthRatioChanged(m_widthRatio);
131 if (changeHeight)
132 emit heightRatioChanged(m_heightRatio);
133}
134
135
137{
138public:
140 : flickable(f), axisData(nullptr), propName(name), active(false)
141 {
142 }
143
145 {
146 flickable = nullptr;
147 }
148
151 if (!fp->rebound || !fp->rebound->enabled())
152 return false;
153 active = true;
154 axisData = data;
155 axisData->transitionTo = toPos;
156 axisData->transitionToSet = true;
157
158 actions.clear();
159 actions << QQuickStateAction(fp->contentItem, propName, toPos);
160 QQuickTransitionManager::transition(actions, fp->rebound, fp->contentItem);
161 return true;
162 }
163
164 bool isActive() const {
165 return active;
166 }
167
169 if (!flickable || !isRunning())
170 return;
172 if (axisData == &fp->hData)
173 axisData->move.setValue(-flickable->contentX());
174 else
175 axisData->move.setValue(-flickable->contentY());
176 active = false;
177 cancel();
178 }
179
180protected:
181 void finished() override {
182 if (!flickable)
183 return;
184 axisData->move.setValue(axisData->transitionTo);
186 active = false;
187
188 if (!fp->hData.transitionToBounds->isActive()
189 && !fp->vData.transitionToBounds->isActive()) {
190 flickable->movementEnding();
191 }
192 }
193
194private:
196 QQuickFlickable *flickable;
198 QString propName;
199 bool active;
200};
201
206
208{
218 bool contains(const QPointF &point) const override
219 {
220 const QQuickItem *flickable = parentItem();
221 const QPointF posInFlickable = flickable->mapFromItem(this, point);
222 return flickable->contains(posInFlickable);
223 }
224};
225
234 , syncDrag(false)
235 , lastPosTime(-1)
236 , lastPressTime(0)
237 , deceleration(QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickDeceleration).toReal())
238 , wheelDeceleration(15000)
239 , maxVelocity(QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickMaximumVelocity).toReal())
241 , flickBoost(1.0), initialWheelFlickDistance(qApp->styleHints()->wheelScrollLines() * 24)
243 , flickableDirection(QQuickFlickable::AutoFlickDirection)
244 , boundsBehavior(QQuickFlickable::DragAndOvershootBounds)
245 , boundsMovement(QQuickFlickable::FollowBoundsBehavior)
247{
248 const int wheelDecelerationEnv = qEnvironmentVariableIntValue("QT_QUICK_FLICKABLE_WHEEL_DECELERATION");
249 if (wheelDecelerationEnv > 0)
250 wheelDeceleration = wheelDecelerationEnv;
251}
252
254{
255 Q_Q(QQuickFlickable);
259 q, QQuickFlickable, SLOT(timelineCompleted()));
261 q, QQuickFlickable, SLOT(velocityTimelineCompleted()));
262 q->setAcceptedMouseButtons(Qt::LeftButton);
263 q->setAcceptTouchEvents(true);
264 q->setFiltersChildMouseEvents(true);
267 viewportPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
269}
270
277{
278 if (maxVelocity <= 0)
279 return 0;
280
281 return qMin(qreal(QML_FLICK_OVERSHOOT), velocity / 3);
282}
283
294
296{
297 velocity = 0;
298 if (velocityBuffer.count() > QML_FLICK_DISCARDSAMPLES) {
299 int count = velocityBuffer.count()-QML_FLICK_DISCARDSAMPLES;
300 for (int i = 0; i < count; ++i) {
301 qreal v = velocityBuffer.at(i);
302 velocity += v;
303 }
304 velocity /= count;
305 }
306}
307
309{
310 Q_Q(QQuickFlickable);
311 if (item == contentItem) {
312 Qt::Orientations orient;
313 if (change.xChange())
314 orient |= Qt::Horizontal;
315 if (change.yChange())
316 orient |= Qt::Vertical;
317 if (orient) {
318 q->viewportMoved(orient);
319 const QPointF deltaMoved = item->position() - oldGeom.topLeft();
321 hData.pressPos += deltaMoved.x();
323 vData.pressPos += deltaMoved.y();
324 }
325 if (orient & Qt::Horizontal)
326 emit q->contentXChanged();
327 if (orient & Qt::Vertical)
328 emit q->contentYChanged();
329 }
330}
331
333{
334 Q_Q(QQuickFlickable);
335 return flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, eventType, velocity);
336}
337
339{
340 Q_Q(QQuickFlickable);
341 return flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, eventType, velocity);
342}
343
346 QEvent::Type eventType, qreal velocity)
347{
348 Q_Q(QQuickFlickable);
349 qreal maxDistance = -1;
350 data.fixingUp = false;
351 // -ve velocity means list is moving up
352 if (velocity > 0) {
353 maxDistance = qAbs(minExtent - data.move.value());
354 data.flickTarget = minExtent;
355 } else {
356 maxDistance = qAbs(maxExtent - data.move.value());
357 data.flickTarget = maxExtent;
358 }
359 if (maxDistance > 0 || boundsBehavior & QQuickFlickable::OvershootBounds) {
360 qreal v = velocity;
361 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
362 if (v < 0)
363 v = -maxVelocity;
364 else
365 v = maxVelocity;
366 }
367
368 qreal accel = eventType == QEvent::Wheel ? wheelDeceleration : deceleration;
369 qCDebug(lcFlickable) << "choosing deceleration" << accel << "for" << eventType;
370 // adjust accel so that we hit a full pixel
371 qreal v2 = v * v;
372 qreal dist = v2 / (accel * 2.0);
373 if (v > 0)
374 dist = -dist;
375 qreal target = std::round(data.move.value() - dist);
376 dist = -target + data.move.value();
377 accel = v2 / (2.0f * qAbs(dist));
378
380 if (!data.inOvershoot) {
382 timeline.accel(data.move, v, accel);
383 else
384 timeline.accel(data.move, v, accel, maxDistance);
385 }
386 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
387
388 if (&data == &hData)
389 return !hData.flicking && q->xflick();
390 else if (&data == &vData)
391 return !vData.flicking && q->yflick();
392 return false;
393 } else {
395 fixup(data, minExtent, maxExtent);
396 return false;
397 }
398}
399
404
409
411{
412 Q_Q(QQuickFlickable);
413 if (!q->isComponentComplete())
414 return; //Do not fixup from initialization values
415 fixup(hData, q->minXExtent(), q->maxXExtent());
416}
417
419{
420 Q_Q(QQuickFlickable);
421 if (!q->isComponentComplete())
422 return; //Do not fixup from initialization values
423 fixup(vData, q->minYExtent(), q->maxYExtent());
424}
425
435{
436 Q_Q(QQuickFlickable);
437 switch (fixupMode) {
438 case Immediate:
439 timeline.set(data.move, toPos);
440 break;
441 case ExtentChanged:
442 // The target has changed. Don't start from the beginning; just complete the
443 // second half of the animation using the new extent.
445 data.fixingUp = true;
446 break;
447 default: {
448 if (data.transitionToBounds && data.transitionToBounds->startTransition(&data, toPos)) {
449 q->movementStarting();
450 data.fixingUp = true;
451 } else {
452 qreal dist = toPos - data.move;
455 data.fixingUp = true;
456 }
457 }
458 }
459}
460
462{
463 timeline.reset(data.move);
464 if (data.transitionToBounds)
465 data.transitionToBounds->stopTransition();
466}
467
476
488{
489 if (data.move.value() >= minExtent || maxExtent > minExtent) {
491 if (data.move.value() != minExtent) {
492 adjustContentPos(data, minExtent);
493 }
494 } else if (data.move.value() <= maxExtent) {
496 adjustContentPos(data, maxExtent);
497 } else if (-std::round(-data.move.value()) != data.move.value()) {
498 // We could animate, but since it is less than 0.5 pixel it's probably not worthwhile.
500 qreal val = data.move.value();
501 if (std::abs(std::round(val) - val) < 0.25) // round small differences
502 val = std::round(val);
503 else if (data.smoothVelocity.value() > 0) // continue direction of motion for larger
504 val = std::ceil(val);
505 else if (data.smoothVelocity.value() < 0)
506 val = std::floor(val);
507 else // otherwise round
508 val = std::round(val);
509 timeline.set(data.move, val);
510 }
511 data.inOvershoot = false;
513 data.vTime = timeline.time();
514}
515
517{
518 if (a == 0.0 || b == 0.0) {
519 // qFuzzyCompare is broken
520 a += 1.0;
521 b += 1.0;
522 }
523 return a <= b || qFuzzyCompare(a, b);
524}
525
536{
537 Q_Q(QQuickFlickable);
538 bool atXBeginningChange = false, atXEndChange = false;
539 bool atYBeginningChange = false, atYEndChange = false;
540
541 // Vertical
542 const qreal maxyextent = -q->maxYExtent();
543 const qreal minyextent = -q->minYExtent();
544 const qreal ypos = pixelAligned ? -std::round(vData.move.value()) : -vData.move.value();
545 bool atBeginning = fuzzyLessThanOrEqualTo(ypos, std::ceil(minyextent));
546 bool atEnd = fuzzyLessThanOrEqualTo(std::floor(maxyextent), ypos);
547
548 if (atBeginning != vData.atBeginning) {
549 vData.atBeginning = atBeginning;
550 atYBeginningChange = true;
551 if (!vData.moving && atBeginning)
553 }
554 if (atEnd != vData.atEnd) {
555 vData.atEnd = atEnd;
556 atYEndChange = true;
557 if (!vData.moving && atEnd)
559 }
560
561 // Horizontal
562 const qreal maxxextent = -q->maxXExtent();
563 const qreal minxextent = -q->minXExtent();
564 const qreal xpos = pixelAligned ? -std::round(hData.move.value()) : -hData.move.value();
565 atBeginning = fuzzyLessThanOrEqualTo(xpos, std::ceil(minxextent));
566 atEnd = fuzzyLessThanOrEqualTo(std::floor(maxxextent), xpos);
567
568 if (atBeginning != hData.atBeginning) {
569 hData.atBeginning = atBeginning;
570 atXBeginningChange = true;
571 if (!hData.moving && atBeginning)
573 }
574 if (atEnd != hData.atEnd) {
575 hData.atEnd = atEnd;
576 atXEndChange = true;
577 if (!hData.moving && atEnd)
579 }
580
581 if (vData.extentsChanged) {
582 vData.extentsChanged = false;
583 qreal originY = q->originY();
584 if (vData.origin != originY) {
585 vData.origin = originY;
586 emit q->originYChanged();
587 }
588 }
589
590 if (hData.extentsChanged) {
591 hData.extentsChanged = false;
592 qreal originX = q->originX();
593 if (hData.origin != originX) {
594 hData.origin = originX;
595 emit q->originXChanged();
596 }
597 }
598
599 if (atXEndChange || atYEndChange || atXBeginningChange || atYBeginningChange)
600 emit q->isAtBoundaryChanged();
601 if (atXEndChange)
602 emit q->atXEndChanged();
603 if (atXBeginningChange)
604 emit q->atXBeginningChanged();
605 if (atYEndChange)
606 emit q->atYEndChanged();
607 if (atYBeginningChange)
608 emit q->atYBeginningChanged();
609
610 if (visibleArea)
612}
613
766
768 : QQuickItem(dd, parent)
769{
770 Q_D(QQuickFlickable);
771 d->init();
772}
773
777
799{
800 Q_D(const QQuickFlickable);
801 return -d->contentItem->x();
802}
803
805{
806 Q_D(QQuickFlickable);
807 d->hData.explicitValue = true;
808 d->resetTimeline(d->hData);
809 d->hData.vTime = d->timeline.time();
810 if (isMoving() || isFlicking())
811 movementEnding(true, false);
812 if (!qFuzzyCompare(-pos, d->hData.move.value())) {
813 d->hData.contentPositionChangedExternallyDuringDrag = d->hData.dragging;
814 d->hData.move.setValue(-pos);
815 d->hData.contentPositionChangedExternallyDuringDrag = false;
816 }
817}
818
820{
821 Q_D(const QQuickFlickable);
822 return -d->contentItem->y();
823}
824
826{
827 Q_D(QQuickFlickable);
828 d->vData.explicitValue = true;
829 d->resetTimeline(d->vData);
830 d->vData.vTime = d->timeline.time();
831 if (isMoving() || isFlicking())
832 movementEnding(false, true);
833 if (!qFuzzyCompare(-pos, d->vData.move.value())) {
834 d->vData.contentPositionChangedExternallyDuringDrag = d->vData.dragging;
835 d->vData.move.setValue(-pos);
836 d->vData.contentPositionChangedExternallyDuringDrag = false;
837 }
838}
839
854{
855 Q_D(const QQuickFlickable);
856 return d->interactive;
857}
858
860{
861 Q_D(QQuickFlickable);
862 if (interactive != d->interactive) {
863 d->interactive = interactive;
864 if (!interactive) {
865 d->cancelInteraction();
866 }
868 }
869}
870
885{
886 Q_D(const QQuickFlickable);
887 return d->hData.smoothVelocity.value();
888}
889
891{
892 Q_D(const QQuickFlickable);
893 return d->vData.smoothVelocity.value();
894}
895
906{
907 Q_D(const QQuickFlickable);
908 return d->hData.atEnd;
909}
910
912{
913 Q_D(const QQuickFlickable);
914 return d->hData.atBeginning;
915}
916
918{
919 Q_D(const QQuickFlickable);
920 return d->vData.atEnd;
921}
922
924{
925 Q_D(const QQuickFlickable);
926 return d->vData.atBeginning;
927}
928
948{
949 Q_D(const QQuickFlickable);
950 return d->contentItem;
951}
952
954{
955 Q_D(QQuickFlickable);
956 if (!d->visibleArea) {
957 d->visibleArea = new QQuickFlickableVisibleArea(this);
958 d->visibleArea->updateVisible(); // calculate initial ratios
959 }
960 return d->visibleArea;
961}
962
983{
984 Q_D(const QQuickFlickable);
985 return d->flickableDirection;
986}
987
989{
990 Q_D(QQuickFlickable);
991 if (direction != d->flickableDirection) {
992 d->flickableDirection = direction;
994 }
995}
996
1010{
1011 Q_D(const QQuickFlickable);
1012 return d->pixelAligned;
1013}
1014
1016{
1017 Q_D(QQuickFlickable);
1018 if (align != d->pixelAligned) {
1019 d->pixelAligned = align;
1021 }
1022}
1023
1037{
1038 Q_D(const QQuickFlickable);
1039 return d->syncDrag;
1040}
1041
1043{
1044 Q_D(QQuickFlickable);
1045 if (v != d->syncDrag) {
1046 d->syncDrag = v;
1047 emit synchronousDragChanged();
1048 }
1049}
1050
1056{
1058 // rotate and scale the velocity vector from scene to local
1059 return QVector2D(transform.map(event->point(0).velocity().toPointF()) - transform.map(QPointF()));
1060}
1061
1063{
1064 if (0 != event->timestamp())
1065 return event->timestamp();
1066 if (!timer.isValid())
1067 return 0LL;
1068 return timer.elapsed();
1069}
1070
1072{
1073 return (window ? window->effectiveDevicePixelRatio() : qApp->devicePixelRatio());
1074}
1075
1077{
1078 Q_Q(QQuickFlickable);
1079 timer.start();
1083 stealMouse = true; // If we've been flicked then steal the click.
1084 int flickTime = timeline.time();
1085 if (flickTime > 600) {
1086 // too long between flicks - cancel boost
1089 flickBoost = 1.0;
1090 } else {
1093 if (flickTime > 300) // slower flicking - reduce boost
1094 flickBoost = qMax(1.0, flickBoost - 0.5);
1095 }
1096 } else {
1097 stealMouse = false;
1100 flickBoost = 1.0;
1101 }
1102 q->setKeepMouseGrab(stealMouse);
1103
1104 maybeBeginDrag(computeCurrentTime(event), event->points().first().position(),
1105 event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->buttons()
1106 : Qt::NoButton);
1107}
1108
1109void QQuickFlickablePrivate::maybeBeginDrag(qint64 currentTimestamp, const QPointF &pressPosn, Qt::MouseButtons buttons)
1110{
1111 Q_Q(QQuickFlickable);
1113 // consider dragging only when event is left mouse button or touch event which has no button
1114 pressed = buttons.testFlag(Qt::LeftButton) || (buttons == Qt::NoButton);
1115
1120 if (!hData.fixingUp)
1122 if (!vData.fixingUp)
1124
1125 hData.reset();
1126 vData.reset();
1127 hData.dragMinBound = q->minXExtent() - hData.startMargin;
1128 vData.dragMinBound = q->minYExtent() - vData.startMargin;
1129 hData.dragMaxBound = q->maxXExtent() + hData.endMargin;
1130 vData.dragMaxBound = q->maxYExtent() + vData.endMargin;
1131 fixupMode = Normal;
1132 lastPos = QPointF();
1133 pressPos = pressPosn;
1136 const bool wasFlicking = hData.flicking || vData.flicking;
1139 if (hData.flicking) {
1140 hData.flicking = false;
1141 emit q->flickingHorizontallyChanged();
1142 }
1143 if (vData.flicking) {
1144 vData.flicking = false;
1145 emit q->flickingVerticallyChanged();
1146 }
1147 if (wasFlicking)
1148 emit q->flickingChanged();
1149 lastPosTime = lastPressTime = currentTimestamp;
1152}
1153
1154void QQuickFlickablePrivate::drag(qint64 currentTimestamp, QEvent::Type eventType, const QPointF &localPos,
1155 const QVector2D &deltas, bool overThreshold, bool momentum,
1156 bool velocitySensitiveOverBounds, const QVector2D &velocity)
1157{
1158 Q_Q(QQuickFlickable);
1159 bool rejectY = false;
1160 bool rejectX = false;
1161
1162 bool keepY = q->yflick();
1163 bool keepX = q->xflick();
1164
1165 bool stealY = false;
1166 bool stealX = false;
1167 if (eventType == QEvent::MouseMove) {
1168 stealX = stealY = stealMouse;
1169 } else if (eventType == QEvent::Wheel) {
1170 stealX = stealY = scrollingPhase;
1171 }
1172
1173 bool prevHMoved = hMoved;
1174 bool prevVMoved = vMoved;
1175
1176 qint64 elapsedSincePress = currentTimestamp - lastPressTime;
1177 qCDebug(lcFlickable).nospace() << currentTimestamp << ' ' << eventType << " drag @ " << localPos.x() << ',' << localPos.y()
1178 << " \u0394 " << deltas.x() << ',' << deltas.y() << " vel " << velocity.x() << ',' << velocity.y()
1179 << " thrsld? " << overThreshold << " momentum? " << momentum << " velSens? " << velocitySensitiveOverBounds
1180 << " sincePress " << elapsedSincePress;
1181
1182 if (q->yflick()) {
1183 qreal dy = deltas.y();
1184 if (overThreshold || elapsedSincePress > 200) {
1185 if (!vMoved)
1187 qreal newY = dy + vData.pressPos - (syncDrag ? 0 : vData.dragStartOffset);
1188 // Recalculate bounds in case margins have changed, but use the content
1189 // size estimate taken at the start of the drag in case the drag causes
1190 // the estimate to be altered
1194 if (fuzzyLessThanOrEqualTo(newY, maxY)) {
1195 newY = maxY;
1196 rejectY = vData.pressPos == maxY && vData.move.value() == maxY && dy < 0;
1197 }
1198 if (fuzzyLessThanOrEqualTo(minY, newY)) {
1199 newY = minY;
1200 rejectY |= vData.pressPos == minY && vData.move.value() == minY && dy > 0;
1201 }
1202 } else {
1203 qreal vel = velocity.y() / QML_FLICK_OVERSHOOTFRICTION;
1204 if (vel > 0. && vel > vData.velocity)
1206 else if (vel < 0. && vel < vData.velocity)
1208 if (newY > minY) {
1209 // Overshoot beyond the top. But don't wait for momentum phase to end before returning to bounds.
1210 if (momentum && vData.atBeginning) {
1211 if (!vData.inRebound) {
1212 vData.inRebound = true;
1213 q->returnToBounds();
1214 }
1215 return;
1216 }
1217 if (velocitySensitiveOverBounds) {
1220 newY = minY + overshoot;
1221 } else {
1222 newY = minY + (newY - minY) / 2;
1223 }
1224 } else if (newY < maxY && maxY - minY <= 0) {
1225 // Overshoot beyond the bottom. But don't wait for momentum phase to end before returning to bounds.
1226 if (momentum && vData.atEnd) {
1227 if (!vData.inRebound) {
1228 vData.inRebound = true;
1229 q->returnToBounds();
1230 }
1231 return;
1232 }
1233 if (velocitySensitiveOverBounds) {
1236 newY = maxY - overshoot;
1237 } else {
1238 newY = maxY + (newY - maxY) / 2;
1239 }
1240 }
1241 }
1242 if (!rejectY && stealMouse && dy != 0.0 && dy != vData.previousDragDelta) {
1243 clearTimeline();
1244 vData.move.setValue(newY);
1245 vMoved = true;
1246 }
1247 if (!rejectY && overThreshold)
1248 stealY = true;
1249
1250 if ((newY >= minY && vData.pressPos == minY && vData.move.value() == minY && dy > 0)
1251 || (newY <= maxY && vData.pressPos == maxY && vData.move.value() == maxY && dy < 0)) {
1252 keepY = false;
1253 }
1254 }
1256 }
1257
1258 if (q->xflick()) {
1259 qreal dx = deltas.x();
1260 if (overThreshold || elapsedSincePress > 200) {
1261 if (!hMoved)
1263 qreal newX = dx + hData.pressPos - (syncDrag ? 0 : hData.dragStartOffset);
1264 const qreal minX = hData.dragMinBound + hData.startMargin;
1267 if (fuzzyLessThanOrEqualTo(newX, maxX)) {
1268 newX = maxX;
1269 rejectX = hData.pressPos == maxX && hData.move.value() == maxX && dx < 0;
1270 }
1271 if (fuzzyLessThanOrEqualTo(minX, newX)) {
1272 newX = minX;
1273 rejectX |= hData.pressPos == minX && hData.move.value() == minX && dx > 0;
1274 }
1275 } else {
1276 qreal vel = velocity.x() / QML_FLICK_OVERSHOOTFRICTION;
1277 if (vel > 0. && vel > hData.velocity)
1279 else if (vel < 0. && vel < hData.velocity)
1281 if (newX > minX) {
1282 // Overshoot beyond the left. But don't wait for momentum phase to end before returning to bounds.
1283 if (momentum && hData.atBeginning) {
1284 if (!hData.inRebound) {
1285 hData.inRebound = true;
1286 q->returnToBounds();
1287 }
1288 return;
1289 }
1290 if (velocitySensitiveOverBounds) {
1291 qreal overshoot = (newX - minX) * hData.velocity / maxVelocity / QML_FLICK_OVERSHOOTFRICTION;
1293 newX = minX + overshoot;
1294 } else {
1295 newX = minX + (newX - minX) / 2;
1296 }
1297 } else if (newX < maxX && maxX - minX <= 0) {
1298 // Overshoot beyond the right. But don't wait for momentum phase to end before returning to bounds.
1299 if (momentum && hData.atEnd) {
1300 if (!hData.inRebound) {
1301 hData.inRebound = true;
1302 q->returnToBounds();
1303 }
1304 return;
1305 }
1306 if (velocitySensitiveOverBounds) {
1309 newX = maxX - overshoot;
1310 } else {
1311 newX = maxX + (newX - maxX) / 2;
1312 }
1313 }
1314 }
1315
1316 if (!rejectX && stealMouse && dx != 0.0 && dx != hData.previousDragDelta) {
1317 clearTimeline();
1318 hData.move.setValue(newX);
1319 hMoved = true;
1320 }
1321
1322 if (!rejectX && overThreshold)
1323 stealX = true;
1324
1325 if ((newX >= minX && vData.pressPos == minX && vData.move.value() == minX && dx > 0)
1326 || (newX <= maxX && vData.pressPos == maxX && vData.move.value() == maxX && dx < 0)) {
1327 keepX = false;
1328 }
1329 }
1331 }
1332
1333 stealMouse = stealX || stealY;
1334 if (stealMouse) {
1335 if ((stealX && keepX) || (stealY && keepY))
1336 q->setKeepMouseGrab(true);
1338 }
1339
1340 if (rejectY) {
1342 vData.velocity = 0;
1343 }
1344 if (rejectX) {
1346 hData.velocity = 0;
1347 }
1348
1349 if (momentum && !hData.flicking && !vData.flicking)
1352
1353 if ((hMoved && !prevHMoved) || (vMoved && !prevVMoved))
1354 q->movementStarting();
1355
1356 lastPosTime = currentTimestamp;
1357 if (q->yflick() && !rejectY)
1358 vData.addVelocitySample(velocity.y(), maxVelocity);
1359 if (q->xflick() && !rejectX)
1360 hData.addVelocitySample(velocity.x(), maxVelocity);
1361 lastPos = localPos;
1362}
1363
1365{
1366 Q_Q(QQuickFlickable);
1367 if (!interactive || lastPosTime == -1 ||
1368 (event->isSinglePointEvent() && !static_cast<QSinglePointEvent *>(event)->buttons().testFlag(Qt::LeftButton)))
1369 return;
1370
1371 qint64 currentTimestamp = computeCurrentTime(event);
1372 const auto &firstPoint = event->points().first();
1373 const auto &pos = firstPoint.position();
1374 const QVector2D deltas = QVector2D(pos - q->mapFromGlobal(firstPoint.globalPressPosition()));
1375 const QVector2D velocity = firstPointLocalVelocity(event);
1376 bool overThreshold = false;
1377
1378 if (event->pointCount() == 1) {
1379 if (q->yflick())
1380 overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, firstPoint);
1381 if (q->xflick())
1382 overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.x(), Qt::XAxis, firstPoint);
1383 } else {
1384 qCDebug(lcFilter) << q->objectName() << "ignoring multi-touch" << event;
1385 }
1386
1387 drag(currentTimestamp, event->type(), pos, deltas, overThreshold, false, false, velocity);
1388}
1389
1391{
1392 Q_Q(QQuickFlickable);
1393 stealMouse = false;
1394 q->setKeepMouseGrab(false);
1395 pressed = false;
1396
1397 // if we drag then pause before release we should not cause a flick.
1399
1402
1404
1405 if (lastPosTime == -1)
1406 return;
1407
1409
1410 bool canBoost = false;
1411 const auto pos = event->points().first().position();
1412 const auto pressPos = q->mapFromGlobal(event->points().first().globalPressPosition());
1413 const QVector2D eventVelocity = firstPointLocalVelocity(event);
1414 qCDebug(lcVel) << event->deviceType() << event->type() << "velocity" << event->points().first().velocity() << "transformed to local" << eventVelocity;
1415
1416 qreal vVelocity = 0;
1417 if (elapsed < 100 && vData.velocity != 0.) {
1418 vVelocity = (event->device()->capabilities().testFlag(QInputDevice::Capability::Velocity)
1419 ? eventVelocity.y() : vData.velocity);
1420 }
1421 if ((vData.atBeginning && vVelocity > 0.) || (vData.atEnd && vVelocity < 0.)) {
1422 vVelocity /= 2;
1423 } else if (vData.continuousFlickVelocity != 0.0
1425 && ((vVelocity > 0) == (vData.continuousFlickVelocity > 0))
1426 && qAbs(vVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
1427 // accelerate flick for large view flicked quickly
1428 canBoost = true;
1429 }
1430
1431 qreal hVelocity = 0;
1432 if (elapsed < 100 && hData.velocity != 0.) {
1433 hVelocity = (event->device()->capabilities().testFlag(QInputDevice::Capability::Velocity)
1434 ? eventVelocity.x() : hData.velocity);
1435 }
1436 if ((hData.atBeginning && hVelocity > 0.) || (hData.atEnd && hVelocity < 0.)) {
1437 hVelocity /= 2;
1438 } else if (hData.continuousFlickVelocity != 0.0
1440 && ((hVelocity > 0) == (hData.continuousFlickVelocity > 0))
1441 && qAbs(hVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
1442 // accelerate flick for large view flicked quickly
1443 canBoost = true;
1444 }
1445
1446 flickBoost = canBoost ? qBound(1.0, flickBoost+0.25, QML_FLICK_MULTIFLICK_MAXBOOST) : 1.0;
1447 const int flickThreshold = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickStartDistance).toInt();
1448
1449 bool anyPointGrabbed = event->points().constEnd() !=
1450 std::find_if(event->points().constBegin(),event->points().constEnd(),
1451 [q, event](const QEventPoint &point) { return event->exclusiveGrabber(point) == q; });
1452
1453 bool flickedVertically = false;
1454 vVelocity *= flickBoost;
1455 const bool isVerticalFlickAllowed = anyPointGrabbed &&
1456 q->yflick() && qAbs(vVelocity) > _q_MinimumFlickVelocity &&
1457 qAbs(pos.y() - pressPos.y()) > flickThreshold;
1458 if (isVerticalFlickAllowed) {
1460 vData.smoothVelocity.setValue(-vVelocity);
1461 flickedVertically = flickY(event->type(), vVelocity);
1462 }
1463
1464 bool flickedHorizontally = false;
1465 hVelocity *= flickBoost;
1466 const bool isHorizontalFlickAllowed = anyPointGrabbed &&
1467 q->xflick() && qAbs(hVelocity) > _q_MinimumFlickVelocity &&
1468 qAbs(pos.x() - pressPos.x()) > flickThreshold;
1469 if (isHorizontalFlickAllowed) {
1471 hData.smoothVelocity.setValue(-hVelocity);
1472 flickedHorizontally = flickX(event->type(), hVelocity);
1473 }
1474
1475 if (!isVerticalFlickAllowed)
1476 fixupY();
1477
1478 if (!isHorizontalFlickAllowed)
1479 fixupX();
1480
1481 flickingStarted(flickedHorizontally, flickedVertically);
1482 if (!isViewMoving()) {
1483 q->movementEnding();
1484 } else {
1485 if (flickedVertically)
1486 vMoved = true;
1487 if (flickedHorizontally)
1488 hMoved = true;
1489 q->movementStarting();
1490 }
1491}
1492
1494{
1495 Q_D(QQuickFlickable);
1496 if (d->interactive && !d->replayingPressEvent && d->wantsPointerEvent(event)) {
1497 if (!d->pressed)
1498 d->handlePressEvent(event);
1499 event->accept();
1500 } else {
1502 }
1503}
1504
1506{
1507 Q_D(QQuickFlickable);
1508 if (d->interactive && d->wantsPointerEvent(event)) {
1509 d->handleMoveEvent(event);
1510 event->accept();
1511 } else {
1513 }
1514}
1515
1517{
1518 Q_D(QQuickFlickable);
1519 if (d->interactive && d->wantsPointerEvent(event)) {
1520 if (d->delayedPressEvent) {
1521 d->replayDelayedPress();
1522
1523 auto &firstPoint = event->point(0);
1524 if (const auto *grabber = event->exclusiveGrabber(firstPoint); grabber && grabber->isQuickItemType()) {
1525 // Since we sent the delayed press to the window, we need to resend the release to the window too.
1526 // We're not copying or detaching, so restore the original event position afterwards.
1527 const auto oldPosition = firstPoint.position();
1528 QMutableEventPoint::setPosition(firstPoint, event->scenePosition());
1530 QMutableEventPoint::setPosition(firstPoint, oldPosition);
1531 }
1532
1533 // And the event has been consumed
1534 d->stealMouse = false;
1535 d->pressed = false;
1536 return;
1537 }
1538
1539 d->handleReleaseEvent(event);
1540 event->accept();
1541 } else {
1543 }
1544}
1545
1547{
1548 Q_D(QQuickFlickable);
1549
1550 if (event->type() == QEvent::TouchCancel) {
1551 if (d->interactive && d->wantsPointerEvent(event))
1552 d->cancelInteraction();
1553 else
1555 return;
1556 }
1557
1558 bool unhandled = false;
1559 const auto &firstPoint = event->points().first();
1560 switch (firstPoint.state()) {
1562 if (d->interactive && !d->replayingPressEvent && d->wantsPointerEvent(event)) {
1563 if (!d->pressed)
1564 d->handlePressEvent(event);
1565 event->accept();
1566 } else {
1567 unhandled = true;
1568 }
1569 break;
1571 if (d->interactive && d->wantsPointerEvent(event)) {
1572 d->handleMoveEvent(event);
1573 event->accept();
1574 } else {
1575 unhandled = true;
1576 }
1577 break;
1579 if (d->interactive && d->wantsPointerEvent(event)) {
1580 if (d->delayedPressEvent) {
1581 d->replayDelayedPress();
1582
1583 const auto &firstPoint = event->point(0);
1584 if (const auto *grabber = event->exclusiveGrabber(firstPoint); grabber && grabber->isQuickItemType()) {
1585 // Since we sent the delayed press to the window, we need to resend the release to the window too.
1586 QScopedPointer<QPointerEvent> localizedEvent(
1587 QQuickDeliveryAgentPrivate::clonePointerEvent(event, firstPoint.scenePosition()));
1588 QCoreApplication::sendEvent(window(), localizedEvent.data());
1589 }
1590
1591 // And the event has been consumed
1592 d->stealMouse = false;
1593 d->pressed = false;
1594 return;
1595 }
1596
1597 d->handleReleaseEvent(event);
1598 event->accept();
1599 } else {
1600 unhandled = true;
1601 }
1602 break;
1605 break;
1606 }
1607 if (unhandled)
1609}
1610
1611#if QT_CONFIG(wheelevent)
1612void QQuickFlickable::wheelEvent(QWheelEvent *event)
1613{
1614 Q_D(QQuickFlickable);
1615 if (!d->interactive || !d->wantsPointerEvent(event)) {
1616 QQuickItem::wheelEvent(event);
1617 return;
1618 }
1619 qCDebug(lcWheel) << event->device() << event << event->source();
1620 event->setAccepted(false);
1621 qint64 currentTimestamp = d->computeCurrentTime(event);
1622 switch (event->phase()) {
1623 case Qt::ScrollBegin:
1624 d->scrollingPhase = true;
1625 d->accumulatedWheelPixelDelta = QVector2D();
1626 d->vData.velocity = 0;
1627 d->hData.velocity = 0;
1628 d->timer.start();
1629 d->maybeBeginDrag(currentTimestamp, event->position());
1630 d->lastPosTime = -1;
1631 break;
1632 case Qt::NoScrollPhase: // default phase with an ordinary wheel mouse
1633 case Qt::ScrollUpdate:
1634 if (d->scrollingPhase)
1635 d->pressed = true;
1636 break;
1637 case Qt::ScrollMomentum:
1638 d->pressed = false;
1639 d->scrollingPhase = false;
1640 d->draggingEnding();
1641 if (isMoving())
1642 event->accept();
1643 d->lastPosTime = -1;
1644 break;
1645 case Qt::ScrollEnd:
1646 d->pressed = false;
1647 d->scrollingPhase = false;
1648 d->draggingEnding();
1649 event->accept();
1651 d->lastPosTime = -1;
1652 d->stealMouse = false;
1653 if (!d->velocityTimeline.isActive() && !d->timeline.isActive())
1654 movementEnding(true, true);
1655 return;
1656 }
1657
1658 qreal elapsed = qreal(currentTimestamp - d->lastPosTime) / qreal(1000);
1659 if (elapsed <= 0) {
1660 d->lastPosTime = currentTimestamp;
1661 qCDebug(lcWheel) << "insufficient elapsed time: can't calculate velocity" << elapsed;
1662 return;
1663 }
1664
1665 if (event->source() == Qt::MouseEventNotSynthesized || event->pixelDelta().isNull() || event->phase() == Qt::NoScrollPhase) {
1666 // no pixel delta (physical mouse wheel, or "dumb" touchpad), so use angleDelta
1667 int xDelta = event->angleDelta().x();
1668 int yDelta = event->angleDelta().y();
1669
1670 if (d->wheelDeceleration > _q_MaximumWheelDeceleration) {
1671 const qreal wheelScroll = -qApp->styleHints()->wheelScrollLines() * 24;
1672 // If wheelDeceleration is very large, i.e. the user or the platform does not want to have any mouse wheel
1673 // acceleration behavior, we want to move a distance proportional to QStyleHints::wheelScrollLines()
1674 if (yflick() && yDelta != 0) {
1675 d->moveReason = QQuickFlickablePrivate::Mouse; // ItemViews will set fixupMode to Immediate in fixup() without this.
1676 d->vMoved = true;
1677 qreal scrollPixel = (-yDelta / 120.0 * wheelScroll);
1678 if (scrollPixel > 0) { // Forward direction (away from user)
1679 if (d->vData.move.value() >= minYExtent())
1680 d->vMoved = false;
1681 } else { // Backward direction (towards user)
1682 if (d->vData.move.value() <= maxYExtent())
1683 d->vMoved = false;
1684 }
1685 if (d->vMoved) {
1686 if (d->boundsBehavior == QQuickFlickable::StopAtBounds) {
1687 const qreal estContentPos = scrollPixel + d->vData.move.value();
1688 if (scrollPixel > 0) { // Forward direction (away from user)
1689 if (estContentPos > minYExtent())
1690 scrollPixel = minYExtent() - d->vData.move.value();
1691 } else { // Backward direction (towards user)
1692 if (estContentPos < maxYExtent())
1693 scrollPixel = maxYExtent() - d->vData.move.value();
1694 }
1695 }
1696 d->resetTimeline(d->vData);
1698 d->timeline.moveBy(d->vData.move, scrollPixel, QEasingCurve(QEasingCurve::OutExpo), 3*d->fixupDuration/4);
1699 d->vData.fixingUp = true;
1700 d->timeline.callback(QQuickTimeLineCallback(&d->vData.move, QQuickFlickablePrivate::fixupY_callback, d));
1701 }
1702 event->accept();
1703 }
1704 if (xflick() && xDelta != 0) {
1705 d->moveReason = QQuickFlickablePrivate::Mouse; // ItemViews will set fixupMode to Immediate in fixup() without this.
1706 d->hMoved = true;
1707 qreal scrollPixel = (-xDelta / 120.0 * wheelScroll);
1708 if (scrollPixel > 0) { // Forward direction (away from user)
1709 if (d->hData.move.value() >= minXExtent())
1710 d->hMoved = false;
1711 } else { // Backward direction (towards user)
1712 if (d->hData.move.value() <= maxXExtent())
1713 d->hMoved = false;
1714 }
1715 if (d->hMoved) {
1716 if (d->boundsBehavior == QQuickFlickable::StopAtBounds) {
1717 const qreal estContentPos = scrollPixel + d->hData.move.value();
1718 if (scrollPixel > 0) { // Forward direction (away from user)
1719 if (estContentPos > minXExtent())
1720 scrollPixel = minXExtent() - d->hData.move.value();
1721 } else { // Backward direction (towards user)
1722 if (estContentPos < maxXExtent())
1723 scrollPixel = maxXExtent() - d->hData.move.value();
1724 }
1725 }
1726 d->resetTimeline(d->hData);
1728 d->timeline.moveBy(d->hData.move, scrollPixel, QEasingCurve(QEasingCurve::OutExpo), 3*d->fixupDuration/4);
1729 d->hData.fixingUp = true;
1730 d->timeline.callback(QQuickTimeLineCallback(&d->hData.move, QQuickFlickablePrivate::fixupX_callback, d));
1731 }
1732 event->accept();
1733 }
1734 } else {
1735 // wheelDeceleration is set to some reasonable value: the user or the platform wants to have
1736 // the classic Qt Quick mouse wheel acceleration behavior.
1737 // For a single "clicky" wheel event (angleDelta +/- 120),
1738 // we want flick() to end up moving a distance proportional to QStyleHints::wheelScrollLines().
1739 // The decel algo from there is
1740 // qreal dist = v2 / (accel * 2.0);
1741 // i.e. initialWheelFlickDistance = (120 / dt)^2 / (deceleration * 2)
1742 // now solve for dt:
1743 // dt = 120 / sqrt(deceleration * 2 * initialWheelFlickDistance)
1744 if (!isMoving())
1745 elapsed = 120 / qSqrt(d->wheelDeceleration * 2 * d->initialWheelFlickDistance);
1746 if (yflick() && yDelta != 0) {
1747 qreal instVelocity = yDelta / elapsed;
1748 // if the direction has changed, start over with filtering, to allow instant movement in the opposite direction
1749 if ((instVelocity < 0 && d->vData.velocity > 0) || (instVelocity > 0 && d->vData.velocity < 0))
1750 d->vData.velocityBuffer.clear();
1751 d->vData.addVelocitySample(instVelocity, d->maxVelocity);
1752 d->vData.updateVelocity();
1753 if ((yDelta > 0 && contentY() > -minYExtent()) || (yDelta < 0 && contentY() < -maxYExtent())) {
1754 const bool newFlick = d->flickY(event->type(), d->vData.velocity);
1755 if (newFlick && (d->vData.atBeginning != (yDelta > 0) || d->vData.atEnd != (yDelta < 0))) {
1756 d->flickingStarted(false, true);
1757 d->vMoved = true;
1759 }
1760 event->accept();
1761 }
1762 }
1763 if (xflick() && xDelta != 0) {
1764 qreal instVelocity = xDelta / elapsed;
1765 // if the direction has changed, start over with filtering, to allow instant movement in the opposite direction
1766 if ((instVelocity < 0 && d->hData.velocity > 0) || (instVelocity > 0 && d->hData.velocity < 0))
1767 d->hData.velocityBuffer.clear();
1768 d->hData.addVelocitySample(instVelocity, d->maxVelocity);
1769 d->hData.updateVelocity();
1770 if ((xDelta > 0 && contentX() > -minXExtent()) || (xDelta < 0 && contentX() < -maxXExtent())) {
1771 const bool newFlick = d->flickX(event->type(), d->hData.velocity);
1772 if (newFlick && (d->hData.atBeginning != (xDelta > 0) || d->hData.atEnd != (xDelta < 0))) {
1773 d->flickingStarted(true, false);
1774 d->hMoved = true;
1776 }
1777 event->accept();
1778 }
1779 }
1780 }
1781 } else {
1782 // use pixelDelta (probably from a trackpad): this is where we want to be on most platforms eventually
1783 int xDelta = event->pixelDelta().x();
1784 int yDelta = event->pixelDelta().y();
1785
1786 QVector2D velocity(xDelta / elapsed, yDelta / elapsed);
1787 d->accumulatedWheelPixelDelta += QVector2D(event->pixelDelta());
1788 // Try to drag if 1) we already are dragging or flicking, or
1789 // 2) the flickable is free to flick both directions, or
1790 // 3) the movement so far has been mostly horizontal AND it's free to flick horizontally, or
1791 // 4) the movement so far has been mostly vertical AND it's free to flick vertically.
1792 // Otherwise, wait until the next event. Wheel events with pixel deltas tend to come frequently.
1793 if (isMoving() || isFlicking() || (yflick() && xflick())
1794 || (xflick() && qAbs(d->accumulatedWheelPixelDelta.x()) > qAbs(d->accumulatedWheelPixelDelta.y() * 2))
1795 || (yflick() && qAbs(d->accumulatedWheelPixelDelta.y()) > qAbs(d->accumulatedWheelPixelDelta.x() * 2))) {
1796 d->drag(currentTimestamp, event->type(), event->position(), d->accumulatedWheelPixelDelta,
1797 true, !d->scrollingPhase, true, velocity);
1798 event->accept();
1799 } else {
1800 qCDebug(lcWheel) << "not dragging: accumulated deltas" << d->accumulatedWheelPixelDelta <<
1801 "moving?" << isMoving() << "can flick horizontally?" << xflick() << "vertically?" << yflick();
1802 }
1803 }
1804 d->lastPosTime = currentTimestamp;
1805
1806 if (!event->isAccepted())
1807 QQuickItem::wheelEvent(event);
1808}
1809#endif
1810
1812{
1813 Q_Q(const QQuickFlickable);
1814 QQuickItem *item = i;
1815 while (item) {
1816 QQuickFlickable *flick = qobject_cast<QQuickFlickable*>(item);
1817 if (flick && flick->pressDelay() > 0 && flick->isInteractive()) {
1818 // Found the innermost flickable with press delay - is it me?
1819 return (flick == q);
1820 }
1821 item = item->parentItem();
1822 }
1823 return false;
1824}
1825
1827{
1828 Q_Q(QQuickFlickable);
1829 if (!q->window() || pressDelay <= 0)
1830 return;
1831
1832 // Only the innermost flickable should handle the delayed press; this allows
1833 // flickables up the parent chain to all see the events in their filter functions
1835 return;
1836
1840 qCDebug(lcReplay) << "begin press delay" << pressDelay << "ms with" << delayedPressEvent;
1841}
1842
1844{
1845 if (delayedPressEvent) {
1847 qCDebug(lcReplay) << "clear delayed press" << delayedPressEvent;
1848 delete delayedPressEvent;
1849 delayedPressEvent = nullptr;
1850 }
1851}
1852
1854{
1855 Q_Q(QQuickFlickable);
1856 if (delayedPressEvent) {
1857 // Losing the grab will clear the delayed press event; take control of it here
1858 QScopedPointer<QPointerEvent> event(delayedPressEvent);
1859 delayedPressEvent = nullptr;
1861
1862 // If we have the grab, release before delivering the event
1863 if (QQuickWindow *window = q->window()) {
1864 auto da = deliveryAgentPrivate();
1865 da->allowChildEventFiltering = false; // don't allow re-filtering during replay
1866 replayingPressEvent = true;
1867 auto &firstPoint = event->point(0);
1868 // At first glance, it's weird for delayedPressEvent to already have a grabber;
1869 // but on press, filterMouseEvent() took the exclusive grab, and that's stored
1870 // in the device-specific EventPointData instance in QPointingDevicePrivate::activePoints,
1871 // not in the event itself. If this Flickable is still the grabber of that point on that device,
1872 // that's the reason; but now it doesn't need that grab anymore.
1873 if (event->exclusiveGrabber(firstPoint) == q)
1874 event->setExclusiveGrabber(firstPoint, nullptr);
1875
1876 qCDebug(lcReplay) << "replaying" << event.data();
1877 // Put scenePosition into position, for the sake of QQuickWindowPrivate::translateTouchEvent()
1878 // TODO remove this if we remove QQuickWindowPrivate::translateTouchEvent()
1879 QMutableEventPoint::setPosition(firstPoint, firstPoint.scenePosition());
1880 // Send it through like a fresh press event, and let QQuickWindow
1881 // (more specifically, QQuickWindowPrivate::deliverPressOrReleaseEvent)
1882 // find the item or handler that should receive it, as usual.
1884 qCDebug(lcReplay) << "replay done";
1885
1886 // We're done with replay, go back to normal delivery behavior
1887 replayingPressEvent = false;
1888 da->allowChildEventFiltering = true;
1889 }
1890 }
1891}
1892
1893//XXX pixelAligned ignores the global position of the Flickable, i.e. assumes Flickable itself is pixel aligned.
1894
1904{
1905 Q_Q(QQuickFlickable);
1906 qreal effectiveX = pixelAligned ? -std::round(-x) : x;
1907
1908 const qreal maxX = q->maxXExtent();
1909 const qreal minX = q->minXExtent();
1910
1912 effectiveX = qBound(maxX, effectiveX, minX);
1913
1914 contentItem->setX(effectiveX);
1915 if (contentItem->x() != effectiveX)
1916 return; // reentered
1917
1918 qreal overshoot = 0.0;
1919 if (x <= maxX)
1920 overshoot = maxX - x;
1921 else if (x >= minX)
1922 overshoot = minX - x;
1923
1924 if (overshoot != hData.overshoot) {
1925 hData.overshoot = overshoot;
1926 emit q->horizontalOvershootChanged();
1927 }
1928}
1929
1939{
1940 Q_Q(QQuickFlickable);
1941 qreal effectiveY = pixelAligned ? -std::round(-y) : y;
1942
1943 const qreal maxY = q->maxYExtent();
1944 const qreal minY = q->minYExtent();
1945
1947 effectiveY = qBound(maxY, effectiveY, minY);
1948
1949 contentItem->setY(effectiveY);
1950 if (contentItem->y() != effectiveY)
1951 return; // reentered
1952
1953 qreal overshoot = 0.0;
1954 if (y <= maxY)
1955 overshoot = maxY - y;
1956 else if (y >= minY)
1957 overshoot = minY - y;
1958
1959 if (overshoot != vData.overshoot) {
1960 vData.overshoot = overshoot;
1961 emit q->verticalOvershootChanged();
1962 }
1963}
1964
1966{
1967 Q_D(QQuickFlickable);
1968 if (event->timerId() == d->delayedPressTimer.timerId()) {
1969 d->delayedPressTimer.stop();
1970 if (d->delayedPressEvent) {
1971 d->replayDelayedPress();
1972 }
1973 }
1974}
1975
1977{
1978 Q_D(const QQuickFlickable);
1979 return d->vData.startMargin;
1980}
1981
1983{
1984 Q_D(const QQuickFlickable);
1985 return d->hData.startMargin;
1986}
1987
1988/* returns -ve */
1990{
1991 Q_D(const QQuickFlickable);
1992 return qMin<qreal>(minXExtent(), width() - vWidth() - d->hData.endMargin);
1993}
1994/* returns -ve */
1996{
1997 Q_D(const QQuickFlickable);
1998 return qMin<qreal>(minYExtent(), height() - vHeight() - d->vData.endMargin);
1999}
2000
2002{
2003 Q_D(QQuickFlickable);
2005 if (!d->hData.explicitValue && d->hData.startMargin != 0.)
2007 if (!d->vData.explicitValue && d->vData.startMargin != 0.)
2009 if (lcWheel().isDebugEnabled() || lcVel().isDebugEnabled()) {
2010 d->timeline.setObjectName(QLatin1String("timeline for Flickable ") + objectName());
2011 d->velocityTimeline.setObjectName(QLatin1String("velocity timeline for Flickable ") + objectName());
2012 }
2013}
2014
2015void QQuickFlickable::viewportMoved(Qt::Orientations orient)
2016{
2017 Q_D(QQuickFlickable);
2018 if (orient & Qt::Vertical)
2019 d->viewportAxisMoved(d->vData, minYExtent(), maxYExtent(), d->fixupY_callback);
2020 if (orient & Qt::Horizontal)
2021 d->viewportAxisMoved(d->hData, minXExtent(), maxXExtent(), d->fixupX_callback);
2022 d->updateBeginningEnd();
2023}
2024
2027{
2028 if (!scrollingPhase && (pressed || calcVelocity)) {
2029 int elapsed = data.velocityTime.restart();
2030 if (elapsed > 0) {
2031 qreal velocity = (data.lastPos - data.move.value()) * 1000 / elapsed;
2032 if (qAbs(velocity) > 0) {
2033 velocityTimeline.reset(data.smoothVelocity);
2034 velocityTimeline.set(data.smoothVelocity, velocity);
2035 qCDebug(lcVel) << "touchpad scroll phase: velocity" << velocity;
2036 }
2037 }
2038 } else {
2039 if (timeline.time() > data.vTime) {
2040 velocityTimeline.reset(data.smoothVelocity);
2041 int dt = timeline.time() - data.vTime;
2042 if (dt > 2) {
2043 qreal velocity = (data.lastPos - data.move.value()) * 1000 / dt;
2044 if (!qFuzzyCompare(data.smoothVelocity.value(), velocity))
2045 qCDebug(lcVel) << "velocity" << data.smoothVelocity.value() << "->" << velocity
2046 << "computed as (" << data.lastPos << "-" << data.move.value() << ") * 1000 / ("
2047 << timeline.time() << "-" << data.vTime << ")";
2048 data.smoothVelocity.setValue(velocity);
2049 }
2050 }
2051 }
2052
2053 if (!data.inOvershoot && !data.fixingUp && data.flicking
2054 && (data.move.value() > minExtent || data.move.value() < maxExtent)
2055 && qAbs(data.smoothVelocity.value()) > 10) {
2056 // Increase deceleration if we've passed a bound
2057 qreal overBound = data.move.value() > minExtent
2058 ? data.move.value() - minExtent
2059 : maxExtent - data.move.value();
2060 data.inOvershoot = true;
2061 qreal maxDistance = overShootDistance(qAbs(data.smoothVelocity.value())) - overBound;
2063 if (maxDistance > 0)
2064 timeline.accel(data.move, -data.smoothVelocity.value(), deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
2065 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
2066 }
2067
2068 data.lastPos = data.move.value();
2069 data.vTime = timeline.time();
2070}
2071
2072void QQuickFlickable::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
2073{
2074 Q_D(QQuickFlickable);
2075 QQuickItem::geometryChange(newGeometry, oldGeometry);
2076
2077 bool changed = false;
2078 if (newGeometry.width() != oldGeometry.width()) {
2079 changed = true; // we must update visualArea.widthRatio
2080 if (d->hData.viewSize < 0)
2081 d->contentItem->setWidth(width() - d->hData.startMargin - d->hData.endMargin);
2082 // Make sure that we're entirely in view.
2083 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2085 d->fixupX();
2086 }
2087 }
2088 if (newGeometry.height() != oldGeometry.height()) {
2089 changed = true; // we must update visualArea.heightRatio
2090 if (d->vData.viewSize < 0)
2091 d->contentItem->setHeight(height() - d->vData.startMargin - d->vData.endMargin);
2092 // Make sure that we're entirely in view.
2093 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2095 d->fixupY();
2096 }
2097 }
2098
2099 if (changed)
2100 d->updateBeginningEnd();
2101}
2102
2112void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity)
2113{
2114 Q_D(QQuickFlickable);
2115 d->hData.reset();
2116 d->vData.reset();
2117 d->hData.velocity = xVelocity;
2118 d->vData.velocity = yVelocity;
2119 d->hData.vTime = d->vData.vTime = d->timeline.time();
2120
2121 const bool flickedX = xflick() && !qFuzzyIsNull(xVelocity) && d->flickX(QEvent::TouchUpdate, xVelocity);
2122 const bool flickedY = yflick() && !qFuzzyIsNull(yVelocity) && d->flickY(QEvent::TouchUpdate, yVelocity);
2123
2124 if (flickedX)
2125 d->hMoved = true;
2126 if (flickedY)
2127 d->vMoved = true;
2129 d->flickingStarted(flickedX, flickedY);
2130}
2131
2132void QQuickFlickablePrivate::flickingStarted(bool flickingH, bool flickingV)
2133{
2134 Q_Q(QQuickFlickable);
2135 if (!flickingH && !flickingV)
2136 return;
2137
2138 bool wasFlicking = hData.flicking || vData.flicking;
2139 if (flickingH && !hData.flicking) {
2140 hData.flicking = true;
2141 emit q->flickingHorizontallyChanged();
2142 }
2143 if (flickingV && !vData.flicking) {
2144 vData.flicking = true;
2145 emit q->flickingVerticallyChanged();
2146 }
2147 if (!wasFlicking && (hData.flicking || vData.flicking)) {
2148 emit q->flickingChanged();
2149 emit q->flickStarted();
2150 }
2151}
2152
2160{
2161 Q_D(QQuickFlickable);
2162 d->resetTimeline(d->hData);
2163 d->resetTimeline(d->vData);
2165}
2166
2167void QQuickFlickablePrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
2168{
2169 if (!prop || !prop->data)
2170 return;
2171
2172 if (QQuickItem *i = qmlobject_cast<QQuickItem *>(o)) {
2173 i->setParentItem(static_cast<QQuickFlickablePrivate*>(prop->data)->contentItem);
2174 } else if (QQuickPointerHandler *pointerHandler = qmlobject_cast<QQuickPointerHandler *>(o)) {
2175 static_cast<QQuickFlickablePrivate*>(prop->data)->addPointerHandler(pointerHandler);
2176 } else {
2177 o->setParent(prop->object); // XXX todo - do we want this?
2178 }
2179}
2180
2182{
2183 // XXX todo
2184 return 0;
2185}
2186
2188{
2189 // XXX todo
2190 return nullptr;
2191}
2192
2193void QQuickFlickablePrivate::data_clear(QQmlListProperty<QObject> *)
2194{
2195 // XXX todo
2196}
2197
2198QQmlListProperty<QObject> QQuickFlickable::flickableData()
2199{
2200 Q_D(QQuickFlickable);
2201 return QQmlListProperty<QObject>(this, (void *)d, QQuickFlickablePrivate::data_append,
2205}
2206
2207QQmlListProperty<QQuickItem> QQuickFlickable::flickableChildren()
2208{
2209 Q_D(QQuickFlickable);
2210 return QQuickItemPrivate::get(d->contentItem)->children();
2211}
2212
2239QQuickFlickable::BoundsBehavior QQuickFlickable::boundsBehavior() const
2240{
2241 Q_D(const QQuickFlickable);
2242 return d->boundsBehavior;
2243}
2244
2246{
2247 Q_D(QQuickFlickable);
2248 if (b == d->boundsBehavior)
2249 return;
2250 d->boundsBehavior = b;
2252}
2253
2295{
2296 Q_D(const QQuickFlickable);
2297 return d->rebound;
2298}
2299
2301{
2302 Q_D(QQuickFlickable);
2303 if (transition) {
2304 if (!d->hData.transitionToBounds)
2305 d->hData.transitionToBounds = new QQuickFlickableReboundTransition(this, QLatin1String("x"));
2306 if (!d->vData.transitionToBounds)
2307 d->vData.transitionToBounds = new QQuickFlickableReboundTransition(this, QLatin1String("y"));
2308 }
2309 if (d->rebound != transition) {
2310 d->rebound = transition;
2312 }
2313}
2314
2340{
2341 Q_D(const QQuickFlickable);
2342 return d->hData.viewSize;
2343}
2344
2346{
2347 Q_D(QQuickFlickable);
2348 if (d->hData.viewSize == w)
2349 return;
2350 d->hData.viewSize = w;
2351 if (w < 0)
2352 d->contentItem->setWidth(width() - d->hData.startMargin - d->hData.endMargin);
2353 else
2354 d->contentItem->setWidth(w);
2355 d->hData.markExtentsDirty();
2356 // Make sure that we're entirely in view.
2357 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2359 d->fixupX();
2360 } else if (!d->pressed && d->hData.fixingUp) {
2362 d->fixupX();
2363 }
2365 d->updateBeginningEnd();
2366}
2367
2369{
2370 Q_D(const QQuickFlickable);
2371 return d->vData.viewSize;
2372}
2373
2375{
2376 Q_D(QQuickFlickable);
2377 if (d->vData.viewSize == h)
2378 return;
2379 d->vData.viewSize = h;
2380 if (h < 0)
2381 d->contentItem->setHeight(height() - d->vData.startMargin - d->vData.endMargin);
2382 else
2383 d->contentItem->setHeight(h);
2384 d->vData.markExtentsDirty();
2385 // Make sure that we're entirely in view.
2386 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2388 d->fixupY();
2389 } else if (!d->pressed && d->vData.fixingUp) {
2391 d->fixupY();
2392 }
2394 d->updateBeginningEnd();
2395}
2396
2409{
2410 Q_D(const QQuickFlickable);
2411 return d->vData.startMargin;
2412}
2413
2415{
2416 Q_D(QQuickFlickable);
2417 if (d->vData.startMargin == m)
2418 return;
2419 d->vData.startMargin = m;
2420 d->vData.markExtentsDirty();
2421 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2423 d->fixupY();
2424 }
2426 d->updateBeginningEnd();
2427}
2428
2430{
2431 Q_D(const QQuickFlickable);
2432 return d->vData.endMargin;
2433}
2434
2436{
2437 Q_D(QQuickFlickable);
2438 if (d->vData.endMargin == m)
2439 return;
2440 d->vData.endMargin = m;
2441 d->vData.markExtentsDirty();
2442 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2444 d->fixupY();
2445 }
2447 d->updateBeginningEnd();
2448}
2449
2451{
2452 Q_D(const QQuickFlickable);
2453 return d->hData.startMargin;
2454}
2455
2457{
2458 Q_D(QQuickFlickable);
2459 if (d->hData.startMargin == m)
2460 return;
2461 d->hData.startMargin = m;
2462 d->hData.markExtentsDirty();
2463 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2465 d->fixupX();
2466 }
2468 d->updateBeginningEnd();
2469}
2470
2472{
2473 Q_D(const QQuickFlickable);
2474 return d->hData.endMargin;
2475}
2476
2478{
2479 Q_D(QQuickFlickable);
2480 if (d->hData.endMargin == m)
2481 return;
2482 d->hData.endMargin = m;
2483 d->hData.markExtentsDirty();
2484 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2486 d->fixupX();
2487 }
2489 d->updateBeginningEnd();
2490}
2491
2507{
2508 Q_D(const QQuickFlickable);
2509 return -minYExtent() + d->vData.startMargin;
2510}
2511
2513{
2514 Q_D(const QQuickFlickable);
2515 return -minXExtent() + d->hData.startMargin;
2516}
2517
2518
2532{
2533 Q_D(QQuickFlickable);
2534 const qreal oldHSize = d->hData.viewSize;
2535 const qreal oldVSize = d->vData.viewSize;
2536 const bool needToUpdateWidth = w != oldHSize;
2537 const bool needToUpdateHeight = h != oldVSize;
2538 d->hData.viewSize = w;
2539 d->vData.viewSize = h;
2540 d->contentItem->setSize(QSizeF(w, h));
2541 if (needToUpdateWidth)
2543 if (needToUpdateHeight)
2545
2546 if (center.x() != 0) {
2547 qreal pos = center.x() * w / oldHSize;
2548 setContentX(contentX() + pos - center.x());
2549 }
2550 if (center.y() != 0) {
2551 qreal pos = center.y() * h / oldVSize;
2552 setContentY(contentY() + pos - center.y());
2553 }
2554 d->updateBeginningEnd();
2555}
2556
2566{
2567 Q_D(QQuickFlickable);
2568 d->fixupX();
2569 d->fixupY();
2570}
2571
2573{
2574 Q_D(const QQuickFlickable);
2575 if (d->hData.viewSize < 0)
2576 return width();
2577 else
2578 return d->hData.viewSize;
2579}
2580
2582{
2583 Q_D(const QQuickFlickable);
2584 if (d->vData.viewSize < 0)
2585 return height();
2586 else
2587 return d->vData.viewSize;
2588}
2589
2598{
2599 Q_D(const QQuickFlickable);
2600 const int contentWidthWithMargins = d->contentItem->width() + d->hData.startMargin + d->hData.endMargin;
2601 if ((d->flickableDirection & QQuickFlickable::AutoFlickIfNeeded) && (contentWidthWithMargins > width()))
2602 return true;
2603 if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
2604 return std::floor(qAbs(contentWidthWithMargins - width()));
2605 return d->flickableDirection & QQuickFlickable::HorizontalFlick;
2606}
2607
2616{
2617 Q_D(const QQuickFlickable);
2618 const int contentHeightWithMargins = d->contentItem->height() + d->vData.startMargin + d->vData.endMargin;
2619 if ((d->flickableDirection & QQuickFlickable::AutoFlickIfNeeded) && (contentHeightWithMargins > height()))
2620 return true;
2621 if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
2622 return std::floor(qAbs(contentHeightWithMargins - height()));
2623 return d->flickableDirection & QQuickFlickable::VerticalFlick;
2624}
2625
2627{
2628 Q_D(QQuickFlickable);
2629 // if our mouse grab has been removed (probably by another Flickable),
2630 // fix our state
2631 if (!d->replayingPressEvent)
2632 d->cancelInteraction();
2633}
2634
2636{
2637 Q_Q(QQuickFlickable);
2638 if (pressed) {
2640 pressed = false;
2642 stealMouse = false;
2643 q->setKeepMouseGrab(false);
2644 fixupX();
2645 fixupY();
2646 if (!isViewMoving())
2647 q->movementEnding();
2648 }
2649}
2650
2652{
2653 Q_Q(const QQuickFlickable);
2654 qCDebug(lcHandlerParent) << "reparenting handler" << h << "to contentItem of" << q;
2655 h->setParent(contentItem);
2656 QQuickItemPrivate::get(contentItem)->addPointerHandler(h);
2657}
2658
2668{
2669 const bool isTouch = QQuickDeliveryAgentPrivate::isTouchEvent(event);
2672 return false; // don't filter hover events or wheel events, for example
2673 Q_ASSERT_X(receiver != this, "", "Flickable received a filter event for itself");
2674 Q_D(QQuickFlickable);
2675 // If a touch event contains a new press point, don't steal right away: watch the movements for a while
2676 if (isTouch && static_cast<QTouchEvent *>(event)->touchPointStates().testFlag(QEventPoint::State::Pressed))
2677 d->stealMouse = false;
2678 // If multiple touchpoints are within bounds, don't grab: it's probably meant for multi-touch interaction in some child
2679 if (event->pointCount() > 1) {
2680 qCDebug(lcFilter) << objectName() << "ignoring multi-touch" << event << "for" << receiver;
2681 d->stealMouse = false;
2682 } else {
2683 qCDebug(lcFilter) << objectName() << "filtering" << event << "for" << receiver;
2684 }
2685
2686 const auto &firstPoint = event->points().first();
2687
2688 if (event->pointCount() == 1 && event->exclusiveGrabber(firstPoint) == this) {
2689 // We have an exclusive grab (since we're e.g dragging), but at the same time, we have
2690 // a child with a passive grab (which is why this filter is being called). And because
2691 // of that, we end up getting the same pointer events twice; First in our own event
2692 // handlers (because of the grab), then once more in here, since we filter the child.
2693 // To avoid processing the event twice (e.g avoid calling handleReleaseEvent once more
2694 // from below), we mark the event as filtered, and simply return.
2695 event->setAccepted(true);
2696 return true;
2697 }
2698
2699 QPointF localPos = mapFromScene(firstPoint.scenePosition());
2700 bool receiverDisabled = receiver && !receiver->isEnabled();
2701 bool stealThisEvent = d->stealMouse;
2702 bool receiverKeepsGrab = receiver && (receiver->keepMouseGrab() || receiver->keepTouchGrab());
2703 bool receiverRelinquishGrab = false;
2704
2705 // Special case for MouseArea, try to guess what it does with the event
2706 if (auto *mouseArea = qmlobject_cast<QQuickMouseArea *>(receiver)) {
2707 bool preventStealing = mouseArea->preventStealing();
2708#if QT_CONFIG(quick_draganddrop)
2709 if (mouseArea->drag() && mouseArea->drag()->target())
2710 preventStealing = true;
2711#endif
2712 if (!preventStealing && receiverKeepsGrab) {
2713 receiverRelinquishGrab = !receiverDisabled
2715 && firstPoint.state() == QEventPoint::State::Pressed
2716 && (receiver->acceptedMouseButtons() & static_cast<QMouseEvent *>(event)->button()));
2717 if (receiverRelinquishGrab)
2718 receiverKeepsGrab = false;
2719 }
2720 }
2721
2722 if ((stealThisEvent || contains(localPos)) && (!receiver || !receiverKeepsGrab || receiverDisabled)) {
2723 QScopedPointer<QPointerEvent> localizedEvent(QQuickDeliveryAgentPrivate::clonePointerEvent(event, localPos));
2724 localizedEvent->setAccepted(false);
2725 switch (firstPoint.state()) {
2727 d->handleMoveEvent(localizedEvent.data());
2728 break;
2730 d->handlePressEvent(localizedEvent.data());
2731 d->captureDelayedPress(receiver, event);
2732 // never grab the pointing device on press during filtering: do it later, during a move
2733 d->stealMouse = false;
2734 stealThisEvent = false;
2735 break;
2737 d->handleReleaseEvent(localizedEvent.data());
2738 stealThisEvent = d->stealMouse;
2739 break;
2742 break;
2743 }
2744 if ((receiver && stealThisEvent && !receiverKeepsGrab && receiver != this) || receiverDisabled) {
2745 d->clearDelayedPress();
2746 event->setExclusiveGrabber(firstPoint, this);
2747 } else if (d->delayedPressEvent) {
2748 event->setExclusiveGrabber(firstPoint, this);
2749 }
2750
2751 const bool filtered = !receiverRelinquishGrab && (stealThisEvent || d->delayedPressEvent || receiverDisabled);
2752 if (filtered) {
2753 event->setAccepted(true);
2754 }
2755 return filtered;
2756 } else if (d->lastPosTime != -1) {
2757 d->lastPosTime = -1;
2759 }
2760 if (firstPoint.state() == QEventPoint::State::Released || (receiverKeepsGrab && !receiverDisabled)) {
2761 // mouse released, or another item has claimed the grab
2762 d->lastPosTime = -1;
2763 d->clearDelayedPress();
2764 d->stealMouse = false;
2765 d->pressed = false;
2766 }
2767 return false;
2768}
2769
2775{
2776 Q_D(QQuickFlickable);
2777 QPointerEvent *pointerEvent = e->isPointerEvent() ? static_cast<QPointerEvent *>(e) : nullptr;
2778
2779 auto wantsPointerEvent_helper = [this, d, i, pointerEvent]() {
2780 Q_ASSERT(pointerEvent);
2782 const bool wants = d->wantsPointerEvent(pointerEvent);
2783 // re-localize event back to \a i before returning
2785 return wants;
2786 };
2787
2788 if (!isVisible() || !isEnabled() || !isInteractive() ||
2789 (pointerEvent && !wantsPointerEvent_helper())) {
2790 d->cancelInteraction();
2792 }
2793
2794 if (e->type() == QEvent::UngrabMouse) {
2796 auto spe = static_cast<QSinglePointEvent *>(e);
2797 const QObject *grabber = spe->exclusiveGrabber(spe->points().first());
2798 qCDebug(lcFilter) << "filtering UngrabMouse" << spe->points().first() << "for" << i << "grabber is" << grabber;
2799 if (grabber != this)
2800 mouseUngrabEvent(); // A child has been ungrabbed
2801 } else if (pointerEvent) {
2802 return filterPointerEvent(i, pointerEvent);
2803 }
2804
2806}
2807
2815{
2816 Q_D(const QQuickFlickable);
2817 return d->maxVelocity;
2818}
2819
2821{
2822 Q_D(QQuickFlickable);
2823 if (v == d->maxVelocity)
2824 return;
2825 d->maxVelocity = v;
2827}
2828
2839{
2840 Q_D(const QQuickFlickable);
2841 return d->deceleration;
2842}
2843
2845{
2846 Q_D(QQuickFlickable);
2847 if (deceleration == d->deceleration)
2848 return;
2849 d->deceleration = qMax(0.001, deceleration);
2851}
2852
2854{
2855 Q_D(const QQuickFlickable);
2856 return d->hData.flicking || d->vData.flicking;
2857}
2858
2868{
2869 Q_D(const QQuickFlickable);
2870 return d->hData.flicking;
2871}
2872
2874{
2875 Q_D(const QQuickFlickable);
2876 return d->vData.flicking;
2877}
2878
2888{
2889 Q_D(const QQuickFlickable);
2890 return d->hData.dragging || d->vData.dragging;
2891}
2892
2894{
2895 Q_D(const QQuickFlickable);
2896 return d->hData.dragging;
2897}
2898
2900{
2901 Q_D(const QQuickFlickable);
2902 return d->vData.dragging;
2903}
2904
2906{
2907 Q_Q(QQuickFlickable);
2908 bool wasDragging = hData.dragging || vData.dragging;
2909 if (hMoved && !hData.dragging) {
2910 hData.dragging = true;
2911 emit q->draggingHorizontallyChanged();
2912 }
2913 if (vMoved && !vData.dragging) {
2914 vData.dragging = true;
2915 emit q->draggingVerticallyChanged();
2916 }
2917 if (!wasDragging && (hData.dragging || vData.dragging)) {
2918 emit q->draggingChanged();
2919 emit q->dragStarted();
2920 }
2921}
2922
2924{
2925 Q_Q(QQuickFlickable);
2926 const bool wasDragging = hData.dragging || vData.dragging;
2927 if (hData.dragging) {
2928 hData.dragging = false;
2929 emit q->draggingHorizontallyChanged();
2930 }
2931 if (vData.dragging) {
2932 vData.dragging = false;
2933 emit q->draggingVerticallyChanged();
2934 }
2935 if (wasDragging) {
2936 if (!hData.dragging && !vData.dragging) {
2937 emit q->draggingChanged();
2938 emit q->dragEnded();
2939 }
2940 hData.inRebound = false;
2941 vData.inRebound = false;
2942 }
2943}
2944
2946{
2947 if (timeline.isActive()
2950 return true;
2951 }
2952 return false;
2953}
2954
2974{
2975 Q_D(const QQuickFlickable);
2976 return d->pressDelay;
2977}
2978
2980{
2981 Q_D(QQuickFlickable);
2982 if (d->pressDelay == delay)
2983 return;
2984 d->pressDelay = delay;
2986}
2987
2999{
3000 Q_D(const QQuickFlickable);
3001 return d->hData.moving || d->vData.moving;
3002}
3003
3005{
3006 Q_D(const QQuickFlickable);
3007 return d->hData.moving;
3008}
3009
3011{
3012 Q_D(const QQuickFlickable);
3013 return d->vData.moving;
3014}
3015
3017{
3018 Q_D(QQuickFlickable);
3019 if ( (d->hData.transitionToBounds && d->hData.transitionToBounds->isActive())
3020 || (d->vData.transitionToBounds && d->vData.transitionToBounds->isActive()) ) {
3021 return;
3022 }
3023 // With subclasses such as GridView, velocityTimeline.completed is emitted repeatedly:
3024 // for example setting currentIndex results in a visual "flick" which the user
3025 // didn't initiate directly. We don't want to end movement repeatedly, and in
3026 // that case movementEnding will happen after the sequence of movements ends.
3027 if (d->vData.flicking)
3029 d->updateBeginningEnd();
3030}
3031
3033{
3034 Q_D(QQuickFlickable);
3035 if ( (d->hData.transitionToBounds && d->hData.transitionToBounds->isActive())
3036 || (d->vData.transitionToBounds && d->vData.transitionToBounds->isActive()) ) {
3037 return;
3038 }
3040 d->updateBeginningEnd();
3041}
3042
3044{
3045 Q_D(QQuickFlickable);
3046 bool wasMoving = d->hData.moving || d->vData.moving;
3047 if (d->hMoved && !d->hData.moving) {
3048 d->hData.moving = true;
3050 }
3051 if (d->vMoved && !d->vData.moving) {
3052 d->vData.moving = true;
3054 }
3055
3056 if (!wasMoving && (d->hData.moving || d->vData.moving)) {
3059#if QT_CONFIG(accessibility)
3060 if (QAccessible::isActive()) {
3061 QAccessibleEvent ev(this, QAccessible::ScrollingStart);
3062 QAccessible::updateAccessibility(&ev);
3063 }
3064#endif
3065 }
3066}
3067
3069{
3070 movementEnding(true, true);
3071}
3072
3073void QQuickFlickable::movementEnding(bool hMovementEnding, bool vMovementEnding)
3074{
3075 Q_D(QQuickFlickable);
3076
3077 // emit flicking signals
3078 const bool wasFlicking = d->hData.flicking || d->vData.flicking;
3079 if (hMovementEnding && d->hData.flicking) {
3080 d->hData.flicking = false;
3082 }
3083 if (vMovementEnding && d->vData.flicking) {
3084 d->vData.flicking = false;
3086 }
3087 if (wasFlicking && (!d->hData.flicking || !d->vData.flicking)) {
3089 emit flickEnded();
3090 } else if (d->hData.flickingWhenDragBegan || d->vData.flickingWhenDragBegan) {
3091 d->hData.flickingWhenDragBegan = !hMovementEnding;
3092 d->vData.flickingWhenDragBegan = !vMovementEnding;
3093 emit flickEnded();
3094 }
3095
3096 // emit moving signals
3097 bool wasMoving = isMoving();
3098 if (hMovementEnding && d->hData.moving
3099 && (!d->pressed && !d->stealMouse)) {
3100 d->hData.moving = false;
3101 d->hMoved = false;
3103 }
3104 if (vMovementEnding && d->vData.moving
3105 && (!d->pressed && !d->stealMouse)) {
3106 d->vData.moving = false;
3107 d->vMoved = false;
3109 }
3110 if (wasMoving && !isMoving()) {
3113#if QT_CONFIG(accessibility)
3114 if (QAccessible::isActive()) {
3115 QAccessibleEvent ev(this, QAccessible::ScrollingEnd);
3116 QAccessible::updateAccessibility(&ev);
3117 }
3118#endif
3119 }
3120
3121 if (hMovementEnding) {
3122 d->hData.fixingUp = false;
3123 d->hData.smoothVelocity.setValue(0);
3124 d->hData.previousDragDelta = 0.0;
3125 }
3126 if (vMovementEnding) {
3127 d->vData.fixingUp = false;
3128 d->vData.smoothVelocity.setValue(0);
3129 d->vData.previousDragDelta = 0.0;
3130 }
3131}
3132
3134{
3135 Q_Q(QQuickFlickable);
3136 emit q->horizontalVelocityChanged();
3137 emit q->verticalVelocityChanged();
3138}
3139
3156{
3157 Q_D(const QQuickFlickable);
3158 return d->hData.overshoot;
3159}
3160
3177{
3178 Q_D(const QQuickFlickable);
3179 return d->vData.overshoot;
3180}
3181
3229{
3230 Q_D(const QQuickFlickable);
3231 return d->boundsMovement;
3232}
3233
3235{
3236 Q_D(QQuickFlickable);
3237 if (d->boundsMovement == movement)
3238 return;
3239
3240 d->boundsMovement = movement;
3241 emit boundsMovementChanged();
3242}
3243
3245
3246#include "moc_qquickflickable_p_p.cpp"
3247
3248#include "moc_qquickflickable_p.cpp"
void start(int msec, QObject *obj)
\obsolete Use chrono overload instead.
void stop()
Stops the timer.
static bool sendEvent(QObject *receiver, QEvent *event)
Sends event event directly to receiver receiver, using the notify() function.
\inmodule QtCore
qint64 elapsed() const noexcept
Returns the number of milliseconds since this QElapsedTimer was last started.
void start() noexcept
\typealias QElapsedTimer::Duration Synonym for std::chrono::nanoseconds.
bool isValid() const noexcept
Returns false if the timer has never been started or invalidated by a call to invalidate().
The QEventPoint class provides information about a point in a QPointerEvent.
Definition qeventpoint.h:20
\inmodule QtCore
Definition qcoreevent.h:45
bool isSinglePointEvent() const noexcept
Definition qcoreevent.h:315
Type
This enum type defines the valid event types in Qt.
Definition qcoreevent.h:51
@ UngrabMouse
Definition qcoreevent.h:234
@ MouseMove
Definition qcoreevent.h:63
@ TouchCancel
Definition qcoreevent.h:264
@ TouchUpdate
Definition qcoreevent.h:242
Type type() const
Returns the event type.
Definition qcoreevent.h:304
bool isPointerEvent() const noexcept
Definition qcoreevent.h:314
QGraphicsItem * parentItem() const
Returns a pointer to this item's parent item.
static QPlatformTheme * platformTheme()
\inmodule QtGui
Definition qevent.h:49
static constexpr Policy Preferred
void clear()
Definition qlist.h:434
\inmodule QtGui
Definition qevent.h:196
QObject * parent
Definition qobject.h:73
\inmodule QtCore
Definition qobject.h:103
QString objectName
the name of this object
Definition qobject.h:107
void clear()
void remove(int idx, int count=1)
void append(const T &v)
int count() const
The QPlatformTheme class allows customizing the UI based on themes.
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:343
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:348
A base class for pointer events.
Definition qevent.h:73
virtual void setAccepted(bool accepted) override
\reimp
Definition qevent.cpp:318
static bool isTabletEvent(const QPointerEvent *ev)
static bool isTouchEvent(const QPointerEvent *ev)
static bool isMouseEvent(const QPointerEvent *ev)
static QPointerEvent * clonePointerEvent(QPointerEvent *event, std::optional< QPointF > transformedLocalPos=std::nullopt)
static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold=-1)
static void localizePointerEvent(QPointerEvent *ev, const QQuickItem *dest)
bool contains(const QPointF &point) const override
void resetTimeline(AxisData &data)
static void fixupX_callback(void *)
virtual bool flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, QQuickTimeLineCallback::Callback fixupCallback, QEvent::Type eventType, qreal velocity)
static qsizetype data_count(QQmlListProperty< QObject > *)
void flickingStarted(bool flickingH, bool flickingV)
void captureDelayedPress(QQuickItem *item, QPointerEvent *event)
qint64 computeCurrentTime(QInputEvent *event) const
qreal overShootDistance(qreal velocity) const
bool flickY(QEvent::Type eventType, qreal velocity)
void handleMoveEvent(QPointerEvent *)
bool isInnermostPressDelay(QQuickItem *item) const
QQuickFlickable::BoundsMovement boundsMovement
void adjustContentPos(AxisData &data, qreal toPos)
void drag(qint64 currentTimestamp, QEvent::Type eventType, const QPointF &localPos, const QVector2D &deltas, bool overThreshold, bool momentum, bool velocitySensitiveOverBounds, const QVector2D &velocity)
void addPointerHandler(QQuickPointerHandler *h) override
virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent)
static QQuickFlickablePrivate * get(QQuickFlickable *o)
QQuickFlickable::FlickableDirection flickableDirection
void maybeBeginDrag(qint64 currentTimestamp, const QPointF &pressPosn, Qt::MouseButtons buttons=Qt::NoButton)
static void fixupY_callback(void *)
static void data_clear(QQmlListProperty< QObject > *)
void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) override
static void data_append(QQmlListProperty< QObject > *, QObject *)
QPointerEvent * delayedPressEvent
QQuickFlickableVisibleArea * visibleArea
void handleReleaseEvent(QPointerEvent *)
void viewportAxisMoved(AxisData &data, qreal minExtent, qreal maxExtent, QQuickTimeLineCallback::Callback fixupCallback)
void handlePressEvent(QPointerEvent *)
QQuickFlickable::BoundsBehavior boundsBehavior
static QObject * data_at(QQmlListProperty< QObject > *, qsizetype)
QVector2D firstPointLocalVelocity(QPointerEvent *event)
bool flickX(QEvent::Type eventType, qreal velocity)
bool startTransition(QQuickFlickablePrivate::AxisData *data, qreal toPos)
QQuickFlickableReboundTransition(QQuickFlickable *f, const QString &name)
void yPositionChanged(qreal yPosition)
void heightRatioChanged(qreal heightRatio)
void widthRatioChanged(qreal widthRatio)
void xPositionChanged(qreal xPosition)
QQuickFlickableVisibleArea(QQuickFlickable *parent=nullptr)
void flickDecelerationChanged()
void setMaximumFlickVelocity(qreal)
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
friend class QQuickFlickableReboundTransition
void movementEnded()
void touchEvent(QTouchEvent *event) override
This event handler can be reimplemented in a subclass to receive touch events for an item.
void mouseUngrabEvent() override
This event handler can be reimplemented in a subclass to be notified when a mouse ungrab event has oc...
void setBottomMargin(qreal m)
Q_INVOKABLE void flick(qreal xVelocity, qreal yVelocity)
\qmlmethod QtQuick::Flickable::flick(qreal xVelocity, qreal yVelocity)
void setPixelAligned(bool align)
void flickingChanged()
void maximumFlickVelocityChanged()
bool isAtXEnd() const
\qmlproperty bool QtQuick::Flickable::atXBeginning \qmlproperty bool QtQuick::Flickable::atXEnd \qmlp...
virtual qreal minYExtent() const
void movementStarted()
QQuickFlickable(QQuickItem *parent=nullptr)
\qmlsignal QtQuick::Flickable::dragStarted()
void boundsBehaviorChanged()
Q_INVOKABLE void returnToBounds()
\qmlmethod QtQuick::Flickable::returnToBounds()
void setPressDelay(int delay)
bool isMovingHorizontally() const
QQmlListProperty< QObject > flickableData
bool isInteractive() const
\qmlproperty bool QtQuick::Flickable::interactive
void timerEvent(QTimerEvent *event) override
This event handler can be reimplemented in a subclass to receive timer events for the object.
~QQuickFlickable() override
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
void setContentWidth(qreal)
void setLeftMargin(qreal m)
bool isFlickingVertically() const
void movingChanged()
BoundsBehavior boundsBehavior
virtual void setContentX(qreal pos)
void mousePressEvent(QMouseEvent *event) override
This event handler can be reimplemented in a subclass to receive mouse press events for an item.
Q_INVOKABLE void resizeContent(qreal w, qreal h, QPointF center)
\qmlmethod QtQuick::Flickable::resizeContent(real width, real height, QPointF center)
void leftMarginChanged()
FlickableDirection flickableDirection
void topMarginChanged()
QQuickTransition * rebound
void setRightMargin(qreal m)
void setTopMargin(qreal m)
bool filterPointerEvent(QQuickItem *receiver, QPointerEvent *event)
void interactiveChanged()
virtual void viewportMoved(Qt::Orientations orient)
bool isDraggingHorizontally() const
void flickableDirectionChanged()
void pixelAlignedChanged()
bool isDragging() const
\qmlproperty bool QtQuick::Flickable::dragging \qmlproperty bool QtQuick::Flickable::draggingHorizont...
bool isAtYEnd() const
void setRebound(QQuickTransition *transition)
void bottomMarginChanged()
void movingHorizontallyChanged()
Q_INVOKABLE void cancelFlick()
\qmlmethod QtQuick::Flickable::cancelFlick()
bool childMouseEventFilter(QQuickItem *, QEvent *) override
void flickingVerticallyChanged()
bool isAtYBeginning() const
bool isFlickingHorizontally() const
\qmlproperty bool QtQuick::Flickable::flicking \qmlproperty bool QtQuick::Flickable::flickingHorizont...
void setFlickDeceleration(qreal)
qreal vHeight() const
void setInteractive(bool)
bool isMoving() const
\qmlproperty bool QtQuick::Flickable::moving \qmlproperty bool QtQuick::Flickable::movingHorizontally...
bool isAtXBeginning() const
void mouseReleaseEvent(QMouseEvent *event) override
This event handler can be reimplemented in a subclass to receive mouse release events for an item.
void contentWidthChanged()
friend class QQuickFlickableVisibleArea
virtual void setContentY(qreal pos)
void movingVerticallyChanged()
void pressDelayChanged()
void rightMarginChanged()
void mouseMoveEvent(QMouseEvent *event) override
This event handler can be reimplemented in a subclass to receive mouse move events for an item.
QQuickFlickableVisibleArea * visibleArea
void setFlickableDirection(FlickableDirection)
virtual qreal maxXExtent() const
void flickingHorizontallyChanged()
virtual qreal maxYExtent() const
BoundsMovement boundsMovement
void contentHeightChanged()
void setSynchronousDrag(bool v)
bool isDraggingVertically() const
QQuickItem * contentItem
virtual qreal minXExtent() const
QQmlListProperty< QQuickItem > flickableChildren
void setContentHeight(qreal)
void reboundChanged()
void setBoundsMovement(BoundsMovement movement)
bool isMovingVertically() const
void setBoundsBehavior(BoundsBehavior)
bool isFlicking() const
QTransform windowToItemTransform() const
Returns a transform that maps points from window space into item space.
quint32 replayingPressEvent
void setSizePolicy(const QLayoutPolicy::Policy &horizontalPolicy, const QLayoutPolicy::Policy &verticalPolicy)
QQuickWindow * window
QQuickDeliveryAgentPrivate * deliveryAgentPrivate()
static QQuickItemPrivate * get(QQuickItem *item)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
virtual void mouseReleaseEvent(QMouseEvent *event)
This event handler can be reimplemented in a subclass to receive mouse release events for an item.
Q_INVOKABLE QPointF mapFromItem(const QQuickItem *item, const QPointF &point) const
Maps the given point in item's coordinate system to the equivalent point within this item's coordinat...
Qt::MouseButtons acceptedMouseButtons() const
Returns the mouse buttons accepted by this item.
virtual void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
qreal x
\qmlproperty real QtQuick::Item::x \qmlproperty real QtQuick::Item::y \qmlproperty real QtQuick::Item...
Definition qquickitem.h:72
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...
qreal y
Defines the item's y position relative to its parent.
Definition qquickitem.h:73
bool isVisible() const
virtual Q_INVOKABLE bool contains(const QPointF &point) const
\qmlmethod bool QtQuick::Item::contains(point point)
bool keepTouchGrab() const
Returns whether the touch points grabbed by this item should exclusively remain with this item.
QQuickWindow * window() const
Returns the window in which this item is rendered.
virtual void mousePressEvent(QMouseEvent *event)
This event handler can be reimplemented in a subclass to receive mouse press events for an item.
qreal width
This property holds the width of this item.
Definition qquickitem.h:75
bool keepMouseGrab() const
Returns whether mouse input should exclusively remain with this item.
bool isEnabled() const
virtual void touchEvent(QTouchEvent *event)
This event handler can be reimplemented in a subclass to receive touch events for an item.
qreal height
This property holds the height of this item.
Definition qquickitem.h:76
virtual bool childMouseEventFilter(QQuickItem *, QEvent *)
Reimplement this method to filter the pointer events that are received by this item's children.
void setX(qreal)
void setY(qreal)
virtual void mouseMoveEvent(QMouseEvent *event)
This event handler can be reimplemented in a subclass to receive mouse move events for an item.
void setValue(qreal v) override
Set the current value.
virtual qreal value() const
Return 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...
void transition(const QList< QQuickStateAction > &, QQuickTransition *transition, QObject *defaultTarget=nullptr)
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
\inmodule QtCore\reentrant
Definition qrect.h:484
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:732
constexpr qreal width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:729
A base class for pointer events containing a single point, such as mouse events.
Definition qevent.h:109
Qt::MouseButtons buttons() const
Returns the button state when the event was generated.
Definition qevent.h:117
\inmodule QtCore
Definition qsize.h:208
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
\inmodule QtCore
Definition qcoreevent.h:366
The QTouchEvent class contains parameters that describe a touch event.
Definition qevent.h:917
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:502
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:501
#define this
Definition dialogs.cpp:9
QPushButton * button
[2]
direction
Combined button and popup list for selecting options.
@ LeftButton
Definition qnamespace.h:58
@ NoButton
Definition qnamespace.h:57
@ Horizontal
Definition qnamespace.h:99
@ Vertical
Definition qnamespace.h:100
@ MouseEventNotSynthesized
@ ScrollBegin
@ ScrollUpdate
@ ScrollMomentum
@ NoScrollPhase
@ ScrollEnd
@ XAxis
@ YAxis
#define qApp
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:349
qfloat16 qSqrt(qfloat16 f)
Definition qfloat16.h:289
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
auto qAtan(T v)
Definition qmath.h:84
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
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:52
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLint GLfloat GLfloat GLfloat v2
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLfloat GLfloat GLfloat GLfloat GLfloat maxY
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLenum GLsizei count
GLfloat minY
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLint GLsizei width
GLenum target
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLuint name
GLint y
GLfloat GLfloat GLfloat GLfloat maxX
GLfloat GLfloat GLfloat GLfloat h
struct _cl_event * event
GLuint GLenum GLenum transform
GLuint GLfloat * val
GLdouble GLdouble t
Definition qopenglext.h:243
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLfloat GLfloat p
[1]
#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.
static bool fuzzyLessThanOrEqualTo(qreal a, qreal b)
static qreal EaseOvershoot(qreal t)
static QT_BEGIN_NAMESPACE const int RetainGrabVelocity
const qreal _q_MaximumWheelDeceleration
#define QML_FLICK_SAMPLEBUFFER
const qreal _q_MinimumFlickVelocity
#define QML_FLICK_OVERSHOOT
#define QML_FLICK_MULTIFLICK_MAXBOOST
#define QML_FLICK_MULTIFLICK_THRESHOLD
#define QML_FLICK_OVERSHOOTFRICTION
#define QML_FLICK_DISCARDSAMPLES
#define QML_FLICK_MULTIFLICK_RATIO
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define fp
static double elapsed(qint64 after, qint64 before)
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
#define emit
ptrdiff_t qsizetype
Definition qtypes.h:165
long long qint64
Definition qtypes.h:60
double qreal
Definition qtypes.h:187
bool testFlag(MaskType mask, FlagType flag)
std::uniform_real_distribution dist(1, 2.5)
[2]
QObject::connect nullptr
QPoint oldPosition
[6]
QGraphicsItem * item
void addVelocitySample(qreal v, qreal maxVelocity)
QQuickFlickableReboundTransition * transitionToBounds
QPODVector< qreal, 10 > velocityBuffer
QQuickFlickablePrivate::Velocity smoothVelocity
QQuickTimeLineValueProxy< QQuickFlickablePrivate > move
void setValue(qreal v) override
Set the current value.