diff options
Diffstat (limited to 'src/quick/items')
-rw-r--r-- | src/quick/items/qquickflickable.cpp | 159 | ||||
-rw-r--r-- | src/quick/items/qquickflickable_p_p.h | 2 |
2 files changed, 127 insertions, 34 deletions
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 2401f008aa..7690389936 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -53,6 +53,7 @@ #include <QtGui/qguiapplication.h> #include <QtGui/private/qguiapplication_p.h> #include <QtGui/qstylehints.h> +#include <QtGui/qpa/qplatformtheme.h> #include <QtCore/qmath.h> #include "qplatformdefs.h" @@ -62,8 +63,11 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcHandlerParent) +Q_LOGGING_CATEGORY(lcFlickable, "qt.quick.flickable") Q_LOGGING_CATEGORY(lcFilter, "qt.quick.flickable.filter") Q_LOGGING_CATEGORY(lcReplay, "qt.quick.flickable.replay") +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. @@ -251,6 +255,8 @@ QQuickFlickablePrivate::AxisData::~AxisData() } +QVarLengthArray<const QPointingDevice *, 4> QQuickFlickablePrivate::nonClickyWheelMice; + QQuickFlickablePrivate::QQuickFlickablePrivate() : contentItem(new QQuickItem) , hData(this, &QQuickFlickablePrivate::setViewportX) @@ -390,10 +396,13 @@ bool QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExt resetTimeline(data); if (!data.inOvershoot) { - if (boundsBehavior & QQuickFlickable::OvershootBounds) + if (boundsBehavior & QQuickFlickable::OvershootBounds) { + qCDebug(lcVel) << "timeline.accel(" << data.move << v << accel << ')'; timeline.accel(data.move, v, accel); - else + } else { + qCDebug(lcVel) << "timeline.accel(" << data.move << v << accel << "maxDist" << maxDistance << ')'; timeline.accel(data.move, v, accel, maxDistance); + } } timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this)); @@ -1130,6 +1139,10 @@ void QQuickFlickablePrivate::drag(qint64 currentTimestamp, QEvent::Type eventTyp bool prevVMoved = vMoved; qint64 elapsedSincePress = currentTimestamp - lastPressTime; + qCDebug(lcFlickable).nospace() << currentTimestamp << ' ' << eventType << " drag @ " << localPos.x() << ',' << localPos.y() + << " \u0394 " << deltas.x() << ',' << deltas.y() << " vel " << velocity.x() << ',' << velocity.y() + << " thrsld? " << overThreshold << " momentum? " << momentum << " velSens? " << velocitySensitiveOverBounds + << " sincePress " << elapsedSincePress; if (q->yflick()) { qreal dy = deltas.y(); @@ -1529,6 +1542,21 @@ void QQuickFlickable::touchEvent(QTouchEvent *event) QQuickItem::touchEvent(event); } +enum class WheelMomentumSetting { + Default = -1, + Disabled = 0, + Enabled +}; + +static WheelMomentumSetting wheelMomentumEnabled() +{ + bool envIsSet = true; + const bool wheelMomentumEnabled = qEnvironmentVariableIntValue("QT_QUICK_FLICKABLE_WHEEL_MOMENTUM_ENABLED", &envIsSet); + if (!envIsSet) + return WheelMomentumSetting::Default; + return wheelMomentumEnabled ? WheelMomentumSetting::Enabled : WheelMomentumSetting::Disabled; +} + #if QT_CONFIG(wheelevent) void QQuickFlickable::wheelEvent(QWheelEvent *event) { @@ -1537,6 +1565,7 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event) QQuickItem::wheelEvent(event); return; } + qCDebug(lcWheel) << event->device() << event; event->setAccepted(false); qint64 currentTimestamp = d->computeCurrentTime(event); switch (event->phase()) { @@ -1577,46 +1606,102 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event) // physical mouse wheel, so use angleDelta int xDelta = event->angleDelta().x(); int yDelta = event->angleDelta().y(); - 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) { - d->flickY(d->vData.velocity); - d->flickingStarted(false, true); - if (d->vData.flicking) { - d->vMoved = true; - movementStarting(); + /*! \internal + Will we get smooth scrolling with momentum, or not? + + |env var|enabled |delta|momentum| + |-------|--------|-----|--------| + |not set|Default |120x |y | + |not set|Default |other|n | + |0 |Disabled|120x |n | + |0 |Disabled|other|n | + |1 |Enabled |120x |y | + |1 |Enabled |other|y | + */ + static const WheelMomentumSetting momentumEnvEnabled = wheelMomentumEnabled(); + bool momentumEnabled = (momentumEnvEnabled != WheelMomentumSetting::Disabled); + if (momentumEnvEnabled == WheelMomentumSetting::Default) { + momentumEnabled = !d->nonClickyWheelMice.contains(event->pointingDevice()); + if (momentumEnabled) { + // If a particular mouse ever generates deltas that are not a multiple of 120, + // momentum scrolling is disabled. On a laptop, the "core pointer" might sometimes + // be the touchpad and sometimes a USB mouse (until Qt's multi-mouse support is + // better developed to keep them separate); but if you miss momentum, you can set + // QT_QUICK_FLICKABLE_WHEEL_MOMENTUM_ENABLED=1 to opt back in again. + if (xDelta % 120 || yDelta % 120) { + d->nonClickyWheelMice.append(event->pointingDevice()); + qCDebug(lcWheel) << event->pointingDevice() << "doesn't have a 'clicky' mouse wheel"; + momentumEnabled = false; } - event->accept(); } } - 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; + qCDebug(lcWheel) << "momentum env:" << int(momentumEnvEnabled) << "this device:" << momentumEnabled; + if (momentumEnabled) { + // engage the velocity timeline for smooth scrolling + 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) { + d->flickY(d->vData.velocity); + d->flickingStarted(false, true); + if (d->vData.flicking) { + d->vMoved = true; + movementStarting(); + } + event->accept(); + } } - if (valid) { - d->flickX(d->hData.velocity); - d->flickingStarted(true, false); - if (d->hData.flicking) { - d->hMoved = true; - movementStarting(); + 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) { + d->flickX(d->hData.velocity); + d->flickingStarted(true, false); + if (d->hData.flicking) { + d->hMoved = true; + movementStarting(); + } + event->accept(); + } + } + } else { + // if it's a laptop touchpad, ignore velocity and make scroll distance directly proportional to angleDelta + if (yDelta > 0) + yDelta = qMin(yDelta, int(contentY() + minYExtent())); + else if (yDelta < 0) + yDelta = qMax(yDelta, int(contentY() + maxYExtent())); + else if (xDelta > 0) + xDelta = qMin(xDelta, int(contentX() + minXExtent())); + else if (xDelta < 0) + xDelta = qMax(xDelta, int(contentX() + maxXExtent())); + + if (xDelta != 0 || yDelta != 0) { + // if QStyleHints::wheelScrollLines() != 3, scale it accordingly + // (Flickable has no idea how many "lines" are being scrolled: depends on font size etc.) + static const float defaultWheelScrollLines = QPlatformTheme::defaultThemeHint(QPlatformTheme::WheelScrollLines).toFloat(); + const float configuredWheelScrollLines = qGuiApp->styleHints()->wheelScrollLines(); + const float deltaRatio = configuredWheelScrollLines / defaultWheelScrollLines; + d->accumulatedWheelPixelDelta += QVector2D(xDelta * deltaRatio, yDelta * deltaRatio); + qCDebug(lcWheel) << "scroll lines: default" << defaultWheelScrollLines + << "style hint" << configuredWheelScrollLines << "scaled acc delta" << d->accumulatedWheelPixelDelta; + d->drag(currentTimestamp, event->type(), event->position(), d->accumulatedWheelPixelDelta, true, false, false, {}); event->accept(); } } } else { - // use pixelDelta (probably from a trackpad) + // use pixelDelta (probably from a trackpad): this is where we want to be on most platforms eventually int xDelta = event->pixelDelta().x(); int yDelta = event->pixelDelta().y(); @@ -1845,12 +1930,15 @@ void QQuickFlickablePrivate::viewportAxisMoved(AxisData &data, qreal minExtent, else velocityTimeline.move(data.smoothVelocity, velocity, reportedVelocitySmoothing); velocityTimeline.move(data.smoothVelocity, 0, reportedVelocitySmoothing); + qCDebug(lcVel) << "touchpad scroll phase: velocity" << velocity; } } } else { if (timeline.time() > data.vTime) { velocityTimeline.reset(data.smoothVelocity); qreal velocity = (data.lastPos - data.move.value()) * 1000 / (timeline.time() - data.vTime); + if (!qFuzzyCompare(data.smoothVelocity.value(), velocity)) + qCDebug(lcVel) << "velocity" << data.smoothVelocity.value() << "->" << velocity; data.smoothVelocity.setValue(velocity); } } @@ -1865,8 +1953,11 @@ void QQuickFlickablePrivate::viewportAxisMoved(AxisData &data, qreal minExtent, data.inOvershoot = true; qreal maxDistance = overShootDistance(vSize) - overBound; resetTimeline(data); - if (maxDistance > 0) + if (maxDistance > 0) { + qCDebug(lcVel) << "timeline.accel(" << data.move << -data.smoothVelocity.value() + << deceleration*QML_FLICK_OVERSHOOTFRICTION << "maxDist" << maxDistance << ')'; timeline.accel(data.move, -data.smoothVelocity.value(), deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance); + } timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this)); } diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h index f9f5ce7628..5f46c881b5 100644 --- a/src/quick/items/qquickflickable_p_p.h +++ b/src/quick/items/qquickflickable_p_p.h @@ -276,6 +276,8 @@ public: static qsizetype data_count(QQmlListProperty<QObject> *); static QObject *data_at(QQmlListProperty<QObject> *, qsizetype); static void data_clear(QQmlListProperty<QObject> *); + + static QVarLengthArray<const QPointingDevice *, 4> nonClickyWheelMice; }; class QQuickFlickableVisibleArea : public QObject |