11#if QT_CONFIG(quick_draganddrop)
12#include "qquickdrag_p.h"
15#include <QtQuick/private/qquickpointerhandler_p.h>
16#include <QtQuick/private/qquicktransition_p.h>
17#include <private/qqmlglobal_p.h>
19#include <QtQml/qqmlinfo.h>
20#include <QtGui/qevent.h>
21#include <QtGui/qguiapplication.h>
22#include <QtGui/private/qguiapplication_p.h>
23#include <QtGui/private/qeventpoint_p.h>
24#include <QtGui/qstylehints.h>
25#include <QtGui/qaccessible.h>
26#include <QtCore/qmath.h>
27#include <qpa/qplatformtheme.h>
35Q_STATIC_LOGGING_CATEGORY(lcFilter,
"qt.quick.flickable.filter")
36Q_STATIC_LOGGING_CATEGORY(lcReplay,
"qt.quick.flickable.replay")
37Q_STATIC_LOGGING_CATEGORY(lcWheel,
"qt.quick.flickable.wheel")
38Q_STATIC_LOGGING_CATEGORY(lcVel,
"qt.quick.flickable.velocity")
42static const int RetainGrabVelocity = 100;
48QQuickFlickableVisibleArea::QQuickFlickableVisibleArea(QQuickFlickable *parent)
49 : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.)
50 , m_yPosition(0.), m_heightRatio(0.)
54qreal QQuickFlickableVisibleArea::widthRatio()
const
59qreal QQuickFlickableVisibleArea::xPosition()
const
64qreal QQuickFlickableVisibleArea::heightRatio()
const
69qreal QQuickFlickableVisibleArea::yPosition()
const
74void QQuickFlickableVisibleArea::updateVisible()
76 QQuickFlickablePrivate *p = QQuickFlickablePrivate::get(flickable);
80 bool changeWidth =
false;
81 bool changeHeight =
false;
84 const qreal viewheight = flickable->height();
85 const qreal maxyextent = -flickable->maxYExtent() + flickable->minYExtent();
86 const qreal maxYBounds = maxyextent + viewheight;
89 if (!qFuzzyIsNull(maxYBounds)) {
90 qreal y = p->pixelAligned ? std::round(p->vData.move.value()) : p->vData.move.value();
91 pagePos = (-y + flickable->minYExtent()) / maxYBounds;
92 pageSize = viewheight / maxYBounds;
95 if (pageSize != m_heightRatio) {
96 m_heightRatio = pageSize;
99 if (pagePos != m_yPosition) {
100 m_yPosition = pagePos;
105 const qreal viewwidth = flickable->width();
106 const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent();
107 const qreal maxXBounds = maxxextent + viewwidth;
108 if (!qFuzzyIsNull(maxXBounds)) {
109 qreal x = p->pixelAligned ? std::round(p->hData.move.value()) : p->hData.move.value();
110 pagePos = (-x + flickable->minXExtent()) / maxXBounds;
111 pageSize = viewwidth / maxXBounds;
117 if (pageSize != m_widthRatio) {
118 m_widthRatio = pageSize;
121 if (pagePos != m_xPosition) {
122 m_xPosition = pagePos;
127 emit xPositionChanged(m_xPosition);
129 emit yPositionChanged(m_yPosition);
131 emit widthRatioChanged(m_widthRatio);
133 emit heightRatioChanged(m_heightRatio);
151 QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flickable);
152 if (!fp->rebound || !fp->rebound->enabled())
156 axisData->transitionTo = toPos;
157 axisData->transitionToSet =
true;
160 actions << QQuickStateAction(fp->contentItem, propName, toPos);
161 QQuickTransitionManager::transition(actions, fp->rebound, fp->contentItem);
170 if (!flickable || !isRunning())
172 QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flickable);
173 if (axisData == &fp->hData)
174 axisData->move.setValue(-flickable->contentX());
176 axisData->move.setValue(-flickable->contentY());
185 axisData->move.setValue(axisData->transitionTo);
186 QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flickable);
189 if (!fp->hData.transitionToBounds->isActive()
190 && !fp->vData.transitionToBounds->isActive()) {
191 flickable->movementEnding();
196 QQuickStateOperation::ActionList actions;
197 QQuickFlickable *flickable;
198 QQuickFlickablePrivate::AxisData *axisData;
203QQuickFlickablePrivate::AxisData::~AxisData()
205 delete transitionToBounds;
213 auto *d = QQuickItemPrivate::get(
this);
218 d->eventHandlingChildrenWithinBounds =
false;
219 d->eventHandlingChildrenWithinBoundsSet =
true;
224
225
226
227
228
229
230
231
234 const QQuickItem *flickable = parentItem();
235 const QPointF posInFlickable = flickable->mapFromItem(
this, point);
236 return flickable->contains(posInFlickable);
240QQuickFlickablePrivate::QQuickFlickablePrivate()
241 : contentItem(
new QQuickFlickableContentItem)
242 , hData(
this, &QQuickFlickablePrivate::setViewportX)
243 , vData(
this, &QQuickFlickablePrivate::setViewportY)
244 , hMoved(
false), vMoved(
false)
245 , stealGrab(
false), pressed(
false)
246 , scrollingPhase(
false), interactive(
true), calcVelocity(
false)
247 , pixelAligned(
false)
249 , acceptedButtons(Qt::LeftButton)
252 , deceleration(QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickDeceleration).toReal())
253 , wheelDeceleration(15000)
254 , maxVelocity(QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickMaximumVelocity).toReal())
255 , delayedPressEvent(
nullptr), pressDelay(0), fixupDuration(400)
256 , flickBoost(1.0), initialWheelFlickDistance(qApp->styleHints()->wheelScrollLines() * 24)
257 , fixupMode(Normal), vTime(0), visibleArea(
nullptr)
258 , flickableDirection(QQuickFlickable::AutoFlickDirection)
259 , boundsBehavior(QQuickFlickable::DragAndOvershootBounds)
260 , boundsMovement(QQuickFlickable::FollowBoundsBehavior)
263 const int wheelDecelerationEnv = qEnvironmentVariableIntValue(
"QT_QUICK_FLICKABLE_WHEEL_DECELERATION");
264 if (wheelDecelerationEnv > 0)
265 wheelDeceleration = wheelDecelerationEnv;
268void QQuickFlickablePrivate::init()
270 Q_Q(QQuickFlickable);
271 QQml_setParent_noEvent(contentItem, q);
272 contentItem->setParentItem(q);
273 qmlobject_connect(&timeline, QQuickTimeLine, SIGNAL(completed()),
274 q, QQuickFlickable, SLOT(timelineCompleted()));
275 qmlobject_connect(&velocityTimeline, QQuickTimeLine, SIGNAL(completed()),
276 q, QQuickFlickable, SLOT(velocityTimelineCompleted()));
277 q->setAcceptedMouseButtons(acceptedButtons);
278 q->setAcceptTouchEvents(
true);
279 q->setFiltersChildMouseEvents(
true);
280 q->setFlag(QQuickItem::ItemIsViewport);
281 QQuickItemPrivate *viewportPrivate = QQuickItemPrivate::get(contentItem);
282 viewportPrivate->addItemChangeListener(
this, QQuickItemPrivate::Geometry);
283 setSizePolicy(QLayoutPolicy::Expanding, QLayoutPolicy::Expanding);
287
288
289
290
291qreal QQuickFlickablePrivate::overShootDistance(qreal velocity)
const
293 if (maxVelocity <= 0)
299void QQuickFlickablePrivate::AxisData::addVelocitySample(qreal v, qreal maxVelocity)
303 else if (v < -maxVelocity)
305 velocityBuffer.append(v);
307 velocityBuffer.remove(0);
310void QQuickFlickablePrivate::AxisData::updateVelocity()
315 for (
int i = 0; i < count; ++i) {
316 qreal v = velocityBuffer.at(i);
323void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change,
const QRectF &oldGeom)
325 Q_Q(QQuickFlickable);
326 if (item == contentItem) {
327 Qt::Orientations orient;
328 if (change.xChange())
329 orient |= Qt::Horizontal;
330 if (change.yChange())
331 orient |= Qt::Vertical;
333 q->viewportMoved(orient);
334 const QPointF deltaMoved = item->position() - oldGeom.topLeft();
335 if (hData.contentPositionChangedExternallyDuringDrag)
336 hData.pressPos += deltaMoved.x();
337 if (vData.contentPositionChangedExternallyDuringDrag)
338 vData.pressPos += deltaMoved.y();
340 if (orient & Qt::Horizontal)
341 emit q->contentXChanged();
342 if (orient & Qt::Vertical)
343 emit q->contentYChanged();
347bool QQuickFlickablePrivate::flickX(QEvent::Type eventType, qreal velocity)
349 Q_Q(QQuickFlickable);
350 return flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, eventType, velocity);
353bool QQuickFlickablePrivate::flickY(QEvent::Type eventType, qreal velocity)
355 Q_Q(QQuickFlickable);
356 return flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, eventType, velocity);
359bool QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal,
360 QQuickTimeLineCallback::Callback fixupCallback,
361 QEvent::Type eventType, qreal velocity)
363 Q_Q(QQuickFlickable);
364 qreal maxDistance = -1;
365 data.fixingUp =
false;
368 maxDistance = qAbs(minExtent - data.move.value());
369 data.flickTarget = minExtent;
371 maxDistance = qAbs(maxExtent - data.move.value());
372 data.flickTarget = maxExtent;
374 if (maxDistance > 0 || boundsBehavior & QQuickFlickable::OvershootBounds) {
376 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
383 qreal accel = eventType == QEvent::Wheel ? wheelDeceleration : deceleration;
384 qCDebug(lcFlickable) <<
"choosing deceleration" << accel <<
"for" << eventType;
387 qreal dist = v2 / (accel * 2.0);
390 qreal target = std::round(data.move.value() - dist);
391 dist = -target + data.move.value();
392 accel = v2 / (2.0f * qAbs(dist));
395 if (!data.inOvershoot) {
396 if (boundsBehavior & QQuickFlickable::OvershootBounds)
397 timeline.accel(data.move, v, accel);
399 timeline.accel(data.move, v, accel, maxDistance);
401 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback,
this));
404 return !hData.flicking && q->xflick();
405 else if (&data == &vData)
406 return !vData.flicking && q->yflick();
410 fixup(data, minExtent, maxExtent);
415void QQuickFlickablePrivate::fixupY_callback(
void *data)
417 ((QQuickFlickablePrivate *)data)->fixupY();
420void QQuickFlickablePrivate::fixupX_callback(
void *data)
422 ((QQuickFlickablePrivate *)data)->fixupX();
425void QQuickFlickablePrivate::fixupX()
427 Q_Q(QQuickFlickable);
428 if (!q->isComponentComplete())
430 fixup(hData, q->minXExtent(), q->maxXExtent());
433void QQuickFlickablePrivate::fixupY()
435 Q_Q(QQuickFlickable);
436 if (!q->isComponentComplete())
438 fixup(vData, q->minYExtent(), q->maxYExtent());
442
443
444
445
446
447
448
449void QQuickFlickablePrivate::adjustContentPos(AxisData &data, qreal toPos)
451 Q_Q(QQuickFlickable);
454 timeline.set(data.move, toPos);
459 timeline.move(data.move, toPos, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
460 data.fixingUp =
true;
463 if (data.transitionToBounds && data.transitionToBounds->startTransition(&data, toPos)) {
464 q->movementStarting();
465 data.fixingUp =
true;
467 qreal dist = toPos - data.move;
468 timeline.move(data.move, toPos - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
469 timeline.move(data.move, toPos, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
470 data.fixingUp =
true;
476void QQuickFlickablePrivate::resetTimeline(AxisData &data)
478 timeline.reset(data.move);
479 if (data.transitionToBounds)
480 data.transitionToBounds->stopTransition();
483void QQuickFlickablePrivate::clearTimeline()
486 if (hData.transitionToBounds)
487 hData.transitionToBounds->stopTransition();
488 if (vData.transitionToBounds)
489 vData.transitionToBounds->stopTransition();
493
494
495
496
497
498
499
500
501
502void QQuickFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
507 if (data.move.value() >= minExtent || maxExtent > minExtent) {
508 const bool wasActive = timeline.isActive();
510 if (data.move.value() != minExtent)
511 adjustContentPos(data, minExtent);
513 timeline.move(data.move, data.move.value(), QEasingCurve(QEasingCurve::Linear), 1);
514 }
else if (data.move.value() <= maxExtent) {
516 adjustContentPos(data, maxExtent);
517 }
else if (-std::round(-data.move.value()) != data.move.value()) {
520 qreal val = data.move.value();
521 if (std::abs(std::round(val) - val) < 0.25)
522 val = std::round(val);
523 else if (data.smoothVelocity.value() > 0)
524 val = std::ceil(val);
525 else if (data.smoothVelocity.value() < 0)
526 val = std::floor(val);
528 val = std::round(val);
529 timeline.set(data.move, val);
531 data.inOvershoot =
false;
533 data.vTime = timeline.time();
538 if (a == 0.0 || b == 0.0) {
543 return a <= b || qFuzzyCompare(a, b);
547
548
549
550
551
552
553
554
555void QQuickFlickablePrivate::updateBeginningEnd()
557 Q_Q(QQuickFlickable);
558 bool atXBeginningChange =
false, atXEndChange =
false;
559 bool atYBeginningChange =
false, atYEndChange =
false;
562 const qreal maxyextent = -q->maxYExtent();
563 const qreal minyextent = -q->minYExtent();
564 const qreal ypos = pixelAligned ? -std::round(vData.move.value()) : -vData.move.value();
565 bool atBeginning = fuzzyLessThanOrEqualTo(ypos, std::ceil(minyextent));
566 bool atEnd = fuzzyLessThanOrEqualTo(std::floor(maxyextent), ypos);
568 if (atBeginning != vData.atBeginning) {
569 vData.atBeginning = atBeginning;
570 atYBeginningChange =
true;
571 if (!vData.moving && atBeginning)
572 vData.smoothVelocity.setValue(0);
574 if (atEnd != vData.atEnd) {
577 if (!vData.moving && atEnd)
578 vData.smoothVelocity.setValue(0);
582 const qreal maxxextent = -q->maxXExtent();
583 const qreal minxextent = -q->minXExtent();
584 const qreal xpos = pixelAligned ? -std::round(hData.move.value()) : -hData.move.value();
585 atBeginning = fuzzyLessThanOrEqualTo(xpos, std::ceil(minxextent));
586 atEnd = fuzzyLessThanOrEqualTo(std::floor(maxxextent), xpos);
588 if (atBeginning != hData.atBeginning) {
589 hData.atBeginning = atBeginning;
590 atXBeginningChange =
true;
591 if (!hData.moving && atBeginning)
592 hData.smoothVelocity.setValue(0);
594 if (atEnd != hData.atEnd) {
597 if (!hData.moving && atEnd)
598 hData.smoothVelocity.setValue(0);
601 if (vData.extentsChanged) {
602 vData.extentsChanged =
false;
603 qreal originY = q->originY();
604 if (vData.origin != originY) {
605 vData.origin = originY;
606 emit q->originYChanged();
610 if (hData.extentsChanged) {
611 hData.extentsChanged =
false;
612 qreal originX = q->originX();
613 if (hData.origin != originX) {
614 hData.origin = originX;
615 emit q->originXChanged();
619 if (atXEndChange || atYEndChange || atXBeginningChange || atYBeginningChange)
620 emit q->isAtBoundaryChanged();
622 emit q->atXEndChanged();
623 if (atXBeginningChange)
624 emit q->atXBeginningChanged();
626 emit q->atYEndChanged();
627 if (atYBeginningChange)
628 emit q->atYBeginningChanged();
631 visibleArea->updateVisible();
635
636
637
638
639
642
643
644
645
646
647
648
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
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
729
730
731
732
733
736
737
738
739
740
741
742
743
746
747
748
749
750
751
754
755
756
757
758
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780QQuickFlickable::QQuickFlickable(QQuickItem *parent)
781 : QQuickItem(*(
new QQuickFlickablePrivate), parent)
783 Q_D(QQuickFlickable);
787QQuickFlickable::QQuickFlickable(QQuickFlickablePrivate &dd, QQuickItem *parent)
788 : QQuickItem(dd, parent)
790 Q_D(QQuickFlickable);
794QQuickFlickable::~QQuickFlickable()
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818qreal QQuickFlickable::contentX()
const
820 Q_D(
const QQuickFlickable);
821 return -d->contentItem->x();
824void QQuickFlickable::setContentX(qreal pos)
826 Q_D(QQuickFlickable);
827 d->hData.explicitValue =
true;
828 d->resetTimeline(d->hData);
829 d->hData.vTime = d->timeline.time();
830 if (isMoving() || isFlicking())
831 movementEnding(
true,
false);
832 if (!qFuzzyCompare(-pos, d->hData.move.value())) {
833 d->hData.contentPositionChangedExternallyDuringDrag = d->hData.dragging;
834 d->hData.move.setValue(-pos);
835 d->hData.contentPositionChangedExternallyDuringDrag =
false;
839qreal QQuickFlickable::contentY()
const
841 Q_D(
const QQuickFlickable);
842 return -d->contentItem->y();
845void QQuickFlickable::setContentY(qreal pos)
847 Q_D(QQuickFlickable);
848 d->vData.explicitValue =
true;
849 d->resetTimeline(d->vData);
850 d->vData.vTime = d->timeline.time();
851 if (isMoving() || isFlicking())
852 movementEnding(
false,
true);
853 if (!qFuzzyCompare(-pos, d->vData.move.value())) {
854 d->vData.contentPositionChangedExternallyDuringDrag = d->vData.dragging;
855 d->vData.move.setValue(-pos);
856 d->vData.contentPositionChangedExternallyDuringDrag =
false;
861
862
863
864
865
866
867
868
869
870
871
872
873bool QQuickFlickable::isInteractive()
const
875 Q_D(
const QQuickFlickable);
876 return d->interactive;
879void QQuickFlickable::setInteractive(
bool interactive)
881 Q_D(QQuickFlickable);
882 if (interactive != d->interactive) {
883 d->interactive = interactive;
885 d->cancelInteraction();
887 emit interactiveChanged();
892
893
894
895
896
897
898
899
900
901
902
903
904qreal QQuickFlickable::horizontalVelocity()
const
906 Q_D(
const QQuickFlickable);
907 return d->hData.smoothVelocity.value();
910qreal QQuickFlickable::verticalVelocity()
const
912 Q_D(
const QQuickFlickable);
913 return d->vData.smoothVelocity.value();
917
918
919
920
921
922
923
924
925bool QQuickFlickable::isAtXEnd()
const
927 Q_D(
const QQuickFlickable);
928 return d->hData.atEnd;
931bool QQuickFlickable::isAtXBeginning()
const
933 Q_D(
const QQuickFlickable);
934 return d->hData.atBeginning;
937bool QQuickFlickable::isAtYEnd()
const
939 Q_D(
const QQuickFlickable);
940 return d->vData.atEnd;
943bool QQuickFlickable::isAtYBeginning()
const
945 Q_D(
const QQuickFlickable);
946 return d->vData.atBeginning;
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967QQuickItem *QQuickFlickable::contentItem()
const
969 Q_D(
const QQuickFlickable);
970 return d->contentItem;
973QQuickFlickableVisibleArea *QQuickFlickable::visibleArea()
975 Q_D(QQuickFlickable);
976 if (!d->visibleArea) {
977 d->visibleArea =
new QQuickFlickableVisibleArea(
this);
978 d->visibleArea->updateVisible();
980 return d->visibleArea;
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002QQuickFlickable::FlickableDirection QQuickFlickable::flickableDirection()
const
1004 Q_D(
const QQuickFlickable);
1005 return d->flickableDirection;
1008void QQuickFlickable::setFlickableDirection(FlickableDirection direction)
1010 Q_D(QQuickFlickable);
1011 if (direction != d->flickableDirection) {
1012 d->flickableDirection = direction;
1013 emit flickableDirectionChanged();
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029bool QQuickFlickable::pixelAligned()
const
1031 Q_D(
const QQuickFlickable);
1032 return d->pixelAligned;
1035void QQuickFlickable::setPixelAligned(
bool align)
1037 Q_D(QQuickFlickable);
1038 if (align != d->pixelAligned) {
1039 d->pixelAligned = align;
1040 emit pixelAlignedChanged();
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056bool QQuickFlickable::synchronousDrag()
const
1058 Q_D(
const QQuickFlickable);
1062void QQuickFlickable::setSynchronousDrag(
bool v)
1064 Q_D(QQuickFlickable);
1065 if (v != d->syncDrag) {
1067 emit synchronousDragChanged();
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086Qt::MouseButtons QQuickFlickable::acceptedButtons()
const
1088 Q_D(
const QQuickFlickable);
1089 return d->acceptedButtons;
1092void QQuickFlickable::setAcceptedButtons(Qt::MouseButtons buttons)
1094 Q_D(QQuickFlickable);
1095 if (d->acceptedButtons == buttons)
1098 d->acceptedButtons = buttons;
1099 setAcceptedMouseButtons(buttons);
1100 emit acceptedButtonsChanged();
1104
1105
1106
1107QVector2D QQuickFlickablePrivate::firstPointLocalVelocity(QPointerEvent *event)
1109 QTransform transform = windowToItemTransform();
1111 return QVector2D(transform.map(event->point(0).velocity().toPointF()) - transform.map(QPointF()));
1114qint64 QQuickFlickablePrivate::computeCurrentTime(QInputEvent *event)
const
1116 if (0 != event->timestamp())
1117 return event->timestamp();
1118 if (!timer.isValid())
1120 return timer.elapsed();
1123void QQuickFlickablePrivate::handlePressEvent(QPointerEvent *event)
1125 Q_Q(QQuickFlickable);
1127 if (interactive && timeline.isActive()
1128 && ((qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity && !hData.fixingUp && !hData.inOvershoot)
1129 || (qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity && !vData.fixingUp && !vData.inOvershoot))) {
1133 int flickTime = timeline.time();
1134 if (flickTime > 600) {
1136 hData.continuousFlickVelocity = 0;
1137 vData.continuousFlickVelocity = 0;
1140 hData.continuousFlickVelocity = -hData.smoothVelocity.value();
1141 vData.continuousFlickVelocity = -vData.smoothVelocity.value();
1142 if (flickTime > 300)
1143 flickBoost = qMax(1.0, flickBoost - 0.5);
1147 hData.continuousFlickVelocity = 0;
1148 vData.continuousFlickVelocity = 0;
1151 if (event->isSinglePointEvent())
1152 q->setKeepMouseGrab(stealGrab);
1154 q->setKeepTouchGrab(stealGrab);
1156 maybeBeginDrag(computeCurrentTime(event), event->points().first().position(),
1157 event->isSinglePointEvent() ?
static_cast<QSinglePointEvent *>(event)->buttons()
1161void QQuickFlickablePrivate::maybeBeginDrag(qint64 currentTimestamp,
const QPointF &pressPosn, Qt::MouseButtons buttons)
1163 Q_Q(QQuickFlickable);
1164 clearDelayedPress();
1166 pressed = (buttons == Qt::NoButton) || (acceptedButtons != Qt::NoButton && (buttons & acceptedButtons) != 0);
1168 if (hData.transitionToBounds)
1169 hData.transitionToBounds->stopTransition();
1170 if (vData.transitionToBounds)
1171 vData.transitionToBounds->stopTransition();
1172 if (!hData.fixingUp)
1173 resetTimeline(hData);
1174 if (!vData.fixingUp)
1175 resetTimeline(vData);
1179 hData.dragMinBound = q->minXExtent() - hData.startMargin;
1180 vData.dragMinBound = q->minYExtent() - vData.startMargin;
1181 hData.dragMaxBound = q->maxXExtent() + hData.endMargin;
1182 vData.dragMaxBound = q->maxYExtent() + vData.endMargin;
1184 lastPos = QPointF();
1185 pressPos = pressPosn;
1186 hData.pressPos = hData.move.value();
1187 vData.pressPos = vData.move.value();
1188 const bool wasFlicking = hData.flicking || vData.flicking;
1189 hData.flickingWhenDragBegan = hData.flicking;
1190 vData.flickingWhenDragBegan = vData.flicking;
1191 if (hData.flicking) {
1192 hData.flicking =
false;
1193 emit q->flickingHorizontallyChanged();
1195 if (vData.flicking) {
1196 vData.flicking =
false;
1197 emit q->flickingVerticallyChanged();
1200 emit q->flickingChanged();
1201 lastPosTime = lastPressTime = currentTimestamp;
1202 vData.velocityTime.start();
1203 hData.velocityTime.start();
1206void QQuickFlickablePrivate::drag(qint64 currentTimestamp, QEvent::Type eventType,
const QPointF &localPos,
1207 const QVector2D &deltas,
bool overThreshold,
bool momentum,
1208 bool velocitySensitiveOverBounds,
const QVector2D &velocity)
1210 Q_Q(QQuickFlickable);
1211 bool rejectY =
false;
1212 bool rejectX =
false;
1214 bool keepY = q->yflick();
1215 bool keepX = q->xflick();
1217 bool stealY =
false;
1218 bool stealX =
false;
1219 bool isTouchEvent =
false;
1220 switch (eventType) {
1221 case QEvent::MouseMove:
1222 stealX = stealY = stealGrab;
1225 stealX = stealY = scrollingPhase;
1227 case QEvent::TouchUpdate:
1228 stealX = stealY = stealGrab;
1229 isTouchEvent =
true;
1235 bool prevHMoved = hMoved;
1236 bool prevVMoved = vMoved;
1238 qint64 elapsedSincePress = currentTimestamp - lastPressTime;
1239 qCDebug(lcFlickable).nospace() << currentTimestamp <<
' ' << eventType <<
" drag @ " << localPos.x() <<
',' << localPos.y()
1240 <<
" \u0394 " << deltas.x() <<
',' << deltas.y() <<
" vel " << velocity.x() <<
',' << velocity.y()
1241 <<
" thrsld? " << overThreshold <<
" momentum? " << momentum <<
" velSens? " << velocitySensitiveOverBounds
1242 <<
" sincePress " << elapsedSincePress;
1245 qreal dy = deltas.y();
1246 if (overThreshold || elapsedSincePress > 200) {
1247 if (!vMoved && !vData.dragging)
1248 vData.dragStartOffset = dy;
1249 qreal newY = dy + vData.pressPos - (syncDrag ? 0 : vData.dragStartOffset);
1253 const qreal minY = vData.dragMinBound + vData.startMargin;
1254 const qreal maxY = vData.dragMaxBound - vData.endMargin;
1255 if (!(boundsBehavior & QQuickFlickable::DragOverBounds)) {
1256 if (fuzzyLessThanOrEqualTo(newY, maxY)) {
1258 rejectY = vData.pressPos == maxY && vData.move.value() == maxY && dy < 0;
1260 if (fuzzyLessThanOrEqualTo(minY, newY)) {
1262 rejectY |= vData.pressPos == minY && vData.move.value() == minY && dy > 0;
1266 if (vel > 0. && vel > vData.velocity)
1268 else if (vel < 0. && vel < vData.velocity)
1272 if (momentum && vData.atBeginning) {
1273 if (!vData.inRebound) {
1274 vData.inRebound =
true;
1275 q->returnToBounds();
1279 if (velocitySensitiveOverBounds) {
1282 newY = minY + overshoot;
1284 newY = minY + (newY - minY) / 2;
1286 }
else if (newY < maxY && maxY - minY <= 0) {
1288 if (momentum && vData.atEnd) {
1289 if (!vData.inRebound) {
1290 vData.inRebound =
true;
1291 q->returnToBounds();
1295 if (velocitySensitiveOverBounds) {
1298 newY = maxY - overshoot;
1300 newY = maxY + (newY - maxY) / 2;
1304 if (!rejectY && stealGrab && dy != vData.previousDragDelta) {
1306 vData.move.setValue(newY);
1309 if (!rejectY && overThreshold)
1312 if ((newY >= minY && vData.pressPos == minY && vData.move.value() == minY && dy > 0)
1313 || (newY <= maxY && vData.pressPos == maxY && vData.move.value() == maxY && dy < 0)) {
1317 vData.previousDragDelta = dy;
1321 qreal dx = deltas.x();
1322 if (overThreshold || elapsedSincePress > 200) {
1323 if (!hMoved && !hData.dragging)
1324 hData.dragStartOffset = dx;
1325 qreal newX = dx + hData.pressPos - (syncDrag ? 0 : hData.dragStartOffset);
1326 const qreal minX = hData.dragMinBound + hData.startMargin;
1327 const qreal maxX = hData.dragMaxBound - hData.endMargin;
1328 if (!(boundsBehavior & QQuickFlickable::DragOverBounds)) {
1329 if (fuzzyLessThanOrEqualTo(newX, maxX)) {
1331 rejectX = hData.pressPos == maxX && hData.move.value() == maxX && dx < 0;
1333 if (fuzzyLessThanOrEqualTo(minX, newX)) {
1335 rejectX |= hData.pressPos == minX && hData.move.value() == minX && dx > 0;
1339 if (vel > 0. && vel > hData.velocity)
1341 else if (vel < 0. && vel < hData.velocity)
1345 if (momentum && hData.atBeginning) {
1346 if (!hData.inRebound) {
1347 hData.inRebound =
true;
1348 q->returnToBounds();
1352 if (velocitySensitiveOverBounds) {
1355 newX = minX + overshoot;
1357 newX = minX + (newX - minX) / 2;
1359 }
else if (newX < maxX && maxX - minX <= 0) {
1361 if (momentum && hData.atEnd) {
1362 if (!hData.inRebound) {
1363 hData.inRebound =
true;
1364 q->returnToBounds();
1368 if (velocitySensitiveOverBounds) {
1371 newX = maxX - overshoot;
1373 newX = maxX + (newX - maxX) / 2;
1377 if (!rejectX && stealGrab && dx != hData.previousDragDelta) {
1379 hData.move.setValue(newX);
1383 if (!rejectX && overThreshold)
1386 if ((newX >= minX && vData.pressPos == minX && vData.move.value() == minX && dx > 0)
1387 || (newX <= maxX && vData.pressPos == maxX && vData.move.value() == maxX && dx < 0)) {
1391 hData.previousDragDelta = dx;
1394 stealGrab = stealX || stealY;
1396 if ((stealX && keepX) || (stealY && keepY)) {
1398 q->setKeepTouchGrab(
true);
1400 q->setKeepMouseGrab(
true);
1402 clearDelayedPress();
1406 vData.velocityBuffer.clear();
1410 hData.velocityBuffer.clear();
1414 if (momentum && !hData.flicking && !vData.flicking)
1415 flickingStarted(hData.velocity != 0, vData.velocity != 0);
1418 if ((hMoved && !prevHMoved) || (vMoved && !prevVMoved))
1419 q->movementStarting();
1421 lastPosTime = currentTimestamp;
1422 if (q->yflick() && !rejectY)
1423 vData.addVelocitySample(velocity.y(), maxVelocity);
1424 if (q->xflick() && !rejectX)
1425 hData.addVelocitySample(velocity.x(), maxVelocity);
1429void QQuickFlickablePrivate::handleMoveEvent(QPointerEvent *event)
1431 Q_Q(QQuickFlickable);
1432 if (!interactive || lastPosTime == -1 ||
1433 (event->isSinglePointEvent() && !buttonsAccepted(
static_cast<QSinglePointEvent *>(event))))
1436 qint64 currentTimestamp = computeCurrentTime(event);
1437 const auto &firstPoint = event->points().first();
1438 const auto &pos = firstPoint.position();
1439 const QVector2D deltas = QVector2D(pos - q->mapFromGlobal(firstPoint.globalPressPosition()));
1440 const QVector2D velocity = firstPointLocalVelocity(event);
1441 bool overThreshold =
false;
1443 if (q->isMoving()) {
1445
1446
1447
1448
1449
1450
1451
1452 overThreshold =
true;
1453 }
else if (event->pointCount() == 1) {
1455 overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, firstPoint);
1457 overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.x(), Qt::XAxis, firstPoint);
1459 qCDebug(lcFilter) << q->objectName() <<
"ignoring multi-touch" << event;
1462 drag(currentTimestamp, event->type(), pos, deltas, overThreshold,
false,
false, velocity);
1465void QQuickFlickablePrivate::handleReleaseEvent(QPointerEvent *event)
1467 Q_Q(QQuickFlickable);
1469 q->setKeepMouseGrab(
false);
1470 q->setKeepTouchGrab(
false);
1474 qint64 elapsed = computeCurrentTime(event) - lastPosTime;
1476 vData.updateVelocity();
1477 hData.updateVelocity();
1481 if (lastPosTime == -1)
1484 hData.vTime = vData.vTime = timeline.time();
1486 bool canBoost =
false;
1487 const auto pos = event->points().first().position();
1488 const auto pressPos = q->mapFromGlobal(event->points().first().globalPressPosition());
1489 const QVector2D eventVelocity = firstPointLocalVelocity(event);
1490 qCDebug(lcVel) << event->deviceType() << event->type() <<
"velocity" << event->points().first().velocity() <<
"transformed to local" << eventVelocity;
1492 qreal vVelocity = 0;
1493 if (elapsed < 100 && vData.velocity != 0.) {
1494 vVelocity = (event->device()->capabilities().testFlag(QInputDevice::Capability::Velocity)
1495 ? eventVelocity.y() : vData.velocity);
1497 if ((vData.atBeginning && vVelocity > 0.) || (vData.atEnd && vVelocity < 0.)) {
1499 }
else if (vData.continuousFlickVelocity != 0.0
1501 && ((vVelocity > 0) == (vData.continuousFlickVelocity > 0))
1507 qreal hVelocity = 0;
1508 if (elapsed < 100 && hData.velocity != 0.) {
1509 hVelocity = (event->device()->capabilities().testFlag(QInputDevice::Capability::Velocity)
1510 ? eventVelocity.x() : hData.velocity);
1512 if ((hData.atBeginning && hVelocity > 0.) || (hData.atEnd && hVelocity < 0.)) {
1514 }
else if (hData.continuousFlickVelocity != 0.0
1516 && ((hVelocity > 0) == (hData.continuousFlickVelocity > 0))
1523 const int flickThreshold = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickStartDistance).toInt();
1525 bool anyPointGrabbed = event->points().constEnd() !=
1526 std::find_if(event->points().constBegin(),event->points().constEnd(),
1527 [q, event](
const QEventPoint &point) {
return event->exclusiveGrabber(point) == q; });
1529 bool flickedVertically =
false;
1530 vVelocity *= flickBoost;
1531 const bool isVerticalFlickAllowed = anyPointGrabbed &&
1532 q->yflick() && qAbs(vVelocity) > _q_MinimumFlickVelocity &&
1533 qAbs(pos.y() - pressPos.y()) > flickThreshold;
1534 if (isVerticalFlickAllowed) {
1535 velocityTimeline.reset(vData.smoothVelocity);
1536 vData.smoothVelocity.setValue(-vVelocity);
1537 flickedVertically = flickY(event->type(), vVelocity);
1540 bool flickedHorizontally =
false;
1541 hVelocity *= flickBoost;
1542 const bool isHorizontalFlickAllowed = anyPointGrabbed &&
1543 q->xflick() && qAbs(hVelocity) > _q_MinimumFlickVelocity &&
1544 qAbs(pos.x() - pressPos.x()) > flickThreshold;
1545 if (isHorizontalFlickAllowed) {
1546 velocityTimeline.reset(hData.smoothVelocity);
1547 hData.smoothVelocity.setValue(-hVelocity);
1548 flickedHorizontally = flickX(event->type(), hVelocity);
1551 if (!isVerticalFlickAllowed)
1554 if (!isHorizontalFlickAllowed)
1557 flickingStarted(flickedHorizontally, flickedVertically);
1558 if (!isViewMoving()) {
1559 q->movementEnding();
1561 if (flickedVertically)
1563 if (flickedHorizontally)
1565 q->movementStarting();
1569bool QQuickFlickablePrivate::buttonsAccepted(
const QSinglePointEvent *event)
1571 return !((event->button() & acceptedButtons) == 0 && (event->buttons() & acceptedButtons) == 0);
1574void QQuickFlickable::mousePressEvent(QMouseEvent *event)
1576 Q_D(QQuickFlickable);
1577 if (d->interactive && !d->replayingPressEvent && d->buttonsAccepted(event) && d->wantsPointerEvent(event)) {
1579 d->handlePressEvent(event);
1582 QQuickItem::mousePressEvent(event);
1586void QQuickFlickable::mouseMoveEvent(QMouseEvent *event)
1588 Q_D(QQuickFlickable);
1589 if (d->interactive && d->buttonsAccepted(event) && d->wantsPointerEvent(event)) {
1590 d->handleMoveEvent(event);
1593 QQuickItem::mouseMoveEvent(event);
1597void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
1599 Q_D(QQuickFlickable);
1600 if (d->interactive && d->buttonsAccepted(event) && d->wantsPointerEvent(event)) {
1601 if (d->delayedPressEvent) {
1602 d->replayDelayedPress();
1604 auto &firstPoint = event->point(0);
1605 if (
const auto *grabber = event->exclusiveGrabber(firstPoint); grabber && grabber->isQuickItemType()) {
1608 const auto oldPosition = firstPoint.position();
1609 QMutableEventPoint::setPosition(firstPoint, event->scenePosition());
1610 QCoreApplication::sendEvent(window(), event);
1611 QMutableEventPoint::setPosition(firstPoint, oldPosition);
1615 d->stealGrab =
false;
1620 d->handleReleaseEvent(event);
1623 QQuickItem::mouseReleaseEvent(event);
1627void QQuickFlickable::touchEvent(QTouchEvent *event)
1629 Q_D(QQuickFlickable);
1631 if (event->type() == QEvent::TouchCancel) {
1632 if (d->interactive && d->wantsPointerEvent(event))
1633 d->cancelInteraction();
1635 QQuickItem::touchEvent(event);
1639 bool unhandled =
false;
1640 const auto &firstPoint = event->points().first();
1641 switch (firstPoint.state()) {
1642 case QEventPoint::State::Pressed:
1643 if (d->interactive && !d->replayingPressEvent && d->wantsPointerEvent(event)) {
1645 d->handlePressEvent(event);
1651 case QEventPoint::State::Updated:
1652 if (d->interactive && d->wantsPointerEvent(event)) {
1653 d->handleMoveEvent(event);
1659 case QEventPoint::State::Released:
1660 if (d->interactive && d->wantsPointerEvent(event)) {
1661 if (d->delayedPressEvent) {
1662 d->replayDelayedPress();
1664 const auto &firstPoint = event->point(0);
1665 if (
const auto *grabber = event->exclusiveGrabber(firstPoint); grabber && grabber->isQuickItemType()) {
1667 QScopedPointer<QPointerEvent> localizedEvent(
1668 QQuickDeliveryAgentPrivate::clonePointerEvent(event, firstPoint.scenePosition()));
1669 QCoreApplication::sendEvent(window(), localizedEvent.data());
1673 d->stealGrab =
false;
1678 d->handleReleaseEvent(event);
1684 case QEventPoint::State::Stationary:
1685 case QEventPoint::State::Unknown:
1689 QQuickItem::touchEvent(event);
1692#if QT_CONFIG(wheelevent)
1693void QQuickFlickable::wheelEvent(QWheelEvent *event)
1695 Q_D(QQuickFlickable);
1696 if (!d->interactive || !d->wantsPointerEvent(event)) {
1697 QQuickItem::wheelEvent(event);
1700 qCDebug(lcWheel) << event->device() << event << event->source();
1701 event->setAccepted(
false);
1702 qint64 currentTimestamp = d->computeCurrentTime(event);
1703 switch (event->phase()) {
1704 case Qt::ScrollBegin:
1705 d->scrollingPhase =
true;
1706 d->accumulatedWheelPixelDelta = QVector2D();
1707 d->vData.velocity = 0;
1708 d->hData.velocity = 0;
1710 d->maybeBeginDrag(currentTimestamp, event->position());
1711 d->lastPosTime = -1;
1713 case Qt::NoScrollPhase:
1714 case Qt::ScrollUpdate:
1715 if (d->scrollingPhase)
1718 case Qt::ScrollMomentum:
1720 d->scrollingPhase =
false;
1721 d->draggingEnding();
1724 d->lastPosTime = -1;
1728 d->scrollingPhase =
false;
1729 d->draggingEnding();
1731 d->lastPosTime = -1;
1732 d->stealGrab =
false;
1733 if (!d->velocityTimeline.isActive() && !d->timeline.isActive())
1734 movementEnding(
true,
true);
1738 qreal elapsed = qreal(currentTimestamp - d->lastPosTime) / qreal(1000);
1740 d->lastPosTime = currentTimestamp;
1741 qCDebug(lcWheel) <<
"insufficient elapsed time: can't calculate velocity" << elapsed;
1745 if (event->source() == Qt::MouseEventNotSynthesized || event->pixelDelta().isNull() || event->phase() == Qt::NoScrollPhase) {
1747 int xDelta = event->angleDelta().x();
1748 int yDelta = event->angleDelta().y();
1750 if (d->wheelDeceleration > _q_MaximumWheelDeceleration) {
1751 const qreal wheelScroll = -qApp->styleHints()->wheelScrollLines() * 24;
1754 if (yflick() && yDelta != 0) {
1755 d->moveReason = QQuickFlickablePrivate::Mouse;
1757 qreal scrollPixel = (-yDelta / 120.0 * wheelScroll);
1758 bool acceptEvent =
true;
1759 if (scrollPixel > 0) {
1760 if (d->vData.move.value() >= minYExtent()) {
1762 acceptEvent =
false;
1765 if (d->vData.move.value() <= maxYExtent()) {
1767 acceptEvent =
false;
1771 if (d->boundsBehavior == QQuickFlickable::StopAtBounds) {
1772 const qreal estContentPos = scrollPixel + d->vData.move.value();
1773 if (scrollPixel > 0) {
1774 if (estContentPos > minYExtent()) {
1775 scrollPixel = minYExtent() - d->vData.move.value();
1776 acceptEvent =
false;
1779 if (estContentPos < maxYExtent()) {
1780 scrollPixel = maxYExtent() - d->vData.move.value();
1781 acceptEvent =
false;
1785 d->resetTimeline(d->vData);
1787 d->timeline.moveBy(d->vData.move, scrollPixel, QEasingCurve(QEasingCurve::OutExpo), 3*d->fixupDuration/4);
1788 d->vData.fixingUp =
true;
1789 d->timeline.callback(QQuickTimeLineCallback(&d->vData.move, QQuickFlickablePrivate::fixupY_callback, d));
1794 if (xflick() && xDelta != 0) {
1795 d->moveReason = QQuickFlickablePrivate::Mouse;
1797 qreal scrollPixel = (-xDelta / 120.0 * wheelScroll);
1798 bool acceptEvent =
true;
1799 if (scrollPixel > 0) {
1800 if (d->hData.move.value() >= minXExtent()) {
1802 acceptEvent =
false;
1805 if (d->hData.move.value() <= maxXExtent()) {
1807 acceptEvent =
false;
1811 if (d->boundsBehavior == QQuickFlickable::StopAtBounds) {
1812 const qreal estContentPos = scrollPixel + d->hData.move.value();
1813 if (scrollPixel > 0) {
1814 if (estContentPos > minXExtent()) {
1815 scrollPixel = minXExtent() - d->hData.move.value();
1816 acceptEvent =
false;
1819 if (estContentPos < maxXExtent()) {
1820 scrollPixel = maxXExtent() - d->hData.move.value();
1821 acceptEvent =
false;
1825 d->resetTimeline(d->hData);
1827 d->timeline.moveBy(d->hData.move, scrollPixel, QEasingCurve(QEasingCurve::OutExpo), 3*d->fixupDuration/4);
1828 d->hData.fixingUp =
true;
1829 d->timeline.callback(QQuickTimeLineCallback(&d->hData.move, QQuickFlickablePrivate::fixupX_callback, d));
1845 elapsed = 120 / qSqrt(d->wheelDeceleration * 2 * d->initialWheelFlickDistance);
1846 if (yflick() && yDelta != 0) {
1847 qreal instVelocity = yDelta / elapsed;
1849 if ((instVelocity < 0 && d->vData.velocity > 0) || (instVelocity > 0 && d->vData.velocity < 0))
1850 d->vData.velocityBuffer.clear();
1851 d->vData.addVelocitySample(instVelocity, d->maxVelocity);
1852 d->vData.updateVelocity();
1853 if ((yDelta > 0 && contentY() > -minYExtent()) || (yDelta < 0 && contentY() < -maxYExtent())) {
1854 const bool newFlick = d->flickY(event->type(), d->vData.velocity);
1855 if (newFlick && (d->vData.atBeginning != (yDelta > 0) || d->vData.atEnd != (yDelta < 0))) {
1856 d->flickingStarted(
false,
true);
1863 if (xflick() && xDelta != 0) {
1864 qreal instVelocity = xDelta / elapsed;
1866 if ((instVelocity < 0 && d->hData.velocity > 0) || (instVelocity > 0 && d->hData.velocity < 0))
1867 d->hData.velocityBuffer.clear();
1868 d->hData.addVelocitySample(instVelocity, d->maxVelocity);
1869 d->hData.updateVelocity();
1870 if ((xDelta > 0 && contentX() > -minXExtent()) || (xDelta < 0 && contentX() < -maxXExtent())) {
1871 const bool newFlick = d->flickX(event->type(), d->hData.velocity);
1872 if (newFlick && (d->hData.atBeginning != (xDelta > 0) || d->hData.atEnd != (xDelta < 0))) {
1873 d->flickingStarted(
true,
false);
1883 int xDelta = event->pixelDelta().x();
1884 int yDelta = event->pixelDelta().y();
1886 QVector2D velocity(xDelta / elapsed, yDelta / elapsed);
1887 d->accumulatedWheelPixelDelta += QVector2D(event->pixelDelta());
1893 if (isMoving() || isFlicking() || (yflick() && xflick())
1894 || (xflick() && qAbs(d->accumulatedWheelPixelDelta.x()) > qAbs(d->accumulatedWheelPixelDelta.y() * 2))
1895 || (yflick() && qAbs(d->accumulatedWheelPixelDelta.y()) > qAbs(d->accumulatedWheelPixelDelta.x() * 2))) {
1896 d->drag(currentTimestamp, event->type(), event->position(), d->accumulatedWheelPixelDelta,
1897 true, !d->scrollingPhase,
true, velocity);
1898 d->updateBeginningEnd();
1899 if ((xflick() && !isAtXBeginning() && !isAtXEnd()) || (yflick() && !isAtYBeginning() && !isAtYEnd()))
1902 qCDebug(lcWheel) <<
"not dragging: accumulated deltas" << d->accumulatedWheelPixelDelta <<
1903 "moving?" << isMoving() <<
"can flick horizontally?" << xflick() <<
"vertically?" << yflick();
1906 d->lastPosTime = currentTimestamp;
1908 if (!event->isAccepted())
1909 QQuickItem::wheelEvent(event);
1913bool QQuickFlickablePrivate::isInnermostPressDelay(QQuickItem *i)
const
1915 Q_Q(
const QQuickFlickable);
1916 QQuickItem *item = i;
1918 QQuickFlickable *flick = qobject_cast<QQuickFlickable*>(item);
1919 if (flick && flick->pressDelay() > 0 && flick->isInteractive()) {
1921 return (flick == q);
1923 item = item->parentItem();
1928void QQuickFlickablePrivate::captureDelayedPress(QQuickItem *item, QPointerEvent *event)
1930 Q_Q(QQuickFlickable);
1931 if (!q->window() || pressDelay <= 0)
1936 if (!isInnermostPressDelay(item))
1939 delayedPressEvent = QQuickDeliveryAgentPrivate::clonePointerEvent(event);
1940 delayedPressEvent->setAccepted(
false);
1941 delayedPressTimer.start(pressDelay, q);
1942 qCDebug(lcReplay) <<
"begin press delay" << pressDelay <<
"ms with" << delayedPressEvent;
1945void QQuickFlickablePrivate::clearDelayedPress()
1947 if (delayedPressEvent) {
1948 delayedPressTimer.stop();
1949 qCDebug(lcReplay) <<
"clear delayed press" << delayedPressEvent;
1950 delete delayedPressEvent;
1951 delayedPressEvent =
nullptr;
1955void QQuickFlickablePrivate::replayDelayedPress()
1957 Q_Q(QQuickFlickable);
1958 if (delayedPressEvent) {
1960 QScopedPointer<QPointerEvent> event(delayedPressEvent);
1961 delayedPressEvent =
nullptr;
1962 delayedPressTimer.stop();
1965 if (QQuickWindow *window = q->window()) {
1966 auto da = deliveryAgentPrivate();
1967 da->allowChildEventFiltering =
false;
1968 replayingPressEvent =
true;
1969 auto &firstPoint = event->point(0);
1975 if (event->exclusiveGrabber(firstPoint) == q)
1976 event->setExclusiveGrabber(firstPoint,
nullptr);
1978 qCDebug(lcReplay) <<
"replaying" << event.data();
1981 QMutableEventPoint::setPosition(firstPoint, firstPoint.scenePosition());
1985 QCoreApplication::sendEvent(window, event.data());
1986 qCDebug(lcReplay) <<
"replay done";
1989 replayingPressEvent =
false;
1990 da->allowChildEventFiltering =
true;
1998
1999
2000
2001
2002
2003
2004
2005void QQuickFlickablePrivate::setViewportX(qreal x)
2007 Q_Q(QQuickFlickable);
2008 qreal effectiveX = pixelAligned ? -std::round(-x) : x;
2010 const qreal maxX = q->maxXExtent();
2011 const qreal minX = q->minXExtent();
2013 if (boundsMovement ==
int(QQuickFlickable::StopAtBounds))
2014 effectiveX = qBound(maxX, effectiveX, minX);
2016 contentItem->setX(effectiveX);
2017 if (contentItem->x() != effectiveX)
2020 qreal overshoot = 0.0;
2022 overshoot = maxX - x;
2024 overshoot = minX - x;
2026 if (overshoot != hData.overshoot) {
2027 hData.overshoot = overshoot;
2028 emit q->horizontalOvershootChanged();
2033
2034
2035
2036
2037
2038
2039
2040void QQuickFlickablePrivate::setViewportY(qreal y)
2042 Q_Q(QQuickFlickable);
2043 qreal effectiveY = pixelAligned ? -std::round(-y) : y;
2045 const qreal maxY = q->maxYExtent();
2046 const qreal minY = q->minYExtent();
2048 if (boundsMovement ==
int(QQuickFlickable::StopAtBounds))
2049 effectiveY = qBound(maxY, effectiveY, minY);
2051 contentItem->setY(effectiveY);
2052 if (contentItem->y() != effectiveY)
2055 qreal overshoot = 0.0;
2057 overshoot = maxY - y;
2059 overshoot = minY - y;
2061 if (overshoot != vData.overshoot) {
2062 vData.overshoot = overshoot;
2063 emit q->verticalOvershootChanged();
2067void QQuickFlickable::timerEvent(QTimerEvent *event)
2069 Q_D(QQuickFlickable);
2070 if (event->timerId() == d->delayedPressTimer.timerId()) {
2071 d->delayedPressTimer.stop();
2072 if (d->delayedPressEvent) {
2073 d->replayDelayedPress();
2078qreal QQuickFlickable::minYExtent()
const
2080 Q_D(
const QQuickFlickable);
2081 return d->vData.startMargin;
2084qreal QQuickFlickable::minXExtent()
const
2086 Q_D(
const QQuickFlickable);
2087 return d->hData.startMargin;
2091qreal QQuickFlickable::maxXExtent()
const
2093 Q_D(
const QQuickFlickable);
2094 return qMin<qreal>(minXExtent(), width() - vWidth() - d->hData.endMargin);
2097qreal QQuickFlickable::maxYExtent()
const
2099 Q_D(
const QQuickFlickable);
2100 return qMin<qreal>(minYExtent(), height() - vHeight() - d->vData.endMargin);
2103void QQuickFlickable::componentComplete()
2105 Q_D(QQuickFlickable);
2106 QQuickItem::componentComplete();
2107 if (!d->hData.explicitValue && d->hData.startMargin != 0.)
2108 setContentX(-minXExtent());
2109 if (!d->vData.explicitValue && d->vData.startMargin != 0.)
2110 setContentY(-minYExtent());
2111 if (lcWheel().isDebugEnabled() || lcVel().isDebugEnabled()) {
2112 d->timeline.setObjectName(QLatin1String(
"timeline for Flickable ") + objectName());
2113 d->velocityTimeline.setObjectName(QLatin1String(
"velocity timeline for Flickable ") + objectName());
2117void QQuickFlickable::viewportMoved(Qt::Orientations orient)
2119 Q_D(QQuickFlickable);
2120 if (orient & Qt::Vertical)
2121 d->viewportAxisMoved(d->vData, minYExtent(), maxYExtent(), d->fixupY_callback);
2122 if (orient & Qt::Horizontal)
2123 d->viewportAxisMoved(d->hData, minXExtent(), maxXExtent(), d->fixupX_callback);
2124 d->updateBeginningEnd();
2127void QQuickFlickablePrivate::viewportAxisMoved(AxisData &data, qreal minExtent, qreal maxExtent,
2128 QQuickTimeLineCallback::Callback fixupCallback)
2130 if (!scrollingPhase && (pressed || calcVelocity)) {
2131 int elapsed = data.velocityTime.restart();
2133 qreal velocity = (data.lastPos - data.move.value()) * 1000 / elapsed;
2134 if (qAbs(velocity) > 0) {
2135 velocityTimeline.reset(data.smoothVelocity);
2136 velocityTimeline.set(data.smoothVelocity, velocity);
2137 qCDebug(lcVel) <<
"touchpad scroll phase: velocity" << velocity;
2141 if (timeline.time() > data.vTime) {
2142 velocityTimeline.reset(data.smoothVelocity);
2143 int dt = timeline.time() - data.vTime;
2145 qreal velocity = (data.lastPos - data.move.value()) * 1000 / dt;
2146 if (!qFuzzyCompare(data.smoothVelocity.value(), velocity))
2147 qCDebug(lcVel) <<
"velocity" << data.smoothVelocity.value() <<
"->" << velocity
2148 <<
"computed as (" << data.lastPos <<
"-" << data.move.value() <<
") * 1000 / ("
2149 << timeline.time() <<
"-" << data.vTime <<
")";
2150 data.smoothVelocity.setValue(velocity);
2155 if (!data.inOvershoot && !data.fixingUp && data.flicking
2156 && (data.move.value() > minExtent || data.move.value() < maxExtent)
2157 && qAbs(data.smoothVelocity.value()) > 10) {
2159 qreal overBound = data.move.value() > minExtent
2160 ? data.move.value() - minExtent
2161 : maxExtent - data.move.value();
2162 data.inOvershoot =
true;
2163 qreal maxDistance = overShootDistance(qAbs(data.smoothVelocity.value())) - overBound;
2164 resetTimeline(data);
2165 if (maxDistance > 0)
2167 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback,
this));
2170 data.lastPos = data.move.value();
2171 data.vTime = timeline.time();
2174void QQuickFlickable::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
2176 Q_D(QQuickFlickable);
2177 QQuickItem::geometryChange(newGeometry, oldGeometry);
2179 bool changed =
false;
2180 if (newGeometry.width() != oldGeometry.width()) {
2182 if (d->hData.viewSize < 0)
2183 d->contentItem->setWidth(width() - d->hData.startMargin - d->hData.endMargin);
2185 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2186 d->fixupMode = QQuickFlickablePrivate::Immediate;
2190 if (newGeometry.height() != oldGeometry.height()) {
2192 if (d->vData.viewSize < 0)
2193 d->contentItem->setHeight(height() - d->vData.startMargin - d->vData.endMargin);
2195 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2196 d->fixupMode = QQuickFlickablePrivate::Immediate;
2202 d->updateBeginningEnd();
2206
2207
2208
2209
2210
2211
2212
2214void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity)
2216 Q_D(QQuickFlickable);
2219 d->hData.velocity = xVelocity;
2220 d->vData.velocity = yVelocity;
2221 d->hData.vTime = d->vData.vTime = d->timeline.time();
2223 const bool flickedX = xflick() && !qFuzzyIsNull(xVelocity) && d->flickX(QEvent::TouchUpdate, xVelocity);
2224 const bool flickedY = yflick() && !qFuzzyIsNull(yVelocity) && d->flickY(QEvent::TouchUpdate, yVelocity);
2231 d->flickingStarted(flickedX, flickedY);
2234void QQuickFlickablePrivate::flickingStarted(
bool flickingH,
bool flickingV)
2236 Q_Q(QQuickFlickable);
2237 if (!flickingH && !flickingV)
2240 bool wasFlicking = hData.flicking || vData.flicking;
2241 if (flickingH && !hData.flicking) {
2242 hData.flicking =
true;
2243 emit q->flickingHorizontallyChanged();
2245 if (flickingV && !vData.flicking) {
2246 vData.flicking =
true;
2247 emit q->flickingVerticallyChanged();
2249 if (!wasFlicking && (hData.flicking || vData.flicking)) {
2250 emit q->flickingChanged();
2251 emit q->flickStarted();
2256
2257
2258
2259
2261void QQuickFlickable::cancelFlick()
2263 Q_D(QQuickFlickable);
2264 d->resetTimeline(d->hData);
2265 d->resetTimeline(d->vData);
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2302void QQuickFlickable::positionViewAtChild(QQuickItem *child, PositionMode mode,
const QPointF &offset)
2304 Q_D(QQuickFlickable);
2307 if (!d->contentItem->isAncestorOf(child))
2310 const QRectF itemRect =
2311 child->mapRectToItem(d->contentItem, QRectF(0, 0, child->width(), child->height()));
2313 QPointF currentPosition = QPointF(contentX(), contentY());
2314 QPointF newPosition = computePosition(currentPosition, itemRect, mode, offset);
2316 if (newPosition.x() != currentPosition.x()) {
2317 setContentX(newPosition.x());
2320 if (newPosition.y() != currentPosition.y()) {
2321 setContentY(newPosition.y());
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2358void QQuickFlickable::flickToChild(QQuickItem *child, PositionMode mode,
const QPointF &offset)
2360 Q_D(QQuickFlickable);
2363 if (!d->contentItem->isAncestorOf(child))
2366 const QRectF itemRect =
2367 child->mapRectToItem(d->contentItem, QRectF(0, 0, child->width(), child->height()));
2369 QPointF currentPosition = QPointF(contentX(), contentY());
2370 QPointF newPosition = computePosition(currentPosition, itemRect, mode, offset);
2372 flickTo(newPosition);
2376
2377
2378
2379
2380
2381
2382
2383
2385void QQuickFlickable::flickTo(
const QPointF &newPosition)
2387 Q_D(QQuickFlickable);
2389 QPointF currentPosition = QPointF(contentX(), contentY());
2391 qreal xVelocity = 0.0;
2392 qreal yVelocity = 0.0;
2394 const qreal deltaX = newPosition.x() - currentPosition.x();
2395 const qreal deltaY = newPosition.y() - currentPosition.y();
2400 if (xflick() && qAbs(deltaX) > 0.5) {
2401 const qreal decel = flickDeceleration();
2402 qreal velocity = qSqrt(2.0 * decel * qAbs(deltaX));
2403 if (qAbs(velocity) < _q_MinimumFlickVelocity)
2405 const qreal maxVel = maximumFlickVelocity();
2406 if (maxVel > 0 && velocity > maxVel)
2408 xVelocity = deltaX > 0 ? -velocity : velocity;
2411 if (yflick() && qAbs(deltaY) > 0.5) {
2412 const qreal decel = flickDeceleration();
2413 qreal velocity = qSqrt(2.0 * decel * qAbs(deltaY));
2414 if (qAbs(velocity) < _q_MinimumFlickVelocity)
2416 const qreal maxVel = maximumFlickVelocity();
2417 if (maxVel > 0 && velocity > maxVel)
2419 yVelocity = deltaY > 0 ? -velocity : velocity;
2422 if (qAbs(xVelocity) > 0.0 || qAbs(yVelocity) > 0.0) {
2423 flick(xVelocity, yVelocity);
2425 if (newPosition.x() != currentPosition.x()) {
2426 setContentX(newPosition.x());
2429 if (newPosition.y() != currentPosition.y()) {
2430 setContentY(newPosition.y());
2436void QQuickFlickablePrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
2438 if (!prop || !prop->data)
2441 if (QQuickItem *i = qmlobject_cast<QQuickItem *>(o)) {
2442 i->setParentItem(
static_cast<QQuickFlickablePrivate*>(prop->data)->contentItem);
2443 }
else if (QQuickPointerHandler *pointerHandler = qmlobject_cast<QQuickPointerHandler *>(o)) {
2444 static_cast<QQuickFlickablePrivate*>(prop->data)->addPointerHandler(pointerHandler);
2446 o->setParent(prop->object);
2450qsizetype QQuickFlickablePrivate::data_count(QQmlListProperty<QObject> *)
2456QObject *QQuickFlickablePrivate::data_at(QQmlListProperty<QObject> *, qsizetype)
2462void QQuickFlickablePrivate::data_clear(QQmlListProperty<QObject> *)
2467QQmlListProperty<QObject> QQuickFlickable::flickableData()
2469 Q_D(QQuickFlickable);
2470 return QQmlListProperty<QObject>(
this, (
void *)d, QQuickFlickablePrivate::data_append,
2471 QQuickFlickablePrivate::data_count,
2472 QQuickFlickablePrivate::data_at,
2473 QQuickFlickablePrivate::data_clear);
2476QQmlListProperty<QQuickItem> QQuickFlickable::flickableChildren()
2478 Q_D(QQuickFlickable);
2479 return QQuickItemPrivate::get(d->contentItem)->children();
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508QQuickFlickable::BoundsBehavior QQuickFlickable::boundsBehavior()
const
2510 Q_D(
const QQuickFlickable);
2511 return d->boundsBehavior;
2514void QQuickFlickable::setBoundsBehavior(BoundsBehavior b)
2516 Q_D(QQuickFlickable);
2517 if (b == d->boundsBehavior)
2519 d->boundsBehavior = b;
2520 emit boundsBehaviorChanged();
2524
2525
2526
2527
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
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563QQuickTransition *QQuickFlickable::rebound()
const
2565 Q_D(
const QQuickFlickable);
2569void QQuickFlickable::setRebound(QQuickTransition *transition)
2571 Q_D(QQuickFlickable);
2573 if (!d->hData.transitionToBounds)
2574 d->hData.transitionToBounds =
new QQuickFlickableReboundTransition(
this, QLatin1String(
"x"));
2575 if (!d->vData.transitionToBounds)
2576 d->vData.transitionToBounds =
new QQuickFlickableReboundTransition(
this, QLatin1String(
"y"));
2578 if (d->rebound != transition) {
2579 d->rebound = transition;
2580 emit reboundChanged();
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608qreal QQuickFlickable::contentWidth()
const
2610 Q_D(
const QQuickFlickable);
2611 return d->hData.viewSize;
2614void QQuickFlickable::setContentWidth(qreal w)
2616 Q_D(QQuickFlickable);
2617 if (d->hData.viewSize == w)
2619 d->hData.viewSize = w;
2621 d->contentItem->setWidth(width() - d->hData.startMargin - d->hData.endMargin);
2623 d->contentItem->setWidth(w);
2624 d->hData.markExtentsDirty();
2626 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2627 d->fixupMode = QQuickFlickablePrivate::Immediate;
2629 }
else if (!d->pressed && d->hData.fixingUp) {
2630 d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
2633 emit contentWidthChanged();
2634 d->updateBeginningEnd();
2637qreal QQuickFlickable::contentHeight()
const
2639 Q_D(
const QQuickFlickable);
2640 return d->vData.viewSize;
2643void QQuickFlickable::setContentHeight(qreal h)
2645 Q_D(QQuickFlickable);
2646 if (d->vData.viewSize == h)
2648 d->vData.viewSize = h;
2650 d->contentItem->setHeight(height() - d->vData.startMargin - d->vData.endMargin);
2652 d->contentItem->setHeight(h);
2653 d->vData.markExtentsDirty();
2655 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2656 d->fixupMode = QQuickFlickablePrivate::Immediate;
2658 }
else if (!d->pressed && d->vData.fixingUp) {
2659 d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
2662 emit contentHeightChanged();
2663 d->updateBeginningEnd();
2667
2668
2669
2670
2671
2672
2673
2674
2677qreal QQuickFlickable::topMargin()
const
2679 Q_D(
const QQuickFlickable);
2680 return d->vData.startMargin;
2683void QQuickFlickable::setTopMargin(qreal m)
2685 Q_D(QQuickFlickable);
2686 if (d->vData.startMargin == m)
2688 d->vData.startMargin = m;
2689 d->vData.markExtentsDirty();
2690 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2692 d->fixupMode = QQuickFlickablePrivate::Immediate;
2695 emit topMarginChanged();
2696 d->updateBeginningEnd();
2699qreal QQuickFlickable::bottomMargin()
const
2701 Q_D(
const QQuickFlickable);
2702 return d->vData.endMargin;
2705void QQuickFlickable::setBottomMargin(qreal m)
2707 Q_D(QQuickFlickable);
2708 if (d->vData.endMargin == m)
2710 d->vData.endMargin = m;
2711 d->vData.markExtentsDirty();
2712 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2714 d->fixupMode = QQuickFlickablePrivate::Immediate;
2717 emit bottomMarginChanged();
2718 d->updateBeginningEnd();
2721qreal QQuickFlickable::leftMargin()
const
2723 Q_D(
const QQuickFlickable);
2724 return d->hData.startMargin;
2727void QQuickFlickable::setLeftMargin(qreal m)
2729 Q_D(QQuickFlickable);
2730 if (d->hData.startMargin == m)
2732 d->hData.startMargin = m;
2733 d->hData.markExtentsDirty();
2734 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2736 d->fixupMode = QQuickFlickablePrivate::Immediate;
2739 emit leftMarginChanged();
2740 d->updateBeginningEnd();
2743qreal QQuickFlickable::rightMargin()
const
2745 Q_D(
const QQuickFlickable);
2746 return d->hData.endMargin;
2749void QQuickFlickable::setRightMargin(qreal m)
2751 Q_D(QQuickFlickable);
2752 if (d->hData.endMargin == m)
2754 d->hData.endMargin = m;
2755 d->hData.markExtentsDirty();
2756 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
2758 d->fixupMode = QQuickFlickablePrivate::Immediate;
2761 emit rightMarginChanged();
2762 d->updateBeginningEnd();
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2779qreal QQuickFlickable::originY()
const
2781 Q_D(
const QQuickFlickable);
2782 return -minYExtent() + d->vData.startMargin;
2785qreal QQuickFlickable::originX()
const
2787 Q_D(
const QQuickFlickable);
2788 return -minXExtent() + d->hData.startMargin;
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804void QQuickFlickable::resizeContent(qreal w, qreal h, QPointF center)
2806 Q_D(QQuickFlickable);
2807 const qreal oldHSize = d->hData.viewSize;
2808 const qreal oldVSize = d->vData.viewSize;
2809 const bool needToUpdateWidth = w != oldHSize;
2810 const bool needToUpdateHeight = h != oldVSize;
2811 d->hData.viewSize = w;
2812 d->vData.viewSize = h;
2813 d->contentItem->setSize(QSizeF(w, h));
2814 if (needToUpdateWidth)
2815 emit contentWidthChanged();
2816 if (needToUpdateHeight)
2817 emit contentHeightChanged();
2819 if (center.x() != 0) {
2820 qreal pos = center.x() * w / oldHSize;
2821 setContentX(contentX() + pos - center.x());
2823 if (center.y() != 0) {
2824 qreal pos = center.y() * h / oldVSize;
2825 setContentY(contentY() + pos - center.y());
2827 d->updateBeginningEnd();
2831
2832
2833
2834
2835
2836
2837
2838void QQuickFlickable::returnToBounds()
2840 Q_D(QQuickFlickable);
2845qreal QQuickFlickable::vWidth()
const
2847 Q_D(
const QQuickFlickable);
2848 if (d->hData.viewSize < 0)
2851 return d->hData.viewSize;
2854qreal QQuickFlickable::vHeight()
const
2856 Q_D(
const QQuickFlickable);
2857 if (d->vData.viewSize < 0)
2860 return d->vData.viewSize;
2864
2865
2866
2867
2868
2869
2870bool QQuickFlickable::xflick()
const
2872 Q_D(
const QQuickFlickable);
2873 const int contentWidthWithMargins = d->contentItem->width() + d->hData.startMargin + d->hData.endMargin;
2874 if ((d->flickableDirection & QQuickFlickable::AutoFlickIfNeeded) && (contentWidthWithMargins > width()))
2876 if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
2877 return std::floor(qAbs(contentWidthWithMargins - width()));
2878 return d->flickableDirection & QQuickFlickable::HorizontalFlick;
2882
2883
2884
2885
2886
2887
2888bool QQuickFlickable::yflick()
const
2890 Q_D(
const QQuickFlickable);
2891 const int contentHeightWithMargins = d->contentItem->height() + d->vData.startMargin + d->vData.endMargin;
2892 if ((d->flickableDirection & QQuickFlickable::AutoFlickIfNeeded) && (contentHeightWithMargins > height()))
2894 if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
2895 return std::floor(qAbs(contentHeightWithMargins - height()));
2896 return d->flickableDirection & QQuickFlickable::VerticalFlick;
2899QPointF QQuickFlickable::computePosition(QPointF currentPosition, QRectF itemRect, PositionMode mode,
const QPointF &offset)
const
2901 QPointF newPosition = currentPosition;
2904 const qreal viewWidth = width();
2906 if (mode & QQuickFlickable::AlignLeft)
2907 newPosition.setX(itemRect.left());
2908 if (mode & QQuickFlickable::AlignHCenter)
2909 newPosition.setX(itemRect.left() - (viewWidth - itemRect.width()) / 2);
2910 if (mode & QQuickFlickable::AlignRight)
2911 newPosition.setX(itemRect.right() - viewWidth);
2912 if (mode & QQuickFlickable::Visible) {
2913 if (itemRect.right() < currentPosition.x())
2914 newPosition.setX(itemRect.left());
2915 else if (itemRect.left() > currentPosition.x() + viewWidth)
2916 newPosition.setX(itemRect.right() - viewWidth);
2918 if (mode & QQuickFlickable::Contain) {
2919 if (itemRect.right() > currentPosition.x() + viewWidth)
2920 newPosition.setX(itemRect.right() - viewWidth);
2921 if (itemRect.left() < newPosition.x())
2922 newPosition.setX(itemRect.left());
2925 const qreal minX = -minXExtent();
2926 const qreal maxX = -maxXExtent();
2927 newPosition.setX(qMin(newPosition.x(), maxX));
2928 newPosition.setX(qMax(newPosition.x(), minX));
2932 const qreal viewHeight = height();
2934 if (mode & QQuickFlickable::AlignTop)
2935 newPosition.setY(itemRect.top());
2936 if (mode & QQuickFlickable::AlignVCenter)
2937 newPosition.setY(itemRect.top() - (viewHeight - itemRect.height()) / 2);
2938 if (mode & QQuickFlickable::AlignBottom)
2939 newPosition.setY(itemRect.bottom() - viewHeight);
2940 if (mode & QQuickFlickable::Visible) {
2941 if (itemRect.bottom() < currentPosition.y())
2942 newPosition.setY(itemRect.top());
2943 else if (itemRect.top() > currentPosition.y() + viewHeight)
2944 newPosition.setY(itemRect.bottom() - viewHeight);
2946 if (mode & QQuickFlickable::Contain) {
2947 if (itemRect.bottom() > currentPosition.y() + viewHeight)
2948 newPosition.setY(itemRect.bottom() - viewHeight);
2949 if (itemRect.top() < currentPosition.y())
2950 newPosition.setY(itemRect.top());
2953 const qreal minY = -minYExtent();
2954 const qreal maxY = -maxYExtent();
2955 newPosition.setY(qMin(newPosition.y(), maxY));
2956 newPosition.setY(qMax(newPosition.y(), minY));
2959 return newPosition + offset;
2962void QQuickFlickable::mouseUngrabEvent()
2964 Q_D(QQuickFlickable);
2967 if (!d->replayingPressEvent)
2968 d->cancelInteraction();
2971void QQuickFlickablePrivate::cancelInteraction()
2973 Q_Q(QQuickFlickable);
2975 clearDelayedPress();
2979 q->setKeepMouseGrab(
false);
2980 q->setKeepTouchGrab(
false);
2983 if (!isViewMoving())
2984 q->movementEnding();
2988void QQuickFlickablePrivate::addPointerHandler(QQuickPointerHandler *h)
2990 Q_Q(
const QQuickFlickable);
2991 qCDebug(lcHandlerParent) <<
"reparenting handler" << h <<
"to contentItem of" << q;
2992 h->setParent(contentItem);
2993 QQuickItemPrivate::get(contentItem)->addPointerHandler(h);
2997
2998
2999
3000
3001
3002
3003
3004bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *event)
3006 Q_D(QQuickFlickable);
3007 const bool isTouch = QQuickDeliveryAgentPrivate::isTouchEvent(event);
3008 const bool isMouse = QQuickDeliveryAgentPrivate::isMouseEvent(event);
3009 if (isMouse || QQuickDeliveryAgentPrivate::isTabletEvent(event)) {
3010 if (!d->buttonsAccepted(
static_cast<QSinglePointEvent *>(event)))
3011 return QQuickItem::childMouseEventFilter(receiver, event);
3012 }
else if (!isTouch) {
3015 Q_ASSERT_X(receiver !=
this,
"",
"Flickable received a filter event for itself");
3017 if (isTouch &&
static_cast<QTouchEvent *>(event)->touchPointStates().testFlag(QEventPoint::State::Pressed))
3018 d->stealGrab =
false;
3019 const auto &firstPoint = event->points().first();
3021 if (event->pointCount() == 1 && event->exclusiveGrabber(firstPoint) ==
this) {
3033 QPointF localPos = mapFromScene(firstPoint.scenePosition());
3034 bool receiverDisabled = receiver && !receiver->isEnabled();
3035 bool stealThisEvent = d->stealGrab;
3036 bool receiverKeepsGrab = receiver && (receiver->keepMouseGrab() || receiver->keepTouchGrab());
3037 bool receiverRelinquishGrab =
false;
3040 if (
auto *mouseArea = qmlobject_cast<QQuickMouseArea *>(receiver)) {
3041 bool preventStealing = mouseArea->preventStealing();
3042#if QT_CONFIG(quick_draganddrop)
3043 if (mouseArea->drag() && mouseArea->drag()->target())
3044 preventStealing =
true;
3046 if (!preventStealing && receiverKeepsGrab) {
3047 receiverRelinquishGrab = !receiverDisabled || (isMouse
3048 && firstPoint.state() == QEventPoint::State::Pressed
3049 && (receiver->acceptedMouseButtons() &
static_cast<QMouseEvent *>(event)->button()));
3050 if (receiverRelinquishGrab)
3051 receiverKeepsGrab =
false;
3055 if ((stealThisEvent || contains(localPos)) && (!receiver || !receiverKeepsGrab || receiverDisabled)) {
3056 QScopedPointer<QPointerEvent> localizedEvent(QQuickDeliveryAgentPrivate::clonePointerEvent(event, localPos));
3057 localizedEvent->setAccepted(
false);
3058 switch (firstPoint.state()) {
3059 case QEventPoint::State::Updated:
3060 d->handleMoveEvent(localizedEvent.data());
3062 case QEventPoint::State::Pressed:
3063 d->handlePressEvent(localizedEvent.data());
3064 d->captureDelayedPress(receiver, event);
3066 d->stealGrab =
false;
3067 stealThisEvent =
false;
3069 case QEventPoint::State::Released:
3070 d->handleReleaseEvent(localizedEvent.data());
3071 stealThisEvent = d->stealGrab;
3073 case QEventPoint::State::Stationary:
3074 case QEventPoint::State::Unknown:
3077 if ((receiver && stealThisEvent && !receiverKeepsGrab && receiver !=
this) || receiverDisabled) {
3078 d->clearDelayedPress();
3079 event->setExclusiveGrabber(firstPoint,
this);
3080 }
else if (d->delayedPressEvent) {
3081 event->setExclusiveGrabber(firstPoint,
this);
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095 if (isMoving() || (!receiverRelinquishGrab && (stealThisEvent || d->delayedPressEvent || receiverDisabled))) {
3097 event->setAccepted(
true);
3103 }
else if (d->lastPosTime != -1) {
3104 d->lastPosTime = -1;
3107 if (firstPoint.state() == QEventPoint::State::Released || (receiverKeepsGrab && !receiverDisabled)) {
3109 d->lastPosTime = -1;
3110 d->clearDelayedPress();
3111 d->stealGrab =
false;
3118
3119
3120
3121bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
3123 Q_D(QQuickFlickable);
3124 QPointerEvent *pointerEvent = e->isPointerEvent() ?
static_cast<QPointerEvent *>(e) :
nullptr;
3126 auto wantsPointerEvent_helper = [
this, d, i, pointerEvent]() {
3127 Q_ASSERT(pointerEvent);
3128 QQuickDeliveryAgentPrivate::localizePointerEvent(pointerEvent,
this);
3129 const bool wants = d->wantsPointerEvent(pointerEvent);
3131 QQuickDeliveryAgentPrivate::localizePointerEvent(pointerEvent, i);
3135 if (!isVisible() || !isEnabled() || !isInteractive() ||
3136 (pointerEvent && !wantsPointerEvent_helper())) {
3137 d->cancelInteraction();
3138 return QQuickItem::childMouseEventFilter(i, e);
3141 if (e->type() == QEvent::UngrabMouse) {
3142 Q_ASSERT(e->isSinglePointEvent());
3143 auto spe =
static_cast<QSinglePointEvent *>(e);
3144 const QObject *grabber = spe->exclusiveGrabber(spe->points().first());
3145 qCDebug(lcFilter) <<
"filtering UngrabMouse" << spe->points().first() <<
"for" << i <<
"grabber is" << grabber;
3146 if (grabber !=
this)
3148 }
else if (pointerEvent) {
3149 return filterPointerEvent(i, pointerEvent);
3152 return QQuickItem::childMouseEventFilter(i, e);
3156
3157
3158
3159
3160
3161qreal QQuickFlickable::maximumFlickVelocity()
const
3163 Q_D(
const QQuickFlickable);
3164 return d->maxVelocity;
3167void QQuickFlickable::setMaximumFlickVelocity(qreal v)
3169 Q_D(QQuickFlickable);
3170 if (v == d->maxVelocity)
3173 emit maximumFlickVelocityChanged();
3177
3178
3179
3180
3181
3182
3183
3184
3185qreal QQuickFlickable::flickDeceleration()
const
3187 Q_D(
const QQuickFlickable);
3188 return d->deceleration;
3191void QQuickFlickable::setFlickDeceleration(qreal deceleration)
3193 Q_D(QQuickFlickable);
3194 if (deceleration == d->deceleration)
3196 d->deceleration = qMax(0.001, deceleration);
3197 emit flickDecelerationChanged();
3200bool QQuickFlickable::isFlicking()
const
3202 Q_D(
const QQuickFlickable);
3203 return d->hData.flicking || d->vData.flicking;
3207
3208
3209
3210
3211
3212
3213
3214bool QQuickFlickable::isFlickingHorizontally()
const
3216 Q_D(
const QQuickFlickable);
3217 return d->hData.flicking;
3220bool QQuickFlickable::isFlickingVertically()
const
3222 Q_D(
const QQuickFlickable);
3223 return d->vData.flicking;
3227
3228
3229
3230
3231
3232
3233
3234bool QQuickFlickable::isDragging()
const
3236 Q_D(
const QQuickFlickable);
3237 return d->hData.dragging || d->vData.dragging;
3240bool QQuickFlickable::isDraggingHorizontally()
const
3242 Q_D(
const QQuickFlickable);
3243 return d->hData.dragging;
3246bool QQuickFlickable::isDraggingVertically()
const
3248 Q_D(
const QQuickFlickable);
3249 return d->vData.dragging;
3252void QQuickFlickablePrivate::draggingStarting()
3254 Q_Q(QQuickFlickable);
3255 bool wasDragging = hData.dragging || vData.dragging;
3256 if (hMoved && !hData.dragging) {
3257 hData.dragging =
true;
3258 emit q->draggingHorizontallyChanged();
3260 if (vMoved && !vData.dragging) {
3261 vData.dragging =
true;
3262 emit q->draggingVerticallyChanged();
3264 if (!wasDragging && (hData.dragging || vData.dragging)) {
3265 emit q->draggingChanged();
3266 emit q->dragStarted();
3270void QQuickFlickablePrivate::draggingEnding()
3272 Q_Q(QQuickFlickable);
3273 const bool wasDragging = hData.dragging || vData.dragging;
3274 if (hData.dragging) {
3275 hData.dragging =
false;
3276 emit q->draggingHorizontallyChanged();
3278 if (vData.dragging) {
3279 vData.dragging =
false;
3280 emit q->draggingVerticallyChanged();
3283 if (!hData.dragging && !vData.dragging) {
3284 emit q->draggingChanged();
3285 emit q->dragEnded();
3287 hData.inRebound =
false;
3288 vData.inRebound =
false;
3292bool QQuickFlickablePrivate::isViewMoving()
const
3294 if (timeline.isActive()
3295 || (hData.transitionToBounds && hData.transitionToBounds->isActive())
3296 || (vData.transitionToBounds && vData.transitionToBounds->isActive()) ) {
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320int QQuickFlickable::pressDelay()
const
3322 Q_D(
const QQuickFlickable);
3323 return d->pressDelay;
3326void QQuickFlickable::setPressDelay(
int delay)
3328 Q_D(QQuickFlickable);
3329 if (d->pressDelay == delay)
3331 d->pressDelay = delay;
3332 emit pressDelayChanged();
3336
3337
3338
3339
3340
3341
3342
3343
3345bool QQuickFlickable::isMoving()
const
3347 Q_D(
const QQuickFlickable);
3348 return d->hData.moving || d->vData.moving;
3351bool QQuickFlickable::isMovingHorizontally()
const
3353 Q_D(
const QQuickFlickable);
3354 return d->hData.moving;
3357bool QQuickFlickable::isMovingVertically()
const
3359 Q_D(
const QQuickFlickable);
3360 return d->vData.moving;
3363void QQuickFlickable::velocityTimelineCompleted()
3365 Q_D(QQuickFlickable);
3366 if ( (d->hData.transitionToBounds && d->hData.transitionToBounds->isActive())
3367 || (d->vData.transitionToBounds && d->vData.transitionToBounds->isActive()) ) {
3374 if (d->vData.flicking)
3376 d->updateBeginningEnd();
3379void QQuickFlickable::timelineCompleted()
3381 Q_D(QQuickFlickable);
3382 if ( (d->hData.transitionToBounds && d->hData.transitionToBounds->isActive())
3383 || (d->vData.transitionToBounds && d->vData.transitionToBounds->isActive()) ) {
3387 d->updateBeginningEnd();
3390void QQuickFlickable::movementStarting()
3392 Q_D(QQuickFlickable);
3393 bool wasMoving = d->hData.moving || d->vData.moving;
3394 if (d->hMoved && !d->hData.moving) {
3395 d->hData.moving =
true;
3396 emit movingHorizontallyChanged();
3398 if (d->vMoved && !d->vData.moving) {
3399 d->vData.moving =
true;
3400 emit movingVerticallyChanged();
3403 if (!wasMoving && (d->hData.moving || d->vData.moving)) {
3404 emit movingChanged();
3405 emit movementStarted();
3406#if QT_CONFIG(accessibility)
3407 if (QAccessible::isActive()) {
3408 QAccessibleEvent ev(
this, QAccessible::ScrollingStart);
3409 QAccessible::updateAccessibility(&ev);
3415void QQuickFlickable::movementEnding()
3417 movementEnding(
true,
true);
3420void QQuickFlickable::movementEnding(
bool hMovementEnding,
bool vMovementEnding)
3422 Q_D(QQuickFlickable);
3425 const bool wasFlicking = d->hData.flicking || d->vData.flicking;
3426 if (hMovementEnding && d->hData.flicking) {
3427 d->hData.flicking =
false;
3428 emit flickingHorizontallyChanged();
3430 if (vMovementEnding && d->vData.flicking) {
3431 d->vData.flicking =
false;
3432 emit flickingVerticallyChanged();
3434 if (wasFlicking && (!d->hData.flicking || !d->vData.flicking)) {
3435 emit flickingChanged();
3437 }
else if (d->hData.flickingWhenDragBegan || d->vData.flickingWhenDragBegan) {
3438 d->hData.flickingWhenDragBegan = !hMovementEnding;
3439 d->vData.flickingWhenDragBegan = !vMovementEnding;
3444 bool wasMoving = isMoving();
3445 if (hMovementEnding && d->hData.moving
3446 && (!d->pressed && !d->stealGrab)) {
3447 d->hData.moving =
false;
3449 emit movingHorizontallyChanged();
3451 if (vMovementEnding && d->vData.moving
3452 && (!d->pressed && !d->stealGrab)) {
3453 d->vData.moving =
false;
3455 emit movingVerticallyChanged();
3457 if (wasMoving && !isMoving()) {
3458 emit movingChanged();
3459 emit movementEnded();
3460#if QT_CONFIG(accessibility)
3461 if (QAccessible::isActive()) {
3462 QAccessibleEvent ev(
this, QAccessible::ScrollingEnd);
3463 QAccessible::updateAccessibility(&ev);
3468 if (hMovementEnding) {
3469 d->hData.fixingUp =
false;
3470 d->hData.smoothVelocity.setValue(0);
3471 d->hData.previousDragDelta = 0.0;
3473 if (vMovementEnding) {
3474 d->vData.fixingUp =
false;
3475 d->vData.smoothVelocity.setValue(0);
3476 d->vData.previousDragDelta = 0.0;
3480void QQuickFlickablePrivate::updateVelocity()
3482 Q_Q(QQuickFlickable);
3483 emit q->horizontalVelocityChanged();
3484 emit q->verticalVelocityChanged();
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502qreal QQuickFlickable::horizontalOvershoot()
const
3504 Q_D(
const QQuickFlickable);
3505 return d->hData.overshoot;
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523qreal QQuickFlickable::verticalOvershoot()
const
3525 Q_D(
const QQuickFlickable);
3526 return d->vData.overshoot;
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575QQuickFlickable::BoundsMovement QQuickFlickable::boundsMovement()
const
3577 Q_D(
const QQuickFlickable);
3578 return d->boundsMovement;
3581void QQuickFlickable::setBoundsMovement(BoundsMovement movement)
3583 Q_D(QQuickFlickable);
3584 if (d->boundsMovement == movement)
3587 d->boundsMovement = movement;
3588 emit boundsMovementChanged();
3593#include "moc_qquickflickable_p_p.cpp"
3595#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