aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/util/qquickanimation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/util/qquickanimation.cpp')
-rw-r--r--src/quick/util/qquickanimation.cpp514
1 files changed, 335 insertions, 179 deletions
diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp
index 2043b50545..2f3bc66013 100644
--- a/src/quick/util/qquickanimation.cpp
+++ b/src/quick/util/qquickanimation.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQuick 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
#include "qquickanimation_p.h"
#include "qquickanimation_p_p.h"
@@ -165,22 +129,18 @@ void QQuickAbstractAnimationPrivate::commence()
QQuickStateActions actions;
QQmlProperties properties;
- QAbstractAnimationJob *oldInstance = animationInstance;
- animationInstance = q->transition(actions, properties, QQuickAbstractAnimation::Forward);
- if (oldInstance && oldInstance != animationInstance)
- delete oldInstance;
+ auto *newInstance = q->transition(actions, properties, QQuickAbstractAnimation::Forward);
+ Q_ASSERT(newInstance != animationInstance);
+ delete animationInstance;
+ animationInstance = newInstance;
if (animationInstance) {
- if (oldInstance != animationInstance) {
- if (q->threadingModel() == QQuickAbstractAnimation::RenderThread)
- animationInstance = new QQuickAnimatorProxyJob(animationInstance, q);
- animationInstance->addAnimationChangeListener(this, QAbstractAnimationJob::Completion);
- }
+ if (q->threadingModel() == QQuickAbstractAnimation::RenderThread)
+ animationInstance = new QQuickAnimatorProxyJob(animationInstance, q);
+ animationInstance->addAnimationChangeListener(this,
+ QAbstractAnimationJob::Completion | QAbstractAnimationJob::CurrentLoop);
+ emit q->started();
animationInstance->start();
- if (animationInstance->isStopped()) {
- running = false;
- emit q->stopped();
- }
}
}
@@ -205,6 +165,27 @@ QQmlProperty QQuickAbstractAnimationPrivate::createProperty(QObject *obj, const
return prop;
}
+void QQuickAbstractAnimationPrivate::animationGroupDirty()
+{
+ Q_ASSERT(group != nullptr);
+ if (!componentComplete)
+ return;
+
+ auto *animGroupPriv = static_cast<QQuickAnimationGroupPrivate *>(QQuickAnimationGroupPrivate::get(group));
+ if (animGroupPriv->running && !animGroupPriv->animationDirty) {
+ animGroupPriv->animationDirty = true;
+
+ if (animGroupPriv->animationInstance && group->currentTime() == 0) {
+ // restart if the animation didn't proceed yet.
+ animGroupPriv->restartFromCurrentLoop();
+ }
+ }
+
+ // check the animationGroup is one of another animationGroup members
+ if (animGroupPriv->group)
+ animGroupPriv->animationGroupDirty();
+}
+
/*!
\qmlsignal QtQuick::Animation::started()
@@ -213,8 +194,6 @@ QQmlProperty QQuickAbstractAnimationPrivate::createProperty(QObject *obj, const
It is only triggered for top-level, standalone animations. It will not be
triggered for animations in a Behavior or Transition, or animations
that are part of an animation group.
-
- The corresponding handler is \c onStarted.
*/
/*!
@@ -230,8 +209,6 @@ QQmlProperty QQuickAbstractAnimationPrivate::createProperty(QObject *obj, const
If \l alwaysRunToEnd is true, this signal will not be emitted until the animation
has completed its current iteration.
-
- The corresponding handler is \c onStopped.
*/
/*!
@@ -250,8 +227,6 @@ QQmlProperty QQuickAbstractAnimationPrivate::createProperty(QObject *obj, const
If \l alwaysRunToEnd is true, this signal will not be emitted until the
animation has completed its current iteration.
- The corresponding handler is \c onFinished.
-
\sa stopped(), started(), running
*/
@@ -262,14 +237,8 @@ void QQuickAbstractAnimation::setRunning(bool r)
d->running = r;
if (r == false)
d->avoidPropertyValueSourceStart = true;
- else if (!d->registered) {
- d->registered = true;
- QQmlEnginePrivate *engPriv = QQmlEnginePrivate::get(qmlEngine(this));
- static int finalizedIdx = -1;
- if (finalizedIdx < 0)
- finalizedIdx = metaObject()->indexOfSlot("componentFinalized()");
- engPriv->registerFinalizeCallback(this, finalizedIdx);
- }
+ else if (!d->needsDeferredSetRunning)
+ d->needsDeferredSetRunning = true;
return;
}
@@ -293,10 +262,8 @@ void QQuickAbstractAnimation::setRunning(bool r)
d->animationInstance->setLoopCount(d->animationInstance->currentLoop() + d->loopCount);
supressStart = true; //we want the animation to continue, rather than restart
}
- if (!supressStart) {
+ if (!supressStart)
d->commence();
- emit started();
- }
} else {
if (d->paused) {
d->paused = false; //reset paused state to false when stopped
@@ -314,7 +281,21 @@ void QQuickAbstractAnimation::setRunning(bool r)
}
}
- emit runningChanged(d->running);
+
+ if (r == d->running) {
+ // This might happen if we start an animation with 0 duration: This will result in that
+ // commence() will emit started(), and then when it starts it will call setCurrentTime(0),
+ // (which is both start and end time of the animation), so it will also end up calling
+ // setRunning(false) (recursively) and stop the animation.
+ // Therefore, the state of d->running will in that case be different than r if we are back in
+ // the root stack frame of the recursive calls to setRunning()
+ emit runningChanged(d->running);
+ } else if (d->animationInstance) {
+ // If there was a recursive call, make sure the d->running is set correctly
+ d->running = d->animationInstance->isRunning();
+ } else {
+ d->running = r;
+ }
}
/*!
@@ -375,18 +356,15 @@ void QQuickAbstractAnimation::componentComplete()
{
Q_D(QQuickAbstractAnimation);
d->componentComplete = true;
-}
-
-void QQuickAbstractAnimation::componentFinalized()
-{
- Q_D(QQuickAbstractAnimation);
- if (d->running) {
- d->running = false;
- setRunning(true);
- }
- if (d->paused) {
- d->paused = false;
- setPaused(true);
+ if (d->needsDeferredSetRunning) {
+ if (d->running) {
+ d->running = false;
+ setRunning(true);
+ }
+ if (d->paused) {
+ d->paused = false;
+ setPaused(true);
+ }
}
}
@@ -490,7 +468,7 @@ QQuickAnimationGroup *QQuickAbstractAnimation::group() const
return d->group;
}
-void QQuickAbstractAnimation::setGroup(QQuickAnimationGroup *g)
+void QQuickAbstractAnimation::setGroup(QQuickAnimationGroup *g, int index)
{
Q_D(QQuickAbstractAnimation);
if (d->group == g)
@@ -500,8 +478,12 @@ void QQuickAbstractAnimation::setGroup(QQuickAnimationGroup *g)
d->group = g;
- if (d->group && !d->group->d_func()->animations.contains(this))
- d->group->d_func()->animations.append(this);
+ if (d->group && !d->group->d_func()->animations.contains(this)) {
+ if (index >= 0)
+ d->group->d_func()->animations.insert(index, this);
+ else
+ d->group->d_func()->animations.append(this);
+ }
}
/*!
@@ -740,6 +722,8 @@ void QQuickPauseAnimation::setDuration(int duration)
return;
d->duration = duration;
emit durationChanged(duration);
+ if (d->group)
+ d->animationGroupDirty();
}
QAbstractAnimationJob* QQuickPauseAnimation::transition(QQuickStateActions &actions,
@@ -903,7 +887,7 @@ void QActionAnimation::updateState(State newState, State oldState)
void QActionAnimation::debugAnimation(QDebug d) const
{
- d << "ActionAnimation(" << hex << (const void *) this << dec << ")";
+ d << "ActionAnimation(" << Qt::hex << (const void *) this << Qt::dec << ")";
if (animAction) {
int indentLevel = 1;
@@ -1005,11 +989,11 @@ void QQuickScriptActionPrivate::debugAction(QDebug d, int indentLevel) const
if (!scriptStr.isEmpty()) {
QQmlExpression expr(scriptStr);
- QByteArray ind(indentLevel, ' ');
+ QByteArray ind(indentLevel, u' ');
QString exprStr = expr.expression();
- int endOfFirstLine = exprStr.indexOf('\n');
- d << "\n" << ind.constData() << exprStr.leftRef(endOfFirstLine);
- if (endOfFirstLine != -1 && endOfFirstLine < exprStr.length())
+ int endOfFirstLine = exprStr.indexOf(u'\n');
+ d << "\n" << ind.constData() << QStringView{exprStr}.left(endOfFirstLine);
+ if (endOfFirstLine != -1 && endOfFirstLine < exprStr.size())
d << "...";
}
}
@@ -1042,7 +1026,7 @@ QAbstractAnimationJob* QQuickScriptAction::transition(QQuickStateActions &action
d->hasRunScriptScript = false;
d->reversing = (direction == Backward);
if (!d->name.isEmpty()) {
- for (int ii = 0; ii < actions.count(); ++ii) {
+ for (int ii = 0; ii < actions.size(); ++ii) {
QQuickStateAction &action = actions[ii];
if (action.event && action.event->type() == QQuickStateActionEvent::Script
@@ -1096,7 +1080,7 @@ QAbstractAnimationJob* QQuickScriptAction::transition(QQuickStateActions &action
PropertyAction object) so that the rotation animation begins with the
correct transform origin.
- \sa {Animation and Transitions in Qt Quick}, {Qt QML}
+ \sa {Animation and Transitions in Qt Quick}, {Qt Qml}
*/
QQuickPropertyAction::QQuickPropertyAction(QObject *parent)
: QQuickAbstractAnimation(*(new QQuickPropertyActionPrivate), parent)
@@ -1120,6 +1104,8 @@ void QQuickPropertyAction::setTargetObject(QObject *o)
return;
d->target = o;
emit targetChanged();
+ if (d->group)
+ d->animationGroupDirty();
}
QString QQuickPropertyAction::property() const
@@ -1135,11 +1121,13 @@ void QQuickPropertyAction::setProperty(const QString &n)
return;
d->propertyName = n;
emit propertyChanged();
+ if (d->group)
+ d->animationGroupDirty();
}
/*!
- \qmlproperty Object QtQuick::PropertyAction::target
- \qmlproperty list<Object> QtQuick::PropertyAction::targets
+ \qmlproperty QtObject QtQuick::PropertyAction::target
+ \qmlproperty list<QtObject> QtQuick::PropertyAction::targets
\qmlproperty string QtQuick::PropertyAction::property
\qmlproperty string QtQuick::PropertyAction::properties
@@ -1165,16 +1153,18 @@ void QQuickPropertyAction::setProperties(const QString &p)
return;
d->properties = p;
emit propertiesChanged(p);
+ if (d->group)
+ d->animationGroupDirty();
}
QQmlListProperty<QObject> QQuickPropertyAction::targets()
{
Q_D(QQuickPropertyAction);
- return QQmlListProperty<QObject>(this, d->targets);
+ return QQmlListProperty<QObject>(this, &(d->targets));
}
/*!
- \qmlproperty list<Object> QtQuick::PropertyAction::exclude
+ \qmlproperty list<QtObject> QtQuick::PropertyAction::exclude
This property holds the objects that should not be affected by this action.
\sa targets
@@ -1182,11 +1172,11 @@ QQmlListProperty<QObject> QQuickPropertyAction::targets()
QQmlListProperty<QObject> QQuickPropertyAction::exclude()
{
Q_D(QQuickPropertyAction);
- return QQmlListProperty<QObject>(this, d->exclude);
+ return QQmlListProperty<QObject>(this, &(d->exclude));
}
/*!
- \qmlproperty any QtQuick::PropertyAction::value
+ \qmlproperty var QtQuick::PropertyAction::value
This property holds the value to be set on the property.
If the PropertyAction is defined within a \l Transition or \l Behavior,
@@ -1203,7 +1193,7 @@ QVariant QQuickPropertyAction::value() const
void QQuickPropertyAction::setValue(const QVariant &v)
{
Q_D(QQuickPropertyAction);
- if (d->value.isNull || d->value != v) {
+ if (!d->value.isValid() || d->value != v) {
d->value = v;
emit valueChanged(v);
}
@@ -1222,14 +1212,14 @@ QAbstractAnimationJob* QQuickPropertyAction::transition(QQuickStateActions &acti
QQuickStateActions actions;
void doAction() override
{
- for (int ii = 0; ii < actions.count(); ++ii) {
+ for (int ii = 0; ii < actions.size(); ++ii) {
const QQuickStateAction &action = actions.at(ii);
QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
}
}
void debugAction(QDebug d, int indentLevel) const override {
QByteArray ind(indentLevel, ' ');
- for (int ii = 0; ii < actions.count(); ++ii) {
+ for (int ii = 0; ii < actions.size(); ++ii) {
const QQuickStateAction &action = actions.at(ii);
d << "\n" << ind.constData() << "target:" << action.property.object() << "property:" << action.property.name()
<< "value:" << action.toValue;
@@ -1238,7 +1228,7 @@ QAbstractAnimationJob* QQuickPropertyAction::transition(QQuickStateActions &acti
};
QStringList props = d->properties.isEmpty() ? QStringList() : d->properties.split(QLatin1Char(','));
- for (int ii = 0; ii < props.count(); ++ii)
+ for (int ii = 0; ii < props.size(); ++ii)
props[ii] = props.at(ii).trimmed();
if (!d->propertyName.isEmpty())
props << d->propertyName;
@@ -1262,16 +1252,16 @@ QAbstractAnimationJob* QQuickPropertyAction::transition(QQuickStateActions &acti
bool hasExplicit = false;
//an explicit animation has been specified
if (d->value.isValid()) {
- for (int i = 0; i < props.count(); ++i) {
- for (int j = 0; j < targets.count(); ++j) {
+ for (int i = 0; i < props.size(); ++i) {
+ for (int j = 0; j < targets.size(); ++j) {
QQuickStateAction myAction;
myAction.property = d->createProperty(targets.at(j), props.at(i), this);
if (myAction.property.isValid()) {
myAction.toValue = d->value;
- QQuickPropertyAnimationPrivate::convertVariant(myAction.toValue, myAction.property.propertyType());
+ QQuickPropertyAnimationPrivate::convertVariant(myAction.toValue, myAction.property.propertyMetaType());
data->actions << myAction;
hasExplicit = true;
- for (int ii = 0; ii < actions.count(); ++ii) {
+ for (int ii = 0; ii < actions.size(); ++ii) {
QQuickStateAction &action = actions[ii];
if (action.property.object() == myAction.property.object() &&
myAction.property.name() == action.property.name()) {
@@ -1285,7 +1275,7 @@ QAbstractAnimationJob* QQuickPropertyAction::transition(QQuickStateActions &acti
}
if (!hasExplicit)
- for (int ii = 0; ii < actions.count(); ++ii) {
+ for (int ii = 0; ii < actions.size(); ++ii) {
QQuickStateAction &action = actions[ii];
QObject *obj = action.property.object();
@@ -1301,7 +1291,7 @@ QAbstractAnimationJob* QQuickPropertyAction::transition(QQuickStateActions &acti
if (d->value.isValid())
myAction.toValue = d->value;
- QQuickPropertyAnimationPrivate::convertVariant(myAction.toValue, myAction.property.propertyType());
+ QQuickPropertyAnimationPrivate::convertVariant(myAction.toValue, myAction.property.propertyMetaType());
modified << action.property;
data->actions << myAction;
@@ -1310,7 +1300,7 @@ QAbstractAnimationJob* QQuickPropertyAction::transition(QQuickStateActions &acti
}
QActionAnimation *action = new QActionAnimation;
- if (data->actions.count()) {
+ if (data->actions.size()) {
action->setAnimAction(data);
} else {
delete data;
@@ -1382,7 +1372,7 @@ void QQuickNumberAnimation::init()
// ...
]
- transition: Transition {
+ transitions: Transition {
NumberAnimation { properties: "x"; from: 100; duration: 200 }
}
}
@@ -1612,7 +1602,7 @@ QQuickRotationAnimation::~QQuickRotationAnimation()
// ...
]
- transition: Transition {
+ transitions: Transition {
RotationAnimation { properties: "angle"; from: 100; duration: 2000 }
}
}
@@ -1664,14 +1654,16 @@ void QQuickRotationAnimation::setTo(qreal t)
Possible values are:
- \list
- \li RotationAnimation.Numerical (default) - Rotate by linearly interpolating between the two numbers.
- A rotation from 10 to 350 will rotate 340 degrees clockwise.
- \li RotationAnimation.Clockwise - Rotate clockwise between the two values
- \li RotationAnimation.Counterclockwise - Rotate counterclockwise between the two values
- \li RotationAnimation.Shortest - Rotate in the direction that produces the shortest animation path.
- A rotation from 10 to 350 will rotate 20 degrees counterclockwise.
- \endlist
+ \value RotationAnimation.Numerical
+ (default) Rotate by linearly interpolating between the two numbers.
+ A rotation from \c 10 to \c 350 will rotate 340 degrees clockwise.
+ \value RotationAnimation.Clockwise
+ Rotate clockwise between the two values
+ \value RotationAnimation.Counterclockwise
+ Rotate counterclockwise between the two values
+ \value RotationAnimation.Shortest
+ Rotate in the direction that produces the shortest animation path.
+ A rotation from \c 10 to \c 350 will rotate \c 20 degrees counterclockwise.
*/
QQuickRotationAnimation::RotationDirection QQuickRotationAnimation::direction() const
{
@@ -1722,21 +1714,82 @@ void QQuickAnimationGroupPrivate::append_animation(QQmlListProperty<QQuickAbstra
a->setGroup(q);
}
+QQuickAbstractAnimation *QQuickAnimationGroupPrivate::at_animation(QQmlListProperty<QQuickAbstractAnimation> *list, qsizetype index)
+{
+ if (auto q = qmlobject_cast<QQuickAnimationGroup *>(list->object))
+ return q->d_func()->animations.at(index);
+ return nullptr;
+}
+
+qsizetype QQuickAnimationGroupPrivate::count_animation(QQmlListProperty<QQuickAbstractAnimation> *list)
+{
+ if (auto q = qmlobject_cast<QQuickAnimationGroup *>(list->object))
+ return q->d_func()->animations.size();
+ return 0;
+}
+
void QQuickAnimationGroupPrivate::clear_animation(QQmlListProperty<QQuickAbstractAnimation> *list)
{
QQuickAnimationGroup *q = qobject_cast<QQuickAnimationGroup *>(list->object);
if (q) {
- while (q->d_func()->animations.count()) {
+ while (q->d_func()->animations.size()) {
QQuickAbstractAnimation *firstAnim = q->d_func()->animations.at(0);
firstAnim->setGroup(nullptr);
}
}
}
+void QQuickAnimationGroupPrivate::replace_animation(QQmlListProperty<QQuickAbstractAnimation> *list,
+ qsizetype i, QQuickAbstractAnimation *a)
+{
+ if (auto *q = qmlobject_cast<QQuickAnimationGroup *>(list->object)) {
+ if (QQuickAbstractAnimation *anim = q->d_func()->animations.at(i))
+ anim->setGroup(nullptr);
+ if (a)
+ a->setGroup(q, i);
+ }
+}
+
+void QQuickAnimationGroupPrivate::removeLast_animation(QQmlListProperty<QQuickAbstractAnimation> *list)
+{
+ if (auto *q = qobject_cast<QQuickAnimationGroup *>(list->object))
+ q->d_func()->animations.last()->setGroup(nullptr);
+}
+
+void QQuickAnimationGroupPrivate::restartFromCurrentLoop()
+{
+ Q_Q(QQuickAnimationGroup);
+ if (!animationDirty)
+ return;
+
+ animationDirty = false;
+
+ Q_ASSERT(animationInstance);
+ const int currentLoop = animationInstance->currentLoop();
+
+ QSignalBlocker signalBlocker(q);
+ q->stop();
+ q->start();
+
+ Q_ASSERT(animationInstance);
+ // Restarting adjusts animationInstance's loopCount
+ // Since we just want to start it from this loop,
+ // it will be restored again.
+ if (loopCount != -1)
+ animationInstance->setLoopCount(loopCount - currentLoop);
+}
+
+void QQuickAnimationGroupPrivate::animationCurrentLoopChanged(QAbstractAnimationJob *)
+{
+ if (!animationDirty)
+ return;
+ restartFromCurrentLoop();
+}
+
QQuickAnimationGroup::~QQuickAnimationGroup()
{
Q_D(QQuickAnimationGroup);
- for (int i = 0; i < d->animations.count(); ++i)
+ for (int i = 0; i < d->animations.size(); ++i)
d->animations.at(i)->d_func()->group = nullptr;
d->animations.clear();
}
@@ -1744,10 +1797,14 @@ QQuickAnimationGroup::~QQuickAnimationGroup()
QQmlListProperty<QQuickAbstractAnimation> QQuickAnimationGroup::animations()
{
Q_D(QQuickAnimationGroup);
- QQmlListProperty<QQuickAbstractAnimation> list(this, d->animations);
- list.append = &QQuickAnimationGroupPrivate::append_animation;
- list.clear = &QQuickAnimationGroupPrivate::clear_animation;
- return list;
+ return QQmlListProperty<QQuickAbstractAnimation>(
+ this, &(d->animations),
+ &QQuickAnimationGroupPrivate::append_animation,
+ &QQuickAnimationGroupPrivate::count_animation,
+ &QQuickAnimationGroupPrivate::at_animation,
+ &QQuickAnimationGroupPrivate::clear_animation,
+ &QQuickAnimationGroupPrivate::replace_animation,
+ &QQuickAnimationGroupPrivate::removeLast_animation);
}
/*!
@@ -1821,14 +1878,14 @@ QAbstractAnimationJob* QQuickSequentialAnimation::transition(QQuickStateActions
int from = 0;
if (direction == Backward) {
inc = -1;
- from = d->animations.count() - 1;
+ from = d->animations.size() - 1;
}
ThreadingModel execution = threadingModel();
bool valid = d->defaultProperty.isValid();
QAbstractAnimationJob* anim;
- for (int ii = from; ii < d->animations.count() && ii >= 0; ii += inc) {
+ for (int ii = from; ii < d->animations.size() && ii >= 0; ii += inc) {
if (valid)
d->animations.at(ii)->setDefaultTarget(d->defaultProperty);
anim = d->animations.at(ii)->transition(actions, modified, direction, defaultTarget);
@@ -1911,7 +1968,7 @@ QAbstractAnimationJob* QQuickParallelAnimation::transition(QQuickStateActions &a
bool valid = d->defaultProperty.isValid();
QAbstractAnimationJob* anim;
- for (int ii = 0; ii < d->animations.count(); ++ii) {
+ for (int ii = 0; ii < d->animations.size(); ++ii) {
if (valid)
d->animations.at(ii)->setDefaultTarget(d->defaultProperty);
anim = d->animations.at(ii)->transition(actions, modified, direction, defaultTarget);
@@ -1924,42 +1981,67 @@ QAbstractAnimationJob* QQuickParallelAnimation::transition(QQuickStateActions &a
return initInstance(ag);
}
+void QQuickPropertyAnimationPrivate::animationCurrentLoopChanged(QAbstractAnimationJob *)
+{
+ Q_Q(QQuickPropertyAnimation);
+ // We listen to current loop changes in order to restart the animation if e.g. from, to, etc.
+ // are modified while the animation is running.
+ // Restarting is a bit drastic but there is a lot of stuff that commence() (and therefore
+ // QQuickPropertyAnimation::transition() and QQuickPropertyAnimation::createTransitionActions())
+ // does, so we want to avoid trying to take a shortcut and just restart the whole thing.
+ if (ourPropertiesDirty) {
+ ourPropertiesDirty = false;
+
+ // We use animationInstance everywhere for simplicity - if we defined the job parameter
+ // it would be deleted as soon as we call stop().
+ Q_ASSERT(animationInstance);
+ const int currentLoop = animationInstance->currentLoop();
+
+ QSignalBlocker signalBlocker(q);
+ q->stop();
+ q->start();
+
+ Q_ASSERT(animationInstance);
+ // We multiply it ourselves here instead of just saving currentTime(), because otherwise
+ // it seems to accumulate, and changing our properties while the animation is running
+ // can result in the animation starting mid-way through a loop, which is not we want;
+ // we want it to start from the beginning.
+ animationInstance->setCurrentTime(currentLoop * animationInstance->duration());
+ }
+}
+
//convert a variant from string type to another animatable type
-void QQuickPropertyAnimationPrivate::convertVariant(QVariant &variant, int type)
+void QQuickPropertyAnimationPrivate::convertVariant(QVariant &variant, QMetaType type)
{
- if (variant.userType() != QVariant::String) {
+ if (variant.userType() != QMetaType::QString) {
variant.convert(type);
return;
}
- switch (type) {
- case QVariant::Rect:
- case QVariant::RectF:
- case QVariant::Point:
- case QVariant::PointF:
- case QVariant::Size:
- case QVariant::SizeF:
- case QVariant::Color:
- case QVariant::Vector3D:
+ switch (type.id()) {
+ case QMetaType::QRect:
+ case QMetaType::QRectF:
+ case QMetaType::QPoint:
+ case QMetaType::QPointF:
+ case QMetaType::QSize:
+ case QMetaType::QSizeF:
+ case QMetaType::QColor:
+ case QMetaType::QVector3D:
{
bool ok = false;
variant = QQmlStringConverters::variantFromString(variant.toString(), type, &ok);
}
break;
default:
- if (QQmlValueTypeFactory::isValueType((uint)type)) {
- variant.convert(type);
- } else {
- QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(type);
- if (converter)
- variant = converter(variant.toString());
+ if (QQmlMetaType::isValueType(type)) {
+ variant.convert(QMetaType(type));
}
break;
}
}
QQuickBulkValueAnimator::QQuickBulkValueAnimator()
- : QAbstractAnimationJob(), animValue(nullptr), fromSourced(nullptr), m_duration(250)
+ : QAbstractAnimationJob(), animValue(nullptr), fromIsSourced(nullptr), m_duration(250)
{
}
@@ -1988,15 +2070,16 @@ void QQuickBulkValueAnimator::updateCurrentTime(int currentTime)
void QQuickBulkValueAnimator::topLevelAnimationLoopChanged()
{
- //check for new from every top-level loop (when the top level animation is started and all subsequent loops)
- if (fromSourced)
- *fromSourced = false;
+ // Check for new "from" value only when animation has one loop.
+ // Otherwise use the initial "from" value for every iteration.
+ if (m_loopCount == 1 && fromIsSourced)
+ *fromIsSourced = false;
QAbstractAnimationJob::topLevelAnimationLoopChanged();
}
void QQuickBulkValueAnimator::debugAnimation(QDebug d) const
{
- d << "BulkValueAnimation(" << hex << (const void *) this << dec << ")" << "duration:" << duration();
+ d << "BulkValueAnimation(" << Qt::hex << (const void *) this << Qt::dec << ")" << "duration:" << duration();
if (animValue) {
int indentLevel = 1;
@@ -2067,6 +2150,12 @@ void QQuickBulkValueAnimator::debugAnimation(QDebug d) const
Note that PropertyAnimation inherits the abstract \l Animation type.
This includes additional properties and methods for controlling the animation.
+ \section1 Modifying running animations
+
+ Since Qt 6.4, it is possible to set the \l from, \l to, \l duration, and
+ \l easing properties on a top-level animation while it is running. The
+ animation will take the changes into account on the next loop.
+
\sa {Animation and Transitions in Qt Quick}, {Qt Quick Examples - Animation}
*/
@@ -2107,7 +2196,11 @@ void QQuickPropertyAnimation::setDuration(int duration)
if (d->duration == duration)
return;
d->duration = duration;
+ if (d->componentComplete && d->running)
+ d->ourPropertiesDirty = true;
emit durationChanged(duration);
+ if (d->group)
+ d->animationGroupDirty();
}
/*!
@@ -2134,7 +2227,11 @@ void QQuickPropertyAnimation::setFrom(const QVariant &f)
return;
d->from = f;
d->fromIsDefined = f.isValid();
+ if (d->componentComplete && d->running)
+ d->ourPropertiesDirty = true;
emit fromChanged();
+ if (d->group)
+ d->animationGroupDirty();
}
/*!
@@ -2161,7 +2258,11 @@ void QQuickPropertyAnimation::setTo(const QVariant &t)
return;
d->to = t;
d->toIsDefined = t.isValid();
+ if (d->componentComplete && d->running)
+ d->ourPropertiesDirty = true;
emit toChanged();
+ if (d->group)
+ d->animationGroupDirty();
}
/*!
@@ -2356,7 +2457,7 @@ void QQuickPropertyAnimation::setTo(const QVariant &t)
\li Easing curve for a bounce (exponentially decaying parabolic bounce) function easing out/in: deceleration until halfway, then acceleration.
\li \inlineimage qeasingcurve-outinbounce.png
\row
- \li \c Easing.Bezier
+ \li \c Easing.BezierSpline
\li Custom easing curve defined by the easing.bezierCurve property.
\li
\endtable
@@ -2371,7 +2472,7 @@ void QQuickPropertyAnimation::setTo(const QVariant &t)
\c easing.period is only applicable if easing.type is: \c Easing.InElastic, \c Easing.OutElastic,
\c Easing.InOutElastic or \c Easing.OutInElastic.
- \c easing.bezierCurve is only applicable if easing.type is: \c Easing.Bezier. This property is a list<real> containing
+ \c easing.bezierCurve is only applicable if easing.type is: \c Easing.BezierSpline. This property is a list<real> containing
groups of three points defining a curve from 0,0 to 1,1 - control1, control2,
end point: [cx1, cy1, cx2, cy2, endx, endy, ...]. The last point must be 1,1.
@@ -2391,7 +2492,11 @@ void QQuickPropertyAnimation::setEasing(const QEasingCurve &e)
return;
d->easing = e;
+ if (d->componentComplete && d->running)
+ d->ourPropertiesDirty = true;
emit easingChanged(e);
+ if (d->group)
+ d->animationGroupDirty();
}
QObject *QQuickPropertyAnimation::target() const
@@ -2407,6 +2512,8 @@ void QQuickPropertyAnimation::setTargetObject(QObject *o)
return;
d->target = o;
emit targetChanged();
+ if (d->group)
+ d->animationGroupDirty();
}
QString QQuickPropertyAnimation::property() const
@@ -2422,6 +2529,8 @@ void QQuickPropertyAnimation::setProperty(const QString &n)
return;
d->propertyName = n;
emit propertyChanged();
+ if (d->group)
+ d->animationGroupDirty();
}
QString QQuickPropertyAnimation::properties() const
@@ -2438,13 +2547,15 @@ void QQuickPropertyAnimation::setProperties(const QString &prop)
d->properties = prop;
emit propertiesChanged(prop);
+ if (d->group)
+ d->animationGroupDirty();
}
/*!
\qmlproperty string QtQuick::PropertyAnimation::properties
- \qmlproperty list<Object> QtQuick::PropertyAnimation::targets
+ \qmlproperty list<QtObject> QtQuick::PropertyAnimation::targets
\qmlproperty string QtQuick::PropertyAnimation::property
- \qmlproperty Object QtQuick::PropertyAnimation::target
+ \qmlproperty QtObject QtQuick::PropertyAnimation::target
These properties are used as a set to determine which properties should be animated.
The singular and plural forms are functionally identical, e.g.
@@ -2495,8 +2606,18 @@ void QQuickPropertyAnimation::setProperties(const QString &prop)
Item { id: uselessItem }
states: State {
name: "state1"
- PropertyChanges { target: theRect; x: 200; y: 200; z: 4 }
- PropertyChanges { target: uselessItem; x: 10; y: 10; z: 2 }
+ PropertyChanges {
+ theRect {
+ x: 200
+ y: 200
+ z: 4
+ }
+ uselessItem {
+ x: 10
+ y: 10
+ z: 2
+ }
+ }
}
transitions: Transition {
//animate both theRect's and uselessItem's x and y to their final values
@@ -2533,18 +2654,49 @@ void QQuickPropertyAnimation::setProperties(const QString &prop)
QQmlListProperty<QObject> QQuickPropertyAnimation::targets()
{
Q_D(QQuickPropertyAnimation);
- return QQmlListProperty<QObject>(this, d->targets);
+ using ListPtr = QList<QPointer<QObject>> *;
+ using LP = QQmlListProperty<QObject>;
+ LP::AppendFunction appendFn = [](LP *prop, QObject *value)
+ {
+ static_cast<ListPtr>(prop->data)->append(value);
+ };
+ LP::CountFunction countFn = [](LP *prop)
+ {
+ return static_cast<ListPtr>(prop->data)->size();
+ };
+
+ LP::AtFunction atFn = [](LP *prop, qsizetype index) -> QObject *
+ {
+ return static_cast<ListPtr>(prop->data)->at(index);
+ };
+
+ LP::ClearFunction clearFN = [](LP *prop)
+ {
+ return static_cast<ListPtr>(prop->data)->clear();
+ };
+
+ LP::ReplaceFunction replaceFn = [](LP *prop, qsizetype index, QObject *value)
+ {
+ static_cast<ListPtr>(prop->data)->replace(index, value);
+ };
+
+ LP::RemoveLastFunction removeLastFn = [](LP *prop)
+ {
+ static_cast<ListPtr>(prop->data)->removeLast();
+ };
+
+ return QQmlListProperty<QObject>(this, &(d->targets), appendFn, countFn, atFn, clearFN, replaceFn, removeLastFn);
}
/*!
- \qmlproperty list<Object> QtQuick::PropertyAnimation::exclude
+ \qmlproperty list<QtObject> QtQuick::PropertyAnimation::exclude
This property holds the items not to be affected by this animation.
\sa PropertyAnimation::targets
*/
QQmlListProperty<QObject> QQuickPropertyAnimation::exclude()
{
Q_D(QQuickPropertyAnimation);
- return QQmlListProperty<QObject>(this, d->exclude);
+ return QQmlListProperty<QObject>(this, &(d->exclude));
}
void QQuickAnimationPropertyUpdater::setValue(qreal v)
@@ -2553,16 +2705,16 @@ void QQuickAnimationPropertyUpdater::setValue(qreal v)
wasDeleted = &deleted;
if (reverse)
v = 1 - v;
- for (int ii = 0; ii < actions.count(); ++ii) {
+ for (int ii = 0; ii < actions.size(); ++ii) {
QQuickStateAction &action = actions[ii];
if (v == 1.) {
QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
} else {
- if (!fromSourced && !fromDefined) {
+ if (!fromIsSourced && !fromIsDefined) {
action.fromValue = action.property.read();
if (interpolatorType) {
- QQuickPropertyAnimationPrivate::convertVariant(action.fromValue, interpolatorType);
+ QQuickPropertyAnimationPrivate::convertVariant(action.fromValue, QMetaType(interpolatorType));
}
}
if (!interpolatorType) {
@@ -2579,13 +2731,13 @@ void QQuickAnimationPropertyUpdater::setValue(qreal v)
return;
}
wasDeleted = nullptr;
- fromSourced = true;
+ fromIsSourced = true;
}
void QQuickAnimationPropertyUpdater::debugUpdater(QDebug d, int indentLevel) const
{
QByteArray ind(indentLevel, ' ');
- for (int i = 0; i < actions.count(); ++i) {
+ for (int i = 0; i < actions.size(); ++i) {
const QQuickStateAction &action = actions.at(i);
d << "\n" << ind.constData() << "target:" << action.property.object() << "property:" << action.property.name()
<< "from:" << action.fromValue << "to:" << action.toValue;
@@ -2600,12 +2752,12 @@ QQuickStateActions QQuickPropertyAnimation::createTransitionActions(QQuickStateA
QQuickStateActions newActions;
QStringList props = d->properties.isEmpty() ? QStringList() : d->properties.split(QLatin1Char(','));
- for (int ii = 0; ii < props.count(); ++ii)
+ for (int ii = 0; ii < props.size(); ++ii)
props[ii] = props.at(ii).trimmed();
if (!d->propertyName.isEmpty())
props << d->propertyName;
- QList<QObject*> targets = d->targets;
+ QList<QPointer<QObject>> targets = d->targets;
if (d->target)
targets.append(d->target);
@@ -2632,25 +2784,29 @@ QQuickStateActions QQuickPropertyAnimation::createTransitionActions(QQuickStateA
QVector<QString> errorMessages;
bool successfullyCreatedDefaultProperty = false;
- for (int i = 0; i < props.count(); ++i) {
- for (int j = 0; j < targets.count(); ++j) {
+ for (int i = 0; i < props.size(); ++i) {
+ for (int j = 0; j < targets.size(); ++j) {
+ const auto& guarded = targets.at(j);
+ if (guarded.isNull())
+ continue;
+ QObject *target = guarded.get();
QQuickStateAction myAction;
QString errorMessage;
- const QString propertyName = props.at(i);
- myAction.property = d->createProperty(targets.at(j), propertyName, this, &errorMessage);
+ const QString &propertyName = props.at(i);
+ myAction.property = d->createProperty(target, propertyName, this, &errorMessage);
if (myAction.property.isValid()) {
if (usingDefaultProperties)
successfullyCreatedDefaultProperty = true;
if (d->fromIsDefined) {
myAction.fromValue = d->from;
- d->convertVariant(myAction.fromValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType());
+ d->convertVariant(myAction.fromValue, d->interpolatorType ? QMetaType(d->interpolatorType) : myAction.property.propertyMetaType());
}
myAction.toValue = d->to;
- d->convertVariant(myAction.toValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType());
+ d->convertVariant(myAction.toValue, d->interpolatorType ? QMetaType(d->interpolatorType) : myAction.property.propertyMetaType());
newActions << myAction;
hasExplicit = true;
- for (int ii = 0; ii < actions.count(); ++ii) {
+ for (int ii = 0; ii < actions.size(); ++ii) {
QQuickStateAction &action = actions[ii];
if (action.property.object() == myAction.property.object() &&
myAction.property.name() == action.property.name()) {
@@ -2665,13 +2821,13 @@ QQuickStateActions QQuickPropertyAnimation::createTransitionActions(QQuickStateA
}
if (!successfullyCreatedDefaultProperty) {
- for (const QString &errorMessage : qAsConst(errorMessages))
+ for (const QString &errorMessage : std::as_const(errorMessages))
qmlWarning(this) << errorMessage;
}
}
if (!hasExplicit)
- for (int ii = 0; ii < actions.count(); ++ii) {
+ for (int ii = 0; ii < actions.size(); ++ii) {
QQuickStateAction &action = actions[ii];
QObject *obj = action.property.object();
@@ -2693,8 +2849,8 @@ QQuickStateActions QQuickPropertyAnimation::createTransitionActions(QQuickStateA
if (d->toIsDefined)
myAction.toValue = d->to;
- d->convertVariant(myAction.fromValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType());
- d->convertVariant(myAction.toValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType());
+ d->convertVariant(myAction.fromValue, d->interpolatorType ? QMetaType(d->interpolatorType) : myAction.property.propertyMetaType());
+ d->convertVariant(myAction.toValue, d->interpolatorType ? QMetaType(d->interpolatorType) : myAction.property.propertyMetaType());
modified << action.property;
@@ -2723,11 +2879,11 @@ QAbstractAnimationJob* QQuickPropertyAnimation::transition(QQuickStateActions &a
data->interpolatorType = d->interpolatorType;
data->interpolator = d->interpolator;
data->reverse = direction == Backward ? true : false;
- data->fromSourced = false;
- data->fromDefined = d->fromIsDefined;
+ data->fromIsSourced = false;
+ data->fromIsDefined = d->fromIsDefined;
data->actions = dataActions;
animator->setAnimValue(data);
- animator->setFromSourcedValue(&data->fromSourced);
+ animator->setFromIsSourcedValue(&data->fromIsSourced);
d->actions = &data->actions; //remove this?
}