ginebra2/ScrollHelper.cpp
changeset 16 3c88a81ff781
equal deleted inserted replaced
14:6aeb7a756187 16:3c88a81ff781
       
     1 #include <QtCore>
       
     2 #include "ScrollHelper.h"
       
     3 #include "qstmfilelogger.h"
       
     4 
       
     5 
       
     6 #define SCROLL_TIMEOUT   16
       
     7 
       
     8 namespace GVA
       
     9 {
       
    10 static const int MaxFlickDurartion = 1500;
       
    11 static const int MidFlickDurartion = 800;
       
    12 static const int MinFlickDurartion = 500;
       
    13 static const qreal MaxFlickSpeed = 2.0;
       
    14 static const qreal MidFlickSpeed = 1.2;
       
    15 static const qreal MinFlickSpeed = 0.5;
       
    16 static const qreal MaxFlickInViewportUnits = 1.0;
       
    17 static const qreal MidFlickInViewportUnits = 0.8;
       
    18 static const qreal MinFlickInViewportUnits = 0.2;
       
    19 static const qreal DefaultDecel = 0.005;
       
    20 
       
    21 ScrollHelper::ScrollHelper(QObject* scrolledWidget) : QObject(),
       
    22                                   m_scrolledWidget(scrolledWidget),
       
    23                                   m_minFlickDuration(MinFlickDurartion),
       
    24                                   m_maxFlickDuration(MaxFlickDurartion),
       
    25                                   m_midFlickDuration(MidFlickDurartion),
       
    26                                   m_scrollDelta(QPointF()),
       
    27                                   m_minFlickSpeed(MinFlickSpeed),
       
    28                                   m_maxFlickSpeed(MaxFlickSpeed),
       
    29                                   m_midFlickSpeed(MidFlickSpeed),
       
    30                                   m_maxFlickInViewportUnits(MaxFlickInViewportUnits),
       
    31                                   m_minFlickInViewportUnits(MinFlickInViewportUnits),
       
    32                                   m_midFlickInViewportUnits(MidFlickInViewportUnits),
       
    33                                   m_viewportSize(QSize()),
       
    34                                   m_lockToY(false),
       
    35                                   m_lockToX(false),
       
    36                                   m_decel(DefaultDecel)
       
    37                                   
       
    38 {
       
    39     m_scrollAnimation = new QPropertyAnimation(scrolledWidget, "widgetScrollPosition");
       
    40     connect(m_scrollAnimation, SIGNAL(finished()), this, SLOT(stopScroll()));
       
    41     connect(m_scrollAnimation, SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)),
       
    42             this, SLOT(scrollAnimationStateChanged(QAbstractAnimation::State,QAbstractAnimation::State)));
       
    43     m_scrollTimer = UiTimer::New();    
       
    44     m_scrollTimer->setPriority(50);   
       
    45     m_scrollTimer->setTimerCallback("scrollTimerCallback");
       
    46     m_easingCurve = new QEasingCurve(QEasingCurve::OutCubic);
       
    47     m_easingCurveOvershoot= new QEasingCurve(QEasingCurve::OutBack);
       
    48 
       
    49 }
       
    50 
       
    51 ScrollHelper::~ScrollHelper()
       
    52 {
       
    53     m_scrollTimer->stop();
       
    54     delete m_scrollTimer;
       
    55     delete m_easingCurve;
       
    56     delete m_easingCurveOvershoot;
       
    57 }
       
    58 
       
    59 
       
    60 void ScrollHelper::setFlickDurationLimits(int minDuration, int midDuration, int maxDuration)
       
    61 {
       
    62     m_minFlickDuration = minDuration;
       
    63     m_midFlickDuration = midDuration;
       
    64     m_maxFlickDuration = maxDuration;
       
    65 }
       
    66 
       
    67 void ScrollHelper::setFlickSpeedLimits(qreal minSpeed, qreal midSpeed, qreal maxSpeed)
       
    68 {
       
    69     m_minFlickSpeed = minSpeed;
       
    70     m_midFlickSpeed = midSpeed;
       
    71     m_maxFlickSpeed = maxSpeed;
       
    72 }
       
    73 
       
    74 void ScrollHelper::setFlickLimits(qreal minFlick, qreal midFlick, qreal maxFlick)
       
    75 {
       
    76     m_minFlickInViewportUnits = minFlick;
       
    77     m_midFlickInViewportUnits = midFlick;
       
    78     m_maxFlickInViewportUnits = maxFlick;
       
    79 }
       
    80 
       
    81 
       
    82 void ScrollHelper::setDeceleration(qreal decel)
       
    83 {
       
    84     m_decel = decel;
       
    85 }
       
    86 
       
    87 qreal ScrollHelper::getDeceleration()
       
    88 {
       
    89     return m_decel;
       
    90 }
       
    91 
       
    92 
       
    93 void ScrollHelper::scrollTimerCallback()
       
    94 {
       
    95     m_scrollTimer->stop();
       
    96     if (m_scrollState != ScrollHelper::ActiveState) {
       
    97         return;
       
    98     }
       
    99     qstmDebug() << "ScrollHelper::scrollTimerCallback. m_scrollDelta: " << m_scrollDelta <<
       
   100             ", m_scrollMode: " << m_scrollMode << "\n";
       
   101     
       
   102     QPointF speed = m_kineticSpeed;
       
   103     if (m_scrollMode == ScrollHelper::KineticScrollMode) {
       
   104         m_scrollDuration += SCROLL_TIMEOUT;
       
   105         qreal progress = (qreal)m_scrollDuration / m_scrollTotalDuration;
       
   106         if (progress > 0.0 && progress < 1.0) {
       
   107             qreal val = m_curEasingCurve->valueForProgress(progress);
       
   108             QPointF tp = m_startScrollPos + val * (m_targetScrollPos - m_startScrollPos);
       
   109             m_curScrollPos = tp;
       
   110             setScrollPos(tp);
       
   111             m_scrollTimer->start(SCROLL_TIMEOUT, this);
       
   112         }
       
   113         else {
       
   114             stopScroll();
       
   115         }
       
   116     }
       
   117     else if (m_scrollMode == ScrollHelper::PanScrollMode) {
       
   118         doScroll(m_scrollDelta);
       
   119         m_scrollDelta = QPointF();
       
   120         m_scrollTimer->start(SCROLL_TIMEOUT, this);
       
   121     }
       
   122 }
       
   123 
       
   124 
       
   125 QPointF ScrollHelper::speedForNextInterval(const QPointF& initSpeed, long timeInterval, const QPointF& decel)
       
   126 {
       
   127     QPointF speed = initSpeed;
       
   128     if (speed.y() < 0) {
       
   129         speed.ry() += (decel.y() * timeInterval);
       
   130         speed.ry() = qMin(qreal(0.0), speed.y());
       
   131     }
       
   132     else if (speed.y() > 0) {
       
   133         speed.ry() -= (decel.y() * timeInterval);
       
   134         speed.ry() = qMax(qreal(0.0), speed.y());
       
   135     }
       
   136     if (speed.x() < 0) {
       
   137         speed.rx() += (decel.x() * timeInterval);
       
   138         speed.rx() = qMin(qreal(0.0), speed.x());
       
   139     }
       
   140     else if (speed.x() > 0) {
       
   141         speed.rx() -= (decel.x() * timeInterval);
       
   142         speed.rx() = qMax(qreal(0.0), speed.x());
       
   143     }
       
   144     return speed;
       
   145 }
       
   146 
       
   147 
       
   148 void ScrollHelper::scroll(QPointF& delta)
       
   149 {
       
   150     if ( delta.x() == 0 && delta.y() == 0)  return;
       
   151 
       
   152     if (m_scrollState == ScrollHelper::IdleState) {
       
   153         //m_lockToX = (delta.y() == 0);
       
   154         //m_lockToY = (delta.x() == 0);
       
   155 
       
   156         doScroll(delta);
       
   157         m_scrollDelta = QPointF();
       
   158         m_scrollTimer->setSingleShot(true);
       
   159         m_scrollTimer->start(SCROLL_TIMEOUT, this);
       
   160     }
       
   161     else {
       
   162         if (m_lockToX) {
       
   163             delta.ry() = 0.0;
       
   164         }
       
   165         if (m_lockToY) {
       
   166             delta.rx() = 0.0;
       
   167         }
       
   168         m_scrollDelta += delta;
       
   169     }
       
   170     m_scrollMode = ScrollHelper::PanScrollMode;
       
   171     m_scrollState = ScrollHelper::ActiveState;
       
   172 }
       
   173 
       
   174 bool ScrollHelper::isScrolling()
       
   175 {
       
   176     return m_scrollState == ScrollHelper::ActiveState;
       
   177 }
       
   178 
       
   179 void ScrollHelper::doScroll(QPointF& delta)
       
   180 {
       
   181     QPointF scrollPos = getScrollPos();
       
   182     QPointF targetPos;
       
   183     targetPos = scrollPos + delta;
       
   184     setScrollPos(targetPos);
       
   185 }
       
   186 
       
   187 QPointF ScrollHelper::getScrollPos()
       
   188 {
       
   189     return m_scrolledWidget->property("widgetScrollPosition").toPointF();
       
   190 }
       
   191 
       
   192 void  ScrollHelper::setScrollPos(QPointF& pos)
       
   193 {
       
   194      m_scrolledWidget->setProperty("widgetScrollPosition", pos.toPoint());
       
   195 }
       
   196 
       
   197 QPointF ScrollHelper::getMaxScrollPos()
       
   198 {
       
   199     return m_scrolledWidget->property("maxScrollPosition").toPointF();
       
   200 }
       
   201 
       
   202 
       
   203 QSizeF ScrollHelper::viewportSize()
       
   204 {
       
   205     return m_viewportSize;
       
   206 }
       
   207 
       
   208 void ScrollHelper::setViewportSize(const QSizeF& size)
       
   209 {
       
   210     m_viewportSize = size;
       
   211 }
       
   212 
       
   213 void ScrollHelper::stopScrollNoSignal()
       
   214 {
       
   215     m_scrollTimer->stop();
       
   216     reset();    
       
   217 }
       
   218 
       
   219 void ScrollHelper::stopScroll()
       
   220 {     
       
   221     stopScrollNoSignal();
       
   222     emit scrollFinished();
       
   223 }
       
   224 
       
   225 
       
   226 void ScrollHelper::reset()
       
   227 {
       
   228     m_scrollState = ScrollHelper::IdleState;
       
   229     m_scrollMode = ScrollHelper::ReadyMode;
       
   230     m_scrollDelta = QPointF();
       
   231     m_lockToY = false;
       
   232     m_lockToX = false;
       
   233 }
       
   234 
       
   235 void ScrollHelper::panFromOvershoot()
       
   236 {
       
   237     stopScroll();
       
   238     QPointF scrollPos = getScrollPos();
       
   239     QPointF targetScrollPos = scrollPos;
       
   240     QEasingCurve::Type easingCurveType = QEasingCurve::OutCubic;
       
   241     if (clampScrollPosition(targetScrollPos)) {
       
   242         easingCurveType = QEasingCurve::OutBack;
       
   243         m_scrollMode = ScrollHelper::PanScrollMode;
       
   244         startScrollAnimation(scrollPos, targetScrollPos, 300, easingCurveType);
       
   245     }
       
   246 }
       
   247 
       
   248 bool ScrollHelper::clampScrollPosition(QPointF& scrollPos)
       
   249 {
       
   250     QPointF maxScrollPos = getMaxScrollPos();
       
   251     QPointF origPos = scrollPos;
       
   252     scrollPos.ry() = qBound(qreal(0.0), origPos.y(), maxScrollPos.y());
       
   253     scrollPos.rx() = qBound(qreal(0.0), origPos.x(), maxScrollPos.x());
       
   254     bool clampY = (scrollPos.y() != origPos.y()) &&
       
   255                   (scrollPos.y() == qreal(0.0) || scrollPos.y() == maxScrollPos.y());
       
   256 
       
   257     bool clampX = (scrollPos.x() != origPos.x()) &&
       
   258                    (scrollPos.x() == qreal(0.0) || scrollPos.x() == maxScrollPos.x());
       
   259     return (clampX || clampY);
       
   260 }
       
   261 
       
   262 
       
   263 void ScrollHelper::startScrollAnimation(QPointF& scrollPos, QPointF& targetPos,
       
   264                                         int duration, QEasingCurve::Type& easingCurveType)
       
   265 {
       
   266     if (scrollPos != targetPos) {
       
   267         m_scrollAnimation->setStartValue(scrollPos);
       
   268         m_scrollAnimation->setEndValue(targetPos);
       
   269         m_scrollAnimation->setDuration(duration);
       
   270         m_scrollAnimation->setEasingCurve(easingCurveType);
       
   271         m_scrollAnimation->start();
       
   272         m_scrollState = ScrollHelper::ActiveState;
       
   273     }
       
   274 }
       
   275 
       
   276 void ScrollHelper::scrollAnimationStateChanged(QAbstractAnimation::State newState,
       
   277                                                QAbstractAnimation::State)
       
   278 {
       
   279     switch (newState) {
       
   280         case QAbstractAnimation::Stopped:
       
   281             reset();
       
   282             break;
       
   283         case QAbstractAnimation::Running:
       
   284             break;
       
   285         default:
       
   286             break;
       
   287     }
       
   288 }
       
   289 
       
   290 
       
   291 int ScrollHelper::calcScrollDuration(const QPointF& speed, const QPointF& delta)
       
   292 {
       
   293     QPointF v1 = speed;
       
   294     int flickDuration = sqrt(delta.x() * delta.x() + delta.y() * delta.y()) /
       
   295                         sqrt(v1.x() * v1.x() + v1.y() * v1.y());
       
   296     flickDuration = qBound(m_minFlickDuration, flickDuration, m_maxFlickDuration);
       
   297     return flickDuration;
       
   298 }
       
   299 
       
   300 
       
   301 int ScrollHelper::calcScrollDuration(qreal motionFactor)
       
   302 {
       
   303     int duration = m_midFlickDuration;
       
   304     if (motionFactor == m_maxFlickInViewportUnits) {
       
   305          duration = m_maxFlickDuration;
       
   306     }
       
   307     else if (motionFactor == m_minFlickInViewportUnits) {
       
   308         duration = m_minFlickDuration;
       
   309     }
       
   310     return duration;
       
   311 }
       
   312 
       
   313 void ScrollHelper::kineticScroll(QPointF& speed)
       
   314 {
       
   315     if (m_lockToY) {
       
   316         speed.rx() = 0.0;
       
   317     }
       
   318     if (m_lockToX) {
       
   319         speed.ry() = 0.0;
       
   320     }
       
   321     
       
   322     //doScroll(m_scrollDelta);
       
   323     //stopScroll();
       
   324     
       
   325     QEasingCurve::Type easingCurveType = QEasingCurve::InOutCubic;
       
   326     
       
   327     QPointF scrollPos = getScrollPos() + m_scrollDelta;
       
   328     int flickDuration = 0;
       
   329     QPointF targetPos = calcTargetScrollPosAndDuration(speed, scrollPos, flickDuration);
       
   330     QPointF delta = targetPos - scrollPos;
       
   331 
       
   332     m_curEasingCurve = m_easingCurve;
       
   333     if (clampScrollPosition(targetPos)) {
       
   334         easingCurveType = QEasingCurve::OutBack;
       
   335         m_curEasingCurve = m_easingCurveOvershoot;
       
   336     }
       
   337     m_scrollMode = ScrollHelper::KineticScrollMode;
       
   338     qstmDebug() << "ScrollHelper::kineticScroll. initial speed: " << speed <<
       
   339             ", delta: " << delta <<
       
   340             ", flickDuration: " << flickDuration <<
       
   341             ", m_scrollDelta: " << m_scrollDelta << "\n";
       
   342     
       
   343     m_targetScrollPos = targetPos;
       
   344     m_kineticSpeed = speed;
       
   345     m_curScrollPos = scrollPos;
       
   346     m_startScrollPos = scrollPos;
       
   347     m_scrollTotalDuration = flickDuration;
       
   348     m_scrollDuration = 0; 
       
   349     m_lockToX = false;
       
   350     m_lockToY = false;
       
   351     //m_decelVec = calcTargetScrollPosAndDuration(speed, delta, flickDuration);
       
   352 
       
   353     //startScrollAnimation(scrollPos, targetPos, flickDuration, easingCurveType);
       
   354 }
       
   355 
       
   356 QPointF ScrollHelper::calcDeceleration(const QPointF& initSpeed, const QPointF& distance, long time)
       
   357 {
       
   358     return 2 * (initSpeed * time - distance) / (time * time);
       
   359 }
       
   360 
       
   361 QPointF ScrollHelper::calcTargetScrollPosAndDuration(const QPointF& speed, const QPointF& scrollPos, int& duration)
       
   362 {
       
   363     QSizeF  vpSize = viewportSize();
       
   364     QPointF targetPos = scrollPos;
       
   365     if (vpSize.isValid()) {
       
   366         qreal kX = 0.0;
       
   367         qreal kY = 0.0;
       
   368         if (speed.x() != 0.0) {
       
   369             kX = calcMotionFactor(speed.x());
       
   370         }
       
   371 
       
   372         if (speed.y() != 0.0) {
       
   373             kY = calcMotionFactor(speed.y());
       
   374         }
       
   375 
       
   376         targetPos.rx() -= kX * vpSize.width();
       
   377         targetPos.ry() -= kY * vpSize.height();
       
   378         duration = calcScrollDuration(qMax(qAbs(kX), qAbs(kY)));
       
   379     }
       
   380     return targetPos;
       
   381 }
       
   382 
       
   383 
       
   384 qreal ScrollHelper::calcMotionFactor(const qreal speed)
       
   385 {
       
   386     qreal k = 0.0;
       
   387     qreal absSpeed = qAbs(speed);
       
   388     if (absSpeed >= m_maxFlickSpeed) {
       
   389         k = m_maxFlickInViewportUnits;
       
   390     }
       
   391     else if (absSpeed < m_maxFlickSpeed && absSpeed >= m_midFlickSpeed) {
       
   392         k = m_midFlickInViewportUnits;
       
   393     }
       
   394     else {
       
   395         k = m_minFlickInViewportUnits;
       
   396     }
       
   397     if (speed < 0) {
       
   398         k = -k;
       
   399     }
       
   400     return k;
       
   401 }
       
   402 
       
   403 
       
   404 }