[neon/qt/qtdeclarative/Neon/release] debian: Backport upstream patch to fix Flickable wheel velocity calculation.
Dmitry Shachnev
null at kde.org
Thu Jul 14 14:03:40 BST 2022
Git commit b4c0474db54d70a160b2ba9b4e20b04ad1a23e6b by Dmitry Shachnev.
Committed on 21/05/2022 at 22:00.
Pushed by jriddell into branch 'Neon/release'.
Backport upstream patch to fix Flickable wheel velocity calculation.
Thanks Wolfgang Frisch for backporting the patch to 5.15 branch!
Closes: #1011253.
M +2 -0 debian/changelog
A +274 -0 debian/patches/fix_flickable_wheel_velocity_calculation.patch
M +1 -0 debian/patches/series
https://invent.kde.org/neon/qt/qtdeclarative/commit/b4c0474db54d70a160b2ba9b4e20b04ad1a23e6b
diff --git a/debian/changelog b/debian/changelog
index 2d9829f..9f06851 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,6 +2,8 @@ qtdeclarative-opensource-src (5.15.4+dfsg-2) UNRELEASED; urgency=medium
[ Dmitry Shachnev ]
* Use symver directive to catch all private symbols at once.
+ * Backport upstream patch to fix Flickable wheel velocity calculation
+ (closes: #1011253, thanks Wolfgang Frisch).
-- Debian Qt/KDE Maintainers <debian-qt-kde at lists.debian.org> Sun, 22 May 2022 00:35:13 +0300
diff --git a/debian/patches/fix_flickable_wheel_velocity_calculation.patch b/debian/patches/fix_flickable_wheel_velocity_calculation.patch
new file mode 100644
index 0000000..f17a522
--- /dev/null
+++ b/debian/patches/fix_flickable_wheel_velocity_calculation.patch
@@ -0,0 +1,274 @@
+Description: fix Flickable wheel velocity calculation
+Origin: upstream, https://code.qt.io/cgit/qt/qtdeclarative.git/commit/?id=a8fbd865140d4dd1
+Last-Update: 2022-05-22
+
+--- a/src/quick/items/qquickflickable.cpp
++++ b/src/quick/items/qquickflickable.cpp
+@@ -62,6 +62,8 @@
+ QT_BEGIN_NAMESPACE
+
+ Q_DECLARE_LOGGING_CATEGORY(lcHandlerParent)
++Q_LOGGING_CATEGORY(lcWheel, "qt.quick.flickable.wheel")
++Q_LOGGING_CATEGORY(lcVel, "qt.quick.flickable.velocity")
+
+ // FlickThreshold determines how far the "mouse" must have moved
+ // before we perform a flick.
+@@ -263,7 +265,8 @@ QQuickFlickablePrivate::QQuickFlickableP
+ , deceleration(QML_FLICK_DEFAULTDECELERATION)
+ , maxVelocity(QML_FLICK_DEFAULTMAXVELOCITY), reportedVelocitySmoothing(100)
+ , delayedPressEvent(nullptr), pressDelay(0), fixupDuration(400)
+- , flickBoost(1.0), fixupMode(Normal), vTime(0), visibleArea(nullptr)
++ , flickBoost(1.0), initialWheelFlickDistance(qApp->styleHints()->wheelScrollLines() * 24)
++ , fixupMode(Normal), vTime(0), visibleArea(nullptr)
+ , flickableDirection(QQuickFlickable::AutoFlickDirection)
+ , boundsBehavior(QQuickFlickable::DragAndOvershootBounds)
+ , boundsMovement(QQuickFlickable::FollowBoundsBehavior)
+@@ -531,10 +534,14 @@ void QQuickFlickablePrivate::updateBegin
+ if (atBeginning != vData.atBeginning) {
+ vData.atBeginning = atBeginning;
+ atYBeginningChange = true;
++ if (!vData.moving && atBeginning)
++ vData.smoothVelocity.setValue(0);
+ }
+ if (atEnd != vData.atEnd) {
+ vData.atEnd = atEnd;
+ atYEndChange = true;
++ if (!vData.moving && atEnd)
++ vData.smoothVelocity.setValue(0);
+ }
+
+ // Horizontal
+@@ -547,10 +554,14 @@ void QQuickFlickablePrivate::updateBegin
+ if (atBeginning != hData.atBeginning) {
+ hData.atBeginning = atBeginning;
+ atXBeginningChange = true;
++ if (!hData.moving && atBeginning)
++ hData.smoothVelocity.setValue(0);
+ }
+ if (atEnd != hData.atEnd) {
+ hData.atEnd = atEnd;
+ atXEndChange = true;
++ if (!hData.moving && atEnd)
++ hData.smoothVelocity.setValue(0);
+ }
+
+ if (vData.extentsChanged) {
+@@ -1489,6 +1500,7 @@ void QQuickFlickable::wheelEvent(QWheelE
+ d->hData.velocity = 0;
+ d->timer.start();
+ d->maybeBeginDrag(currentTimestamp, event->position());
++ d->lastPosTime = -1;
+ break;
+ case Qt::NoScrollPhase: // default phase with an ordinary wheel mouse
+ case Qt::ScrollUpdate:
+@@ -1515,20 +1527,34 @@ void QQuickFlickable::wheelEvent(QWheelE
+ return;
+ }
+
++ qreal elapsed = qreal(currentTimestamp - d->lastPosTime) / qreal(1000);
++ if (elapsed <= 0) {
++ d->lastPosTime = currentTimestamp;
++ qCDebug(lcWheel) << "insufficient elapsed time: can't calculate velocity" << elapsed;
++ return;
++ }
++
+ if (event->source() == Qt::MouseEventNotSynthesized || event->pixelDelta().isNull()) {
+- // physical mouse wheel, so use angleDelta
++ // no pixel delta (physical mouse wheel, or "dumb" touchpad), so use angleDelta
+ int xDelta = event->angleDelta().x();
+ int yDelta = event->angleDelta().y();
++ // For a single "clicky" wheel event (angleDelta +/- 120),
++ // we want flick() to end up moving a distance proportional to QStyleHints::wheelScrollLines().
++ // The decel algo from there is
++ // qreal dist = v2 / (accel * 2.0);
++ // i.e. initialWheelFlickDistance = (120 / dt)^2 / (deceleration * 2)
++ // now solve for dt:
++ // dt = 120 / sqrt(deceleration * 2 * initialWheelFlickDistance)
++ if (!isMoving())
++ elapsed = 120 / qSqrt(d->deceleration * 2 * d->initialWheelFlickDistance);
+ if (yflick() && yDelta != 0) {
+- bool valid = false;
+- if (yDelta > 0 && contentY() > -minYExtent()) {
+- d->vData.velocity = qMax(yDelta*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4));
+- valid = true;
+- } else if (yDelta < 0 && contentY() < -maxYExtent()) {
+- d->vData.velocity = qMin(yDelta*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
+- valid = true;
+- }
+- if (valid) {
++ qreal instVelocity = yDelta / elapsed;
++ // if the direction has changed, start over with filtering, to allow instant movement in the opposite direction
++ if ((instVelocity < 0 && d->vData.velocity > 0) || (instVelocity > 0 && d->vData.velocity < 0))
++ d->vData.velocityBuffer.clear();
++ d->vData.addVelocitySample(instVelocity, d->maxVelocity);
++ d->vData.updateVelocity();
++ if ((yDelta > 0 && contentY() > -minYExtent()) || (yDelta < 0 && contentY() < -maxYExtent())) {
+ d->flickY(d->vData.velocity);
+ d->flickingStarted(false, true);
+ if (d->vData.flicking) {
+@@ -1539,15 +1565,13 @@ void QQuickFlickable::wheelEvent(QWheelE
+ }
+ }
+ if (xflick() && xDelta != 0) {
+- bool valid = false;
+- if (xDelta > 0 && contentX() > -minXExtent()) {
+- d->hData.velocity = qMax(xDelta*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4));
+- valid = true;
+- } else if (xDelta < 0 && contentX() < -maxXExtent()) {
+- d->hData.velocity = qMin(xDelta*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
+- valid = true;
+- }
+- if (valid) {
++ qreal instVelocity = xDelta / elapsed;
++ // if the direction has changed, start over with filtering, to allow instant movement in the opposite direction
++ if ((instVelocity < 0 && d->hData.velocity > 0) || (instVelocity > 0 && d->hData.velocity < 0))
++ d->hData.velocityBuffer.clear();
++ d->hData.addVelocitySample(instVelocity, d->maxVelocity);
++ d->hData.updateVelocity();
++ if ((xDelta > 0 && contentX() > -minXExtent()) || (xDelta < 0 && contentX() < -maxXExtent())) {
+ d->flickX(d->hData.velocity);
+ d->flickingStarted(true, false);
+ if (d->hData.flicking) {
+@@ -1562,18 +1586,13 @@ void QQuickFlickable::wheelEvent(QWheelE
+ int xDelta = event->pixelDelta().x();
+ int yDelta = event->pixelDelta().y();
+
+- qreal elapsed = qreal(currentTimestamp - d->lastPosTime) / 1000.;
+- if (elapsed <= 0) {
+- d->lastPosTime = currentTimestamp;
+- return;
+- }
+ QVector2D velocity(xDelta / elapsed, yDelta / elapsed);
+- d->lastPosTime = currentTimestamp;
+ d->accumulatedWheelPixelDelta += QVector2D(event->pixelDelta());
+ d->drag(currentTimestamp, event->type(), event->position(), d->accumulatedWheelPixelDelta,
+ true, !d->scrollingPhase, true, velocity);
+ event->accept();
+ }
++ d->lastPosTime = currentTimestamp;
+
+ if (!event->isAccepted())
+ QQuickItem::wheelEvent(event);
+@@ -1744,6 +1763,10 @@ void QQuickFlickable::componentComplete(
+ setContentX(-minXExtent());
+ if (!d->vData.explicitValue && d->vData.startMargin != 0.)
+ setContentY(-minYExtent());
++ if (lcWheel().isDebugEnabled() || lcVel().isDebugEnabled()) {
++ d->timeline.setObjectName(QLatin1String("timeline for Flickable ") + objectName());
++ d->velocityTimeline.setObjectName(QLatin1String("velocity timeline for Flickable ") + objectName());
++ }
+ }
+
+ void QQuickFlickable::viewportMoved(Qt::Orientations orient)
+@@ -2491,9 +2514,23 @@ void QQuickFlickable::setMaximumFlickVel
+
+ /*!
+ \qmlproperty real QtQuick::Flickable::flickDeceleration
+- This property holds the rate at which a flick will decelerate.
+-
+- The default value is platform dependent.
++ This property holds the rate at which a flick will decelerate:
++ the higher the number, the faster it slows down when the user stops
++ flicking via touch, touchpad or mouse wheel. For example 0.0001 is nearly
++ "frictionless", and 10000 feels quite "sticky".
++
++ The default value is platform dependent. Values of zero or less are not allowed.
++
++ \note For touchpad flicking, some platforms drive Flickable directly by
++ sending QWheelEvents with QWheelEvent::phase() being \c Qt::ScrollMomentum,
++ after the user has released all fingers from the touchpad. In that case,
++ the operating system is controlling the deceleration, and this property has
++ no effect.
++
++ \note For mouse wheel scrolling, and for gesture scrolling on touchpads
++ that do not have a momentum phase, extremely large values of
++ flickDeceleration can make Flickable very resistant to scrolling,
++ especially if \l maximumFlickVelocity is too small.
+ */
+ qreal QQuickFlickable::flickDeceleration() const
+ {
+@@ -2506,7 +2543,7 @@ void QQuickFlickable::setFlickDecelerati
+ Q_D(QQuickFlickable);
+ if (deceleration == d->deceleration)
+ return;
+- d->deceleration = deceleration;
++ d->deceleration = qMax(0.001, deceleration);
+ emit flickDecelerationChanged();
+ }
+
+--- a/src/quick/items/qquickflickable_p_p.h
++++ b/src/quick/items/qquickflickable_p_p.h
+@@ -241,6 +241,7 @@ public:
+ int pressDelay;
+ int fixupDuration;
+ qreal flickBoost;
++ qreal initialWheelFlickDistance;
+
+ enum FixupMode { Normal, Immediate, ExtentChanged };
+ FixupMode fixupMode;
+--- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
++++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
+@@ -870,6 +870,7 @@ void tst_qquickflickable::wheel()
+ QVERIFY(flick != nullptr);
+ QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flick);
+ QSignalSpy moveEndSpy(flick, SIGNAL(movementEnded()));
++ quint64 timestamp = 10;
+
+ // test a vertical flick
+ {
+@@ -877,6 +878,7 @@ void tst_qquickflickable::wheel()
+ QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(0,-120),
+ Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
+ event.setAccepted(false);
++ event.setTimestamp(timestamp);
+ QGuiApplication::sendEvent(window.data(), &event);
+ }
+
+@@ -887,6 +889,7 @@ void tst_qquickflickable::wheel()
+ QCOMPARE(fp->velocityTimeline.isActive(), false);
+ QCOMPARE(fp->timeline.isActive(), false);
+ QTest::qWait(50); // make sure that onContentYChanged won't sneak in again
++ timestamp += 50;
+ QCOMPARE(flick->property("movementsAfterEnd").value<int>(), 0); // QTBUG-55886
+
+ // get ready to test horizontal flick
+@@ -900,8 +903,8 @@ void tst_qquickflickable::wheel()
+ QPoint pos(200, 200);
+ QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(-120,0),
+ Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
+-
+ event.setAccepted(false);
++ event.setTimestamp(timestamp);
+ QGuiApplication::sendEvent(window.data(), &event);
+ }
+
+@@ -926,11 +929,13 @@ void tst_qquickflickable::trackpad()
+ QVERIFY(flick != nullptr);
+ QSignalSpy moveEndSpy(flick, SIGNAL(movementEnded()));
+ QPoint pos(200, 200);
++ quint64 timestamp = 10;
+
+ {
+ QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(0,-100), QPoint(0,-120),
+ Qt::NoButton, Qt::NoModifier, Qt::ScrollBegin, false);
+ event.setAccepted(false);
++ event.setTimestamp(timestamp++);
+ QGuiApplication::sendEvent(window.data(), &event);
+ }
+
+@@ -944,6 +949,7 @@ void tst_qquickflickable::trackpad()
+ QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(-100,0), QPoint(-120,0),
+ Qt::NoButton, Qt::NoModifier, Qt::ScrollUpdate, false);
+ event.setAccepted(false);
++ event.setTimestamp(timestamp++);
+ QGuiApplication::sendEvent(window.data(), &event);
+ }
+
+@@ -954,6 +960,7 @@ void tst_qquickflickable::trackpad()
+ QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(0,0), QPoint(0,0),
+ Qt::NoButton, Qt::NoModifier, Qt::ScrollEnd, false);
+ event.setAccepted(false);
++ event.setTimestamp(timestamp++);
+ QGuiApplication::sendEvent(window.data(), &event);
+ }
+
diff --git a/debian/patches/series b/debian/patches/series
index 9024a8b..02be2a5 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -3,6 +3,7 @@ gcc_11.patch
tst_qmldiskcache_big_endian.patch
support_apos_in_styled_text.patch
fix_sweep_step.patch
+fix_flickable_wheel_velocity_calculation.patch
# Debian patches
disableopengltests.patch
More information about the Neon-commits
mailing list