Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-88249

QScroller: non-flicks are recognized as accelerating flick by QScrollerPrivate::releaseWhileDragging()

    XMLWordPrintable

Details

    Description

      We discovered that QScroller misinterprets "stop" gestures as "accelerating flick" gestures in this case:

      1. Flick in direction A.
      2. While the view is coasting, decide that you want to stop close to the current position.
      3. Grab the view (still coasting), and drag it slowly in direction A.
      4. When you reached the desired point, stop your movement, and release the view.
      5. QScroller interprets this release as "accelerating flick", and makes the view coast faster in direction A.

      A video of this is available here: https://invent.kde.org/graphics/okular/uploads/513cb20a3ff47050522a00be09d2a050/vokoscreenNG-2020-11-06_16-57-50.mkv

      QScroller misses to check the speed of the flick gesture; our current workaround is to limit the duration using QScrollerProperties::AcceleratingFlickMaximumTime.

      This is what I found as possible problem:

      bool QScrollerPrivate::releaseWhileDragging(const QPointF &position, qint64 timestamp)
      {
          Q_Q(QScroller);
          const QScrollerPropertiesPrivate *sp = properties.d.data();
      
          // handleDrag updates lastPosition, lastTimestamp and velocity
          // [This calls updateVelocity(), which sets QPointF releaseVelocity to the current drag velocity.]
          handleDrag(position, timestamp);
      
          // check if we moved at all - this can happen if you stop a running
          // scroller with a press and release shortly afterwards
          QPointF deltaPixel = position - pressPosition;
          if (((deltaPixel / q->pixelPerMeter()).manhattanLength()) > sp->dragStartDistance) {
      
              // handle accelerating flicks
              // [This if's condition checks whether this flick gesture meets the requirements for _accelerating_ flick, but not whether it _is_ a flick.]
              if ((oldVelocity != QPointF(0, 0)) && sp->acceleratingFlickMaximumTime &&
                  ((timestamp - pressTimestamp) < qint64(sp->acceleratingFlickMaximumTime * 1000))) {
      
                  // [...]
                  // [Multiplies releaseVelocity]
              }
          }
          // [...]
      }
      

      I suggest to insert this additional condition where I have written "but not whether it is a flick":

                  qMax(qAbs(releaseVelocity.x()), qAbs(releaseVelocity.y())) >= sp->minimumVelocity &&
      

      .
      This would check whether the current movement speed is above the QScrollerProperties::MinimumSpeed setting, using the biggest component, like createScrollingSegments(QPointF, ...) does.

      Attachments

        1. flicktest_minimal.cpp
          2 kB
        2. flicktest_visual.cpp
          6 kB
        3. qscrollertest.pro
          0.1 kB

        Activity

          People

            qt.team.quick.subscriptions Qt Quick and Widgets Team
            checkedtrashcan David Hurka
            Votes:
            1 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated: