src/corelib/animation/qabstractanimation.cpp
changeset 3 41300fa6a67c
parent 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
2:56cd8111b7f7 3:41300fa6a67c
    48     The class defines the functions for the functionality shared by
    48     The class defines the functions for the functionality shared by
    49     all animations. By inheriting this class, you can create custom
    49     all animations. By inheriting this class, you can create custom
    50     animations that plug into the rest of the animation framework.
    50     animations that plug into the rest of the animation framework.
    51 
    51 
    52     The progress of an animation is given by its current time
    52     The progress of an animation is given by its current time
    53     (currentTime()), which is measured in milliseconds from the start
    53     (currentLoopTime()), which is measured in milliseconds from the start
    54     of the animation (0) to its end (duration()). The value is updated
    54     of the animation (0) to its end (duration()). The value is updated
    55     automatically while the animation is running. It can also be set
    55     automatically while the animation is running. It can also be set
    56     directly with setCurrentTime().
    56     directly with setCurrentTime().
    57 
    57 
    58     At any point an animation is in one of three states:
    58     At any point an animation is in one of three states:
   113 
   113 
   114     \sa stateChanged()
   114     \sa stateChanged()
   115 */
   115 */
   116 
   116 
   117 /*!
   117 /*!
   118     \fn QAbstractAnimation::stateChanged(QAbstractAnimation::State oldState, QAbstractAnimation::State newState)
   118     \fn QAbstractAnimation::stateChanged(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
   119 
   119 
   120     QAbstractAnimation emits this signal whenever the state of the animation has
   120     QAbstractAnimation emits this signal whenever the state of the animation has
   121     changed from \a oldState to \a newState. This signal is emitted after the virtual
   121     changed from \a oldState to \a newState. This signal is emitted after the virtual
   122     updateState() function is called.
   122     updateState() function is called.
   123 
   123 
   155 #include <QtCore/qpointer.h>
   155 #include <QtCore/qpointer.h>
   156 
   156 
   157 #ifndef QT_NO_ANIMATION
   157 #ifndef QT_NO_ANIMATION
   158 
   158 
   159 #define DEFAULT_TIMER_INTERVAL 16
   159 #define DEFAULT_TIMER_INTERVAL 16
   160 
       
   161 #ifdef Q_WS_WIN
       
   162     /// Fix for Qt 4.7
       
   163     //on windows if you're currently dragging a widget an inner eventloop was started by the system
       
   164     //to make sure that this timer is getting fired, we need to make sure to use the system timers
       
   165     //that will send a WM_TIMER event. We do that by settings the timer interval to 11
       
   166     //It is 16 because QEventDispatcherWin32Private::registerTimer specifically checks if the interval
       
   167     //is greater than 11 to determine if it should use a system timer (or the multimedia timer).
       
   168 #define STARTSTOP_TIMER_DELAY 16
       
   169 #else
       
   170 #define STARTSTOP_TIMER_DELAY 0
   160 #define STARTSTOP_TIMER_DELAY 0
   171 #endif
       
   172 
       
   173 
   161 
   174 QT_BEGIN_NAMESPACE
   162 QT_BEGIN_NAMESPACE
   175 
   163 
   176 Q_GLOBAL_STATIC(QThreadStorage<QUnifiedTimer *>, unifiedTimer)
   164 Q_GLOBAL_STATIC(QThreadStorage<QUnifiedTimer *>, unifiedTimer)
   177 
   165 
   178 QUnifiedTimer::QUnifiedTimer() :
   166 QUnifiedTimer::QUnifiedTimer() :
   179     QObject(), lastTick(0), timingInterval(DEFAULT_TIMER_INTERVAL),
   167     QObject(), lastTick(0), timingInterval(DEFAULT_TIMER_INTERVAL),
   180     currentAnimationIdx(0), consistentTiming(false), isPauseTimerActive(false),
   168     currentAnimationIdx(0), consistentTiming(false), slowMode(false),
   181     runningLeafAnimations(0)
   169     isPauseTimerActive(false), runningLeafAnimations(0)
   182 {
   170 {
   183 }
   171 }
   184 
   172 
   185 QUnifiedTimer *QUnifiedTimer::instance()
   173 QUnifiedTimer *QUnifiedTimer::instance()
   186 {
   174 {
   192         inst = unifiedTimer()->localData();
   180         inst = unifiedTimer()->localData();
   193     }
   181     }
   194     return inst;
   182     return inst;
   195 }
   183 }
   196 
   184 
   197 void QUnifiedTimer::ensureTimerUpdate(QAbstractAnimation *animation)
   185 void QUnifiedTimer::ensureTimerUpdate()
   198 {
   186 {
   199     if (isPauseTimerActive) {
   187     if (isPauseTimerActive)
   200         updateAnimationsTime();
   188         updateAnimationsTime();
   201     } else {
       
   202         // this code is needed when ensureTimerUpdate is called from setState because we update
       
   203         // the currentTime when an animation starts running (otherwise we could remove it)
       
   204         animation->setCurrentTime(animation->currentTime());
       
   205     }
       
   206 }
   189 }
   207 
   190 
   208 void QUnifiedTimer::updateAnimationsTime()
   191 void QUnifiedTimer::updateAnimationsTime()
   209 {
   192 {
   210     // ignore consistentTiming in case the pause timer is active
   193     // ignore consistentTiming in case the pause timer is active
   211     const int delta = (consistentTiming && !isPauseTimerActive) ?
   194     int delta = (consistentTiming && !isPauseTimerActive) ?
   212                         timingInterval : time.elapsed() - lastTick;
   195                         timingInterval : time.elapsed() - lastTick;
       
   196     if (slowMode)
       
   197         delta /= 5;
   213     lastTick = time.elapsed();
   198     lastTick = time.elapsed();
   214 
   199 
   215     //we make sure we only call update time if the time has actually changed
   200     //we make sure we only call update time if the time has actually changed
   216     //it might happen in some cases that the time doesn't change because events are delayed
   201     //it might happen in some cases that the time doesn't change because events are delayed
   217     //when the CPU load is high
   202     //when the CPU load is high
   228 
   213 
   229 void QUnifiedTimer::restartAnimationTimer()
   214 void QUnifiedTimer::restartAnimationTimer()
   230 {
   215 {
   231     if (runningLeafAnimations == 0 && !runningPauseAnimations.isEmpty()) {
   216     if (runningLeafAnimations == 0 && !runningPauseAnimations.isEmpty()) {
   232         int closestTimeToFinish = closestPauseAnimationTimeToFinish();
   217         int closestTimeToFinish = closestPauseAnimationTimeToFinish();
       
   218         if (closestTimeToFinish < 0) {
       
   219             qDebug() << runningPauseAnimations;
       
   220             qDebug() << closestPauseAnimationTimeToFinish();
       
   221         }
   233         animationTimer.start(closestTimeToFinish, this);
   222         animationTimer.start(closestTimeToFinish, this);
   234         isPauseTimerActive = true;
   223         isPauseTimerActive = true;
   235     } else if (!animationTimer.isActive() || isPauseTimerActive) {
   224     } else if (!animationTimer.isActive() || isPauseTimerActive) {
   236         animationTimer.start(timingInterval, this);
   225         animationTimer.start(timingInterval, this);
   237         isPauseTimerActive = false;
   226         isPauseTimerActive = false;
   238     }
   227     }
   239 }
   228 }
   240 
   229 
   241 void QUnifiedTimer::timerEvent(QTimerEvent *event)
   230 void QUnifiedTimer::timerEvent(QTimerEvent *event)
   242 {
   231 {
   243     if (event->timerId() == startStopAnimationTimer.timerId()) {
   232     //in the case of consistent timing we make sure the orders in which events come is always the same
       
   233    //for that purpose we do as if the startstoptimer would always fire before the animation timer
       
   234     if ((consistentTiming && startStopAnimationTimer.isActive()) ||
       
   235         event->timerId() == startStopAnimationTimer.timerId()) {
   244         startStopAnimationTimer.stop();
   236         startStopAnimationTimer.stop();
   245 
   237 
   246         //we transfer the waiting animations into the "really running" state
   238         //we transfer the waiting animations into the "really running" state
   247         animations += animationsToStart;
   239         animations += animationsToStart;
   248         animationsToStart.clear();
   240         animationsToStart.clear();
   256             if (!time.isValid()) {
   248             if (!time.isValid()) {
   257                 lastTick = 0;
   249                 lastTick = 0;
   258                 time.start();
   250                 time.start();
   259             }
   251             }
   260         }
   252         }
   261     } else if (event->timerId() == animationTimer.timerId()) {
   253     }
       
   254 
       
   255     if (event->timerId() == animationTimer.timerId()) {
   262         // update current time on all top level animations
   256         // update current time on all top level animations
   263         updateAnimationsTime();
   257         updateAnimationsTime();
   264         restartAnimationTimer();
   258         restartAnimationTimer();
   265     }
   259     }
   266 }
   260 }
   302 void QUnifiedTimer::registerRunningAnimation(QAbstractAnimation *animation)
   296 void QUnifiedTimer::registerRunningAnimation(QAbstractAnimation *animation)
   303 {
   297 {
   304     if (QAbstractAnimationPrivate::get(animation)->isGroup)
   298     if (QAbstractAnimationPrivate::get(animation)->isGroup)
   305         return;
   299         return;
   306 
   300 
   307     if (QAbstractAnimationPrivate::get(animation)->isPause)
   301     if (QAbstractAnimationPrivate::get(animation)->isPause) {
       
   302         if (animation->duration() == -1)
       
   303             qDebug() << "toto";
   308         runningPauseAnimations << animation;
   304         runningPauseAnimations << animation;
   309     else
   305     } else
   310         runningLeafAnimations++;
   306         runningLeafAnimations++;
   311 }
   307 }
   312 
   308 
   313 void QUnifiedTimer::unregisterRunningAnimation(QAbstractAnimation *animation)
   309 void QUnifiedTimer::unregisterRunningAnimation(QAbstractAnimation *animation)
   314 {
   310 {
   328     for (int i = 0; i < runningPauseAnimations.size(); ++i) {
   324     for (int i = 0; i < runningPauseAnimations.size(); ++i) {
   329         QAbstractAnimation *animation = runningPauseAnimations.at(i);
   325         QAbstractAnimation *animation = runningPauseAnimations.at(i);
   330         int timeToFinish;
   326         int timeToFinish;
   331 
   327 
   332         if (animation->direction() == QAbstractAnimation::Forward)
   328         if (animation->direction() == QAbstractAnimation::Forward)
   333             timeToFinish = animation->duration() - animation->currentTime();
   329             timeToFinish = animation->duration() - animation->currentLoopTime();
   334         else
   330         else
   335             timeToFinish = animation->currentTime();
   331             timeToFinish = animation->currentLoopTime();
   336 
   332 
   337         if (timeToFinish < closestTimeToFinish)
   333         if (timeToFinish < closestTimeToFinish)
   338             closestTimeToFinish = timeToFinish;
   334             closestTimeToFinish = timeToFinish;
   339     }
   335     }
   340     return closestTimeToFinish;
   336     return closestTimeToFinish;
   362     }
   358     }
   363 
   359 
   364     state = newState;
   360     state = newState;
   365     QWeakPointer<QAbstractAnimation> guard(q);
   361     QWeakPointer<QAbstractAnimation> guard(q);
   366 
   362 
   367     q->updateState(oldState, newState);
   363     //(un)registration of the animation must always happen before calls to
   368     if (!guard)
   364     //virtual function (updateState) to ensure a correct state of the timer
   369         return;
   365     bool isTopLevel = !group || group->state() == QAbstractAnimation::Stopped;
   370 
   366     if (oldState == QAbstractAnimation::Running) {
   371     //this is to be safe if updateState changes the state
   367         if (newState == QAbstractAnimation::Paused && hasRegisteredTimer)
   372     if (state == oldState)
   368             QUnifiedTimer::instance()->ensureTimerUpdate();
       
   369         //the animation, is not running any more
       
   370         QUnifiedTimer::instance()->unregisterAnimation(q);
       
   371     } else if (newState == QAbstractAnimation::Running) {
       
   372         QUnifiedTimer::instance()->registerAnimation(q, isTopLevel);
       
   373     }
       
   374 
       
   375     q->updateState(newState, oldState);
       
   376     if (!guard || newState != state) //this is to be safe if updateState changes the state
   373         return;
   377         return;
   374 
   378 
   375     // Notify state change
   379     // Notify state change
   376     emit q->stateChanged(oldState, newState);
   380     emit q->stateChanged(newState, oldState);
   377     if (!guard)
   381     if (!guard || newState != state) //this is to be safe if updateState changes the state
   378         return;
   382         return;
   379 
   383 
   380     switch (state) {
   384     switch (state) {
   381     case QAbstractAnimation::Paused:
   385     case QAbstractAnimation::Paused:
   382         if (hasRegisteredTimer)
       
   383             // currentTime needs to be updated if pauseTimer is active
       
   384             QUnifiedTimer::instance()->ensureTimerUpdate(q);
       
   385         if (!guard)
       
   386             return;
       
   387         //here we're sure that we were in running state before and that the
       
   388         //animation is currently registered
       
   389         QUnifiedTimer::instance()->unregisterAnimation(q);
       
   390         break;
   386         break;
   391     case QAbstractAnimation::Running:
   387     case QAbstractAnimation::Running:
   392         {
   388         {
   393             bool isTopLevel = !group || group->state() == QAbstractAnimation::Stopped;
       
   394             QUnifiedTimer::instance()->registerAnimation(q, isTopLevel);
       
   395 
   389 
   396             // this ensures that the value is updated now that the animation is running
   390             // this ensures that the value is updated now that the animation is running
   397             if (oldState == QAbstractAnimation::Stopped) {
   391             if (oldState == QAbstractAnimation::Stopped) {
   398                 if (isTopLevel)
   392                 if (isTopLevel) {
   399                     // currentTime needs to be updated if pauseTimer is active
   393                     // currentTime needs to be updated if pauseTimer is active
   400                     QUnifiedTimer::instance()->ensureTimerUpdate(q);
   394                     QUnifiedTimer::instance()->ensureTimerUpdate();
       
   395                     q->setCurrentTime(totalCurrentTime);
       
   396                 }
   401             }
   397             }
   402         }
   398         }
   403         break;
   399         break;
   404     case QAbstractAnimation::Stopped:
   400     case QAbstractAnimation::Stopped:
   405         // Leave running state.
   401         // Leave running state.
   406         int dura = q->duration();
   402         int dura = q->duration();
   407         if (!guard)
       
   408             return;
       
   409 
   403 
   410         if (deleteWhenStopped)
   404         if (deleteWhenStopped)
   411             q->deleteLater();
   405             q->deleteLater();
   412 
       
   413         if (oldState == QAbstractAnimation::Running)
       
   414             QUnifiedTimer::instance()->unregisterAnimation(q);
       
   415 
   406 
   416         if (dura == -1 || loopCount < 0
   407         if (dura == -1 || loopCount < 0
   417             || (oldDirection == QAbstractAnimation::Forward && (oldCurrentTime * (oldCurrentLoop + 1)) == (dura * loopCount))
   408             || (oldDirection == QAbstractAnimation::Forward && (oldCurrentTime * (oldCurrentLoop + 1)) == (dura * loopCount))
   418             || (oldDirection == QAbstractAnimation::Backward && oldCurrentTime == 0)) {
   409             || (oldDirection == QAbstractAnimation::Backward && oldCurrentTime == 0)) {
   419                 emit q->finished();
   410                 emit q->finished();
   556     }
   547     }
   557 
   548 
   558     // the commands order below is important: first we need to setCurrentTime with the old direction,
   549     // the commands order below is important: first we need to setCurrentTime with the old direction,
   559     // then update the direction on this and all children and finally restart the pauseTimer if needed
   550     // then update the direction on this and all children and finally restart the pauseTimer if needed
   560     if (d->hasRegisteredTimer)
   551     if (d->hasRegisteredTimer)
   561         QUnifiedTimer::instance()->ensureTimerUpdate(this);
   552         QUnifiedTimer::instance()->ensureTimerUpdate();
   562 
   553 
   563     d->direction = direction;
   554     d->direction = direction;
   564     updateDirection(direction);
   555     updateDirection(direction);
   565 
   556 
   566     if (d->hasRegisteredTimer)
   557     if (d->hasRegisteredTimer)
   655         return -1;
   646         return -1;
   656     return dura * loopcount;
   647     return dura * loopcount;
   657 }
   648 }
   658 
   649 
   659 /*!
   650 /*!
       
   651     Returns the current time inside the current loop. It can go from 0 to duration().
       
   652 
       
   653     \sa duration(), currentTime
       
   654 */
       
   655 
       
   656 int QAbstractAnimation::currentLoopTime() const
       
   657 {
       
   658     Q_D(const QAbstractAnimation);
       
   659     return d->currentTime;
       
   660 }
       
   661 
       
   662 /*!
   660     \property QAbstractAnimation::currentTime
   663     \property QAbstractAnimation::currentTime
   661     \brief the current time and progress of the animation
   664     \brief the current time and progress of the animation
   662 
   665 
   663     This property describes the animation's current time. You can change the
   666     This property describes the animation's current time. You can change the
   664     current time by calling setCurrentTime, or you can call start() and let
   667     current time by calling setCurrentTime, or you can call start() and let
   665     the animation run, setting the current time automatically as the animation
   668     the animation run, setting the current time automatically as the animation
   666     progresses.
   669     progresses.
   667 
   670 
   668     The animation's current time starts at 0, and ends at duration(). If the
   671     The animation's current time starts at 0, and ends at totalDuration().
   669     animation's loopCount is larger than 1, the current time will rewind and
   672 
   670     start at 0 again for the consecutive loops. If the animation has a pause.
   673     \sa loopCount, currentLoopTime()
   671     currentTime will also include the duration of the pause.
       
   672 
       
   673     \sa loopCount
       
   674  */
   674  */
   675 int QAbstractAnimation::currentTime() const
   675 int QAbstractAnimation::currentTime() const
   676 {
   676 {
   677     Q_D(const QAbstractAnimation);
   677     Q_D(const QAbstractAnimation);
   678     return d->currentTime;
   678     return d->totalCurrentTime;
   679 }
   679 }
   680 void QAbstractAnimation::setCurrentTime(int msecs)
   680 void QAbstractAnimation::setCurrentTime(int msecs)
   681 {
   681 {
   682     Q_D(QAbstractAnimation);
   682     Q_D(QAbstractAnimation);
   683     msecs = qMax(msecs, 0);
   683     msecs = qMax(msecs, 0);
   747 /*!
   747 /*!
   748     Stops the animation. When the animation is stopped, it emits the stateChanged()
   748     Stops the animation. When the animation is stopped, it emits the stateChanged()
   749     signal, and state() returns Stopped. The current time is not changed.
   749     signal, and state() returns Stopped. The current time is not changed.
   750 
   750 
   751     If the animation stops by itself after reaching the end (i.e.,
   751     If the animation stops by itself after reaching the end (i.e.,
   752     currentTime() == duration() and currentLoop() > loopCount() - 1), the
   752     currentLoopTime() == duration() and currentLoop() > loopCount() - 1), the
   753     finished() signal is emitted.
   753     finished() signal is emitted.
   754 
   754 
   755     \sa start(), state()
   755     \sa start(), state()
   756  */
   756  */
   757 void QAbstractAnimation::stop()
   757 void QAbstractAnimation::stop()
   797 
   797 
   798     d->setState(Running);
   798     d->setState(Running);
   799 }
   799 }
   800 
   800 
   801 /*!
   801 /*!
       
   802     If \a paused is true, the animation is paused.
       
   803     If \a paused is false, the animation is resumed.
       
   804 
       
   805     \sa state(), pause(), resume()
       
   806 */
       
   807 void QAbstractAnimation::setPaused(bool paused)
       
   808 {
       
   809     if (paused)
       
   810         pause();
       
   811     else
       
   812         resume();
       
   813 }
       
   814 
       
   815 
       
   816 /*!
   802     \reimp
   817     \reimp
   803 */
   818 */
   804 bool QAbstractAnimation::event(QEvent *event)
   819 bool QAbstractAnimation::event(QEvent *event)
   805 {
   820 {
   806     return QObject::event(event);
   821     return QObject::event(event);
   819     This virtual function is called by QAbstractAnimation when the state
   834     This virtual function is called by QAbstractAnimation when the state
   820     of the animation is changed from \a oldState to \a newState.
   835     of the animation is changed from \a oldState to \a newState.
   821 
   836 
   822     \sa start(), stop(), pause(), resume()
   837     \sa start(), stop(), pause(), resume()
   823 */
   838 */
   824 void QAbstractAnimation::updateState(QAbstractAnimation::State oldState,
   839 void QAbstractAnimation::updateState(QAbstractAnimation::State newState,
   825                                      QAbstractAnimation::State newState)
   840                                      QAbstractAnimation::State oldState)
   826 {
   841 {
   827     Q_UNUSED(oldState);
   842     Q_UNUSED(oldState);
   828     Q_UNUSED(newState);
   843     Q_UNUSED(newState);
   829 }
   844 }
   830 
   845