예제 #1
0
bool QAbstractKineticScrollerPrivate::handleMouseMove(QMouseEvent *e)
{
    Q_Q(QAbstractKineticScroller);
    qKSDebug() << "MM: pos: " << e->globalPos() << " - time: " << QTime::currentTime().msec();
    if (!(e->buttons() & Qt::LeftButton))
        return false;
    if ((state != QAbstractKineticScroller::MousePressed) &&
        (state != QAbstractKineticScroller::Pushing) &&
        (state != QAbstractKineticScroller::AutoScrolling))
        return false;
    if (moved && !lastTime.elapsed())
        return true;
    if (lastType == QEvent::MouseButtonPress)
        firstDrag = true;

    QPoint delta = e->globalPos() - pos;

    if (!moved) {
        checkMove(e, delta);
    }

    if (moved) {
        if (state != QAbstractKineticScroller::Pushing) {
            q->cancelLeftMouseButtonPress(ipos);

            changeState(QAbstractKineticScroller::Pushing);
        }
        handleMove(e, delta);
    }
    lastTime.restart();
    lastType = e->type();

    qKSDebug("MM: end %d", moved);
    return moved;
}
예제 #2
0
/*!
    Starts scrolling the widget so that the point \a pos is visible inside the
    viewport with margins specified in pixels by \a xmargin and \a ymargin.

    If the specified point cannot be reached, the contents are scrolled to the
    nearest valid position.  The default value for both margins is 50 pixels.

    This function performs the actual scrolling by calling scrollTo().

    \sa maximumContentPosition()
*/
void QKineticScroller::ensureVisible(const QPointF &pos, int xmargin, int ymargin, int scrollTime)
{
    QSizeF visible = viewportSize();
    QPointF currentPos = contentPosition();

    qKSDebug() << "QKS::ensureVisible(" << pos << " [pix], " << xmargin << " [pix], " << ymargin << " [pix], " << scrollTime << "[ms])";
    qKSDebug() << "  --> content position:" << contentPosition();

    QRectF posRect(pos.x() - xmargin, pos.y() - ymargin, 2 * xmargin, 2 * ymargin);
    QRectF visibleRect(currentPos, visible);

    if (visibleRect.contains(posRect))
        return;

    QPointF newPos = currentPos;
    if (posRect.top() < visibleRect.top())
        newPos.setY(posRect.top());
    else if (posRect.bottom() > visibleRect.bottom())
        newPos.setY(posRect.bottom() - visible.height());
    if (posRect.left() < visibleRect.left())
        newPos.setX(posRect.left());
    else if (posRect.right() > visibleRect.right())
        newPos.setY(posRect.right() - visible.width());

    scrollTo(newPos, scrollTime);
}
예제 #3
0
bool QAbstractKineticScrollerPrivate::handleMousePress(QMouseEvent *e)
{
    Q_Q(QAbstractKineticScroller);
    qKSDebug("MP: start");
    if (e->button() != Qt::LeftButton)
        return false;

    QPoint maxPos = q->maximumScrollPosition();
    bool canScrollX = (maxPos.x() > 0);
    bool canScrollY = (maxPos.y() > 0);

    if ((!canScrollX || !canScrollY) && (overshootPolicy == QAbstractKineticScroller::OvershootAlwaysOn))
        canScrollX = canScrollY = true;

    scrollTo = QPoint(-1, -1);

    pos = e->globalPos();
    ipos = pos;

    bool swallow = true;

    // don't allow a click if we're still moving fast
    if ((qAbs(velocity.x()) <= (maxVelocity * fastVelocityFactor)) &&
        (qAbs(velocity.y()) <= (maxVelocity * fastVelocityFactor))) {
        swallow = false;

        if (!q->canStartScrollingAt(e->globalPos()))
            canScrollX = canScrollY = false;
    }

    // stop scrolling on mouse press (so you can flick, then hold to stop)
    oldVelocity = velocity;
    velocity = QPointF(0, 0);

    if (idleTimerId) {
        killTimer(idleTimerId);
        idleTimerId = 0;
        changeState(QAbstractKineticScroller::Inactive);
    }

    if (canScrollX || canScrollY) {
        lastTime.start();
        lastPressTime.start();
        lastType = e->type();

        changeState(QAbstractKineticScroller::MousePressed);
    }

    qKSDebug("MP: end %d", swallow);
    e->setAccepted(true);
    return swallow;
}
예제 #4
0
/*!
    Starts scrolling the widget so that the point \a pos is visible inside
    the viewport.

    If the specified point cannot be reached, the contents are scrolled to the
    nearest valid position.

    The scrolling speed will be calculated so that the given position will
    be reached after a platform-defined time span (1 second for Maemo 5).
    The final speed at the end position is not guaranteed to be zero.

    \sa ensureVisible(), maximumScrollPosition()
*/
void QAbstractKineticScroller::scrollTo(const QPoint &pos)
{
    Q_D(QAbstractKineticScroller);

    if ((pos == scrollPosition()) ||
        (d->state == QAbstractKineticScroller::MousePressed) ||
        (d->state == QAbstractKineticScroller::Pushing)) {
        return;
    }

    // --- calculate the initial velocity to get to the point
    // -- calc the number of steps we have:
    int steps = d->scrollTime * d->scrollsPerSecond / 1000;

    // -- calc the distance we will move with a starting velocity of 1.0
    float dist = 0.0;
    float curVel = 1.0;
    for (int i = 0; i<steps; i++) {
        dist += curVel;
        curVel *= d->deceleration;
    }

    // --- start the scrolling
    d->scrollTo = pos;
    d->velocity = - QPointF(pos-scrollPosition()) / dist;
    d->mode = QAbstractKineticScroller::AutoMode;

    qKSDebug() << "QAbstractKineticScroller::scrollTo new pos:" << pos << " velocity:"<<d->velocity;

    if (!d->idleTimerId) {
        d->changeState(QAbstractKineticScroller::AutoScrolling);
        d->idleTimerId = d->startTimer(1000 / d->scrollsPerSecond);
    }
}
예제 #5
0
/*!
    Starts scrolling the widget so that the point \a pos is visible inside the
    viewport with margins specified in pixels by \a xmargin and \a ymargin.

    If the specified point cannot be reached, the contents are scrolled to the
    nearest valid position.  The default value for both margins is 50 pixels.

    This function performs the actual scrolling by calling scrollTo().

    \sa maximumScrollPosition()
*/
void QAbstractKineticScroller::ensureVisible(const QPoint &pos, int xmargin, int ymargin)
{
    QSize visible = viewportSize();
    QPoint currentPos = scrollPosition();

    qKSDebug() << "QAbstractKineticScroller::ensureVisible(" << pos << ", " << xmargin << ", " << ymargin << ") - position: " << scrollPosition();

    QRect posRect(pos.x() - xmargin, pos.y() - ymargin, 2 * xmargin, 2 * ymargin);
    QRect visibleRect(currentPos, visible);

    if (visibleRect.contains(posRect))
        return;

    QPoint newPos = currentPos;
    if (posRect.top() < visibleRect.top())
        newPos.setY(posRect.top());
    else if (posRect.bottom() > visibleRect.bottom())
        newPos.setY(posRect.bottom() - visible.height());
    if (posRect.left() < visibleRect.left())
        newPos.setX(posRect.left());
    else if (posRect.right() > visibleRect.right())
        newPos.setX(posRect.right() - visible.width());

    scrollTo(newPos);
}
예제 #6
0
bool QKineticScroller::handleInput(Input input, const QPointF &position, qint64 timestamp)
{
    Q_D(QKineticScroller);


    qKSDebug() << "QKS::handleInput(" << input << ", " << position << ", " << timestamp << ")";
    struct statechange {
        State state;
        Input input;
        typedef bool (QKineticScrollerPrivate::*inputhandler_t)(Input input, const QPointF &position, qint64 timestamp);
        inputhandler_t handler;
    };

    statechange statechanges[] = {
        { StateInactive,  InputPress,   &QKineticScrollerPrivate::pressWhileInactive },
        { StatePressed,   InputMove,    &QKineticScrollerPrivate::moveWhilePressed },
        { StatePressed,   InputRelease, &QKineticScrollerPrivate::releaseWhilePressed },
        { StateDragging,  InputMove,    &QKineticScrollerPrivate::moveWhileDragging },
        { StateDragging,  InputRelease, &QKineticScrollerPrivate::releaseWhileDragging },
        { StateScrolling, InputPress,   &QKineticScrollerPrivate::pressWhileScrolling }
    };

    for (int i = 0; i < int(sizeof(statechanges) / sizeof(*statechanges)); ++i) {
        statechange *sc = statechanges + i;

         if (d->state == sc->state && input == sc->input)
             return (d->*sc->handler)(input, position, timestamp);
    }

    qWarning() << "Unhandled input: got input " << d->inputName(input) << " while in state " << d->stateName(d->state);
    return false;
}
예제 #7
0
void QKineticScrollerPrivate::timerEventWhileDragging()
{
    if (!dragDistance.isNull()) {
        qKSDebug() << "QKS::timerEventWhileDragging() -- dragDistance:" << dragDistance;

        setContentPositionHelper(-dragDistance);
        dragDistance = QPointF(0, 0);
    }
}
예제 #8
0
/*!
    Starts scrolling the widget so that the point \a pos is visible inside
    the viewport.

    If the specified point cannot be reached, the contents are scrolled to the
    nearest valid position (in this case the scroller might or might not overshoot).

    The scrolling speed will be calculated so that the given position will
    be reached after a platform-defined time span (1 second for Maemo 5).
    The final speed at the end position is not guaranteed to be zero.

    \sa ensureVisible(), maximumContentPosition()
*/
void QKineticScroller::scrollTo(const QPointF &pos, int scrollTime)
{
    Q_D(QKineticScroller);

    if (scrollTime <= 0)
        scrollTime = 1;

    qKSDebug() << "QKS::scrollTo(" << pos << " [pix], " << scrollTime << " [ms])";

    qreal time = qreal(scrollTime) / 1000;

    if ((pos == contentPosition()) ||
            (d->state == QKineticScroller::StatePressed) ||
            (d->state == QKineticScroller::StateDragging)) {
        return;
    }

    // estimate the minimal start velocity
    // if the start velocity is below that then the scrolling would stop before scrollTime.
    //qreal vMin = d->linearDecelerationFactor * time / qPow(d->exponentialDecelerationBase, time);

    /*
    // estimate of the distance passed within the vMin time during scrollTime
    qreal distMin = qreal(scrollTime) * vMin / 2.0;

    QPointF v = QPointF((pos.x()-contentPosition().x()) / distMin * vMin,
                        (pos.y()-contentPosition().y()) / distMin * vMin);

    */

    // v(t) = vstart * exponentialDecelerationBase ^ t - linearDecelerationFactor * t
    // pos(t) = integrate(v(t) * dt)
    // pos(t) = vstart * (eDB ^ t / ln(eDB) + C) -  lDF / 2 * t ^ 2
    //
    // pos(time) = pos - contentsPos()
    // vstart = ((lDF / 2) * time ^ 2 + (pos - contentPos())) / (eDB ^ time / ln(eDB) + C)
    // (for C = -1/ln(eDB) )

    QPointF scrollDir(qSign(pos.x() - contentPosition().x()),
                      qSign(pos.y() - contentPosition().y()));

    QPointF v = (scrollDir * (d->linearDecelerationFactor / qreal(2)) * qreal(time) * qreal(time) + (pos - contentPosition()) / d->pixelPerMeter);
    if (d->exponentialDecelerationBase != qreal(1))
        v /= (qPow(d->exponentialDecelerationBase, time) - 1) / qLn(d->exponentialDecelerationBase);
    else
        v /= time;

    d->scrollToPosition = pos;
    d->scrollToX = true;
    d->scrollToY = true;
    d->releaseVelocity = v;
    d->scrollRelativeTimer.restart();
    d->scrollAbsoluteTimer.restart();
    d->setState(QKineticScroller::StateScrolling);
}
예제 #9
0
QPointF QAbstractKineticScrollerPrivate::calculateVelocity(const QPointF &dPixelRaw, int dTime)
{
    qKSDebug() << "calculateVelocity(dP = " << dPixelRaw << ", dT = " << dTime << ") -- velocity: " << velocity;

    QPointF newv = velocity;
    QPointF dPixel = dPixelRaw;

    // faster than 25 pix / ms seems bogus (that's a screen height in ~20 ms)
    if ((dPixelRaw / qreal(dTime)).manhattanLength() > 25)
        dPixel = dPixelRaw * qreal(25) / (dPixelRaw / qreal(dTime)).manhattanLength();

    QPointF rawv = dPixel / qreal(dTime) * qreal(1000) / qreal(scrollsPerSecond);
    newv = newv * (qreal(1) - dragInertia) + rawv * dragInertia;

    qKSDebug() << " --> " << newv << " (before clamping)";

    newv.setX(dPixel.x() ? qBound(-maxVelocity, newv.x(), maxVelocity) : velocity.x());
    newv.setY(dPixel.y() ? qBound(-maxVelocity, newv.y(), maxVelocity) : velocity.y());
    return newv;
}
예제 #10
0
void QAbstractKineticScrollerPrivate::handleScrollTimer()
{
    Q_Q(QAbstractKineticScroller);

    qKSDebug("handleScrollTimer: %d/%d", motion.x(), motion.y());

    if (!motion.isNull())
        setScrollPositionHelper(q->scrollPosition() - overshootDist - motion);
    killTimer(scrollTimerId);
    scrollTimerId = 0;
}
예제 #11
0
bool QKineticScrollerPrivate::releaseWhileDragging(QKineticScroller::Input, const QPointF &, qint64 timestamp)
{
    Q_Q(QKineticScroller);

    // calculate the fastSwipe velocity
    QPointF maxPos = q->maximumContentPosition();
    QPointF fastSwipeVelocity  = QPoint(0, 0);
    QSizeF size = q->viewportSize();
    if (size.width())
        fastSwipeVelocity.setX(qMin(maximumVelocity, maxPos.x() / size.width() * fastSwipeBaseVelocity));
    if (size.height())
        fastSwipeVelocity.setY(qMin(maximumVelocity, maxPos.y() / size.height() * fastSwipeBaseVelocity));

    if (fastSwipeMaximumTime &&
        ((timestamp - pressTimestamp) < qint64(fastSwipeMaximumTime * 1000)) &&
        (oldVelocity > fastSwipeMinimumVelocity)) {

        // more than one fast swipe in a row: add fastSwipeVelocity
        int signX = 0, signY = 0;
        if (releaseVelocity.x())
            signX = (releaseVelocity.x() > 0) == (oldVelocity.x() > 0) ? 1 : -1;
        if (releaseVelocity.y())
            signY = (releaseVelocity.y() > 0) == (oldVelocity.y() > 0) ? 1 : -1;

        releaseVelocity.setX(signX * (oldVelocity.x() + (oldVelocity.x() > 0 ? fastSwipeVelocity.x() : -fastSwipeVelocity.x())));
        releaseVelocity.setY(signY * (oldVelocity.y() + (oldVelocity.y() > 0 ? fastSwipeVelocity.y() : -fastSwipeVelocity.y())));

    } else if (releaseVelocity >= minimumVelocity) {

        // if we have a fast swipe, accelerate it to the fastSwipe velocity
        if ((qAbs(releaseVelocity.x()) > maximumNonAcceleratedVelocity) &&
            (fastSwipeVelocity.x() > maximumNonAcceleratedVelocity)) {
            releaseVelocity.setX(releaseVelocity.x() > 0 ? fastSwipeVelocity.x() : -fastSwipeVelocity.x());
        }
        if ((qAbs(releaseVelocity.y()) > maximumNonAcceleratedVelocity) &&
            (fastSwipeVelocity.y() > maximumNonAcceleratedVelocity)) {
            releaseVelocity.setY(releaseVelocity.y() > 0 ? fastSwipeVelocity.y() : -fastSwipeVelocity.y());
        }

    }

    qKSDebug() << "QKS::releaseWhileDragging() -- velocity:" << releaseVelocity << "-- minimum velocity:" << minimumVelocity;
    if (overshootX || overshootY)
        setState(QKineticScroller::StateScrolling);
    else if (releaseVelocity >= minimumVelocity)
        setState(QKineticScroller::StateScrolling);
    else
        setState(QKineticScroller::StateInactive);

    return true;
}
예제 #12
0
void QAbstractKineticScrollerPrivate::checkMove(QMouseEvent *me, QPoint &delta)
{
    Q_Q(QAbstractKineticScroller);

    if (firstDrag && !moved && ((qAbs(delta.x()) > panningThreshold) || (qAbs(delta.y()) > panningThreshold))) {
        moved = true;
        // ignore the panning distance
        if (delta.x() > panningThreshold)
            delta.rx() -= panningThreshold;
        else if (delta.x() < -panningThreshold)
            delta.rx() += panningThreshold;
        else
            delta.setX(0);
        if (delta.y() > panningThreshold)
            delta.ry() -= panningThreshold;
        else if (delta.y() < -panningThreshold)
            delta.ry() += panningThreshold;
        else
            delta.setY(0);

        if (firstDrag) {
            int deltaXtoY = qAbs(ipos.x() - me->globalPos().x()) - qAbs(ipos.y() - me->globalPos().y());

            qKSDebug() << "First Drag with delta " << delta << ", greater than " << panningThreshold << " -- deltaXtoY: " << deltaXtoY;

            QPoint maxPos = q->maximumScrollPosition();
            bool canScrollX = (maxPos.x() > 0);
            bool canScrollY = (maxPos.y() > 0);

            if ((!canScrollX || !canScrollY) && (overshootPolicy == QAbstractKineticScroller::OvershootAlwaysOn))
                canScrollX = canScrollY = true;

            if (deltaXtoY < 0) {
                if (!canScrollY && (!canScrollX || (-deltaXtoY >= directionErrorMargin)))
                    moved = false;
            } else {
                if (!canScrollX && (!canScrollY || (deltaXtoY >= directionErrorMargin)))
                    moved = false;
            }
        }
        firstDrag = false;

        if (moved && (mode == QAbstractKineticScroller::AccelerationMode)) {

            if (!idleTimerId) {
                changeState(QAbstractKineticScroller::AutoScrolling);
                idleTimerId = startTimer(1000 / scrollsPerSecond);
            }
        }
    }
}
예제 #13
0
void QKineticScrollerPrivate::updateVelocity(const QPointF &deltaPixelRaw, qint64 deltaTime)
{
    qKSDebug() << "QKS::updateVelocity(" << deltaPixelRaw << " [delta pix], " << deltaTime << " [delta ms])";

    QPointF deltaPixel = deltaPixelRaw;

    // faster than 2.5mm/ms seems bogus (that would be a screen height in ~20 ms)
    if (((deltaPixelRaw / qreal(deltaTime)).manhattanLength() / pixelPerMeter * 1000) > qreal(2.5))
        deltaPixel = deltaPixelRaw * qreal(2.5) * pixelPerMeter / 1000 / (deltaPixelRaw / qreal(deltaTime)).manhattanLength();

    qreal inversSmoothingFactor = ((qreal(1) - dragVelocitySmoothingFactor) * qreal(deltaTime) / qreal(1000));
    QPointF newv = -deltaPixel / qreal(deltaTime) * qreal(1000) / pixelPerMeter;
    newv = newv * (qreal(1) - inversSmoothingFactor) + releaseVelocity * inversSmoothingFactor;

    // newv = newv * dragVelocitySmoothingFactor + velocity * (qreal(1) - dragVelocitySmoothingFactor);

    if (deltaPixel.x())
        releaseVelocity.setX(qBound(-maximumVelocity, newv.x(), maximumVelocity));
    if (deltaPixel.y())
        releaseVelocity.setY(qBound(-maximumVelocity, newv.y(), maximumVelocity));

    qKSDebug() << "  --> new velocity:" << releaseVelocity;
}
예제 #14
0
/*
    Decomposes the position into a scroll and an overshoot part.
    Also keeps track of the current over-shooting value in overshootDist.
*/
void QAbstractKineticScrollerPrivate::setScrollPositionHelper(const QPoint &pos)
{
    Q_Q(QAbstractKineticScroller);

    QPoint maxPos = q->maximumScrollPosition();

    QPoint clampedPos;
    clampedPos.setX(qBound(0, pos.x(), maxPos.x()));
    clampedPos.setY(qBound(0, pos.y(), maxPos.y()));

    bool alwaysOvershoot = (overshootPolicy == QAbstractKineticScroller::OvershootAlwaysOn);
    int overshootX = (maxPos.x() || alwaysOvershoot) ? clampedPos.x() - pos.x() : 0;
    int overshootY = (maxPos.y() || alwaysOvershoot) ? clampedPos.y() - pos.y() : 0;

    overshootDist.setX(qBound(-maxOvershoot.x(), overshootX, maxOvershoot.x()));
    overshootDist.setY(qBound(-maxOvershoot.y(), overshootY, maxOvershoot.y()));

    qKSDebug() << "setPosition raw: " << pos << ", clamped: " << clampedPos << ", overshoot: " << overshootDist;
    q->setScrollPosition(clampedPos, overshootPolicy == QAbstractKineticScroller::OvershootAlwaysOff ? QPoint() : overshootDist);
}
예제 #15
0
void QKineticScrollerPrivate::timerEventWhileScrolling()
{
    qreal deltaTime = qreal(scrollRelativeTimer.restart()) / 1000;
    qreal time = qreal(scrollAbsoluteTimer.elapsed()) / 1000;

    // calculate the velocity for the passed interval deltatime.
    // using the midpoint of the interval gives a better precision than using just time.
    QPointF newVelocity = calculateVelocity(time - deltaTime / 2);
    QPointF deltaPos = newVelocity * deltaTime * pixelPerMeter;

    // -- move (convert from [m/s] to [pix/frame]
    if (!deltaPos.isNull())
        setContentPositionHelper(deltaPos);

    qKSDebug() << "QKS::timerEventWhileScrolling() -- DeltaPos:" << deltaPos << "- NewVel:" <<  newVelocity  << "- Time:" <<  time;

    if (newVelocity.isNull() ||
            (releaseVelocity.isNull() && !scrollToX && !scrollToY && !overshootX && !overshootY))
    // if (newVelocity.isNull())
        setState(QKineticScroller::StateInactive);
}
예제 #16
0
void QKineticScrollerPrivate::setState(QKineticScroller::State newstate)
{
    Q_Q(QKineticScroller);

    if (state == newstate)
        return;

    qKSDebug() << "QKS::setState(" << stateName(newstate) << ")";

    switch (newstate) {
    case QKineticScroller::StateInactive:
        if (state == QKineticScroller::StateScrolling) {
            if (timerId) {
                killTimer(timerId);
                timerId = 0;
            } else {
                qKSDebug() << "  --> state change from " << stateName(state) << " to " << stateName(newstate) << ", but timer is not active.";
            }
        }
        releaseVelocity = QPointF(0, 0);
        break;

    case QKineticScroller::StatePressed:
        if (timerId) {
            killTimer(timerId);
            timerId = 0;
        }
        scrollToX = false;
        scrollToY = false;
        oldVelocity = releaseVelocity;
        // releaseVelocity = QPointF(0, 0);
        break;

    case QKineticScroller::StateDragging:

        dragDistance = QPointF(0, 0);
        if (state == QKineticScroller::StatePressed) {
            if (!timerId) {
                timerId = startTimer(1000 / framesPerSecond);
            } else {
                qKSDebug() << "  --> state change from " << stateName(state) << " to " << stateName(newstate) << ", but timer is already active.";
            }
        }

        break;

    case QKineticScroller::StateScrolling:
        if (!timerId) {
            timerId = startTimer(1000 / framesPerSecond);
        }
        scrollRelativeTimer.start();
        scrollAbsoluteTimer.start();

        if (state == QKineticScroller::StateDragging) {
            // TODO: better calculate StartTime using the current releaseVelocity
            overshootStartTimeX = overshootStartTimeY = qreal(scrollAbsoluteTimer.elapsed()) / 1000 - M_PI / (overshootSpringConstantRoot * 2);
            overshootVelocity = overshootPosition / pixelPerMeter * overshootSpringConstantRoot;
        }

        break;
    }

    qSwap(state, newstate);
    q->stateChanged(newstate);
}
예제 #17
0
/*! \internal
    Helps when setting the content position.
    It will try to move the content by the requested delta but stop in case
    when we are coming back from an overshoot or a scrollTo.
    It will also indicate a new overshooting condition by the overshootX and oversthootY flags.

    In this cases it will reset the velocity variables and other flags.

    Also keeps track of the current over-shooting value in overshootPosition.

    \deltaPos is the amout of pixels the current content position should be moved
*/
void QKineticScrollerPrivate::setContentPositionHelper(const QPointF &deltaPos)
{
    Q_Q(QKineticScroller);

    if (state == QKineticScroller::StateDragging && overshootDragResistanceFactor)
        overshootPosition /= overshootDragResistanceFactor;

    QPointF oldPos = q->contentPosition() + overshootPosition;
    QPointF newPos = oldPos + deltaPos;
    QPointF maxPos = q->maximumContentPosition();

    QPointF oldScrollToDist = scrollToPosition - oldPos;
    QPointF newScrollToDist = scrollToPosition - newPos;

    qKSDebug() << "QKS::setContentPositionHelper(" << deltaPos << " [pix])";
    qKSDebug() << "  --> overshoot:" << overshootPosition << "- old pos:" << oldPos << "- new pos:" << newPos;

    QPointF oldClampedPos;
    oldClampedPos.setX(qBound(qreal(0), oldPos.x(), maxPos.x()));
    oldClampedPos.setY(qBound(qreal(0), oldPos.y(), maxPos.y()));

    QPointF newClampedPos;
    newClampedPos.setX(qBound(qreal(0), newPos.x(), maxPos.x()));
    newClampedPos.setY(qBound(qreal(0), newPos.y(), maxPos.y()));

    // --- handle overshooting and stop if the coordinate is going back inside the normal area
    bool alwaysOvershootX = (hOvershootPolicy == QKineticScroller::OvershootAlwaysOn);
    bool alwaysOvershootY = (vOvershootPolicy == QKineticScroller::OvershootAlwaysOn);
    bool noOvershootX = (hOvershootPolicy == QKineticScroller::OvershootAlwaysOff) ||
                        ((state == QKineticScroller::StateDragging) && !overshootDragResistanceFactor);
    bool noOvershootY = (vOvershootPolicy == QKineticScroller::OvershootAlwaysOff) ||
                        ((state == QKineticScroller::StateDragging) && !overshootDragResistanceFactor);
    bool canOvershootX = !noOvershootX && (alwaysOvershootX || maxPos.x());
    bool canOvershootY = !noOvershootY && (alwaysOvershootY || maxPos.y());

    qreal oldOvershootX = (canOvershootX) ? oldPos.x() - oldClampedPos.x() : 0;
    qreal oldOvershootY = (canOvershootY) ? oldPos.y() - oldClampedPos.y() : 0;

    qreal newOvershootX = (canOvershootX) ? newPos.x() - newClampedPos.x() : 0;
    qreal newOvershootY = (canOvershootY) ? newPos.y() - newClampedPos.y() : 0;

    if (state == QKineticScroller::StateDragging && overshootDragResistanceFactor) {
        oldOvershootX *= overshootDragResistanceFactor;
        oldOvershootY *= overshootDragResistanceFactor;
        newOvershootX *= overshootDragResistanceFactor;
        newOvershootY *= overshootDragResistanceFactor;
    }

    // -- stop at the maximum overshoot distance (if set)
    if (!overshootMaximumDistance.isNull()) {
        newOvershootX = qBound(-overshootMaximumDistance.x() * pixelPerMeter, newOvershootX, overshootMaximumDistance.x() * pixelPerMeter);

        newOvershootY = qBound(-overshootMaximumDistance.y() * pixelPerMeter, newOvershootY, overshootMaximumDistance.y() * pixelPerMeter);
    }

    // --- sanity check for scrollTo in case we can't even scroll that direction
    if (!(maxPos.x() || alwaysOvershootX))
        scrollToX = false;
    if (!(maxPos.y() || alwaysOvershootY))
        scrollToY = false;

    // --- handle crossing over borders (scrollTo and overshoot)
    qKSDebug() << "  --> old overshoot Y:" << oldOvershootY << "- new overshoot Y:" << newOvershootY;
    // -- x axis
    if (scrollToX && qSign(oldScrollToDist.x()) != qSign(newScrollToDist.x())) {
        newClampedPos.setX(scrollToPosition.x());
        newOvershootX = 0;
        releaseVelocity.setX(0);
        scrollToX = false;

    } else if (oldOvershootX && (qSign(oldOvershootX) != qSign(newOvershootX))) {
        newClampedPos.setX((oldOvershootX < 0) ? 0 : maxPos.x());
        newOvershootX = 0;
        releaseVelocity.setX(0);
        overshootVelocity.setX(0);
        overshootX = false;

    } else if (!oldOvershootX && newOvershootX) {
        overshootStartTimeX = qreal(scrollAbsoluteTimer.elapsed()) / 1000;
        overshootVelocity.setX(calculateVelocity(overshootStartTimeX).x());

        // restrict the overshoot to overshootMaximumDistance
        qreal maxOvershootVelocity = overshootMaximumDistance.x() * overshootSpringConstantRoot;
        if (overshootMaximumDistance.x() && qAbs(overshootVelocity.x()) > maxOvershootVelocity)
            overshootVelocity.setX(maxOvershootVelocity * qSign(newOvershootX));

        // -- prevent going into overshoot too far
        if (state != QKineticScroller::StateDragging)
            newOvershootX = qSign(newOvershootX) * qreal(0.0001);

        scrollToX = false;
        overshootX = true;
    }

    // -- y axis
    if (scrollToY && qSign(oldScrollToDist.y()) != qSign(newScrollToDist.y())) {
        newClampedPos.setY(scrollToPosition.y());
        newOvershootY = 0;
        releaseVelocity.setY(0);
        scrollToY = false;

    } else if (oldOvershootY && (qSign(oldOvershootY) != qSign(newOvershootY))) {
        newClampedPos.setY((oldOvershootY < 0) ? 0 : maxPos.y());
        newOvershootY = 0;
        releaseVelocity.setY(0);
        overshootVelocity.setY(0);
        overshootY = false;

    } else if (!oldOvershootY && newOvershootY) {
        overshootStartTimeY = qreal(scrollAbsoluteTimer.elapsed()) / 1000;
        overshootVelocity.setY(calculateVelocity(overshootStartTimeY).y());

        // -- restrict the overshoot to overshootMaximumDistance
        qreal maxOvershootVelocity = overshootMaximumDistance.y() * overshootSpringConstantRoot;
        if (overshootMaximumDistance.y() && (qAbs(overshootVelocity.y()) > maxOvershootVelocity))
            overshootVelocity.setY(maxOvershootVelocity * qSign(newOvershootY));

        // -- prevent going into overshoot too far
        if (state != QKineticScroller::StateDragging)
            newOvershootY = qSign(newOvershootY) * qreal(0.0001);

        scrollToY = false;
        overshootY = true;
    }

    overshootPosition.setX(newOvershootX);
    overshootPosition.setY(newOvershootY);

    q->setContentPosition(newClampedPos, overshootPosition);

    if (debugHook)
        debugHook(debugHookUser, calculateVelocity(qreal(scrollAbsoluteTimer.elapsed()) / 1000), newClampedPos, overshootPosition);

    qKSDebug() << "  --> new position:" << newClampedPos << "- new overshoot:" << overshootPosition <<
                  "- overshoot x/y?:" << overshootX << "/" << overshootY << "- scrollto x/y?:" << scrollToX << "/" << scrollToY;
}
예제 #18
0
void QAbstractKineticScrollerPrivate::handleMove(QMouseEvent *me, QPoint &delta)
{
    Q_Q(QAbstractKineticScroller);

    if (mode == QAbstractKineticScroller::AccelerationMode) {
        // we need delta to be the delta to ipos, not pos in this case
        delta = me->globalPos() - ipos;
    }

    if (axisLockThreshold) {
        int dx = qAbs(delta.x());
        int dy = qAbs(delta.y());
        if (dx || dy) {
            bool vertical = (dy > dx);
            qreal alpha = qreal(vertical ? dx : dy) / qreal(vertical ? dy : dx);
            qKSDebug() << "axis lock: " << alpha << " / " << axisLockThreshold << " - isvertical: " << vertical << " - dx: " << dx << " - dy: " << dy;
            if (alpha <= axisLockThreshold) {
                if (vertical)
                    delta.setX(0);
                else
                    delta.setY(0);
            }
        }
    }

    switch (mode) {
    case QAbstractKineticScroller::PushMode:
        // Scroll by the amount of pixels the cursor has moved
        // since the last motion event.
        scrollUpdate(delta);
        pos = me->globalPos();
        break;

    case QAbstractKineticScroller::AccelerationMode: {
        // Set acceleration relative to the initial click
        QSize size = q->viewportSize();
        qreal signX = 0, signY = 0;
        if (delta.x() < 0)
            signX = -1;
        else if (delta.x() > 0)
            signX = 1;
        if (delta.y() < 0)
            signY = -1;
        else if (delta.y() > 0)
            signY = 1;

        velocity.setX(signX * ((qreal(qAbs(delta.x())) / qreal(size.width()) * (maxVelocity - minVelocity)) + minVelocity));
        velocity.setY(signY * ((qreal(qAbs(delta.y())) / qreal(size.height()) * (maxVelocity - minVelocity)) + minVelocity));
        break;
    }
    case QAbstractKineticScroller::AutoMode:
        QPointF newVelocity = calculateVelocity(delta, lastTime.elapsed());
        QPoint maxPos = q->maximumScrollPosition();

        bool alwaysOvershoot = (overshootPolicy == QAbstractKineticScroller::OvershootAlwaysOn);

        if (!maxPos.x() && !alwaysOvershoot) {
            delta.setX(0);
            newVelocity.setX(0);
        }
        if (!maxPos.y() && !alwaysOvershoot) {
            delta.setY(0);
            newVelocity.setY(0);
        }
        velocity = newVelocity;

        scrollUpdate(delta);

        if (maxPos.x() || alwaysOvershoot)
            pos.setX(me->globalPos().x());
        if (maxPos.y() || alwaysOvershoot)
            pos.setY(me->globalPos().y());
        break;
    }
}
예제 #19
0
bool QAbstractKineticScrollerPrivate::handleMouseRelease(QMouseEvent *e)
{
    qKSDebug() << "MR: pos: " << e->globalPos() << " - time: " << QTime::currentTime().msec();
    Q_Q(QAbstractKineticScroller);

    if (e->button() != Qt::LeftButton)
        return false;
    if ((state != QAbstractKineticScroller::MousePressed) && (state != QAbstractKineticScroller::Pushing))
        return false;

    // if last event was a motion-notify we have to check the
    // movement and launch the animation
    if (lastType == QEvent::MouseMove) {
        if (moved) {
            QPoint delta = e->globalPos() - pos;
            handleMove(e, delta);

            // move all the way to the last position now
            if (scrollTimerId) {
                killTimer(scrollTimerId);
                scrollTimerId = 0;
                setScrollPositionHelper(q->scrollPosition() - overshootDist - motion);
                motion = QPoint(0, 0);
            }
        }
    }
    // If overshoot has been initiated with a finger down,
    // on release set max speed
    if (overshootDist.x()) {
        overshooting = bounceSteps; // Hack to stop a bounce in the finger down case
        velocity.setX(-overshootDist.x() * qreal(0.8));

    }
    if (overshootDist.y()) {
        overshooting = bounceSteps; // Hack to stop a bounce in the finger down case
        velocity.setY(-overshootDist.y() * qreal(0.8));
    }

    bool forceFast = true;

    // if widget was moving fast in the panning, increase speed even more
    if ((lastPressTime.elapsed() < FastClick) &&
        ((qAbs(oldVelocity.x()) > minVelocity) ||
         (qAbs(oldVelocity.y()) > minVelocity)) &&
        ((qAbs(oldVelocity.x()) > MinimumAccelerationThreshold) ||
         (qAbs(oldVelocity.y()) > MinimumAccelerationThreshold))) {

        qKSDebug() << "FAST CLICK - using oldVelocity " << oldVelocity;
        int signX = 0, signY = 0;
        if (velocity.x())
            signX = (velocity.x() > 0) == (oldVelocity.x() > 0) ? 1 : -1;
        if (velocity.y())
            signY = (velocity.y() > 0) == (oldVelocity.y() > 0) ? 1 : -1;

        // calculate the acceleration velocity
        QPoint maxPos = q->maximumScrollPosition();
        accelerationVelocity  = QPoint(0, 0);
        QSize size = q->viewportSize();
        if (size.width())
            accelerationVelocity.setX(qMin(int(q->maximumVelocity()), maxPos.x() / size.width() * AccelFactor));
        if (size.height())
            accelerationVelocity.setY(qMin(int(q->maximumVelocity()), maxPos.y() / size.height() * AccelFactor));

        velocity.setX(signX * (oldVelocity.x() + (oldVelocity.x() > 0 ? accelerationVelocity.x() : -accelerationVelocity.x())));
        velocity.setY(signY * (oldVelocity.y() + (oldVelocity.y() > 0 ? accelerationVelocity.y() : -accelerationVelocity.y())));
        forceFast = false;
    }

    if ((qAbs(velocity.x()) >= minVelocity) ||
        (qAbs(velocity.y()) >= minVelocity)) {

        qKSDebug() << "over min velocity: " << velocity;
        // we have to move because we are in overshooting position
        if (!moved) {
            // (opt) emit panningStarted()
        }
        // (must) enable scrollbars

        if (forceFast) {
            if ((qAbs(velocity.x()) > MaximumVelocityThreshold) &&
                (accelerationVelocity.x() > MaximumVelocityThreshold)) {
                velocity.setX(velocity.x() > 0 ? accelerationVelocity.x() : -accelerationVelocity.x());
            }
            if ((qAbs(velocity.y()) > MaximumVelocityThreshold) &&
                (accelerationVelocity.y() > MaximumVelocityThreshold)) {
                velocity.setY(velocity.y() > 0 ? accelerationVelocity.y() : -accelerationVelocity.y());
            }
            qKSDebug() << "Force fast is on - velocity: " << velocity;

        }
        changeState(QAbstractKineticScroller::AutoScrolling);

    } else {
        if (moved) {
            // (opt) emit panningFinished()
        }
        changeState(QAbstractKineticScroller::Inactive);
    }

    // -- create the idle timer if we are auto scrolling or overshooting.
    if (!idleTimerId
            && ((qAbs(velocity.x()) >= minVelocity)
                || (qAbs(velocity.y()) >= minVelocity)
                || overshootDist.x()
                || overshootDist.y()) ) {
        idleTimerId = startTimer(1000 / scrollsPerSecond);
    }

    lastTime.restart();
    lastType = e->type();

    bool wasMoved = moved;
    moved = false;
    qKSDebug("MR: end %d", wasMoved);
    return wasMoved; // do not swallow the mouse release, if we didn't move at all
}
예제 #20
0
void QAbstractKineticScrollerPrivate::handleIdleTimer()
{
    Q_Q(QAbstractKineticScroller);

    if (mode == QAbstractKineticScroller::PushMode && overshootDist.isNull()) {
        killTimer(idleTimerId);
        idleTimerId = 0;
        changeState(QAbstractKineticScroller::Inactive);
        return;
    }
    qKSDebug() << "idle timer - velocity: " << velocity << " overshoot: " << overshootDist;

    setScrollPositionHelper(q->scrollPosition() - overshootDist - velocity.toPoint());

    if (!overshootDist.isNull()) {
        if (moved)
            return;

        overshooting++;
        scrollTo = QPoint(-1, -1);

        /* When the overshoot has started we continue for
         * PROP_BOUNCE_STEPS more steps into the overshoot before we
         * reverse direction. The deceleration factor is calculated
         * based on the percentage distance from the first item with
         * each iteration, therefore always returning us to the
         * top/bottom most element
         */
        if (overshooting < bounceSteps) {
            velocity.setX( overshootDist.x() / maxOvershoot.x() * velocity.x() );
            velocity.setY( overshootDist.y() / maxOvershoot.y() * velocity.y() );
        } else {
            velocity.setX( -overshootDist.x() * 0.8 );
            velocity.setY( -overshootDist.y() * 0.8 );

            // ensure a minimum speed when scrolling back or else we might never return
            if (velocity.x() > -1.0 && velocity.x() < 0.0)
                velocity.setX(-1.0);
            if (velocity.x() <  1.0 && velocity.x() > 0.0)
                velocity.setX( 1.0);
            if (velocity.y() > -1.0 && velocity.y() < 0.0)
                velocity.setY(-1.0);
            if (velocity.y() <  1.0 && velocity.y() > 0.0)
                velocity.setY( 1.0);
        }

        velocity.setX( qBound((qreal)-vmaxOvershoot, velocity.x(), (qreal)vmaxOvershoot));
        velocity.setY( qBound((qreal)-vmaxOvershoot, velocity.y(), (qreal)vmaxOvershoot));

    } else if (state == QAbstractKineticScroller::AutoScrolling) {
        // Decelerate gradually when pointer is raised
        overshooting = 0;

        // in case we move to a specific point do not decelerate when arriving
        if (scrollTo.x() != -1 || scrollTo.y() != -1) {

            // -- check if target was reached
            QPoint pos = q->scrollPosition();
            QPointF  dist = QPointF(scrollTo - pos);

            qKSDebug() << "handleIdleTimer dist:" << dist << " scrollTo:" << scrollTo;

            // -- break if target was reached
            if ((velocity.x() < 0.0 && dist.x() <= 0.0) ||
                (velocity.x() > 0.0 && dist.x() >= 0.0) )
                velocity.setX(0.0);

            if ((velocity.y() < 0.0 && dist.y() <= 0.0) ||
                (velocity.y() > 0.0 && dist.y() >= 0.0) )
                velocity.setY(0.0);

            // -- break if we reached the borders
            /*
            QPoint maxPos = q->maximumScrollPosition();
            if ((0 > pos.x()-velocity.x() && velocity.x() > 0.0) ||
                (maxPos.x() < pos.x()-velocity.x() && velocity.x() < 0.0))
                velocity.setX(0.0);

            if ((0 > pos.y()-velocity.y() && velocity.y() > 0.0) ||
                (maxPos.y() < pos.y()-velocity.y() && velocity.y() < 0.0))
                velocity.setY(0.0);
            */

            if (velocity.x() == 0.0 && velocity.y() == 0.0) {
                killTimer(idleTimerId);
                idleTimerId = 0;
                changeState(QAbstractKineticScroller::Inactive);
                return;
            }

            // -- don't get too slow if target was not yet reached
            if (qAbs(velocity.x()) >= qreal(1.5))
                velocity.rx() *= deceleration;
            if (qAbs(velocity.y()) >= qreal(1.5))
                velocity.ry() *= deceleration;

        } else {
            if (!lowFrictionMode || (qAbs(velocity.x()) < qreal(0.8) * maxVelocity))
                velocity.rx() *= deceleration;
            if (!lowFrictionMode || (qAbs(velocity.y()) < qreal(0.8) * maxVelocity))
                velocity.ry() *= deceleration;

            if ((qAbs(velocity.x()) < qreal(1.0)) && (qAbs(velocity.y()) < qreal(1.0))) {
                velocity = QPointF(0, 0);
                killTimer(idleTimerId);
                idleTimerId = 0;
                changeState(QAbstractKineticScroller::Inactive);
            }
        }
    } else if (mode == QAbstractKineticScroller::AutoMode) {
        killTimer(idleTimerId);
        idleTimerId = 0;
        changeState(QAbstractKineticScroller::Inactive);
    }
}