10#if QT_CONFIG(quick_draganddrop)
11#include "qquickdrag_p.h"
14#include <QtQuick/private/qquickpointerhandler_p.h>
15#include <QtQuick/private/qquicktransition_p.h>
16#include <private/qqmlglobal_p.h>
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 <QtGui/qaccessible.h>
25#include <QtCore/qmath.h>
26#include <qpa/qplatformtheme.h>
34Q_STATIC_LOGGING_CATEGORY(lcFilter,
"qt.quick.flickable.filter")
35Q_STATIC_LOGGING_CATEGORY(lcReplay,
"qt.quick.flickable.replay")
36Q_STATIC_LOGGING_CATEGORY(lcWheel,
"qt.quick.flickable.wheel")
37Q_STATIC_LOGGING_CATEGORY(lcVel,
"qt.quick.flickable.velocity")
41static const int RetainGrabVelocity = 100;
47QQuickFlickableVisibleArea::QQuickFlickableVisibleArea(QQuickFlickable *parent)
48 : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.)
49 , m_yPosition(0.), m_heightRatio(0.)
53qreal QQuickFlickableVisibleArea::widthRatio()
const
58qreal QQuickFlickableVisibleArea::xPosition()
const
63qreal QQuickFlickableVisibleArea::heightRatio()
const
68qreal QQuickFlickableVisibleArea::yPosition()
const
73void QQuickFlickableVisibleArea::updateVisible()
75 QQuickFlickablePrivate *p = QQuickFlickablePrivate::get(flickable);
79 bool changeWidth =
false;
80 bool changeHeight =
false;
83 const qreal viewheight = flickable->height();
84 const qreal maxyextent = -flickable->maxYExtent() + flickable->minYExtent();
85 const qreal maxYBounds = maxyextent + viewheight;
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;
94 if (pageSize != m_heightRatio) {
95 m_heightRatio = pageSize;
98 if (pagePos != m_yPosition) {
99 m_yPosition = pagePos;
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;
116 if (pageSize != m_widthRatio) {
117 m_widthRatio = pageSize;
120 if (pagePos != m_xPosition) {
121 m_xPosition = pagePos;
126 emit xPositionChanged(m_xPosition);
128 emit yPositionChanged(m_yPosition);
130 emit widthRatioChanged(m_widthRatio);
132 emit heightRatioChanged(m_heightRatio);
150 QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flickable);
151 if (!fp->rebound || !fp->rebound->enabled())
155 axisData->transitionTo = toPos;
156 axisData->transitionToSet =
true;
159 actions << QQuickStateAction(fp->contentItem, propName, toPos);
160 QQuickTransitionManager::transition(actions, fp->rebound, fp->contentItem);
169 if (!flickable || !isRunning())
171 QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flickable);
172 if (axisData == &fp->hData)
173 axisData->move.setValue(-flickable->contentX());
175 axisData->move.setValue(-flickable->contentY());
184 axisData->move.setValue(axisData->transitionTo);
185 QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flickable);
188 if (!fp->hData.transitionToBounds->isActive()
189 && !fp->vData.transitionToBounds->isActive()) {
190 flickable->movementEnding();
195 QQuickStateOperation::ActionList actions;
196 QQuickFlickable *flickable;
197 QQuickFlickablePrivate::AxisData *axisData;
202QQuickFlickablePrivate::AxisData::~AxisData()
204 delete transitionToBounds;
212 auto *d = QQuickItemPrivate::get(
this);
217 d->eventHandlingChildrenWithinBounds =
false;
218 d->eventHandlingChildrenWithinBoundsSet =
true;
223
224
225
226
227
228
229
230
233 const QQuickItem *flickable = parentItem();
234 const QPointF posInFlickable = flickable->mapFromItem(
this, point);
235 return flickable->contains(posInFlickable);
239QQuickFlickablePrivate::QQuickFlickablePrivate()
240 : contentItem(
new QQuickFlickableContentItem)
241 , hData(
this, &QQuickFlickablePrivate::setViewportX)
242 , vData(
this, &QQuickFlickablePrivate::setViewportY)
243 , hMoved(
false), vMoved(
false)
244 , stealGrab(
false), pressed(
false)
245 , scrollingPhase(
false), interactive(
true), calcVelocity(
false)
246 , pixelAligned(
false)
248 , acceptedButtons(Qt::LeftButton)
251 , deceleration(QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickDeceleration).toReal())
252 , wheelDeceleration(15000)
253 , maxVelocity(QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickMaximumVelocity).toReal())
254 , delayedPressEvent(
nullptr), pressDelay(0), fixupDuration(400)
255 , flickBoost(1.0), initialWheelFlickDistance(qApp->styleHints()->wheelScrollLines() * 24)
256 , fixupMode(Normal), vTime(0), visibleArea(
nullptr)
257 , flickableDirection(QQuickFlickable::AutoFlickDirection)
258 , boundsBehavior(QQuickFlickable::DragAndOvershootBounds)
259 , boundsMovement(QQuickFlickable::FollowBoundsBehavior)
262 const int wheelDecelerationEnv = qEnvironmentVariableIntValue(
"QT_QUICK_FLICKABLE_WHEEL_DECELERATION");
263 if (wheelDecelerationEnv > 0)
264 wheelDeceleration = wheelDecelerationEnv;
267void QQuickFlickablePrivate::init()
269 Q_Q(QQuickFlickable);
270 QQml_setParent_noEvent(contentItem, q);
271 contentItem->setParentItem(q);
272 qmlobject_connect(&timeline, QQuickTimeLine, SIGNAL(completed()),
273 q, QQuickFlickable, SLOT(timelineCompleted()));
274 qmlobject_connect(&velocityTimeline, QQuickTimeLine, SIGNAL(completed()),
275 q, QQuickFlickable, SLOT(velocityTimelineCompleted()));
276 q->setAcceptedMouseButtons(acceptedButtons);
277 q->setAcceptTouchEvents(
true);
278 q->setFiltersChildMouseEvents(
true);
279 q->setFlag(QQuickItem::ItemIsViewport);
280 QQuickItemPrivate *viewportPrivate = QQuickItemPrivate::get(contentItem);
281 viewportPrivate->addItemChangeListener(
this, QQuickItemPrivate::Geometry);
282 setSizePolicy(QLayoutPolicy::Expanding, QLayoutPolicy::Expanding);
286
287
288
289
290qreal QQuickFlickablePrivate::overShootDistance(qreal velocity)
const
292 if (maxVelocity <= 0)
298void QQuickFlickablePrivate::AxisData::addVelocitySample(qreal v, qreal maxVelocity)
302 else if (v < -maxVelocity)
305 velocityBuffer[velocityWritePos] = v;
311void QQuickFlickablePrivate::AxisData::updateVelocity()
320 for (
int i = 0; i < count; ++i) {
328void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change,
const QRectF &oldGeom)
330 Q_Q(QQuickFlickable);
331 if (item == contentItem) {
332 Qt::Orientations orient;
333 if (change.xChange())
334 orient |= Qt::Horizontal;
335 if (change.yChange())
336 orient |= Qt::Vertical;
338 q->viewportMoved(orient);
339 const QPointF deltaMoved = item->position() - oldGeom.topLeft();
340 if (hData.contentPositionChangedExternallyDuringDrag)
341 hData.pressPos += deltaMoved.x();
342 if (vData.contentPositionChangedExternallyDuringDrag)
343 vData.pressPos += deltaMoved.y();
345#if QT_CONFIG(accessibility)
346 bool updateAccessibility =
false;
348 if (orient & Qt::Horizontal) {
349 emit q->contentXChanged();
350#if QT_CONFIG(accessibility)
351 updateAccessibility =
true;
354 if (orient & Qt::Vertical) {
355 emit q->contentYChanged();
356#if QT_CONFIG(accessibility)
357 updateAccessibility =
true;
360#if QT_CONFIG(accessibility)
361 if (updateAccessibility && QAccessible::isActive()) {
362 if (!m_scrollEventTimer.isValid() || m_scrollEventTimer.elapsed() >= 250) {
363 m_scrollEventTimer.restart();
364 QAccessibleEvent ev(q, QAccessible::ScrollingPositionChanged);
365 QAccessible::updateAccessibility(&ev);
372bool QQuickFlickablePrivate::flickX(QEvent::Type eventType, qreal velocity)
374 Q_Q(QQuickFlickable);
375 return flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, eventType, velocity);
378bool QQuickFlickablePrivate::flickY(QEvent::Type eventType, qreal velocity)
380 Q_Q(QQuickFlickable);
381 return flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, eventType, velocity);
384bool QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal,
385 QQuickTimeLineCallback::Callback fixupCallback,
386 QEvent::Type eventType, qreal velocity)
388 Q_Q(QQuickFlickable);
389 qreal maxDistance = -1;
390 data.fixingUp =
false;
393 maxDistance = qAbs(minExtent - data.move.value());
394 data.flickTarget = minExtent;
396 maxDistance = qAbs(maxExtent - data.move.value());
397 data.flickTarget = maxExtent;
399 if (maxDistance > 0 || boundsBehavior & QQuickFlickable::OvershootBounds) {
401 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
408 qreal accel = eventType == QEvent::Wheel ? wheelDeceleration : deceleration;
409 qCDebug(lcFlickable) <<
"choosing deceleration" << accel <<
"for" << eventType;
412 qreal dist = v2 / (accel * 2.0);
415 qreal target = std::round(data.move.value() - dist);
416 dist = -target + data.move.value();
417 accel = v2 / (2.0f * qAbs(dist));
420 if (!data.inOvershoot) {
421 if (boundsBehavior & QQuickFlickable::OvershootBounds)
422 timeline.accel(data.move, v, accel);
424 timeline.accel(data.move, v, accel, maxDistance);
426 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback,
this));
429 return !hData.flicking && q->xflick();
430 else if (&data == &vData)
431 return !vData.flicking && q->yflick();
435 fixup(data, minExtent, maxExtent);
440void QQuickFlickablePrivate::fixupY_callback(
void *data)
442 ((QQuickFlickablePrivate *)data)->fixupY();
445void QQuickFlickablePrivate::fixupX_callback(
void *data)
447 ((QQuickFlickablePrivate *)data)->fixupX();
450void QQuickFlickablePrivate::fixupX()
452 Q_Q(QQuickFlickable);
453 if (!q->isComponentComplete())
455 fixup(hData, q->minXExtent(), q->maxXExtent());
458void QQuickFlickablePrivate::fixupY()
460 Q_Q(QQuickFlickable);
461 if (!q->isComponentComplete())
463 fixup(vData, q->minYExtent(), q->maxYExtent());
467
468
469
470
471
472
473
474void QQuickFlickablePrivate::adjustContentPos(AxisData &data, qreal toPos)
476 Q_Q(QQuickFlickable);
479 timeline.set(data.move, toPos);
484 timeline.move(data.move, toPos, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
485 data.fixingUp =
true;
488 if (data.transitionToBounds && data.transitionToBounds->startTransition(&data, toPos)) {
489 q->movementStarting();
490 data.fixingUp =
true;
492 qreal dist = toPos - data.move;
493 timeline.move(data.move, toPos - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
494 timeline.move(data.move, toPos, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
495 data.fixingUp =
true;
501void QQuickFlickablePrivate::resetTimeline(AxisData &data)
503 timeline.reset(data.move);
504 if (data.transitionToBounds)
505 data.transitionToBounds->stopTransition();
508void QQuickFlickablePrivate::clearTimeline()
511 if (hData.transitionToBounds)
512 hData.transitionToBounds->stopTransition();
513 if (vData.transitionToBounds)
514 vData.transitionToBounds->stopTransition();
518
519
520
521
522
523
524
525
526
527void QQuickFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
532 if (data.move.value() >= minExtent || maxExtent > minExtent) {
533 const bool wasActive = timeline.isActive();
535 if (data.move.value() != minExtent)
536 adjustContentPos(data, minExtent);
538 timeline.move(data.move, data.move.value(), QEasingCurve(QEasingCurve::Linear), 1);
539 }
else if (data.move.value() <= maxExtent) {
541 adjustContentPos(data, maxExtent);
542 }
else if (-std::round(-data.move.value()) != data.move.value()) {
545 qreal val = data.move.value();
546 if (std::abs(std::round(val) - val) < 0.25)
547 val = std::round(val);
548 else if (data.smoothVelocity.value() > 0)
549 val = std::ceil(val);
550 else if (data.smoothVelocity.value() < 0)
551 val = std::floor(val);
553 val = std::round(val);
554 timeline.set(data.move, val);
556 data.inOvershoot =
false;
558 data.vTime = timeline.time();
563 if (a == 0.0 || b == 0.0) {
568 return a <= b || qFuzzyCompare(a, b);
572
573
574
575
576
577
578
579
580void QQuickFlickablePrivate::updateBeginningEnd()
582 Q_Q(QQuickFlickable);
583 bool atXBeginningChange =
false, atXEndChange =
false;
584 bool atYBeginningChange =
false, atYEndChange =
false;
587 const qreal maxyextent = -q->maxYExtent();
588 const qreal minyextent = -q->minYExtent();
589 const qreal ypos = pixelAligned ? -std::round(vData.move.value()) : -vData.move.value();
590 bool atBeginning = fuzzyLessThanOrEqualTo(ypos, std::ceil(minyextent));
591 bool atEnd = fuzzyLessThanOrEqualTo(std::floor(maxyextent), ypos);
593 if (atBeginning != vData.atBeginning) {
594 vData.atBeginning = atBeginning;
595 atYBeginningChange =
true;
596 if (!vData.moving && atBeginning)
597 vData.smoothVelocity.setValue(0);
599 if (atEnd != vData.atEnd) {
602 if (!vData.moving && atEnd)
603 vData.smoothVelocity.setValue(0);
607 const qreal maxxextent = -q->maxXExtent();
608 const qreal minxextent = -q->minXExtent();
609 const qreal xpos = pixelAligned ? -std::round(hData.move.value()) : -hData.move.value();
610 atBeginning = fuzzyLessThanOrEqualTo(xpos, std::ceil(minxextent));
611 atEnd = fuzzyLessThanOrEqualTo(std::floor(maxxextent), xpos);
613 if (atBeginning != hData.atBeginning) {
614 hData.atBeginning = atBeginning;
615 atXBeginningChange =
true;
616 if (!hData.moving && atBeginning)
617 hData.smoothVelocity.setValue(0);
619 if (atEnd != hData.atEnd) {
622 if (!hData.moving && atEnd)
623 hData.smoothVelocity.setValue(0);
626 if (vData.extentsChanged) {
627 vData.extentsChanged =
false;
628 qreal originY = q->originY();
629 if (vData.origin != originY) {
630 vData.origin = originY;
631 emit q->originYChanged();
635 if (hData.extentsChanged) {
636 hData.extentsChanged =
false;
637 qreal originX = q->originX();
638 if (hData.origin != originX) {
639 hData.origin = originX;
640 emit q->originXChanged();
644 if (atXEndChange || atYEndChange || atXBeginningChange || atYBeginningChange)
645 emit q->isAtBoundaryChanged();
647 emit q->atXEndChanged();
648 if (atXBeginningChange)
649 emit q->atXBeginningChanged();
651 emit q->atYEndChanged();
652 if (atYBeginningChange)
653 emit q->atYBeginningChanged();
656 visibleArea->updateVisible();
660
661
662
663
664
667
668
669
670
671
672
673
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
760
761
762
763
764
767
768
769
770
771
772
773
774
777
778
779
780
781
782
785
786
787
788
789
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811QQuickFlickable::QQuickFlickable(QQuickItem *parent)
812 : QQuickItem(*(
new QQuickFlickablePrivate), parent)
814 Q_D(QQuickFlickable);
818QQuickFlickable::QQuickFlickable(QQuickFlickablePrivate &dd, QQuickItem *parent)
819 : QQuickItem(dd, parent)
821 Q_D(QQuickFlickable);
825QQuickFlickable::~QQuickFlickable()
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849qreal QQuickFlickable::contentX()
const
851 Q_D(
const QQuickFlickable);
852 return -d->contentItem->x();
855void QQuickFlickable::setContentX(qreal pos)
857 Q_D(QQuickFlickable);
858 d->hData.explicitValue =
true;
859 d->resetTimeline(d->hData);
860 d->hData.vTime = d->timeline.time();
861 if (isMoving() || isFlicking())
862 movementEnding(
true,
false);
863 if (!qFuzzyCompare(-pos, d->hData.move.value())) {
864 d->hData.contentPositionChangedExternallyDuringDrag = d->hData.dragging;
865 d->hData.move.setValue(-pos);
866 d->hData.contentPositionChangedExternallyDuringDrag =
false;
870qreal QQuickFlickable::contentY()
const
872 Q_D(
const QQuickFlickable);
873 return -d->contentItem->y();
876void QQuickFlickable::setContentY(qreal pos)
878 Q_D(QQuickFlickable);
879 d->vData.explicitValue =
true;
880 d->resetTimeline(d->vData);
881 d->vData.vTime = d->timeline.time();
882 if (isMoving() || isFlicking())
883 movementEnding(
false,
true);
884 if (!qFuzzyCompare(-pos, d->vData.move.value())) {
885 d->vData.contentPositionChangedExternallyDuringDrag = d->vData.dragging;
886 d->vData.move.setValue(-pos);
887 d->vData.contentPositionChangedExternallyDuringDrag =
false;
892
893
894
895
896
897
898
899
900
901
902
903
904bool QQuickFlickable::isInteractive()
const
906 Q_D(
const QQuickFlickable);
907 return d->interactive;
910void QQuickFlickable::setInteractive(
bool interactive)
912 Q_D(QQuickFlickable);
913 if (interactive != d->interactive) {
914 d->interactive = interactive;
916 d->cancelInteraction();
918 emit interactiveChanged();
923
924
925
926
927
928
929
930
931
932
933
934
935qreal QQuickFlickable::horizontalVelocity()
const
937 Q_D(
const QQuickFlickable);
938 return d->hData.smoothVelocity.value();
941qreal QQuickFlickable::verticalVelocity()
const
943 Q_D(
const QQuickFlickable);
944 return d->vData.smoothVelocity.value();
948
949
950
951
952
953
954
955
956bool QQuickFlickable::isAtXEnd()
const
958 Q_D(
const QQuickFlickable);
959 return d->hData.atEnd;
962bool QQuickFlickable::isAtXBeginning()
const
964 Q_D(
const QQuickFlickable);
965 return d->hData.atBeginning;
968bool QQuickFlickable::isAtYEnd()
const
970 Q_D(
const QQuickFlickable);
971 return d->vData.atEnd;
974bool QQuickFlickable::isAtYBeginning()
const
976 Q_D(
const QQuickFlickable);
977 return d->vData.atBeginning;
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998QQuickItem *QQuickFlickable::contentItem()
const
1000 Q_D(
const QQuickFlickable);
1001 return d->contentItem;
1004QQuickFlickableVisibleArea *QQuickFlickable::visibleArea()
1006 Q_D(QQuickFlickable);
1007 if (!d->visibleArea) {
1008 d->visibleArea =
new QQuickFlickableVisibleArea(
this);
1009 d->visibleArea->updateVisible();
1011 return d->visibleArea;
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033QQuickFlickable::FlickableDirection QQuickFlickable::flickableDirection()
const
1035 Q_D(
const QQuickFlickable);
1036 return d->flickableDirection;
1039void QQuickFlickable::setFlickableDirection(FlickableDirection direction)
1041 Q_D(QQuickFlickable);
1042 if (direction != d->flickableDirection) {
1043 d->flickableDirection = direction;
1044 emit flickableDirectionChanged();
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060bool QQuickFlickable::pixelAligned()
const
1062 Q_D(
const QQuickFlickable);
1063 return d->pixelAligned;
1066void QQuickFlickable::setPixelAligned(
bool align)
1068 Q_D(QQuickFlickable);
1069 if (align != d->pixelAligned) {
1070 d->pixelAligned = align;
1071 emit pixelAlignedChanged();
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087bool QQuickFlickable::synchronousDrag()
const
1089 Q_D(
const QQuickFlickable);
1093void QQuickFlickable::setSynchronousDrag(
bool v)
1095 Q_D(QQuickFlickable);
1096 if (v != d->syncDrag) {
1098 emit synchronousDragChanged();
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117Qt::MouseButtons QQuickFlickable::acceptedButtons()
const
1119 Q_D(
const QQuickFlickable);
1120 return d->acceptedButtons;
1123void QQuickFlickable::setAcceptedButtons(Qt::MouseButtons buttons)
1125 Q_D(QQuickFlickable);
1126 if (d->acceptedButtons == buttons)
1129 d->acceptedButtons = buttons;
1130 setAcceptedMouseButtons(buttons);
1131 emit acceptedButtonsChanged();
1135
1136
1137
1138QVector2D QQuickFlickablePrivate::firstPointLocalVelocity(QPointerEvent *event)
1140 QTransform transform = windowToItemTransform();
1142 return QVector2D(transform.map(event->point(0).velocity().toPointF()) - transform.map(QPointF()));
1145qint64 QQuickFlickablePrivate::computeCurrentTime(QInputEvent *event)
const
1147 if (0 != event->timestamp())
1148 return event->timestamp();
1149 if (!timer.isValid())
1151 return timer.elapsed();
1154void QQuickFlickablePrivate::handlePressEvent(QPointerEvent *event)
1156 Q_Q(QQuickFlickable);
1158 if (interactive && timeline.isActive()
1159 && ((qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity && !hData.fixingUp && !hData.inOvershoot)
1160 || (qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity && !vData.fixingUp && !vData.inOvershoot))) {
1164 int flickTime = timeline.time();
1165 if (flickTime > 600) {
1167 hData.continuousFlickVelocity = 0;
1168 vData.continuousFlickVelocity = 0;
1171 hData.continuousFlickVelocity = -hData.smoothVelocity.value();
1172 vData.continuousFlickVelocity = -vData.smoothVelocity.value();
1173 if (flickTime > 300)
1174 flickBoost = qMax(1.0, flickBoost - 0.5);
1178 hData.continuousFlickVelocity = 0;
1179 vData.continuousFlickVelocity = 0;
1182 if (event->isSinglePointEvent())
1183 q->setKeepMouseGrab(stealGrab);
1185 q->setKeepTouchGrab(stealGrab);
1187 maybeBeginDrag(computeCurrentTime(event), event->points().first().position(),
1188 event->isSinglePointEvent() ?
static_cast<QSinglePointEvent *>(event)->buttons()
1192void QQuickFlickablePrivate::maybeBeginDrag(qint64 currentTimestamp,
const QPointF &pressPosn, Qt::MouseButtons buttons)
1194 Q_Q(QQuickFlickable);
1195 clearDelayedPress();
1197 pressed = (buttons == Qt::NoButton) || (acceptedButtons != Qt::NoButton && (buttons & acceptedButtons) != 0);
1199 if (hData.transitionToBounds)
1200 hData.transitionToBounds->stopTransition();
1201 if (vData.transitionToBounds)
1202 vData.transitionToBounds->stopTransition();
1203 if (!hData.fixingUp)
1204 resetTimeline(hData);
1205 if (!vData.fixingUp)
1206 resetTimeline(vData);
1210 hData.dragMinBound = q->minXExtent() - hData.startMargin;
1211 vData.dragMinBound = q->minYExtent() - vData.startMargin;
1212 hData.dragMaxBound = q->maxXExtent() + hData.endMargin;
1213 vData.dragMaxBound = q->maxYExtent() + vData.endMargin;
1215 lastPos = QPointF();
1216 pressPos = pressPosn;
1217 hData.pressPos = hData.move.value();
1218 vData.pressPos = vData.move.value();
1219 const bool wasFlicking = hData.flicking || vData.flicking;
1220 hData.flickingWhenDragBegan = hData.flicking;
1221 vData.flickingWhenDragBegan = vData.flicking;
1222 if (hData.flicking) {
1223 hData.flicking =
false;
1224 emit q->flickingHorizontallyChanged();
1226 if (vData.flicking) {
1227 vData.flicking =
false;
1228 emit q->flickingVerticallyChanged();
1231 emit q->flickingChanged();
1232 lastPosTime = lastPressTime = currentTimestamp;
1233 vData.velocityTime.start();
1234 hData.velocityTime.start();
1237void QQuickFlickablePrivate::drag(qint64 currentTimestamp, QEvent::Type eventType,
const QPointF &localPos,
1238 const QVector2D &deltas,
bool overThreshold,
bool momentum,
1239 bool velocitySensitiveOverBounds,
const QVector2D &velocity)
1241 Q_Q(QQuickFlickable);
1242 bool rejectY =
false;
1243 bool rejectX =
false;
1245 bool keepY = q->yflick();
1246 bool keepX = q->xflick();
1248 bool stealY =
false;
1249 bool stealX =
false;
1250 bool isTouchEvent =
false;
1251 switch (eventType) {
1252 case QEvent::MouseMove:
1253 stealX = stealY = stealGrab;
1256 stealX = stealY = scrollingPhase;
1258 case QEvent::TouchUpdate:
1259 stealX = stealY = stealGrab;
1260 isTouchEvent =
true;
1266 bool prevHMoved = hMoved;
1267 bool prevVMoved = vMoved;
1269 qint64 elapsedSincePress = currentTimestamp - lastPressTime;
1270 qCDebug(lcFlickable).nospace() << currentTimestamp <<
' ' << eventType <<
" drag @ " << localPos.x() <<
',' << localPos.y()
1271 <<
" \u0394 " << deltas.x() <<
',' << deltas.y() <<
" vel " << velocity.x() <<
',' << velocity.y()
1272 <<
" thrsld? " << overThreshold <<
" momentum? " << momentum <<
" velSens? " << velocitySensitiveOverBounds
1273 <<
" sincePress " << elapsedSincePress;
1276 qreal dy = deltas.y();
1277 if (overThreshold || elapsedSincePress > 200) {
1278 if (!vMoved && !vData.dragging)
1279 vData.dragStartOffset = dy;
1280 qreal newY = dy + vData.pressPos - (syncDrag ? 0 : vData.dragStartOffset);
1284 const qreal minY = vData.dragMinBound + vData.startMargin;
1285 const qreal maxY = vData.dragMaxBound - vData.endMargin;
1286 if (!(boundsBehavior & QQuickFlickable::DragOverBounds)) {
1287 if (fuzzyLessThanOrEqualTo(newY, maxY)) {
1289 rejectY = vData.pressPos == maxY && vData.move.value() == maxY && dy < 0;
1291 if (fuzzyLessThanOrEqualTo(minY, newY)) {
1293 rejectY |= vData.pressPos == minY && vData.move.value() == minY && dy > 0;
1297 if (vel > 0. && vel > vData.velocity)
1299 else if (vel < 0. && vel < vData.velocity)
1303 if (momentum && vData.atBeginning) {
1304 if (!vData.inRebound) {
1305 vData.inRebound =
true;
1306 q->returnToBounds();
1310 if (velocitySensitiveOverBounds) {
1313 newY = minY + overshoot;
1315 newY = minY + (newY - minY) / 2;
1317 }
else if (newY < maxY && maxY - minY <= 0) {
1319 if (momentum && vData.atEnd) {
1320 if (!vData.inRebound) {
1321 vData.inRebound =
true;
1322 q->returnToBounds();
1326 if (velocitySensitiveOverBounds) {
1329 newY = maxY - overshoot;
1331 newY = maxY + (newY - maxY) / 2;
1335 if (!rejectY && stealGrab && dy != vData.previousDragDelta) {
1337 vData.move.setValue(newY);
1340 if (!rejectY && overThreshold)
1343 if ((newY >= minY && vData.pressPos == minY && vData.move.value() == minY && dy > 0)
1344 || (newY <= maxY && vData.pressPos == maxY && vData.move.value() == maxY && dy < 0)) {
1348 vData.previousDragDelta = dy;
1352 qreal dx = deltas.x();
1353 if (overThreshold || elapsedSincePress > 200) {
1354 if (!hMoved && !hData.dragging)
1355 hData.dragStartOffset = dx;
1356 qreal newX = dx + hData.pressPos - (syncDrag ? 0 : hData.dragStartOffset);
1357 const qreal minX = hData.dragMinBound + hData.startMargin;
1358 const qreal maxX = hData.dragMaxBound - hData.endMargin;
1359 if (!(boundsBehavior & QQuickFlickable::DragOverBounds)) {
1360 if (fuzzyLessThanOrEqualTo(newX, maxX)) {
1362 rejectX = hData.pressPos == maxX && hData.move.value() == maxX && dx < 0;
1364 if (fuzzyLessThanOrEqualTo(minX, newX)) {
1366 rejectX |= hData.pressPos == minX && hData.move.value() == minX && dx > 0;
1370 if (vel > 0. && vel > hData.velocity)
1372 else if (vel < 0. && vel < hData.velocity)
1376 if (momentum && hData.atBeginning) {
1377 if (!hData.inRebound) {
1378 hData.inRebound =
true;
1379 q->returnToBounds();
1383 if (velocitySensitiveOverBounds) {
1386 newX = minX + overshoot;
1388 newX = minX + (newX - minX) / 2;
1390 }
else if (newX < maxX && maxX - minX <= 0) {
1392 if (momentum && hData.atEnd) {
1393 if (!hData.inRebound) {
1394 hData.inRebound =
true;
1395 q->returnToBounds();
1399 if (velocitySensitiveOverBounds) {
1402 newX = maxX - overshoot;
1404 newX = maxX + (newX - maxX) / 2;
1408 if (!rejectX && stealGrab && dx != hData.previousDragDelta) {
1410 hData.move.setValue(newX);
1414 if (!rejectX && overThreshold)
1417 if ((newX >= minX && vData.pressPos == minX && vData.move.value() == minX && dx > 0)
1418 || (newX <= maxX && vData.pressPos == maxX && vData.move.value() == maxX && dx < 0)) {
1422 hData.previousDragDelta = dx;
1425 stealGrab = stealX || stealY;
1427 if ((stealX && keepX) || (stealY && keepY)) {
1429 q->setKeepTouchGrab(
true);
1431 q->setKeepMouseGrab(
true);
1433 clearDelayedPress();
1437 vData.velocitySamples = 0;
1438 vData.velocityWritePos = 0;
1442 hData.velocitySamples = 0;
1443 hData.velocityWritePos = 0;
1447 if (momentum && !hData.flicking && !vData.flicking)
1448 flickingStarted(hData.velocity != 0, vData.velocity != 0);
1451 if ((hMoved && !prevHMoved) || (vMoved && !prevVMoved))
1452 q->movementStarting();
1454 lastPosTime = currentTimestamp;
1455 if (q->yflick() && !rejectY)
1456 vData.addVelocitySample(velocity.y(), maxVelocity);
1457 if (q->xflick() && !rejectX)
1458 hData.addVelocitySample(velocity.x(), maxVelocity);
1462void QQuickFlickablePrivate::handleMoveEvent(QPointerEvent *event)
1464 Q_Q(QQuickFlickable);
1465 if (!interactive || lastPosTime == -1 ||
1466 (event->isSinglePointEvent() && !buttonsAccepted(
static_cast<QSinglePointEvent *>(event))))
1469 qint64 currentTimestamp = computeCurrentTime(event);
1470 const auto &firstPoint = event->points().first();
1471 const auto &pos = firstPoint.position();
1472 const QVector2D deltas = QVector2D(pos - q->mapFromGlobal(firstPoint.globalPressPosition()));
1473 const QVector2D velocity = firstPointLocalVelocity(event);
1474 bool overThreshold =
false;
1476 if (q->isMoving()) {
1478
1479
1480
1481
1482
1483
1484
1485 overThreshold =
true;
1486 }
else if (event->pointCount() == 1) {
1488 overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, firstPoint);
1490 overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.x(), Qt::XAxis, firstPoint);
1492 qCDebug(lcFilter) << q->objectName() <<
"ignoring multi-touch" << event;
1495 drag(currentTimestamp, event->type(), pos, deltas, overThreshold,
false,
false, velocity);
1498void QQuickFlickablePrivate::handleReleaseEvent(QPointerEvent *event)
1500 Q_Q(QQuickFlickable);
1502 q->setKeepMouseGrab(
false);
1503 q->setKeepTouchGrab(
false);
1507 qint64 elapsed = computeCurrentTime(event) - lastPosTime;
1509 vData.updateVelocity();
1510 hData.updateVelocity();
1514 if (lastPosTime == -1)
1517 hData.vTime = vData.vTime = timeline.time();
1519 bool canBoost =
false;
1520 const auto pos = event->points().first().position();
1521 const auto pressPos = q->mapFromGlobal(event->points().first().globalPressPosition());
1522 const QVector2D eventVelocity = firstPointLocalVelocity(event);
1523 qCDebug(lcVel) << event->deviceType() << event->type() <<
"velocity" << event->points().first().velocity() <<
"transformed to local" << eventVelocity;
1525 qreal vVelocity = 0;
1526 if (elapsed < 100 && vData.velocity != 0.) {
1527 vVelocity = (event->device()->capabilities().testFlag(QInputDevice::Capability::Velocity)
1528 ? eventVelocity.y() : vData.velocity);
1530 if ((vData.atBeginning && vVelocity > 0.) || (vData.atEnd && vVelocity < 0.)) {
1532 }
else if (vData.continuousFlickVelocity != 0.0
1534 && ((vVelocity > 0) == (vData.continuousFlickVelocity > 0))
1540 qreal hVelocity = 0;
1541 if (elapsed < 100 && hData.velocity != 0.) {
1542 hVelocity = (event->device()->capabilities().testFlag(QInputDevice::Capability::Velocity)
1543 ? eventVelocity.x() : hData.velocity);
1545 if ((hData.atBeginning && hVelocity > 0.) || (hData.atEnd && hVelocity < 0.)) {
1547 }
else if (hData.continuousFlickVelocity != 0.0
1549 && ((hVelocity > 0) == (hData.continuousFlickVelocity > 0))
1556 const int flickThreshold = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickStartDistance).toInt();
1558 bool anyPointGrabbed = event->points().constEnd() !=
1559 std::find_if(event->points().constBegin(),event->points().constEnd(),
1560 [q, event](
const QEventPoint &point) {
return event->exclusiveGrabber(point) == q; });
1562 bool flickedVertically =
false;
1563 vVelocity *= flickBoost;
1564 const bool isVerticalFlickAllowed = anyPointGrabbed &&
1565 q->yflick() && qAbs(vVelocity) > _q_MinimumFlickVelocity &&
1566 qAbs(pos.y() - pressPos.y()) > flickThreshold;
1567 if (isVerticalFlickAllowed) {
1568 velocityTimeline.reset(vData.smoothVelocity);
1569 vData.smoothVelocity.setValue(-vVelocity);
1570 flickedVertically = flickY(event->type(), vVelocity);
1573 bool flickedHorizontally =
false;
1574 hVelocity *= flickBoost;
1575 const bool isHorizontalFlickAllowed = anyPointGrabbed &&
1576 q->xflick() && qAbs(hVelocity) > _q_MinimumFlickVelocity &&
1577 qAbs(pos.x() - pressPos.x()) > flickThreshold;
1578 if (isHorizontalFlickAllowed) {
1579 velocityTimeline.reset(hData.smoothVelocity);
1580 hData.smoothVelocity.setValue(-hVelocity);
1581 flickedHorizontally = flickX(event->type(), hVelocity);
1584 if (!isVerticalFlickAllowed)
1587 if (!isHorizontalFlickAllowed)
1590 flickingStarted(flickedHorizontally, flickedVertically);
1591 if (!isViewMoving()) {
1592 q->movementEnding();
1594 if (flickedVertically)
1596 if (flickedHorizontally)
1598 q->movementStarting();
1602bool QQuickFlickablePrivate::buttonsAccepted(
const QSinglePointEvent *event)
1604 return !((event->button() & acceptedButtons) == 0 && (event->buttons() & acceptedButtons) == 0);
1607void QQuickFlickable::mousePressEvent(QMouseEvent *event)
1609 Q_D(QQuickFlickable);
1610 if (d->interactive && !d->replayingPressEvent && d->buttonsAccepted(event) && d->wantsPointerEvent(event)) {
1612 d->handlePressEvent(event);
1615 QQuickItem::mousePressEvent(event);
1619void QQuickFlickable::mouseMoveEvent(QMouseEvent *event)
1621 Q_D(QQuickFlickable);
1622 if (d->interactive && d->buttonsAccepted(event) && d->wantsPointerEvent(event)) {
1623 d->handleMoveEvent(event);
1626 QQuickItem::mouseMoveEvent(event);
1630void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
1632 Q_D(QQuickFlickable);
1633 if (d->interactive && d->buttonsAccepted(event) && d->wantsPointerEvent(event)) {
1634 if (d->delayedPressEvent) {
1635 d->replayDelayedPress();
1637 auto &firstPoint = event->point(0);
1638 if (
const auto *grabber = event->exclusiveGrabber(firstPoint); grabber && grabber->isQuickItemType()) {
1641 const auto oldPosition = firstPoint.position();
1642 QMutableEventPoint::setPosition(firstPoint, event->scenePosition());
1643 QCoreApplication::sendEvent(window(), event);
1644 QMutableEventPoint::setPosition(firstPoint, oldPosition);
1648 d->stealGrab =
false;
1653 d->handleReleaseEvent(event);
1656 QQuickItem::mouseReleaseEvent(event);
1660void QQuickFlickable::touchEvent(QTouchEvent *event)
1662 Q_D(QQuickFlickable);
1664 if (event->type() == QEvent::TouchCancel) {
1665 if (d->interactive && d->wantsPointerEvent(event))
1666 d->cancelInteraction();
1668 QQuickItem::touchEvent(event);
1672 bool unhandled =
false;
1673 const auto &firstPoint = event->points().first();
1674 switch (firstPoint.state()) {
1675 case QEventPoint::State::Pressed:
1676 if (d->interactive && !d->replayingPressEvent && d->wantsPointerEvent(event)) {
1678 d->handlePressEvent(event);
1684 case QEventPoint::State::Updated:
1685 if (d->interactive && d->wantsPointerEvent(event)) {
1686 d->handleMoveEvent(event);
1692 case QEventPoint::State::Released:
1693 if (d->interactive && d->wantsPointerEvent(event)) {
1694 if (d->delayedPressEvent) {
1695 d->replayDelayedPress();
1697 const auto &firstPoint = event->point(0);
1698 if (
const auto *grabber = event->exclusiveGrabber(firstPoint); grabber && grabber->isQuickItemType()) {
1700 QScopedPointer<QPointerEvent> localizedEvent(
1701 QQuickDeliveryAgentPrivate::clonePointerEvent(event, firstPoint.scenePosition()));
1702 QCoreApplication::sendEvent(window(), localizedEvent.data());
1706 d->stealGrab =
false;
1711 d->handleReleaseEvent(event);
1717 case QEventPoint::State::Stationary:
1718 case QEventPoint::State::Unknown:
1722 QQuickItem::touchEvent(event);
1725#if QT_CONFIG(wheelevent)
1726void QQuickFlickable::wheelEvent(QWheelEvent *event)
1728 Q_D(QQuickFlickable);
1729 if (!d->interactive || !d->wantsPointerEvent(event)) {
1730 QQuickItem::wheelEvent(event);
1733 qCDebug(lcWheel) << event->device() << event << event->source();
1734 event->setAccepted(
false);
1735 qint64 currentTimestamp = d->computeCurrentTime(event);
1736 switch (event->phase()) {
1737 case Qt::ScrollBegin:
1738 d->scrollingPhase =
true;
1739 d->accumulatedWheelPixelDelta = QVector2D();
1740 d->vData.velocity = 0;
1741 d->hData.velocity = 0;
1743 d->maybeBeginDrag(currentTimestamp, event->position());
1744 d->lastPosTime = -1;
1746 case Qt::NoScrollPhase:
1747 case Qt::ScrollUpdate:
1748 if (d->scrollingPhase)
1751 case Qt::ScrollMomentum:
1753 d->scrollingPhase =
false;
1754 d->draggingEnding();
1757 d->lastPosTime = -1;
1761 d->scrollingPhase =
false;
1762 d->draggingEnding();
1764 d->lastPosTime = -1;
1765 d->stealGrab =
false;
1766 if (!d->velocityTimeline.isActive() && !d->timeline.isActive())
1767 movementEnding(
true,
true);
1771 qreal elapsed = qreal(currentTimestamp - d->lastPosTime) / qreal(1000);
1773 d->lastPosTime = currentTimestamp;
1774 qCDebug(lcWheel) <<
"insufficient elapsed time: can't calculate velocity" << elapsed;
1782 const bool isTouchpadFrameWithLostPixelDelta =
1783 event->deviceType() == QInputDevice::DeviceType::TouchPad
1784 && !event->angleDelta().isNull();
1785 if (event->source() == Qt::MouseEventNotSynthesized || event->phase() == Qt::NoScrollPhase
1786 || (event->pixelDelta().isNull() && !isTouchpadFrameWithLostPixelDelta)) {
1788 int xDelta = event->angleDelta().x();
1789 int yDelta = event->angleDelta().y();
1791 if (d->wheelDeceleration > _q_MaximumWheelDeceleration) {
1792 const qreal wheelScroll = -qApp->styleHints()->wheelScrollLines() * 24;
1795 if (yflick() && yDelta != 0) {
1796 d->moveReason = QQuickFlickablePrivate::Mouse;
1798 qreal scrollPixel = (-yDelta / 120.0 * wheelScroll);
1799 bool acceptEvent =
true;
1800 if (scrollPixel > 0) {
1801 if (d->vData.move.value() >= minYExtent()) {
1803 acceptEvent =
false;
1806 if (d->vData.move.value() <= maxYExtent()) {
1808 acceptEvent =
false;
1812 if (d->boundsBehavior == QQuickFlickable::StopAtBounds) {
1813 const qreal estContentPos = scrollPixel + d->vData.move.value();
1814 if (scrollPixel > 0) {
1815 if (estContentPos > minYExtent()) {
1816 scrollPixel = minYExtent() - d->vData.move.value();
1817 acceptEvent =
false;
1820 if (estContentPos < maxYExtent()) {
1821 scrollPixel = maxYExtent() - d->vData.move.value();
1822 acceptEvent =
false;
1826 d->resetTimeline(d->vData);
1828 d->timeline.moveBy(d->vData.move, scrollPixel, QEasingCurve(QEasingCurve::OutExpo), 3*d->fixupDuration/4);
1829 d->vData.fixingUp =
true;
1830 d->timeline.callback(QQuickTimeLineCallback(&d->vData.move, QQuickFlickablePrivate::fixupY_callback, d));
1835 if (xflick() && xDelta != 0) {
1836 d->moveReason = QQuickFlickablePrivate::Mouse;
1838 qreal scrollPixel = (-xDelta / 120.0 * wheelScroll);
1839 bool acceptEvent =
true;
1840 if (scrollPixel > 0) {
1841 if (d->hData.move.value() >= minXExtent()) {
1843 acceptEvent =
false;
1846 if (d->hData.move.value() <= maxXExtent()) {
1848 acceptEvent =
false;
1852 if (d->boundsBehavior == QQuickFlickable::StopAtBounds) {
1853 const qreal estContentPos = scrollPixel + d->hData.move.value();
1854 if (scrollPixel > 0) {
1855 if (estContentPos > minXExtent()) {
1856 scrollPixel = minXExtent() - d->hData.move.value();
1857 acceptEvent =
false;
1860 if (estContentPos < maxXExtent()) {
1861 scrollPixel = maxXExtent() - d->hData.move.value();
1862 acceptEvent =
false;
1866 d->resetTimeline(d->hData);
1868 d->timeline.moveBy(d->hData.move, scrollPixel, QEasingCurve(QEasingCurve::OutExpo), 3*d->fixupDuration/4);
1869 d->hData.fixingUp =
true;
1870 d->timeline.callback(QQuickTimeLineCallback(&d->hData.move, QQuickFlickablePrivate::fixupX_callback, d));
1886 elapsed = 120 / qSqrt(d->wheelDeceleration * 2 * d->initialWheelFlickDistance);
1887 if (yflick() && yDelta != 0) {
1888 qreal instVelocity = yDelta / elapsed;
1890 if ((instVelocity < 0 && d->vData.velocity > 0) || (instVelocity > 0 && d->vData.velocity < 0)) {
1891 d->vData.velocitySamples = 0;
1892 d->vData.velocityWritePos = 0;
1894 d->vData.addVelocitySample(instVelocity, d->maxVelocity);
1895 d->vData.updateVelocity();
1896 if ((yDelta > 0 && contentY() > -minYExtent()) || (yDelta < 0 && contentY() < -maxYExtent())) {
1897 const bool newFlick = d->flickY(event->type(), d->vData.velocity);
1898 if (newFlick && (d->vData.atBeginning != (yDelta > 0) || d->vData.atEnd != (yDelta < 0))) {
1899 d->flickingStarted(
false,
true);
1906 if (xflick() && xDelta != 0) {
1907 qreal instVelocity = xDelta / elapsed;
1909 if ((instVelocity < 0 && d->hData.velocity > 0) || (instVelocity > 0 && d->hData.velocity < 0)) {
1910 d->hData.velocitySamples = 0;
1911 d->hData.velocityWritePos = 0;
1913 d->hData.addVelocitySample(instVelocity, d->maxVelocity);
1914 d->hData.updateVelocity();
1915 if ((xDelta > 0 && contentX() > -minXExtent()) || (xDelta < 0 && contentX() < -maxXExtent())) {
1916 const bool newFlick = d->flickX(event->type(), d->hData.velocity);
1917 if (newFlick && (d->hData.atBeginning != (xDelta > 0) || d->hData.atEnd != (xDelta < 0))) {
1918 d->flickingStarted(
true,
false);
1928 int xDelta = event->pixelDelta().x();
1929 int yDelta = event->pixelDelta().y();
1931 QVector2D velocity(xDelta / elapsed, yDelta / elapsed);
1932 d->accumulatedWheelPixelDelta += QVector2D(event->pixelDelta());
1938 if (isMoving() || isFlicking() || (yflick() && xflick())
1939 || (xflick() && qAbs(d->accumulatedWheelPixelDelta.x()) > qAbs(d->accumulatedWheelPixelDelta.y() * 2))
1940 || (yflick() && qAbs(d->accumulatedWheelPixelDelta.y()) > qAbs(d->accumulatedWheelPixelDelta.x() * 2))) {
1941 d->drag(currentTimestamp, event->type(), event->position(), d->accumulatedWheelPixelDelta,
1942 true, !d->scrollingPhase,
true, velocity);
1943 d->updateBeginningEnd();
1944 if ((xflick() && !isAtXBeginning() && !isAtXEnd()) || (yflick() && !isAtYBeginning() && !isAtYEnd()))
1947 qCDebug(lcWheel) <<
"not dragging: accumulated deltas" << d->accumulatedWheelPixelDelta <<
1948 "moving?" << isMoving() <<
"can flick horizontally?" << xflick() <<
"vertically?" << yflick();
1951 d->lastPosTime = currentTimestamp;
1953 if (!event->isAccepted())
1954 QQuickItem::wheelEvent(event);
1958bool QQuickFlickablePrivate::isInnermostPressDelay(QQuickItem *i)
const
1960 Q_Q(
const QQuickFlickable);
1961 QQuickItem *item = i;
1963 QQuickFlickable *flick = qobject_cast<QQuickFlickable*>(item);
1964 if (flick && flick->pressDelay() > 0 && flick->isInteractive()) {
1966 return (flick == q);
1968 item = item->parentItem();
1973void QQuickFlickablePrivate::captureDelayedPress(QQuickItem *item, QPointerEvent *event)
1975 Q_Q(QQuickFlickable);
1976 if (!q->window() || pressDelay <= 0)
1981 if (!isInnermostPressDelay(item))
1984 delayedPressEvent = QQuickDeliveryAgentPrivate::clonePointerEvent(event);
1985 delayedPressEvent->setAccepted(
false);
1986 delayedPressTimer.start(pressDelay, q);
1987 qCDebug(lcReplay) <<
"begin press delay" << pressDelay <<
"ms with" << delayedPressEvent;
1990void QQuickFlickablePrivate::clearDelayedPress()
1992 if (delayedPressEvent) {
1993 delayedPressTimer.stop();
1994 qCDebug(lcReplay) <<
"clear delayed press" << delayedPressEvent;
1995 delete delayedPressEvent;
1996 delayedPressEvent =
nullptr;
2000void QQuickFlickablePrivate::replayDelayedPress()
2002 Q_Q(QQuickFlickable);
2003 if (delayedPressEvent) {
2005 QScopedPointer<QPointerEvent> event(delayedPressEvent);
2006 delayedPressEvent =
nullptr;
2007 delayedPressTimer.stop();
2010 if (QQuickWindow *window = q->window()) {
2011 auto da = deliveryAgentPrivate();
2012 da->allowChildEventFiltering =
false;
2013 replayingPressEvent =
true;
2014 auto &firstPoint = event->point(0);
2020 if (event->exclusiveGrabber(firstPoint) == q)
2021 event->setExclusiveGrabber(firstPoint,
nullptr);
2023 qCDebug(lcReplay) <<
"replaying" << event.data();
2026 QMutableEventPoint::setPosition(firstPoint, firstPoint.scenePosition());
2030 QCoreApplication::sendEvent(window, event.data());
2031 qCDebug(lcReplay) <<
"replay done";
2034 replayingPressEvent =
false;
2035 da->allowChildEventFiltering =
true;
2043
2044
2045
2046
2047
2048
2049
2050void QQuickFlickablePrivate::setViewportX(qreal x)
2052 Q_Q(QQuickFlickable);
2053 qreal effectiveX = pixelAligned ? -std::round(-x) : x;
2055 const qreal maxX = q->maxXExtent();
2056 const qreal minX = q->minXExtent();
2058 if (boundsMovement ==
int(QQuickFlickable::StopAtBounds))
2059 effectiveX = qBound(maxX, effectiveX, minX);
2061 contentItem->setX(effectiveX);
2062 if (contentItem->x() != effectiveX)
2065 qreal overshoot = 0.0;
2067 overshoot = maxX - x;
2069 overshoot = minX - x;
2071 if (overshoot != hData.overshoot) {
2072 hData.overshoot = overshoot;
2073 emit q->horizontalOvershootChanged();
2078
2079
2080
2081
2082
2083
2084
2085void QQuickFlickablePrivate::setViewportY(qreal y)
2087 Q_Q(QQuickFlickable);
2088 qreal effectiveY = pixelAligned ? -std::round(-y) : y;
2090 const qreal maxY = q->maxYExtent();
2091 const qreal minY = q->minYExtent();
2093 if (boundsMovement ==
int(QQuickFlickable::StopAtBounds))
2094 effectiveY = qBound(maxY, effectiveY, minY);
2096 contentItem->setY(effectiveY);
2097 if (contentItem->y() != effectiveY)
2100 qreal overshoot = 0.0;
2102 overshoot = maxY - y;
2104 overshoot = minY - y;
2106 if (overshoot != vData.overshoot) {
2107 vData.overshoot = overshoot;
2108 emit q->verticalOvershootChanged();
2112void QQuickFlickable::timerEvent(QTimerEvent *event)
2114 Q_D(QQuickFlickable);
2115 if (event->timerId() == d->delayedPressTimer.timerId()) {
2116 d->delayedPressTimer.stop();
2117 if (d->delayedPressEvent) {
2118 d->replayDelayedPress();
2123qreal QQuickFlickable::minYExtent()
const
2125 Q_D(
const QQuickFlickable);
2126 return d->vData.startMargin;
2129qreal QQuickFlickable::minXExtent()
const
2131 Q_D(
const QQuickFlickable);
2132 return d->hData.startMargin;
2136qreal QQuickFlickable::maxXExtent()
const
2138 Q_D(
const QQuickFlickable);
2139 return qMin<qreal>(minXExtent(), width() - vWidth() - d->hData.endMargin);
2142qreal QQuickFlickable::maxYExtent()
const
2144 Q_D(
const QQuickFlickable);
2145 return qMin<qreal>(minYExtent(), height() - vHeight() - d->vData.endMargin);
2148void QQuickFlickable::componentComplete()
2150 Q_D(QQuickFlickable);
2151 QQuickItem::componentComplete();
2152 if (!d->hData.explicitValue && d->hData.startMargin != 0.)
2153 setContentX(-minXExtent());
2154 if (!d->vData.explicitValue && d->vData.startMargin != 0.)
2155 setContentY(-minYExtent());
2156 if (lcWheel().isDebugEnabled() || lcVel().isDebugEnabled()) {
2157 d->timeline.setObjectName(QLatin1String(
"timeline for Flickable ") + objectName());
2158 d->velocityTimeline.setObjectName(QLatin1String(
"velocity timeline for Flickable ") + objectName());
2162void QQuickFlickable::viewportMoved(Qt::Orientations orient)
2164 Q_D(QQuickFlickable);
2165 if (orient & Qt::Vertical)
2166 d->viewportAxisMoved(d->vData, minYExtent(), maxYExtent(), d->fixupY_callback);
2167 if (orient & Qt::Horizontal)
2168 d->viewportAxisMoved(d->hData, minXExtent(), maxXExtent(), d->fixupX_callback);
2169 d->updateBeginningEnd();
2172void QQuickFlickablePrivate::viewportAxisMoved(AxisData &data, qreal minExtent, qreal maxExtent,
2173 QQuickTimeLineCallback::Callback fixupCallback)
2175 if (!scrollingPhase && (pressed || calcVelocity)) {
2176 int elapsed = data.velocityTime.restart();
2178 qreal velocity = (data.lastPos - data.move.value()) * 1000 / elapsed;
2179 if (qAbs(velocity) > 0) {
2180 velocityTimeline.reset(data.smoothVelocity);
2181 velocityTimeline.set(data.smoothVelocity, velocity);
2182 qCDebug(lcVel) <<
"touchpad scroll phase: velocity" << velocity;
2186 if (timeline.time() > data.vTime) {
2187 velocityTimeline.reset(data.smoothVelocity);
2188 int dt = timeline.time() - data.vTime;
2190 qreal velocity = (data.lastPos - data.move.value()) * 1000 / dt;
2191 if (!qFuzzyCompare(data.smoothVelocity.value(), velocity))
2192 qCDebug(lcVel) <<
"velocity" << data.smoothVelocity.value() <<
"->" << velocity
2193 <<
"computed as (" << data.lastPos <<
"-" << data.move.value() <<
") * 1000 / ("
2194 << timeline.time() <<
"-" << data.vTime <<
")";
2195 data.smoothVelocity.setValue(velocity);
2200 if (!data.inOvershoot && !data.fixingUp && data.flicking
2201 && (data.move.value() > minExtent || data.move.value() < maxExtent)
2202 && qAbs(data.smoothVelocity.value()) > 10) {
2204 qreal overBound = data.move.value() > minExtent
2205 ? data.move.value() - minExtent
2206 : maxExtent - data.move.value();
2207 data.inOvershoot =
true;
2208 qreal maxDistance = overShootDistance(qAbs(data.smoothVelocity.value())) - overBound;
2209 resetTimeline(data);
2210 if (maxDistance > 0)
2212 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback,
this));
2215 data.lastPos = data.move.value();
2216 data.vTime = timeline.time();
2219void QQuickFlickable::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
2221 Q_D(QQuickFlickable);
2222 QQuickItem::geometryChange(newGeometry, oldGeometry);
2224 bool changed =
false;
2225 if (newGeometry.width() != oldGeometry.width()) {
2227 if (d->hData.viewSize < 0)
2228 d->contentItem->setWidth(width() - d->hData.startMargin - d->hData.endMargin);
2230 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2231 d->fixupMode = QQuickFlickablePrivate::Immediate;
2235 if (newGeometry.height() != oldGeometry.height()) {
2237 if (d->vData.viewSize < 0)
2238 d->contentItem->setHeight(height() - d->vData.startMargin - d->vData.endMargin);
2240 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2241 d->fixupMode = QQuickFlickablePrivate::Immediate;
2247 d->updateBeginningEnd();
2251
2252
2253
2254
2255
2256
2257
2259void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity)
2261 Q_D(QQuickFlickable);
2264 d->hData.velocity = xVelocity;
2265 d->vData.velocity = yVelocity;
2266 d->hData.vTime = d->vData.vTime = d->timeline.time();
2268 const bool flickedX = xflick() && !qFuzzyIsNull(xVelocity) && d->flickX(QEvent::TouchUpdate, xVelocity);
2269 const bool flickedY = yflick() && !qFuzzyIsNull(yVelocity) && d->flickY(QEvent::TouchUpdate, yVelocity);
2276 d->flickingStarted(flickedX, flickedY);
2279void QQuickFlickablePrivate::flickingStarted(
bool flickingH,
bool flickingV)
2281 Q_Q(QQuickFlickable);
2282 if (!flickingH && !flickingV)
2285 bool wasFlicking = hData.flicking || vData.flicking;
2286 if (flickingH && !hData.flicking) {
2287 hData.flicking =
true;
2288 emit q->flickingHorizontallyChanged();
2290 if (flickingV && !vData.flicking) {
2291 vData.flicking =
true;
2292 emit q->flickingVerticallyChanged();
2294 if (!wasFlicking && (hData.flicking || vData.flicking)) {
2295 emit q->flickingChanged();
2296 emit q->flickStarted();
2301
2302
2303
2304
2306void QQuickFlickable::cancelFlick()
2308 Q_D(QQuickFlickable);
2309 d->resetTimeline(d->hData);
2310 d->resetTimeline(d->vData);
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2347void QQuickFlickable::positionViewAtChild(QQuickItem *child, PositionMode mode,
const QPointF &offset)
2349 Q_D(QQuickFlickable);
2352 if (!d->contentItem->isAncestorOf(child))
2355 const QRectF itemRect =
2356 child->mapRectToItem(d->contentItem, QRectF(0, 0, child->width(), child->height()));
2358 QPointF currentPosition = QPointF(contentX(), contentY());
2359 QPointF newPosition = computePosition(currentPosition, itemRect, mode, offset);
2361 if (newPosition.x() != currentPosition.x()) {
2362 setContentX(newPosition.x());
2365 if (newPosition.y() != currentPosition.y()) {
2366 setContentY(newPosition.y());
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2403void QQuickFlickable::flickToChild(QQuickItem *child, PositionMode mode,
const QPointF &offset)
2405 Q_D(QQuickFlickable);
2408 if (!d->contentItem->isAncestorOf(child))
2411 const QRectF itemRect =
2412 child->mapRectToItem(d->contentItem, QRectF(0, 0, child->width(), child->height()));
2414 QPointF currentPosition = QPointF(contentX(), contentY());
2415 QPointF newPosition = computePosition(currentPosition, itemRect, mode, offset);
2417 flickTo(newPosition);
2421
2422
2423
2424
2425
2426
2427
2428
2430void QQuickFlickable::flickTo(
const QPointF &newPosition)
2432 Q_D(QQuickFlickable);
2434 QPointF currentPosition = QPointF(contentX(), contentY());
2436 qreal xVelocity = 0.0;
2437 qreal yVelocity = 0.0;
2439 const qreal deltaX = newPosition.x() - currentPosition.x();
2440 const qreal deltaY = newPosition.y() - currentPosition.y();
2445 if (xflick() && qAbs(deltaX) > 0.5) {
2446 const qreal decel = flickDeceleration();
2447 qreal velocity = qSqrt(2.0 * decel * qAbs(deltaX));
2448 if (qAbs(velocity) < _q_MinimumFlickVelocity)
2450 const qreal maxVel = maximumFlickVelocity();
2451 if (maxVel > 0 && velocity > maxVel)
2453 xVelocity = deltaX > 0 ? -velocity : velocity;
2456 if (yflick() && qAbs(deltaY) > 0.5) {
2457 const qreal decel = flickDeceleration();
2458 qreal velocity = qSqrt(2.0 * decel * qAbs(deltaY));
2459 if (qAbs(velocity) < _q_MinimumFlickVelocity)
2461 const qreal maxVel = maximumFlickVelocity();
2462 if (maxVel > 0 && velocity > maxVel)
2464 yVelocity = deltaY > 0 ? -velocity : velocity;
2467 if (qAbs(xVelocity) > 0.0 || qAbs(yVelocity) > 0.0) {
2468 flick(xVelocity, yVelocity);
2470 if (newPosition.x() != currentPosition.x()) {
2471 setContentX(newPosition.x());
2474 if (newPosition.y() != currentPosition.y()) {
2475 setContentY(newPosition.y());
2481void QQuickFlickablePrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
2483 if (!prop || !prop->data)
2486 if (QQuickItem *i = qmlobject_cast<QQuickItem *>(o)) {
2487 i->setParentItem(
static_cast<QQuickFlickablePrivate*>(prop->data)->contentItem);
2488 }
else if (QQuickPointerHandler *pointerHandler = qmlobject_cast<QQuickPointerHandler *>(o)) {
2489 static_cast<QQuickFlickablePrivate*>(prop->data)->addPointerHandler(pointerHandler);
2491 o->setParent(prop->object);
2495qsizetype QQuickFlickablePrivate::data_count(QQmlListProperty<QObject> *)
2501QObject *QQuickFlickablePrivate::data_at(QQmlListProperty<QObject> *, qsizetype)
2507void QQuickFlickablePrivate::data_clear(QQmlListProperty<QObject> *)
2512QQmlListProperty<QObject> QQuickFlickable::flickableData()
2514 Q_D(QQuickFlickable);
2515 return QQmlListProperty<QObject>(
this, (
void *)d, QQuickFlickablePrivate::data_append,
2516 QQuickFlickablePrivate::data_count,
2517 QQuickFlickablePrivate::data_at,
2518 QQuickFlickablePrivate::data_clear);
2521QQmlListProperty<QQuickItem> QQuickFlickable::flickableChildren()
2523 Q_D(QQuickFlickable);
2524 return QQuickItemPrivate::get(d->contentItem)->children();
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553QQuickFlickable::BoundsBehavior QQuickFlickable::boundsBehavior()
const
2555 Q_D(
const QQuickFlickable);
2556 return d->boundsBehavior;
2559void QQuickFlickable::setBoundsBehavior(BoundsBehavior b)
2561 Q_D(QQuickFlickable);
2562 if (b == d->boundsBehavior)
2564 d->boundsBehavior = b;
2565 emit boundsBehaviorChanged();
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609QQuickTransition *QQuickFlickable::rebound()
const
2611 Q_D(
const QQuickFlickable);
2615void QQuickFlickable::setRebound(QQuickTransition *transition)
2617 Q_D(QQuickFlickable);
2619 if (!d->hData.transitionToBounds)
2620 d->hData.transitionToBounds =
new QQuickFlickableReboundTransition(
this, QLatin1String(
"x"));
2621 if (!d->vData.transitionToBounds)
2622 d->vData.transitionToBounds =
new QQuickFlickableReboundTransition(
this, QLatin1String(
"y"));
2624 if (d->rebound != transition) {
2625 d->rebound = transition;
2626 emit reboundChanged();
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654qreal QQuickFlickable::contentWidth()
const
2656 Q_D(
const QQuickFlickable);
2657 return d->hData.viewSize;
2660void QQuickFlickable::setContentWidth(qreal w)
2662 Q_D(QQuickFlickable);
2663 if (d->hData.viewSize == w)
2665 d->hData.viewSize = w;
2667 d->contentItem->setWidth(width() - d->hData.startMargin - d->hData.endMargin);
2669 d->contentItem->setWidth(w);
2670 d->hData.markExtentsDirty();
2672 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2673 d->fixupMode = QQuickFlickablePrivate::Immediate;
2675 }
else if (!d->pressed && d->hData.fixingUp) {
2676 d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
2679 emit contentWidthChanged();
2680 d->updateBeginningEnd();
2683qreal QQuickFlickable::contentHeight()
const
2685 Q_D(
const QQuickFlickable);
2686 return d->vData.viewSize;
2689void QQuickFlickable::setContentHeight(qreal h)
2691 Q_D(QQuickFlickable);
2692 if (d->vData.viewSize == h)
2694 d->vData.viewSize = h;
2696 d->contentItem->setHeight(height() - d->vData.startMargin - d->vData.endMargin);
2698 d->contentItem->setHeight(h);
2699 d->vData.markExtentsDirty();
2701 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2702 d->fixupMode = QQuickFlickablePrivate::Immediate;
2704 }
else if (!d->pressed && d->vData.fixingUp) {
2705 d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
2708 emit contentHeightChanged();
2709 d->updateBeginningEnd();
2713
2714
2715
2716
2717
2718
2719
2720
2723qreal QQuickFlickable::topMargin()
const
2725 Q_D(
const QQuickFlickable);
2726 return d->vData.startMargin;
2729void QQuickFlickable::setTopMargin(qreal m)
2731 Q_D(QQuickFlickable);
2732 if (d->vData.startMargin == m)
2734 d->vData.startMargin = m;
2735 d->vData.markExtentsDirty();
2736 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2738 d->fixupMode = QQuickFlickablePrivate::Immediate;
2741 emit topMarginChanged();
2742 d->updateBeginningEnd();
2745qreal QQuickFlickable::bottomMargin()
const
2747 Q_D(
const QQuickFlickable);
2748 return d->vData.endMargin;
2751void QQuickFlickable::setBottomMargin(qreal m)
2753 Q_D(QQuickFlickable);
2754 if (d->vData.endMargin == m)
2756 d->vData.endMargin = m;
2757 d->vData.markExtentsDirty();
2758 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2760 d->fixupMode = QQuickFlickablePrivate::Immediate;
2763 emit bottomMarginChanged();
2764 d->updateBeginningEnd();
2767qreal QQuickFlickable::leftMargin()
const
2769 Q_D(
const QQuickFlickable);
2770 return d->hData.startMargin;
2773void QQuickFlickable::setLeftMargin(qreal m)
2775 Q_D(QQuickFlickable);
2776 if (d->hData.startMargin == m)
2778 d->hData.startMargin = m;
2779 d->hData.markExtentsDirty();
2780 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2782 d->fixupMode = QQuickFlickablePrivate::Immediate;
2785 emit leftMarginChanged();
2786 d->updateBeginningEnd();
2789qreal QQuickFlickable::rightMargin()
const
2791 Q_D(
const QQuickFlickable);
2792 return d->hData.endMargin;
2795void QQuickFlickable::setRightMargin(qreal m)
2797 Q_D(QQuickFlickable);
2798 if (d->hData.endMargin == m)
2800 d->hData.endMargin = m;
2801 d->hData.markExtentsDirty();
2802 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2804 d->fixupMode = QQuickFlickablePrivate::Immediate;
2807 emit rightMarginChanged();
2808 d->updateBeginningEnd();
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2825qreal QQuickFlickable::originY()
const
2827 Q_D(
const QQuickFlickable);
2828 return -minYExtent() + d->vData.startMargin;
2831qreal QQuickFlickable::originX()
const
2833 Q_D(
const QQuickFlickable);
2834 return -minXExtent() + d->hData.startMargin;
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850void QQuickFlickable::resizeContent(qreal w, qreal h, QPointF center)
2852 Q_D(QQuickFlickable);
2853 const qreal oldHSize = d->hData.viewSize;
2854 const qreal oldVSize = d->vData.viewSize;
2855 const bool needToUpdateWidth = w != oldHSize;
2856 const bool needToUpdateHeight = h != oldVSize;
2857 d->hData.viewSize = w;
2858 d->vData.viewSize = h;
2859 d->contentItem->setSize(QSizeF(w, h));
2860 if (needToUpdateWidth)
2861 emit contentWidthChanged();
2862 if (needToUpdateHeight)
2863 emit contentHeightChanged();
2865 if (center.x() != 0) {
2866 qreal pos = center.x() * w / oldHSize;
2867 setContentX(contentX() + pos - center.x());
2869 if (center.y() != 0) {
2870 qreal pos = center.y() * h / oldVSize;
2871 setContentY(contentY() + pos - center.y());
2873 d->updateBeginningEnd();
2877
2878
2879
2880
2881
2882
2883
2884void QQuickFlickable::returnToBounds()
2886 Q_D(QQuickFlickable);
2891qreal QQuickFlickable::vWidth()
const
2893 Q_D(
const QQuickFlickable);
2894 if (d->hData.viewSize < 0)
2897 return d->hData.viewSize;
2900qreal QQuickFlickable::vHeight()
const
2902 Q_D(
const QQuickFlickable);
2903 if (d->vData.viewSize < 0)
2906 return d->vData.viewSize;
2910
2911
2912
2913
2914
2915
2916bool QQuickFlickable::xflick()
const
2918 Q_D(
const QQuickFlickable);
2919 const int contentWidthWithMargins = d->contentItem->width() + d->hData.startMargin + d->hData.endMargin;
2920 if ((d->flickableDirection & QQuickFlickable::AutoFlickIfNeeded) && (contentWidthWithMargins > width()))
2922 if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
2923 return std::floor(qAbs(contentWidthWithMargins - width()));
2924 return d->flickableDirection & QQuickFlickable::HorizontalFlick;
2928
2929
2930
2931
2932
2933
2934bool QQuickFlickable::yflick()
const
2936 Q_D(
const QQuickFlickable);
2937 const int contentHeightWithMargins = d->contentItem->height() + d->vData.startMargin + d->vData.endMargin;
2938 if ((d->flickableDirection & QQuickFlickable::AutoFlickIfNeeded) && (contentHeightWithMargins > height()))
2940 if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
2941 return std::floor(qAbs(contentHeightWithMargins - height()));
2942 return d->flickableDirection & QQuickFlickable::VerticalFlick;
2945QPointF QQuickFlickable::computePosition(QPointF currentPosition, QRectF itemRect, PositionMode mode,
const QPointF &offset)
const
2947 QPointF newPosition = currentPosition;
2950 const qreal viewWidth = width();
2952 if (mode & QQuickFlickable::AlignLeft)
2953 newPosition.setX(itemRect.left());
2954 if (mode & QQuickFlickable::AlignHCenter)
2955 newPosition.setX(itemRect.left() - (viewWidth - itemRect.width()) / 2);
2956 if (mode & QQuickFlickable::AlignRight)
2957 newPosition.setX(itemRect.right() - viewWidth);
2958 if (mode & QQuickFlickable::Visible) {
2959 if (itemRect.right() < currentPosition.x())
2960 newPosition.setX(itemRect.left());
2961 else if (itemRect.left() > currentPosition.x() + viewWidth)
2962 newPosition.setX(itemRect.right() - viewWidth);
2964 if (mode & QQuickFlickable::Contain) {
2965 if (itemRect.right() > currentPosition.x() + viewWidth)
2966 newPosition.setX(itemRect.right() - viewWidth);
2967 if (itemRect.left() < newPosition.x())
2968 newPosition.setX(itemRect.left());
2971 const qreal minX = -minXExtent();
2972 const qreal maxX = -maxXExtent();
2973 newPosition.setX(qMin(newPosition.x(), maxX));
2974 newPosition.setX(qMax(newPosition.x(), minX));
2978 const qreal viewHeight = height();
2980 if (mode & QQuickFlickable::AlignTop)
2981 newPosition.setY(itemRect.top());
2982 if (mode & QQuickFlickable::AlignVCenter)
2983 newPosition.setY(itemRect.top() - (viewHeight - itemRect.height()) / 2);
2984 if (mode & QQuickFlickable::AlignBottom)
2985 newPosition.setY(itemRect.bottom() - viewHeight);
2986 if (mode & QQuickFlickable::Visible) {
2987 if (itemRect.bottom() < currentPosition.y())
2988 newPosition.setY(itemRect.top());
2989 else if (itemRect.top() > currentPosition.y() + viewHeight)
2990 newPosition.setY(itemRect.bottom() - viewHeight);
2992 if (mode & QQuickFlickable::Contain) {
2993 if (itemRect.bottom() > currentPosition.y() + viewHeight)
2994 newPosition.setY(itemRect.bottom() - viewHeight);
2995 if (itemRect.top() < currentPosition.y())
2996 newPosition.setY(itemRect.top());
2999 const qreal minY = -minYExtent();
3000 const qreal maxY = -maxYExtent();
3001 newPosition.setY(qMin(newPosition.y(), maxY));
3002 newPosition.setY(qMax(newPosition.y(), minY));
3005 return newPosition + offset;
3008void QQuickFlickable::mouseUngrabEvent()
3010 Q_D(QQuickFlickable);
3013 if (!d->replayingPressEvent)
3014 d->cancelInteraction();
3017void QQuickFlickablePrivate::cancelInteraction()
3019 Q_Q(QQuickFlickable);
3021 clearDelayedPress();
3025 q->setKeepMouseGrab(
false);
3026 q->setKeepTouchGrab(
false);
3029 if (!isViewMoving())
3030 q->movementEnding();
3034void QQuickFlickablePrivate::addPointerHandler(QQuickPointerHandler *h)
3036 Q_Q(
const QQuickFlickable);
3037 qCDebug(lcHandlerParent) <<
"reparenting handler" << h <<
"to contentItem of" << q;
3038 h->setParent(contentItem);
3039 QQuickItemPrivate::get(contentItem)->addPointerHandler(h);
3043
3044
3045
3046
3047
3048
3049
3050bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *event)
3052 Q_D(QQuickFlickable);
3053 const bool isTouch = QQuickDeliveryAgentPrivate::isTouchEvent(event);
3054 const bool isMouse = QQuickDeliveryAgentPrivate::isMouseEvent(event);
3055 if (isMouse || QQuickDeliveryAgentPrivate::isTabletEvent(event)) {
3056 if (!d->buttonsAccepted(
static_cast<QSinglePointEvent *>(event)))
3057 return QQuickItem::childMouseEventFilter(receiver, event);
3058 }
else if (!isTouch) {
3061 Q_ASSERT_X(receiver !=
this,
"",
"Flickable received a filter event for itself");
3063 if (isTouch &&
static_cast<QTouchEvent *>(event)->touchPointStates().testFlag(QEventPoint::State::Pressed))
3064 d->stealGrab =
false;
3065 const auto &firstPoint = event->points().first();
3067 if (event->pointCount() == 1 && event->exclusiveGrabber(firstPoint) ==
this) {
3079 QPointF localPos = mapFromScene(firstPoint.scenePosition());
3080 bool receiverDisabled = receiver && !receiver->isEnabled();
3081 bool stealThisEvent = d->stealGrab;
3082 bool receiverKeepsGrab = receiver && (receiver->keepMouseGrab() || receiver->keepTouchGrab());
3083 bool receiverRelinquishGrab =
false;
3086 if (
auto *mouseArea = qmlobject_cast<QQuickMouseArea *>(receiver)) {
3087 bool preventStealing = mouseArea->preventStealing();
3088#if QT_CONFIG(quick_draganddrop)
3089 if (mouseArea->drag() && mouseArea->drag()->target())
3090 preventStealing =
true;
3092 if (!preventStealing && receiverKeepsGrab) {
3093 receiverRelinquishGrab = !receiverDisabled || (isMouse
3094 && firstPoint.state() == QEventPoint::State::Pressed
3095 && (receiver->acceptedMouseButtons() &
static_cast<QMouseEvent *>(event)->button()));
3096 if (receiverRelinquishGrab)
3097 receiverKeepsGrab =
false;
3101 if ((stealThisEvent || contains(localPos)) && (!receiver || !receiverKeepsGrab || receiverDisabled)) {
3102 QScopedPointer<QPointerEvent> localizedEvent(QQuickDeliveryAgentPrivate::clonePointerEvent(event, localPos));
3103 localizedEvent->setAccepted(
false);
3104 switch (firstPoint.state()) {
3105 case QEventPoint::State::Updated:
3106 d->handleMoveEvent(localizedEvent.data());
3108 case QEventPoint::State::Pressed:
3109 d->handlePressEvent(localizedEvent.data());
3110 d->captureDelayedPress(receiver, event);
3112 d->stealGrab =
false;
3113 stealThisEvent =
false;
3115 case QEventPoint::State::Released:
3116 d->handleReleaseEvent(localizedEvent.data());
3117 stealThisEvent = d->stealGrab;
3119 case QEventPoint::State::Stationary:
3120 case QEventPoint::State::Unknown:
3123 if ((receiver && stealThisEvent && !receiverKeepsGrab && receiver !=
this) || receiverDisabled) {
3124 d->clearDelayedPress();
3125 event->setExclusiveGrabber(firstPoint,
this);
3126 }
else if (d->delayedPressEvent) {
3127 event->setExclusiveGrabber(firstPoint,
this);
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141 if (isMoving() || (!receiverRelinquishGrab && (stealThisEvent || d->delayedPressEvent || receiverDisabled))) {
3143 event->setAccepted(
true);
3149 }
else if (d->lastPosTime != -1) {
3150 d->lastPosTime = -1;
3153 if (firstPoint.state() == QEventPoint::State::Released || (receiverKeepsGrab && !receiverDisabled)) {
3155 d->lastPosTime = -1;
3156 d->clearDelayedPress();
3157 d->stealGrab =
false;
3164
3165
3166
3167bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
3169 Q_D(QQuickFlickable);
3170 QPointerEvent *pointerEvent = e->isPointerEvent() ?
static_cast<QPointerEvent *>(e) :
nullptr;
3172 auto wantsPointerEvent_helper = [
this, d, i, pointerEvent]() {
3173 Q_ASSERT(pointerEvent);
3174 QQuickDeliveryAgentPrivate::localizePointerEvent(pointerEvent,
this);
3175 const bool wants = d->wantsPointerEvent(pointerEvent);
3177 QQuickDeliveryAgentPrivate::localizePointerEvent(pointerEvent, i);
3181 if (!isVisible() || !isEnabled() || !isInteractive() ||
3182 (pointerEvent && !wantsPointerEvent_helper())) {
3183 d->cancelInteraction();
3184 return QQuickItem::childMouseEventFilter(i, e);
3187 if (e->type() == QEvent::UngrabMouse) {
3188 Q_ASSERT(e->isSinglePointEvent());
3189 auto spe =
static_cast<QSinglePointEvent *>(e);
3190 const QObject *grabber = spe->exclusiveGrabber(spe->points().first());
3191 qCDebug(lcFilter) <<
"filtering UngrabMouse" << spe->points().first() <<
"for" << i <<
"grabber is" << grabber;
3192 if (grabber !=
this)
3194 }
else if (pointerEvent) {
3195 return filterPointerEvent(i, pointerEvent);
3198 return QQuickItem::childMouseEventFilter(i, e);
3202
3203
3204
3205
3206
3207qreal QQuickFlickable::maximumFlickVelocity()
const
3209 Q_D(
const QQuickFlickable);
3210 return d->maxVelocity;
3213void QQuickFlickable::setMaximumFlickVelocity(qreal v)
3215 Q_D(QQuickFlickable);
3216 if (v == d->maxVelocity)
3219 emit maximumFlickVelocityChanged();
3223
3224
3225
3226
3227
3228
3229
3230
3231qreal QQuickFlickable::flickDeceleration()
const
3233 Q_D(
const QQuickFlickable);
3234 return d->deceleration;
3237void QQuickFlickable::setFlickDeceleration(qreal deceleration)
3239 Q_D(QQuickFlickable);
3240 if (deceleration == d->deceleration)
3242 d->deceleration = qMax(0.001, deceleration);
3243 emit flickDecelerationChanged();
3246bool QQuickFlickable::isFlicking()
const
3248 Q_D(
const QQuickFlickable);
3249 return d->hData.flicking || d->vData.flicking;
3253
3254
3255
3256
3257
3258
3259
3260bool QQuickFlickable::isFlickingHorizontally()
const
3262 Q_D(
const QQuickFlickable);
3263 return d->hData.flicking;
3266bool QQuickFlickable::isFlickingVertically()
const
3268 Q_D(
const QQuickFlickable);
3269 return d->vData.flicking;
3273
3274
3275
3276
3277
3278
3279
3280bool QQuickFlickable::isDragging()
const
3282 Q_D(
const QQuickFlickable);
3283 return d->hData.dragging || d->vData.dragging;
3286bool QQuickFlickable::isDraggingHorizontally()
const
3288 Q_D(
const QQuickFlickable);
3289 return d->hData.dragging;
3292bool QQuickFlickable::isDraggingVertically()
const
3294 Q_D(
const QQuickFlickable);
3295 return d->vData.dragging;
3298void QQuickFlickablePrivate::draggingStarting()
3300 Q_Q(QQuickFlickable);
3301 bool wasDragging = hData.dragging || vData.dragging;
3302 if (hMoved && !hData.dragging) {
3303 hData.dragging =
true;
3304 emit q->draggingHorizontallyChanged();
3306 if (vMoved && !vData.dragging) {
3307 vData.dragging =
true;
3308 emit q->draggingVerticallyChanged();
3310 if (!wasDragging && (hData.dragging || vData.dragging)) {
3311 emit q->draggingChanged();
3312 emit q->dragStarted();
3316void QQuickFlickablePrivate::draggingEnding()
3318 Q_Q(QQuickFlickable);
3319 const bool wasDragging = hData.dragging || vData.dragging;
3320 if (hData.dragging) {
3321 hData.dragging =
false;
3322 emit q->draggingHorizontallyChanged();
3324 if (vData.dragging) {
3325 vData.dragging =
false;
3326 emit q->draggingVerticallyChanged();
3329 if (!hData.dragging && !vData.dragging) {
3330 emit q->draggingChanged();
3331 emit q->dragEnded();
3333 hData.inRebound =
false;
3334 vData.inRebound =
false;
3338bool QQuickFlickablePrivate::isViewMoving()
const
3340 if (timeline.isActive()
3341 || (hData.transitionToBounds && hData.transitionToBounds->isActive())
3342 || (vData.transitionToBounds && vData.transitionToBounds->isActive()) ) {
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366int QQuickFlickable::pressDelay()
const
3368 Q_D(
const QQuickFlickable);
3369 return d->pressDelay;
3372void QQuickFlickable::setPressDelay(
int delay)
3374 Q_D(QQuickFlickable);
3375 if (d->pressDelay == delay)
3377 d->pressDelay = delay;
3378 emit pressDelayChanged();
3382
3383
3384
3385
3386
3387
3388
3389
3391bool QQuickFlickable::isMoving()
const
3393 Q_D(
const QQuickFlickable);
3394 return d->hData.moving || d->vData.moving;
3397bool QQuickFlickable::isMovingHorizontally()
const
3399 Q_D(
const QQuickFlickable);
3400 return d->hData.moving;
3403bool QQuickFlickable::isMovingVertically()
const
3405 Q_D(
const QQuickFlickable);
3406 return d->vData.moving;
3409void QQuickFlickable::velocityTimelineCompleted()
3411 Q_D(QQuickFlickable);
3412 if ( (d->hData.transitionToBounds && d->hData.transitionToBounds->isActive())
3413 || (d->vData.transitionToBounds && d->vData.transitionToBounds->isActive()) ) {
3420 if (d->vData.flicking)
3422 d->updateBeginningEnd();
3425void QQuickFlickable::timelineCompleted()
3427 Q_D(QQuickFlickable);
3428 if ( (d->hData.transitionToBounds && d->hData.transitionToBounds->isActive())
3429 || (d->vData.transitionToBounds && d->vData.transitionToBounds->isActive()) ) {
3433 d->updateBeginningEnd();
3436void QQuickFlickable::movementStarting()
3438 Q_D(QQuickFlickable);
3439 bool wasMoving = d->hData.moving || d->vData.moving;
3440 if (d->hMoved && !d->hData.moving) {
3441 d->hData.moving =
true;
3442 emit movingHorizontallyChanged();
3444 if (d->vMoved && !d->vData.moving) {
3445 d->vData.moving =
true;
3446 emit movingVerticallyChanged();
3449 if (!wasMoving && (d->hData.moving || d->vData.moving)) {
3450 emit movingChanged();
3451 emit movementStarted();
3452#if QT_CONFIG(accessibility)
3453 if (QAccessible::isActive()) {
3454 QAccessibleEvent ev(
this, QAccessible::ScrollingStart);
3455 QAccessible::updateAccessibility(&ev);
3461void QQuickFlickable::movementEnding()
3463 movementEnding(
true,
true);
3466void QQuickFlickable::movementEnding(
bool hMovementEnding,
bool vMovementEnding)
3468 Q_D(QQuickFlickable);
3471 const bool wasFlicking = d->hData.flicking || d->vData.flicking;
3472 if (hMovementEnding && d->hData.flicking) {
3473 d->hData.flicking =
false;
3474 emit flickingHorizontallyChanged();
3476 if (vMovementEnding && d->vData.flicking) {
3477 d->vData.flicking =
false;
3478 emit flickingVerticallyChanged();
3480 if (wasFlicking && (!d->hData.flicking || !d->vData.flicking)) {
3481 emit flickingChanged();
3483 }
else if (d->hData.flickingWhenDragBegan || d->vData.flickingWhenDragBegan) {
3484 d->hData.flickingWhenDragBegan = !hMovementEnding;
3485 d->vData.flickingWhenDragBegan = !vMovementEnding;
3490 bool wasMoving = isMoving();
3491 if (hMovementEnding && d->hData.moving
3492 && (!d->pressed && !d->stealGrab)) {
3493 d->hData.moving =
false;
3495 emit movingHorizontallyChanged();
3497 if (vMovementEnding && d->vData.moving
3498 && (!d->pressed && !d->stealGrab)) {
3499 d->vData.moving =
false;
3501 emit movingVerticallyChanged();
3503 if (wasMoving && !isMoving()) {
3504 emit movingChanged();
3505 emit movementEnded();
3506#if QT_CONFIG(accessibility)
3507 if (QAccessible::isActive()) {
3508 QAccessibleEvent ev(
this, QAccessible::ScrollingEnd);
3509 QAccessible::updateAccessibility(&ev);
3514 if (hMovementEnding) {
3515 d->hData.fixingUp =
false;
3516 d->hData.smoothVelocity.setValue(0);
3517 d->hData.previousDragDelta = 0.0;
3519 if (vMovementEnding) {
3520 d->vData.fixingUp =
false;
3521 d->vData.smoothVelocity.setValue(0);
3522 d->vData.previousDragDelta = 0.0;
3526void QQuickFlickablePrivate::updateVelocity()
3528 Q_Q(QQuickFlickable);
3529 emit q->horizontalVelocityChanged();
3530 emit q->verticalVelocityChanged();
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548qreal QQuickFlickable::horizontalOvershoot()
const
3550 Q_D(
const QQuickFlickable);
3551 return d->hData.overshoot;
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569qreal QQuickFlickable::verticalOvershoot()
const
3571 Q_D(
const QQuickFlickable);
3572 return d->vData.overshoot;
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621QQuickFlickable::BoundsMovement QQuickFlickable::boundsMovement()
const
3623 Q_D(
const QQuickFlickable);
3624 return d->boundsMovement;
3627void QQuickFlickable::setBoundsMovement(BoundsMovement movement)
3629 Q_D(QQuickFlickable);
3630 if (d->boundsMovement == movement)
3633 d->boundsMovement = movement;
3634 emit boundsMovementChanged();
3639#include "moc_qquickflickable_p_p.cpp"
3641#include "moc_qquickflickable_p.cpp"
bool contains(const QPointF &point) const override
QQuickFlickableContentItem(QQuickItem *parent=nullptr)
~QQuickFlickableReboundTransition()
bool startTransition(QQuickFlickablePrivate::AxisData *data, qreal toPos)
QQuickFlickableReboundTransition(QQuickFlickable *f, const QString &name)
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
static bool fuzzyLessThanOrEqualTo(qreal a, qreal b)
static qreal EaseOvershoot(qreal t)
#define QML_FLICK_SAMPLEBUFFER
#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