summaryrefslogtreecommitdiffstats
path: root/src/corelib/animation/qabstractanimation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/animation/qabstractanimation.cpp')
-rw-r--r--src/corelib/animation/qabstractanimation.cpp232
1 files changed, 147 insertions, 85 deletions
diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp
index 133bc5689d..9687af0987 100644
--- a/src/corelib/animation/qabstractanimation.cpp
+++ b/src/corelib/animation/qabstractanimation.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
/*!
\class QAbstractAnimation
@@ -149,7 +113,6 @@
#include "qabstractanimation_p.h"
#include <QtCore/qmath.h>
-#include <QtCore/qthreadstorage.h>
#include <QtCore/qcoreevent.h>
#include <QtCore/qpointer.h>
#include <QtCore/qscopedvaluerollback.h>
@@ -214,12 +177,10 @@ typedef QList<QAbstractAnimation*>::ConstIterator AnimationListConstIt;
QUnifiedTimer drives animations indirectly, via QAbstractAnimationTimer.
*/
-Q_GLOBAL_STATIC(QThreadStorage<QUnifiedTimer *>, unifiedTimer)
-
QUnifiedTimer::QUnifiedTimer() :
QObject(), defaultDriver(this), lastTick(0), timingInterval(DEFAULT_TIMER_INTERVAL),
currentAnimationIdx(0), insideTick(false), insideRestart(false), consistentTiming(false), slowMode(false),
- startTimersPending(false), stopTimerPending(false),
+ startTimersPending(false), stopTimerPending(false), allowNegativeDelta(false),
slowdownFactor(5.0f), profilerCallback(nullptr),
driverStartTime(0), temporalDrift(0)
{
@@ -227,15 +188,18 @@ QUnifiedTimer::QUnifiedTimer() :
driver = &defaultDriver;
}
+QUnifiedTimer::~QUnifiedTimer()
+ = default;
QUnifiedTimer *QUnifiedTimer::instance(bool create)
{
QUnifiedTimer *inst;
- if (create && !unifiedTimer()->hasLocalData()) {
+ static thread_local std::unique_ptr<QUnifiedTimer> unifiedTimer;
+ if (create && !unifiedTimer) {
inst = new QUnifiedTimer;
- unifiedTimer()->setLocalData(inst);
+ unifiedTimer.reset(inst);
} else {
- inst = unifiedTimer() ? unifiedTimer()->localData() : nullptr;
+ inst = unifiedTimer.get();
}
return inst;
}
@@ -315,11 +279,11 @@ void QUnifiedTimer::updateAnimationTimers()
// when the CPU load is high
//* it might happen in some cases that the delta is negative because the animation driver
// advances faster than time.elapsed()
- if (delta > 0) {
+ if (delta != 0 && (allowNegativeDelta || delta > 0)) {
QScopedValueRollback<bool> guard(insideTick, true);
if (profilerCallback)
profilerCallback(delta);
- for (currentAnimationIdx = 0; currentAnimationIdx < animationTimers.count(); ++currentAnimationIdx) {
+ for (currentAnimationIdx = 0; currentAnimationIdx < animationTimers.size(); ++currentAnimationIdx) {
QAbstractAnimationTimer *animation = animationTimers.at(currentAnimationIdx);
animation->updateAnimationsTime(delta);
}
@@ -330,7 +294,7 @@ void QUnifiedTimer::updateAnimationTimers()
int QUnifiedTimer::runningAnimationCount()
{
int count = 0;
- for (int i = 0; i < animationTimers.count(); ++i)
+ for (int i = 0; i < animationTimers.size(); ++i)
count += animationTimers.at(i)->runningAnimationCount();
return count;
}
@@ -345,7 +309,7 @@ void QUnifiedTimer::localRestart()
if (insideRestart)
return;
- if (!pausedAnimationTimers.isEmpty() && (animationTimers.count() + animationTimersToStart.count() == pausedAnimationTimers.count())) {
+ if (!pausedAnimationTimers.isEmpty() && (animationTimers.size() + animationTimersToStart.size() == pausedAnimationTimers.size())) {
driver->stop();
int closestTimeToFinish = closestPausedAnimationTimerTimeToFinish();
// use a precise timer if the pause will be short
@@ -363,7 +327,7 @@ void QUnifiedTimer::restart()
{
{
QScopedValueRollback<bool> guard(insideRestart, true);
- for (int i = 0; i < animationTimers.count(); ++i)
+ for (int i = 0; i < animationTimers.size(); ++i)
animationTimers.at(i)->restartAnimationTimer();
}
@@ -517,6 +481,8 @@ void QUnifiedTimer::installAnimationDriver(QAnimationDriver *d)
if (running)
stopAnimationDriver();
driver = d;
+ if (driver)
+ allowNegativeDelta = driver->property("allowNegativeDelta").toBool();
if (running)
startAnimationDriver();
}
@@ -532,6 +498,7 @@ void QUnifiedTimer::uninstallAnimationDriver(QAnimationDriver *d)
if (running)
stopAnimationDriver();
driver = &defaultDriver;
+ allowNegativeDelta = false;
if (running)
startAnimationDriver();
}
@@ -545,10 +512,6 @@ bool QUnifiedTimer::canUninstallAnimationDriver(QAnimationDriver *d)
return d == driver && driver != &defaultDriver;
}
-#if QT_CONFIG(thread)
-Q_GLOBAL_STATIC(QThreadStorage<QAnimationTimer *>, animationTimer)
-#endif
-
QAnimationTimer::QAnimationTimer() :
QAbstractAnimationTimer(), lastTick(0),
currentAnimationIdx(0), insideTick(false),
@@ -557,15 +520,19 @@ QAnimationTimer::QAnimationTimer() :
{
}
+QAnimationTimer::~QAnimationTimer()
+ = default;
+
QAnimationTimer *QAnimationTimer::instance(bool create)
{
QAnimationTimer *inst;
#if QT_CONFIG(thread)
- if (create && !animationTimer()->hasLocalData()) {
+ static thread_local std::unique_ptr<QAnimationTimer> animationTimer;
+ if (create && !animationTimer) {
inst = new QAnimationTimer;
- animationTimer()->setLocalData(inst);
+ animationTimer.reset(inst);
} else {
- inst = animationTimer() ? animationTimer()->localData() : nullptr;
+ inst = animationTimer.get();
}
#else
Q_UNUSED(create);
@@ -601,7 +568,7 @@ void QAnimationTimer::updateAnimationsTime(qint64 delta)
//when the CPU load is high
if (delta) {
QScopedValueRollback<bool> guard(insideTick, true);
- for (currentAnimationIdx = 0; currentAnimationIdx < animations.count(); ++currentAnimationIdx) {
+ for (currentAnimationIdx = 0; currentAnimationIdx < animations.size(); ++currentAnimationIdx) {
QAbstractAnimation *animation = animations.at(currentAnimationIdx);
int elapsed = QAbstractAnimationPrivate::get(animation)->totalCurrentTime
+ (animation->direction() == QAbstractAnimation::Forward ? delta : -delta);
@@ -792,7 +759,7 @@ void QAnimationDriver::advanceAnimation()
/*!
- Advances the animation. This function should be continously called
+ Advances the animation. This function should be continuously called
by the driver while the animation is running.
*/
@@ -889,8 +856,14 @@ qint64 QAnimationDriver::elapsed() const
QDefaultAnimationDriver::QDefaultAnimationDriver(QUnifiedTimer *timer)
: QAnimationDriver(nullptr), m_unified_timer(timer)
{
- connect(this, SIGNAL(started()), this, SLOT(startTimer()));
- connect(this, SIGNAL(stopped()), this, SLOT(stopTimer()));
+ connect(this, &QAnimationDriver::started, this, &QDefaultAnimationDriver::startTimer);
+ connect(this, &QAnimationDriver::stopped, this, &QDefaultAnimationDriver::stopTimer);
+}
+
+QDefaultAnimationDriver::~QDefaultAnimationDriver()
+{
+ disconnect(this, &QAnimationDriver::started, this, &QDefaultAnimationDriver::startTimer);
+ disconnect(this, &QAnimationDriver::stopped, this, &QDefaultAnimationDriver::stopTimer);
}
void QDefaultAnimationDriver::timerEvent(QTimerEvent *e)
@@ -911,18 +884,33 @@ void QDefaultAnimationDriver::stopTimer()
m_timer.stop();
}
+QAnimationDriverPrivate::QAnimationDriverPrivate()
+ = default;
+
+QAnimationDriverPrivate::~QAnimationDriverPrivate()
+ = default;
+
+QAbstractAnimationTimer::QAbstractAnimationTimer()
+ = default;
+
+QAbstractAnimationTimer::~QAbstractAnimationTimer()
+ = default;
+QAbstractAnimationPrivate::QAbstractAnimationPrivate()
+ = default;
+
+QAbstractAnimationPrivate::~QAbstractAnimationPrivate() { }
void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState)
{
Q_Q(QAbstractAnimation);
- if (state == newState)
+ const QAbstractAnimation::State oldState = state.valueBypassingBindings();
+ if (oldState == newState)
return;
if (loopCount == 0)
return;
- QAbstractAnimation::State oldState = state;
int oldCurrentTime = currentTime;
int oldCurrentLoop = currentLoop;
QAbstractAnimation::Direction oldDirection = direction;
@@ -930,14 +918,17 @@ void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState)
// check if we should Rewind
if ((newState == QAbstractAnimation::Paused || newState == QAbstractAnimation::Running)
&& oldState == QAbstractAnimation::Stopped) {
+ const int oldTotalCurrentTime = totalCurrentTime;
//here we reset the time if needed
//we don't call setCurrentTime because this might change the way the animation
//behaves: changing the state or changing the current value
totalCurrentTime = currentTime = (direction == QAbstractAnimation::Forward) ?
0 : (loopCount == -1 ? q->duration() : q->totalDuration());
+ if (totalCurrentTime != oldTotalCurrentTime)
+ totalCurrentTime.notify();
}
- state = newState;
+ state.setValueBypassingBindings(newState);
QPointer<QAbstractAnimation> guard(q);
//(un)registration of the animation must always happen before calls to
@@ -953,12 +944,15 @@ void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState)
}
q->updateState(newState, oldState);
- if (!guard || newState != state) //this is to be safe if updateState changes the state
+ //this is to be safe if updateState changes the state
+ if (!guard || newState != state.valueBypassingBindings())
return;
// Notify state change
+ state.notify();
emit q->stateChanged(newState, oldState);
- if (!guard || newState != state) //this is to be safe if updateState changes the state
+ //this is to be safe if updateState changes the state
+ if (!guard || newState != state.valueBypassingBindings())
return;
switch (state) {
@@ -1028,6 +1022,7 @@ QAbstractAnimation::~QAbstractAnimation()
if (d->state != Stopped) {
QAbstractAnimation::State oldState = d->state;
d->state = Stopped;
+ d->state.notify();
emit stateChanged(d->state, oldState);
if (oldState == QAbstractAnimation::Running)
QAnimationTimer::unregisterAnimation(this);
@@ -1043,6 +1038,11 @@ QAbstractAnimation::~QAbstractAnimation()
This property describes the current state of the animation. When the
animation state changes, QAbstractAnimation emits the stateChanged()
signal.
+
+ \note State updates might cause updates of the currentTime property,
+ which, in turn, can cancel its bindings. So be careful when setting
+ bindings to the currentTime property, when you expect the state of the
+ animation to change.
*/
QAbstractAnimation::State QAbstractAnimation::state() const
{
@@ -1050,6 +1050,12 @@ QAbstractAnimation::State QAbstractAnimation::state() const
return d->state;
}
+QBindable<QAbstractAnimation::State> QAbstractAnimation::bindableState() const
+{
+ Q_D(const QAbstractAnimation);
+ return &d->state;
+}
+
/*!
If this animation is part of a QAnimationGroup, this function returns a
pointer to the group; otherwise, it returns \nullptr.
@@ -1115,9 +1121,13 @@ QAbstractAnimation::Direction QAbstractAnimation::direction() const
void QAbstractAnimation::setDirection(Direction direction)
{
Q_D(QAbstractAnimation);
- if (d->direction == direction)
+ if (d->direction == direction) {
+ d->direction.removeBindingUnlessInWrapper();
return;
+ }
+ const QScopedPropertyUpdateGroup guard;
+ const int oldCurrentLoop = d->currentLoop;
if (state() == Stopped) {
if (direction == Backward) {
d->currentTime = duration();
@@ -1140,7 +1150,15 @@ void QAbstractAnimation::setDirection(Direction direction)
// needed to update the timer interval in case of a pause animation
QAnimationTimer::updateAnimationTimer();
- emit directionChanged(direction);
+ if (d->currentLoop != oldCurrentLoop)
+ d->currentLoop.notify();
+ d->direction.notify();
+}
+
+QBindable<QAbstractAnimation::Direction> QAbstractAnimation::bindableDirection()
+{
+ Q_D(QAbstractAnimation);
+ return &d->direction;
}
/*!
@@ -1175,6 +1193,12 @@ void QAbstractAnimation::setLoopCount(int loopCount)
d->loopCount = loopCount;
}
+QBindable<int> QAbstractAnimation::bindableLoopCount()
+{
+ Q_D(QAbstractAnimation);
+ return &d->loopCount;
+}
+
/*!
\property QAbstractAnimation::currentLoop
\brief the current loop of the animation
@@ -1194,6 +1218,12 @@ int QAbstractAnimation::currentLoop() const
return d->currentLoop;
}
+QBindable<int> QAbstractAnimation::bindableCurrentLoop() const
+{
+ Q_D(const QAbstractAnimation);
+ return &d->currentLoop;
+}
+
/*!
\fn virtual int QAbstractAnimation::duration() const = 0
@@ -1252,6 +1282,10 @@ int QAbstractAnimation::currentLoopTime() const
The animation's current time starts at 0, and ends at totalDuration().
+ \note You can bind other properties to currentTime, but it is not
+ recommended setting bindings to it. As animation progresses, the currentTime
+ is updated automatically, which cancels its bindings.
+
\sa loopCount, currentLoopTime()
*/
int QAbstractAnimation::currentTime() const
@@ -1259,44 +1293,72 @@ int QAbstractAnimation::currentTime() const
Q_D(const QAbstractAnimation);
return d->totalCurrentTime;
}
+
+QBindable<int> QAbstractAnimation::bindableCurrentTime()
+{
+ Q_D(QAbstractAnimation);
+ return &d->totalCurrentTime;
+}
+
void QAbstractAnimation::setCurrentTime(int msecs)
{
Q_D(QAbstractAnimation);
msecs = qMax(msecs, 0);
// Calculate new time and loop.
- int dura = duration();
- int totalDura = dura <= 0 ? dura : ((d->loopCount < 0) ? -1 : dura * d->loopCount);
+ const int dura = duration();
+ const int totalLoopCount = d->loopCount;
+ const int totalDura = dura <= 0 ? dura : ((totalLoopCount < 0) ? -1 : dura * totalLoopCount);
if (totalDura != -1)
msecs = qMin(totalDura, msecs);
- d->totalCurrentTime = msecs;
+
+ d->totalCurrentTime.removeBindingUnlessInWrapper();
+
+ const int oldCurrentTime = d->totalCurrentTime.valueBypassingBindings();
+ d->totalCurrentTime.setValueBypassingBindings(msecs);
+
+ QAbstractAnimation::Direction currentDirection = d->direction;
// Update new values.
- int oldLoop = d->currentLoop;
- d->currentLoop = ((dura <= 0) ? 0 : (msecs / dura));
- if (d->currentLoop == d->loopCount) {
+ const int oldLoop = d->currentLoop.valueBypassingBindings();
+ int newCurrentLoop = (dura <= 0) ? 0 : (msecs / dura);
+ if (newCurrentLoop == totalLoopCount) {
//we're at the end
d->currentTime = qMax(0, dura);
- d->currentLoop = qMax(0, d->loopCount - 1);
+ newCurrentLoop = qMax(0, totalLoopCount - 1);
} else {
- if (d->direction == Forward) {
+ if (currentDirection == Forward) {
d->currentTime = (dura <= 0) ? msecs : (msecs % dura);
} else {
d->currentTime = (dura <= 0) ? msecs : ((msecs - 1) % dura) + 1;
if (d->currentTime == dura)
- --d->currentLoop;
+ newCurrentLoop = newCurrentLoop - 1;
}
}
+ d->currentLoop.setValueBypassingBindings(newCurrentLoop);
+ // this is a virtual function, so it can update the properties as well
updateCurrentTime(d->currentTime);
- if (d->currentLoop != oldLoop)
- emit currentLoopChanged(d->currentLoop);
+ // read the property values again
+ newCurrentLoop = d->currentLoop.valueBypassingBindings();
+ currentDirection = d->direction;
+ const int newTotalCurrentTime = d->totalCurrentTime.valueBypassingBindings();
+
+ if (newCurrentLoop != oldLoop)
+ d->currentLoop.notify();
+
+ /* Notify before calling stop: As seen in tst_QSequentialAnimationGroup::clear
+ * we might delete the animation when stop is called. Thus after stop no member
+ * of the object must be used anymore.
+ */
+ if (oldCurrentTime != newTotalCurrentTime)
+ d->totalCurrentTime.notify();
// All animations are responsible for stopping the animation when their
// own end state is reached; in this case the animation is time driven,
// and has reached the end.
- if ((d->direction == Forward && d->totalCurrentTime == totalDura)
- || (d->direction == Backward && d->totalCurrentTime == 0)) {
+ if ((currentDirection == Forward && newTotalCurrentTime == totalDura)
+ || (currentDirection == Backward && newTotalCurrentTime == 0)) {
stop();
}
}
@@ -1320,7 +1382,7 @@ void QAbstractAnimation::setCurrentTime(int msecs)
void QAbstractAnimation::start(DeletionPolicy policy)
{
Q_D(QAbstractAnimation);
- if (d->state == Running)
+ if (d->state.valueBypassingBindings() == Running)
return;
d->deleteWhenStopped = policy;
d->setState(Running);
@@ -1340,7 +1402,7 @@ void QAbstractAnimation::stop()
{
Q_D(QAbstractAnimation);
- if (d->state == Stopped)
+ if (d->state.valueBypassingBindings() == Stopped)
return;
d->setState(Stopped);
@@ -1356,7 +1418,7 @@ void QAbstractAnimation::stop()
void QAbstractAnimation::pause()
{
Q_D(QAbstractAnimation);
- if (d->state == Stopped) {
+ if (d->state.valueBypassingBindings() == Stopped) {
qWarning("QAbstractAnimation::pause: Cannot pause a stopped animation");
return;
}
@@ -1374,7 +1436,7 @@ void QAbstractAnimation::pause()
void QAbstractAnimation::resume()
{
Q_D(QAbstractAnimation);
- if (d->state != Paused) {
+ if (d->state.valueBypassingBindings() != Paused) {
qWarning("QAbstractAnimation::resume: "
"Cannot resume an animation that is not paused");
return;