ginebra2/Kinetics/KineticScroller.cpp
author hgs
Fri, 06 Aug 2010 17:23:08 -0400
changeset 9 b39122337a00
permissions -rw-r--r--
201031
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
9
hgs
parents:
diff changeset
     1
/*
hgs
parents:
diff changeset
     2
* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
hgs
parents:
diff changeset
     3
* All rights reserved.
hgs
parents:
diff changeset
     4
*
hgs
parents:
diff changeset
     5
* This program is free software: you can redistribute it and/or modify
hgs
parents:
diff changeset
     6
* it under the terms of the GNU Lesser General Public License as published by
hgs
parents:
diff changeset
     7
* the Free Software Foundation, version 2.1 of the License.
hgs
parents:
diff changeset
     8
*
hgs
parents:
diff changeset
     9
* This program is distributed in the hope that it will be useful,
hgs
parents:
diff changeset
    10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
hgs
parents:
diff changeset
    11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
hgs
parents:
diff changeset
    12
* GNU Lesser General Public License for more details.
hgs
parents:
diff changeset
    13
*
hgs
parents:
diff changeset
    14
* You should have received a copy of the GNU Lesser General Public License
hgs
parents:
diff changeset
    15
* along with this program.  If not,
hgs
parents:
diff changeset
    16
* see "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html/".
hgs
parents:
diff changeset
    17
*
hgs
parents:
diff changeset
    18
* Description:
hgs
parents:
diff changeset
    19
*
hgs
parents:
diff changeset
    20
*/
hgs
parents:
diff changeset
    21
hgs
parents:
diff changeset
    22
#include "KineticScroller.h"
hgs
parents:
diff changeset
    23
hgs
parents:
diff changeset
    24
#include <QTimerEvent>
hgs
parents:
diff changeset
    25
hgs
parents:
diff changeset
    26
namespace GVA {
hgs
parents:
diff changeset
    27
hgs
parents:
diff changeset
    28
KineticScroller::KineticScroller(KineticScrollable* scrollableView)
hgs
parents:
diff changeset
    29
    : m_scrollableView(scrollableView)
hgs
parents:
diff changeset
    30
    , m_mode(KineticScroller::AutoMode)
hgs
parents:
diff changeset
    31
    , m_state(KineticScrollable::Inactive)
hgs
parents:
diff changeset
    32
    , m_overshootPolicy(KineticScroller::OvershootWhenScrollable)
hgs
parents:
diff changeset
    33
    , m_motion(QPoint(0,0))
hgs
parents:
diff changeset
    34
    , m_move(false)
hgs
parents:
diff changeset
    35
    , m_bounceSteps(3)
hgs
parents:
diff changeset
    36
    , m_maxOvershoot(150, 150)
hgs
parents:
diff changeset
    37
    , m_vmaxOvershoot(130)
hgs
parents:
diff changeset
    38
    , m_overshootDist(0, 0)
hgs
parents:
diff changeset
    39
    , m_overshooting(0)
hgs
parents:
diff changeset
    40
    , m_minVelocity(10)
hgs
parents:
diff changeset
    41
    , m_maxVelocity(3500)
hgs
parents:
diff changeset
    42
    , m_deceleration(0.85)
hgs
parents:
diff changeset
    43
    , m_scrollsPerSecond(20)
hgs
parents:
diff changeset
    44
    , m_idleTimerId(0)
hgs
parents:
diff changeset
    45
    , m_scrollTimerId(0)
hgs
parents:
diff changeset
    46
    , m_inactivityTimerId(0)
hgs
parents:
diff changeset
    47
{}
hgs
parents:
diff changeset
    48
hgs
parents:
diff changeset
    49
KineticScroller::~KineticScroller()
hgs
parents:
diff changeset
    50
{}
hgs
parents:
diff changeset
    51
hgs
parents:
diff changeset
    52
KineticScroller::OvershootPolicy KineticScroller::overshootPolicy() const
hgs
parents:
diff changeset
    53
{
hgs
parents:
diff changeset
    54
    return m_overshootPolicy;
hgs
parents:
diff changeset
    55
}
hgs
parents:
diff changeset
    56
hgs
parents:
diff changeset
    57
void KineticScroller::setOvershootPolicy(KineticScroller::OvershootPolicy policy)
hgs
parents:
diff changeset
    58
{
hgs
parents:
diff changeset
    59
    m_overshootPolicy  = policy;
hgs
parents:
diff changeset
    60
}
hgs
parents:
diff changeset
    61
hgs
parents:
diff changeset
    62
qreal KineticScroller::decelerationFactor() const
hgs
parents:
diff changeset
    63
{
hgs
parents:
diff changeset
    64
    return m_deceleration;
hgs
parents:
diff changeset
    65
}
hgs
parents:
diff changeset
    66
hgs
parents:
diff changeset
    67
void KineticScroller::setDecelerationFactor(qreal f)
hgs
parents:
diff changeset
    68
{
hgs
parents:
diff changeset
    69
    m_deceleration = f;
hgs
parents:
diff changeset
    70
}
hgs
parents:
diff changeset
    71
hgs
parents:
diff changeset
    72
int KineticScroller::scrollsPerSecond() const
hgs
parents:
diff changeset
    73
{
hgs
parents:
diff changeset
    74
    return m_scrollsPerSecond;
hgs
parents:
diff changeset
    75
}
hgs
parents:
diff changeset
    76
hgs
parents:
diff changeset
    77
void KineticScroller::setScrollsPerSecond(int sps)
hgs
parents:
diff changeset
    78
{
hgs
parents:
diff changeset
    79
    m_scrollsPerSecond = qBound(1, sps, 100);
hgs
parents:
diff changeset
    80
}
hgs
parents:
diff changeset
    81
hgs
parents:
diff changeset
    82
void KineticScroller::doPan(QPoint delta)
hgs
parents:
diff changeset
    83
{
hgs
parents:
diff changeset
    84
    //stop inactivity timer 
hgs
parents:
diff changeset
    85
    if (m_inactivityTimerId) {
hgs
parents:
diff changeset
    86
        killTimer(m_inactivityTimerId);
hgs
parents:
diff changeset
    87
        m_inactivityTimerId = 0;
hgs
parents:
diff changeset
    88
    }
hgs
parents:
diff changeset
    89
hgs
parents:
diff changeset
    90
    //overshoot only if OvershootAlwaysOn is set
hgs
parents:
diff changeset
    91
    QPoint maxPos = m_scrollableView->maximumScrollPosition();
hgs
parents:
diff changeset
    92
    bool alwaysOvershoot = (overshootPolicy() == KineticScroller::OvershootAlwaysOn);
hgs
parents:
diff changeset
    93
hgs
parents:
diff changeset
    94
    if (!maxPos.x() && !alwaysOvershoot) {
hgs
parents:
diff changeset
    95
        delta.setX(0);
hgs
parents:
diff changeset
    96
    }
hgs
parents:
diff changeset
    97
    if (!maxPos.y() && !alwaysOvershoot) {
hgs
parents:
diff changeset
    98
        delta.setY(0);
hgs
parents:
diff changeset
    99
    }
hgs
parents:
diff changeset
   100
hgs
parents:
diff changeset
   101
    if (m_scrollTimerId)
hgs
parents:
diff changeset
   102
        m_motion += delta;
hgs
parents:
diff changeset
   103
    else {
hgs
parents:
diff changeset
   104
        m_mode = KineticScroller::PushMode;
hgs
parents:
diff changeset
   105
        changeState(KineticScrollable::Pushing);
hgs
parents:
diff changeset
   106
        // we do not delay the first event but the next ones
hgs
parents:
diff changeset
   107
        setScrollPositionHelper(m_scrollableView->scrollPosition() - m_overshootDist - delta);
hgs
parents:
diff changeset
   108
        m_motion = QPoint(0, 0);
hgs
parents:
diff changeset
   109
        m_scrollTimerId = startTimer(1000 / KineticScroller::MotionEventsPerSecond);
hgs
parents:
diff changeset
   110
    }
hgs
parents:
diff changeset
   111
}
hgs
parents:
diff changeset
   112
hgs
parents:
diff changeset
   113
void KineticScroller::doFlick(QPointF velocity)
hgs
parents:
diff changeset
   114
{
hgs
parents:
diff changeset
   115
    //stop inactivity timer 
hgs
parents:
diff changeset
   116
    if (m_inactivityTimerId) {
hgs
parents:
diff changeset
   117
        killTimer(m_inactivityTimerId);
hgs
parents:
diff changeset
   118
        m_inactivityTimerId = 0;
hgs
parents:
diff changeset
   119
    }
hgs
parents:
diff changeset
   120
hgs
parents:
diff changeset
   121
    m_motion = QPoint(0,0);
hgs
parents:
diff changeset
   122
    m_mode = KineticScroller::AutoMode;
hgs
parents:
diff changeset
   123
    changeState(KineticScrollable::AutoScrolling);
hgs
parents:
diff changeset
   124
    m_velocity = velocity;
hgs
parents:
diff changeset
   125
    m_idleTimerId = startTimer(1000 / m_scrollsPerSecond);
hgs
parents:
diff changeset
   126
}
hgs
parents:
diff changeset
   127
hgs
parents:
diff changeset
   128
void KineticScroller::stop()
hgs
parents:
diff changeset
   129
{
hgs
parents:
diff changeset
   130
    if (m_inactivityTimerId) {
hgs
parents:
diff changeset
   131
        killTimer(m_inactivityTimerId);
hgs
parents:
diff changeset
   132
        m_inactivityTimerId = 0;
hgs
parents:
diff changeset
   133
    }
hgs
parents:
diff changeset
   134
hgs
parents:
diff changeset
   135
    if (m_scrollTimerId) {
hgs
parents:
diff changeset
   136
        killTimer(m_scrollTimerId);
hgs
parents:
diff changeset
   137
        m_scrollTimerId = 0;
hgs
parents:
diff changeset
   138
        setScrollPositionHelper(m_scrollableView->scrollPosition() - m_overshootDist - m_motion);
hgs
parents:
diff changeset
   139
    }
hgs
parents:
diff changeset
   140
hgs
parents:
diff changeset
   141
    if (m_idleTimerId) {
hgs
parents:
diff changeset
   142
        killTimer(m_idleTimerId);
hgs
parents:
diff changeset
   143
        m_idleTimerId = 0;
hgs
parents:
diff changeset
   144
    }
hgs
parents:
diff changeset
   145
    
hgs
parents:
diff changeset
   146
    if (m_overshootDist.x()) {
hgs
parents:
diff changeset
   147
        m_overshooting = m_bounceSteps; 
hgs
parents:
diff changeset
   148
        m_velocity.setX(-m_overshootDist.x() * qreal(0.8));
hgs
parents:
diff changeset
   149
    }
hgs
parents:
diff changeset
   150
    if (m_overshootDist.y()) {
hgs
parents:
diff changeset
   151
        m_overshooting = m_bounceSteps; 
hgs
parents:
diff changeset
   152
        m_velocity.setY(-m_overshootDist.y() * qreal(0.8));
hgs
parents:
diff changeset
   153
    }
hgs
parents:
diff changeset
   154
hgs
parents:
diff changeset
   155
    //if page is overshoot doFlick() to switch back
hgs
parents:
diff changeset
   156
    if (!m_overshootDist.isNull()) {    
hgs
parents:
diff changeset
   157
        doFlick(m_velocity);
hgs
parents:
diff changeset
   158
        return;
hgs
parents:
diff changeset
   159
    }
hgs
parents:
diff changeset
   160
    
hgs
parents:
diff changeset
   161
    m_motion = QPoint(0, 0);
hgs
parents:
diff changeset
   162
    m_velocity = QPointF(0,0);
hgs
parents:
diff changeset
   163
    m_mode = KineticScroller::AutoMode;
hgs
parents:
diff changeset
   164
    changeState(KineticScrollable::Inactive);
hgs
parents:
diff changeset
   165
}
hgs
parents:
diff changeset
   166
hgs
parents:
diff changeset
   167
void KineticScroller::changeState(KineticScrollable::State newState)
hgs
parents:
diff changeset
   168
{
hgs
parents:
diff changeset
   169
    if (newState != m_state) {
hgs
parents:
diff changeset
   170
        KineticScrollable::State oldState = m_state;
hgs
parents:
diff changeset
   171
        m_state = newState;
hgs
parents:
diff changeset
   172
        m_scrollableView->stateChanged(oldState, newState);
hgs
parents:
diff changeset
   173
    }
hgs
parents:
diff changeset
   174
}
hgs
parents:
diff changeset
   175
hgs
parents:
diff changeset
   176
void KineticScroller::reset()
hgs
parents:
diff changeset
   177
{
hgs
parents:
diff changeset
   178
    changeState(KineticScrollable::Inactive);
hgs
parents:
diff changeset
   179
hgs
parents:
diff changeset
   180
    if (m_idleTimerId)
hgs
parents:
diff changeset
   181
        killTimer(m_idleTimerId);
hgs
parents:
diff changeset
   182
hgs
parents:
diff changeset
   183
    m_idleTimerId = 0;
hgs
parents:
diff changeset
   184
hgs
parents:
diff changeset
   185
    if (m_scrollTimerId)
hgs
parents:
diff changeset
   186
        killTimer(m_scrollTimerId);
hgs
parents:
diff changeset
   187
hgs
parents:
diff changeset
   188
    m_scrollTimerId = 0;
hgs
parents:
diff changeset
   189
hgs
parents:
diff changeset
   190
    if (m_inactivityTimerId)
hgs
parents:
diff changeset
   191
        killTimer(m_inactivityTimerId);
hgs
parents:
diff changeset
   192
    m_inactivityTimerId = 0;
hgs
parents:
diff changeset
   193
hgs
parents:
diff changeset
   194
    m_velocity = QPointF(0, 0);
hgs
parents:
diff changeset
   195
    m_overshootDist = QPoint(0, 0);
hgs
parents:
diff changeset
   196
    m_overshooting = 0;
hgs
parents:
diff changeset
   197
}
hgs
parents:
diff changeset
   198
hgs
parents:
diff changeset
   199
void KineticScroller::setScrollPositionHelper(const QPoint &pos)
hgs
parents:
diff changeset
   200
{
hgs
parents:
diff changeset
   201
    QPoint maxPos = m_scrollableView->maximumScrollPosition();
hgs
parents:
diff changeset
   202
hgs
parents:
diff changeset
   203
    QPoint clampedPos;
hgs
parents:
diff changeset
   204
    clampedPos.setX(qBound(0, pos.x(), maxPos.x()));
hgs
parents:
diff changeset
   205
    clampedPos.setY(qBound(0, pos.y(), maxPos.y()));
hgs
parents:
diff changeset
   206
hgs
parents:
diff changeset
   207
    bool alwaysOvershoot = (m_overshootPolicy == KineticScroller::OvershootAlwaysOn);
hgs
parents:
diff changeset
   208
    int overshootX = (maxPos.x() || alwaysOvershoot) ? clampedPos.x() - pos.x() : 0;
hgs
parents:
diff changeset
   209
    int overshootY = (maxPos.y() || alwaysOvershoot) ? clampedPos.y() - pos.y() : 0;
hgs
parents:
diff changeset
   210
hgs
parents:
diff changeset
   211
    m_overshootDist.setX(qBound(-m_maxOvershoot.x(), overshootX, m_maxOvershoot.x()));
hgs
parents:
diff changeset
   212
    m_overshootDist.setY(qBound(-m_maxOvershoot.y(), overshootY, m_maxOvershoot.y()));
hgs
parents:
diff changeset
   213
hgs
parents:
diff changeset
   214
    m_scrollableView->setScrollPosition(clampedPos
hgs
parents:
diff changeset
   215
                                        , m_overshootPolicy == KineticScroller::OvershootAlwaysOff
hgs
parents:
diff changeset
   216
                                                               ? QPoint()
hgs
parents:
diff changeset
   217
                                                               : m_overshootDist);
hgs
parents:
diff changeset
   218
}
hgs
parents:
diff changeset
   219
hgs
parents:
diff changeset
   220
void KineticScroller::handleScrollTimer()
hgs
parents:
diff changeset
   221
{
hgs
parents:
diff changeset
   222
    if (!m_motion.isNull())
hgs
parents:
diff changeset
   223
        setScrollPositionHelper(m_scrollableView->scrollPosition() - m_overshootDist - m_motion);
hgs
parents:
diff changeset
   224
hgs
parents:
diff changeset
   225
    killTimer(m_scrollTimerId);
hgs
parents:
diff changeset
   226
    m_scrollTimerId = 0;
hgs
parents:
diff changeset
   227
hgs
parents:
diff changeset
   228
    m_inactivityTimerId = startTimer(300);
hgs
parents:
diff changeset
   229
}
hgs
parents:
diff changeset
   230
hgs
parents:
diff changeset
   231
void KineticScroller::handleIdleTimer()
hgs
parents:
diff changeset
   232
{
hgs
parents:
diff changeset
   233
    if (m_mode == KineticScroller::PushMode && m_overshootDist.isNull()) {
hgs
parents:
diff changeset
   234
        stop();
hgs
parents:
diff changeset
   235
        return;
hgs
parents:
diff changeset
   236
    }
hgs
parents:
diff changeset
   237
 
hgs
parents:
diff changeset
   238
    setScrollPositionHelper(m_scrollableView->scrollPosition() - m_overshootDist - m_velocity.toPoint());
hgs
parents:
diff changeset
   239
hgs
parents:
diff changeset
   240
    if (!m_overshootDist.isNull()) {
hgs
parents:
diff changeset
   241
        m_overshooting++;
hgs
parents:
diff changeset
   242
       
hgs
parents:
diff changeset
   243
        /* When the overshoot has started we continue for
hgs
parents:
diff changeset
   244
         * PROP_BOUNCE_STEPS more steps into the overshoot before we
hgs
parents:
diff changeset
   245
         * reverse direction. The deceleration factor is calculated
hgs
parents:
diff changeset
   246
         * based on the percentage distance from the first item with
hgs
parents:
diff changeset
   247
         * each iteration, therefore always returning us to the
hgs
parents:
diff changeset
   248
         * top/bottom most element
hgs
parents:
diff changeset
   249
         */
hgs
parents:
diff changeset
   250
        if (m_overshooting < m_bounceSteps) {
hgs
parents:
diff changeset
   251
            m_velocity.setX( m_overshootDist.x() / m_maxOvershoot.x() * m_velocity.x() );
hgs
parents:
diff changeset
   252
            m_velocity.setY( m_overshootDist.y() / m_maxOvershoot.y() * m_velocity.y() );
hgs
parents:
diff changeset
   253
        } else {
hgs
parents:
diff changeset
   254
            m_velocity.setX( -m_overshootDist.x() * 0.8 );
hgs
parents:
diff changeset
   255
            m_velocity.setY( -m_overshootDist.y() * 0.8 );
hgs
parents:
diff changeset
   256
hgs
parents:
diff changeset
   257
            // ensure a minimum speed when scrolling back or else we might never return
hgs
parents:
diff changeset
   258
            if (m_velocity.x() > -1.0 && m_velocity.x() < 0.0)
hgs
parents:
diff changeset
   259
                m_velocity.setX(-1.0);
hgs
parents:
diff changeset
   260
            if (m_velocity.x() <  1.0 && m_velocity.x() > 0.0)
hgs
parents:
diff changeset
   261
                m_velocity.setX( 1.0);
hgs
parents:
diff changeset
   262
            if (m_velocity.y() > -1.0 && m_velocity.y() < 0.0)
hgs
parents:
diff changeset
   263
                m_velocity.setY(-1.0);
hgs
parents:
diff changeset
   264
            if (m_velocity.y() <  1.0 && m_velocity.y() > 0.0)
hgs
parents:
diff changeset
   265
                m_velocity.setY( 1.0);
hgs
parents:
diff changeset
   266
        }
hgs
parents:
diff changeset
   267
hgs
parents:
diff changeset
   268
        m_velocity.setX( qBound((qreal)-m_vmaxOvershoot, m_velocity.x(), (qreal)m_vmaxOvershoot));
hgs
parents:
diff changeset
   269
        m_velocity.setY( qBound((qreal)-m_vmaxOvershoot, m_velocity.y(), (qreal)m_vmaxOvershoot));
hgs
parents:
diff changeset
   270
hgs
parents:
diff changeset
   271
    } else if (m_state == KineticScrollable::AutoScrolling) {
hgs
parents:
diff changeset
   272
        // Decelerate gradually when pointer is raised
hgs
parents:
diff changeset
   273
        m_overshooting = 0;
hgs
parents:
diff changeset
   274
hgs
parents:
diff changeset
   275
        if (qAbs(m_velocity.x()) < qreal(0.8) * m_maxVelocity)
hgs
parents:
diff changeset
   276
            m_velocity.rx() *= m_deceleration;
hgs
parents:
diff changeset
   277
        if (qAbs(m_velocity.y()) < qreal(0.8) * m_maxVelocity)
hgs
parents:
diff changeset
   278
            m_velocity.ry() *= m_deceleration;
hgs
parents:
diff changeset
   279
hgs
parents:
diff changeset
   280
        if ((qAbs(m_velocity.x()) < qreal(1.0)) && (qAbs(m_velocity.y()) < qreal(1.0))) 
hgs
parents:
diff changeset
   281
            stop();
hgs
parents:
diff changeset
   282
      
hgs
parents:
diff changeset
   283
    } else if (m_mode == KineticScroller::AutoMode) 
hgs
parents:
diff changeset
   284
          stop();
hgs
parents:
diff changeset
   285
}
hgs
parents:
diff changeset
   286
hgs
parents:
diff changeset
   287
void KineticScroller::timerEvent(QTimerEvent *ev)
hgs
parents:
diff changeset
   288
{
hgs
parents:
diff changeset
   289
    if (ev->timerId() == m_idleTimerId)
hgs
parents:
diff changeset
   290
        handleIdleTimer();
hgs
parents:
diff changeset
   291
    else if (ev->timerId() == m_scrollTimerId)
hgs
parents:
diff changeset
   292
        handleScrollTimer();
hgs
parents:
diff changeset
   293
    else if (ev->timerId() == m_inactivityTimerId) {
hgs
parents:
diff changeset
   294
        //Do not move to inactive state if page is still moving  
hgs
parents:
diff changeset
   295
        if (!m_idleTimerId && !m_scrollTimerId)
hgs
parents:
diff changeset
   296
            stop();
hgs
parents:
diff changeset
   297
    }
hgs
parents:
diff changeset
   298
}
hgs
parents:
diff changeset
   299
hgs
parents:
diff changeset
   300
}//namespace GVA