1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40/*!
41 \class QPropertyAnimation
42 \inmodule QtCore
43 \brief The QPropertyAnimation class animates Qt properties.
44 \since 4.6
45
46 \ingroup animation
47
48 QPropertyAnimation interpolates over \l{Qt's Property System}{Qt
49 properties}. As property values are stored in \l{QVariant}s, the
50 class inherits QVariantAnimation, and supports animation of the
51 same \l{QMetaType::Type}{meta types} as its super class.
52
53 A class declaring properties must be a QObject. To make it
54 possible to animate a property, it must provide a setter (so that
55 QPropertyAnimation can set the property's value). Note that this
56 makes it possible to animate many of Qt's widgets. Let's look at
57 an example:
58
59 \snippet code/src_corelib_animation_qpropertyanimation.cpp 0
60
61 The property name and the QObject instance of which property
62 should be animated are passed to the constructor. You can then
63 specify the start and end value of the property. The procedure is
64 equal for properties in classes you have implemented
65 yourself--just check with QVariantAnimation that your QVariant
66 type is supported.
67
68 The QVariantAnimation class description explains how to set up the
69 animation in detail. Note, however, that if a start value is not
70 set, the property will start at the value it had when the
71 QPropertyAnimation instance was created.
72
73 QPropertyAnimation works like a charm on its own. For complex
74 animations that, for instance, contain several objects,
75 QAnimationGroup is provided. An animation group is an animation
76 that can contain other animations, and that can manage when its
77 animations are played. Look at QParallelAnimationGroup for an
78 example.
79
80 \sa QVariantAnimation, QAnimationGroup, {The Animation Framework}
81*/
82
83#include "qpropertyanimation.h"
84#include "qanimationgroup.h"
85#include "qpropertyanimation_p.h"
86
87#include <QtCore/QMutex>
88#include <QtCore/private/qlocking_p.h>
89
90QT_BEGIN_NAMESPACE
91
92void QPropertyAnimationPrivate::updateMetaProperty()
93{
94 if (!target || propertyName.isEmpty()) {
95 propertyType = QMetaType::UnknownType;
96 propertyIndex = -1;
97 return;
98 }
99
100 //propertyType will be set to a valid type only if there is a Q_PROPERTY
101 //otherwise it will be set to QVariant::Invalid at the end of this function
102 propertyType = targetValue->property(propertyName).userType();
103 propertyIndex = targetValue->metaObject()->indexOfProperty(propertyName);
104
105 if (propertyType != QMetaType::UnknownType)
106 convertValues(propertyType);
107 if (propertyIndex == -1) {
108 //there is no Q_PROPERTY on the object
109 propertyType = QMetaType::UnknownType;
110 if (!targetValue->dynamicPropertyNames().contains(propertyName))
111 qWarning("QPropertyAnimation: you're trying to animate a non-existing property %s of your QObject", propertyName.constData());
112 } else if (!targetValue->metaObject()->property(propertyIndex).isWritable()) {
113 qWarning("QPropertyAnimation: you're trying to animate the non-writable property %s of your QObject", propertyName.constData());
114 }
115}
116
117void QPropertyAnimationPrivate::updateProperty(const QVariant &newValue)
118{
119 if (state == QAbstractAnimation::Stopped)
120 return;
121
122 if (!target) {
123 q_func()->stop(); //the target was destroyed we need to stop the animation
124 return;
125 }
126
127 if (newValue.userType() == propertyType) {
128 //no conversion is needed, we directly call the QMetaObject::metacall
129 //check QMetaProperty::write for an explanation of these
130 int status = -1;
131 int flags = 0;
132 void *argv[] = { const_cast<void *>(newValue.constData()), const_cast<QVariant *>(&newValue), &status, &flags };
133 QMetaObject::metacall(targetValue, QMetaObject::WriteProperty, propertyIndex, argv);
134 } else {
135 targetValue->setProperty(propertyName.constData(), newValue);
136 }
137}
138
139/*!
140 Construct a QPropertyAnimation object. \a parent is passed to QObject's
141 constructor.
142*/
143QPropertyAnimation::QPropertyAnimation(QObject *parent)
144 : QVariantAnimation(*new QPropertyAnimationPrivate, parent)
145{
146}
147
148/*!
149 Construct a QPropertyAnimation object. \a parent is passed to QObject's
150 constructor. The animation changes the property \a propertyName on \a
151 target. The default duration is 250ms.
152
153 \sa targetObject, propertyName
154*/
155QPropertyAnimation::QPropertyAnimation(QObject *target, const QByteArray &propertyName, QObject *parent)
156 : QVariantAnimation(*new QPropertyAnimationPrivate, parent)
157{
158 setTargetObject(target);
159 setPropertyName(propertyName);
160}
161
162/*!
163 Destroys the QPropertyAnimation instance.
164 */
165QPropertyAnimation::~QPropertyAnimation()
166{
167 stop();
168}
169
170/*!
171 \property QPropertyAnimation::targetObject
172 \brief the target QObject for this animation.
173
174 This property defines the target QObject for this animation.
175 */
176QObject *QPropertyAnimation::targetObject() const
177{
178 return d_func()->target.data();
179}
180
181void QPropertyAnimation::setTargetObject(QObject *target)
182{
183 Q_D(QPropertyAnimation);
184 if (d->target.data() == target)
185 return;
186
187 if (d->state != QAbstractAnimation::Stopped) {
188 qWarning("QPropertyAnimation::setTargetObject: you can't change the target of a running animation");
189 return;
190 }
191
192 d->target = d->targetValue = target;
193 d->updateMetaProperty();
194}
195
196/*!
197 \property QPropertyAnimation::propertyName
198 \brief the target property name for this animation
199
200 This property defines the target property name for this animation. The
201 property name is required for the animation to operate.
202 */
203QByteArray QPropertyAnimation::propertyName() const
204{
205 Q_D(const QPropertyAnimation);
206 return d->propertyName;
207}
208
209void QPropertyAnimation::setPropertyName(const QByteArray &propertyName)
210{
211 Q_D(QPropertyAnimation);
212 if (d->state != QAbstractAnimation::Stopped) {
213 qWarning("QPropertyAnimation::setPropertyName: you can't change the property name of a running animation");
214 return;
215 }
216
217 d->propertyName = propertyName;
218 d->updateMetaProperty();
219}
220
221
222/*!
223 \reimp
224 */
225bool QPropertyAnimation::event(QEvent *event)
226{
227 return QVariantAnimation::event(event);
228}
229
230/*!
231 This virtual function is called by QVariantAnimation whenever the current value
232 changes. \a value is the new, updated value. It updates the current value
233 of the property on the target object.
234
235 \sa currentValue, currentTime
236 */
237void QPropertyAnimation::updateCurrentValue(const QVariant &value)
238{
239 Q_D(QPropertyAnimation);
240 d->updateProperty(value);
241}
242
243/*!
244 \reimp
245
246 If the startValue is not defined when the state of the animation changes from Stopped to Running,
247 the current property value is used as the initial value for the animation.
248*/
249void QPropertyAnimation::updateState(QAbstractAnimation::State newState,
250 QAbstractAnimation::State oldState)
251{
252 Q_D(QPropertyAnimation);
253
254 if (!d->target && oldState == Stopped) {
255 qWarning("QPropertyAnimation::updateState (%s): Changing state of an animation without target",
256 d->propertyName.constData());
257 return;
258 }
259
260 QVariantAnimation::updateState(newState, oldState);
261
262 QPropertyAnimation *animToStop = nullptr;
263 {
264 static QBasicMutex mutex;
265 auto locker = qt_unique_lock(mutex);
266 typedef QPair<QObject *, QByteArray> QPropertyAnimationPair;
267 typedef QHash<QPropertyAnimationPair, QPropertyAnimation*> QPropertyAnimationHash;
268 static QPropertyAnimationHash hash;
269 //here we need to use value because we need to know to which pointer
270 //the animation was referring in case stopped because the target was destroyed
271 QPropertyAnimationPair key(d->targetValue, d->propertyName);
272 if (newState == Running) {
273 d->updateMetaProperty();
274 animToStop = hash.value(key, nullptr);
275 hash.insert(key, this);
276 locker.unlock();
277 // update the default start value
278 if (oldState == Stopped) {
279 d->setDefaultStartEndValue(d->targetValue->property(d->propertyName.constData()));
280 //let's check if we have a start value and an end value
281 const char *what = nullptr;
282 if (!startValue().isValid() && (d->direction == Backward || !d->defaultStartEndValue.isValid())) {
283 what = "start";
284 }
285 if (!endValue().isValid() && (d->direction == Forward || !d->defaultStartEndValue.isValid())) {
286 if (what)
287 what = "start and end";
288 else
289 what = "end";
290 }
291 if (Q_UNLIKELY(what)) {
292 qWarning("QPropertyAnimation::updateState (%s, %s, %ls): starting an animation without %s value",
293 d->propertyName.constData(), d->target.data()->metaObject()->className(),
294 qUtf16Printable(d->target.data()->objectName()), what);
295 }
296 }
297 } else if (hash.value(key) == this) {
298 hash.remove(key);
299 }
300 }
301
302 //we need to do that after the mutex was unlocked
303 if (animToStop) {
304 // try to stop the top level group
305 QAbstractAnimation *current = animToStop;
306 while (current->group() && current->state() != Stopped)
307 current = current->group();
308 current->stop();
309 }
310}
311
312QT_END_NAMESPACE
313
314#include "moc_qpropertyanimation.cpp"
315