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#include "qvariantanimation.h"
41#include "qvariantanimation_p.h"
42
43#include <QtCore/qrect.h>
44#include <QtCore/qline.h>
45#include <QtCore/qmutex.h>
46#include <QtCore/private/qlocking_p.h>
47
48#include <algorithm>
49
50QT_BEGIN_NAMESPACE
51
52/*!
53 \class QVariantAnimation
54 \inmodule QtCore
55 \ingroup animation
56 \brief The QVariantAnimation class provides a base class for animations.
57 \since 4.6
58
59 This class is part of \l{The Animation Framework}. It serves as a
60 base class for property and item animations, with functions for
61 shared functionality.
62
63 The class performs interpolation over
64 \l{QVariant}s, but leaves using the interpolated values to its
65 subclasses. Currently, Qt provides QPropertyAnimation, which
66 animates Qt \l{Qt's Property System}{properties}. See the
67 QPropertyAnimation class description if you wish to animate such
68 properties.
69
70 You can then set start and end values for the property by calling
71 setStartValue() and setEndValue(), and finally call start() to
72 start the animation. QVariantAnimation will interpolate the
73 property of the target object and emit valueChanged(). To react to
74 a change in the current value you have to reimplement the
75 updateCurrentValue() virtual function or connect to said signal.
76
77 It is also possible to set values at specified steps situated
78 between the start and end value. The interpolation will then
79 touch these points at the specified steps. Note that the start and
80 end values are defined as the key values at 0.0 and 1.0.
81
82 There are two ways to affect how QVariantAnimation interpolates
83 the values. You can set an easing curve by calling
84 setEasingCurve(), and configure the duration by calling
85 setDuration(). You can change how the \l{QVariant}s are interpolated
86 by creating a subclass of QVariantAnimation, and reimplementing
87 the virtual interpolated() function.
88
89 Subclassing QVariantAnimation can be an alternative if you have
90 \l{QVariant}s that you do not wish to declare as Qt properties.
91 Note, however, that you in most cases will be better off declaring
92 your QVariant as a property.
93
94 Not all QVariant types are supported. Below is a list of currently
95 supported QVariant types:
96
97 \list
98 \li \l{QMetaType::}{Int}
99 \li \l{QMetaType::}{UInt}
100 \li \l{QMetaType::}{Double}
101 \li \l{QMetaType::}{Float}
102 \li \l{QMetaType::}{QLine}
103 \li \l{QMetaType::}{QLineF}
104 \li \l{QMetaType::}{QPoint}
105 \li \l{QMetaType::}{QPointF}
106 \li \l{QMetaType::}{QSize}
107 \li \l{QMetaType::}{QSizeF}
108 \li \l{QMetaType::}{QRect}
109 \li \l{QMetaType::}{QRectF}
110 \li \l{QMetaType::}{QColor}
111 \endlist
112
113 If you need to interpolate other variant types, including custom
114 types, you have to implement interpolation for these yourself.
115 To do this, you can register an interpolator function for a given
116 type. This function takes 3 parameters: the start value, the end value,
117 and the current progress.
118
119 Example:
120 \snippet code/src_corelib_animation_qvariantanimation.cpp 0
121
122 Another option is to reimplement interpolated(), which returns
123 interpolation values for the value being interpolated.
124
125 \omit We need some snippets around here. \endomit
126
127 \sa QPropertyAnimation, QAbstractAnimation, {The Animation Framework}
128*/
129
130/*!
131 \fn void QVariantAnimation::valueChanged(const QVariant &value)
132
133 QVariantAnimation emits this signal whenever the current \a value changes.
134
135 \sa currentValue, startValue, endValue
136*/
137
138/*!
139 This virtual function is called every time the animation's current
140 value changes. The \a value argument is the new current value.
141
142 The base class implementation does nothing.
143
144 \sa currentValue
145*/
146void QVariantAnimation::updateCurrentValue(const QVariant &) {}
147
148static bool animationValueLessThan(const QVariantAnimation::KeyValue &p1, const QVariantAnimation::KeyValue &p2)
149{
150 return p1.first < p2.first;
151}
152
153static QVariant defaultInterpolator(const void *, const void *, qreal)
154{
155 return QVariant();
156}
157
158template<> Q_INLINE_TEMPLATE QRect _q_interpolate(const QRect &f, const QRect &t, qreal progress)
159{
160 QRect ret;
161 ret.setCoords(_q_interpolate(f.left(), t.left(), progress),
162 _q_interpolate(f.top(), t.top(), progress),
163 _q_interpolate(f.right(), t.right(), progress),
164 _q_interpolate(f.bottom(), t.bottom(), progress));
165 return ret;
166}
167
168template<> Q_INLINE_TEMPLATE QRectF _q_interpolate(const QRectF &f, const QRectF &t, qreal progress)
169{
170 qreal x1, y1, w1, h1;
171 f.getRect(&x1, &y1, &w1, &h1);
172 qreal x2, y2, w2, h2;
173 t.getRect(&x2, &y2, &w2, &h2);
174 return QRectF(_q_interpolate(x1, x2, progress), _q_interpolate(y1, y2, progress),
175 _q_interpolate(w1, w2, progress), _q_interpolate(h1, h2, progress));
176}
177
178template<> Q_INLINE_TEMPLATE QLine _q_interpolate(const QLine &f, const QLine &t, qreal progress)
179{
180 return QLine( _q_interpolate(f.p1(), t.p1(), progress), _q_interpolate(f.p2(), t.p2(), progress));
181}
182
183template<> Q_INLINE_TEMPLATE QLineF _q_interpolate(const QLineF &f, const QLineF &t, qreal progress)
184{
185 return QLineF( _q_interpolate(f.p1(), t.p1(), progress), _q_interpolate(f.p2(), t.p2(), progress));
186}
187
188QVariantAnimationPrivate::QVariantAnimationPrivate() : duration(250), interpolator(&defaultInterpolator)
189{ }
190
191void QVariantAnimationPrivate::convertValues(int t)
192{
193 auto type = QMetaType(t);
194 //this ensures that all the keyValues are of type t
195 for (int i = 0; i < keyValues.count(); ++i) {
196 QVariantAnimation::KeyValue &pair = keyValues[i];
197 pair.second.convert(type);
198 }
199 //we also need update to the current interval if needed
200 currentInterval.start.second.convert(type);
201 currentInterval.end.second.convert(type);
202
203 //... and the interpolator
204 updateInterpolator();
205}
206
207void QVariantAnimationPrivate::updateInterpolator()
208{
209 int type = currentInterval.start.second.userType();
210 if (type == currentInterval.end.second.userType())
211 interpolator = getInterpolator(type);
212 else
213 interpolator = nullptr;
214
215 //we make sure that the interpolator is always set to something
216 if (!interpolator)
217 interpolator = &defaultInterpolator;
218}
219
220/*!
221 \internal
222 The goal of this function is to update the currentInterval member. As a consequence, we also
223 need to update the currentValue.
224 Set \a force to true to always recalculate the interval.
225*/
226void QVariantAnimationPrivate::recalculateCurrentInterval(bool force/*=false*/)
227{
228 // can't interpolate if we don't have at least 2 values
229 if ((keyValues.count() + (defaultStartEndValue.isValid() ? 1 : 0)) < 2)
230 return;
231
232 const qreal endProgress = (direction == QAbstractAnimation::Forward) ? qreal(1) : qreal(0);
233 const qreal progress = easing.valueForProgress(((duration == 0) ? endProgress : qreal(currentTime) / qreal(duration)));
234
235 //0 and 1 are still the boundaries
236 if (force || (currentInterval.start.first > 0 && progress < currentInterval.start.first)
237 || (currentInterval.end.first < 1 && progress > currentInterval.end.first)) {
238 //let's update currentInterval
239 QVariantAnimation::KeyValues::const_iterator it = std::lower_bound(keyValues.constBegin(),
240 keyValues.constEnd(),
241 qMakePair(progress, QVariant()),
242 animationValueLessThan);
243 if (it == keyValues.constBegin()) {
244 //the item pointed to by it is the start element in the range
245 if (it->first == 0 && keyValues.count() > 1) {
246 currentInterval.start = *it;
247 currentInterval.end = *(it+1);
248 } else {
249 currentInterval.start = qMakePair(qreal(0), defaultStartEndValue);
250 currentInterval.end = *it;
251 }
252 } else if (it == keyValues.constEnd()) {
253 --it; //position the iterator on the last item
254 if (it->first == 1 && keyValues.count() > 1) {
255 //we have an end value (item with progress = 1)
256 currentInterval.start = *(it-1);
257 currentInterval.end = *it;
258 } else {
259 //we use the default end value here
260 currentInterval.start = *it;
261 currentInterval.end = qMakePair(qreal(1), defaultStartEndValue);
262 }
263 } else {
264 currentInterval.start = *(it-1);
265 currentInterval.end = *it;
266 }
267
268 // update all the values of the currentInterval
269 updateInterpolator();
270 }
271 setCurrentValueForProgress(progress);
272}
273
274void QVariantAnimationPrivate::setCurrentValueForProgress(const qreal progress)
275{
276 Q_Q(QVariantAnimation);
277
278 const qreal startProgress = currentInterval.start.first;
279 const qreal endProgress = currentInterval.end.first;
280 const qreal localProgress = (progress - startProgress) / (endProgress - startProgress);
281
282 QVariant ret = q->interpolated(currentInterval.start.second,
283 currentInterval.end.second,
284 localProgress);
285 qSwap(currentValue, ret);
286 q->updateCurrentValue(currentValue);
287 static QBasicAtomicInt changedSignalIndex = Q_BASIC_ATOMIC_INITIALIZER(0);
288 if (!changedSignalIndex.loadRelaxed()) {
289 //we keep the mask so that we emit valueChanged only when needed (for performance reasons)
290 changedSignalIndex.testAndSetRelaxed(0, signalIndex("valueChanged(QVariant)"));
291 }
292 if (isSignalConnected(changedSignalIndex.loadRelaxed()) && currentValue != ret) {
293 //the value has changed
294 emit q->valueChanged(currentValue);
295 }
296}
297
298QVariant QVariantAnimationPrivate::valueAt(qreal step) const
299{
300 QVariantAnimation::KeyValues::const_iterator result =
301 std::lower_bound(keyValues.constBegin(), keyValues.constEnd(), qMakePair(step, QVariant()), animationValueLessThan);
302 if (result != keyValues.constEnd() && !animationValueLessThan(qMakePair(step, QVariant()), *result))
303 return result->second;
304
305 return QVariant();
306}
307
308void QVariantAnimationPrivate::setValueAt(qreal step, const QVariant &value)
309{
310 if (step < qreal(0.0) || step > qreal(1.0)) {
311 qWarning("QVariantAnimation::setValueAt: invalid step = %f", step);
312 return;
313 }
314
315 QVariantAnimation::KeyValue pair(step, value);
316
317 QVariantAnimation::KeyValues::iterator result = std::lower_bound(keyValues.begin(), keyValues.end(), pair, animationValueLessThan);
318 if (result == keyValues.end() || result->first != step) {
319 keyValues.insert(result, pair);
320 } else {
321 if (value.isValid())
322 result->second = value; // replaces the previous value
323 else
324 keyValues.erase(result); // removes the previous value
325 }
326
327 recalculateCurrentInterval(/*force=*/true);
328}
329
330void QVariantAnimationPrivate::setDefaultStartEndValue(const QVariant &value)
331{
332 defaultStartEndValue = value;
333 recalculateCurrentInterval(/*force=*/true);
334}
335
336/*!
337 Construct a QVariantAnimation object. \a parent is passed to QAbstractAnimation's
338 constructor.
339*/
340QVariantAnimation::QVariantAnimation(QObject *parent) : QAbstractAnimation(*new QVariantAnimationPrivate, parent)
341{
342}
343
344/*!
345 \internal
346*/
347QVariantAnimation::QVariantAnimation(QVariantAnimationPrivate &dd, QObject *parent) : QAbstractAnimation(dd, parent)
348{
349}
350
351/*!
352 Destroys the animation.
353*/
354QVariantAnimation::~QVariantAnimation()
355{
356}
357
358/*!
359 \property QVariantAnimation::easingCurve
360 \brief the easing curve of the animation
361
362 This property defines the easing curve of the animation. By
363 default, a linear easing curve is used, resulting in linear
364 interpolation. Other curves are provided, for instance,
365 QEasingCurve::InCirc, which provides a circular entry curve.
366 Another example is QEasingCurve::InOutElastic, which provides an
367 elastic effect on the values of the interpolated variant.
368
369 QVariantAnimation will use the QEasingCurve::valueForProgress() to
370 transform the "normalized progress" (currentTime / totalDuration)
371 of the animation into the effective progress actually
372 used by the animation. It is this effective progress that will be
373 the progress when interpolated() is called. Also, the steps in the
374 keyValues are referring to this effective progress.
375
376 The easing curve is used with the interpolator, the interpolated()
377 virtual function, and the animation's duration to control how the
378 current value changes as the animation progresses.
379*/
380QEasingCurve QVariantAnimation::easingCurve() const
381{
382 Q_D(const QVariantAnimation);
383 return d->easing;
384}
385
386void QVariantAnimation::setEasingCurve(const QEasingCurve &easing)
387{
388 Q_D(QVariantAnimation);
389 d->easing = easing;
390 d->recalculateCurrentInterval();
391}
392
393typedef QList<QVariantAnimation::Interpolator> QInterpolatorVector;
394Q_GLOBAL_STATIC(QInterpolatorVector, registeredInterpolators)
395static QBasicMutex registeredInterpolatorsMutex;
396
397/*!
398 \fn template <typename T> void qRegisterAnimationInterpolator(QVariant (*func)(const T &from, const T &to, qreal progress))
399 \relates QVariantAnimation
400 \threadsafe
401
402 Registers a custom interpolator \a func for the template type \c{T}.
403 The interpolator has to be registered before the animation is constructed.
404 To unregister (and use the default interpolator) set \a func to \nullptr.
405 */
406
407/*!
408 \internal
409 \typedef QVariantAnimation::Interpolator
410
411 This is a typedef for a pointer to a function with the following
412 signature:
413 \snippet code/src_corelib_animation_qvariantanimation.cpp 1
414
415*/
416
417/*!
418 * \internal
419 * Registers a custom interpolator \a func for the specific \a interpolationType.
420 * The interpolator has to be registered before the animation is constructed.
421 * To unregister (and use the default interpolator) set \a func to \nullptr.
422 */
423void QVariantAnimation::registerInterpolator(QVariantAnimation::Interpolator func, int interpolationType)
424{
425 // will override any existing interpolators
426 QInterpolatorVector *interpolators = registeredInterpolators();
427 // When built on solaris with GCC, the destructors can be called
428 // in such an order that we get here with interpolators == NULL,
429 // to continue causes the app to crash on exit with a SEGV
430 if (interpolators) {
431 const auto locker = qt_scoped_lock(registeredInterpolatorsMutex);
432 if (int(interpolationType) >= interpolators->count())
433 interpolators->resize(int(interpolationType) + 1);
434 interpolators->replace(interpolationType, func);
435 }
436}
437
438
439template<typename T> static inline QVariantAnimation::Interpolator castToInterpolator(QVariant (*func)(const T &from, const T &to, qreal progress))
440{
441 return reinterpret_cast<QVariantAnimation::Interpolator>(reinterpret_cast<void(*)()>(func));
442}
443
444QVariantAnimation::Interpolator QVariantAnimationPrivate::getInterpolator(int interpolationType)
445{
446 {
447 QInterpolatorVector *interpolators = registeredInterpolators();
448 const auto locker = qt_scoped_lock(registeredInterpolatorsMutex);
449 QVariantAnimation::Interpolator ret = nullptr;
450 if (interpolationType < interpolators->count()) {
451 ret = interpolators->at(interpolationType);
452 if (ret) return ret;
453 }
454 }
455
456 switch(interpolationType)
457 {
458 case QMetaType::Int:
459 return castToInterpolator(_q_interpolateVariant<int>);
460 case QMetaType::UInt:
461 return castToInterpolator(_q_interpolateVariant<uint>);
462 case QMetaType::Double:
463 return castToInterpolator(_q_interpolateVariant<double>);
464 case QMetaType::Float:
465 return castToInterpolator(_q_interpolateVariant<float>);
466 case QMetaType::QLine:
467 return castToInterpolator(_q_interpolateVariant<QLine>);
468 case QMetaType::QLineF:
469 return castToInterpolator(_q_interpolateVariant<QLineF>);
470 case QMetaType::QPoint:
471 return castToInterpolator(_q_interpolateVariant<QPoint>);
472 case QMetaType::QPointF:
473 return castToInterpolator(_q_interpolateVariant<QPointF>);
474 case QMetaType::QSize:
475 return castToInterpolator(_q_interpolateVariant<QSize>);
476 case QMetaType::QSizeF:
477 return castToInterpolator(_q_interpolateVariant<QSizeF>);
478 case QMetaType::QRect:
479 return castToInterpolator(_q_interpolateVariant<QRect>);
480 case QMetaType::QRectF:
481 return castToInterpolator(_q_interpolateVariant<QRectF>);
482 default:
483 return nullptr; //this type is not handled
484 }
485}
486
487/*!
488 \property QVariantAnimation::duration
489 \brief the duration of the animation
490
491 This property describes the duration in milliseconds of the
492 animation. The default duration is 250 milliseconds.
493
494 \sa QAbstractAnimation::duration()
495 */
496int QVariantAnimation::duration() const
497{
498 Q_D(const QVariantAnimation);
499 return d->duration;
500}
501
502void QVariantAnimation::setDuration(int msecs)
503{
504 Q_D(QVariantAnimation);
505 if (msecs < 0) {
506 qWarning("QVariantAnimation::setDuration: cannot set a negative duration");
507 return;
508 }
509 if (d->duration == msecs)
510 return;
511 d->duration = msecs;
512 d->recalculateCurrentInterval();
513}
514
515/*!
516 \property QVariantAnimation::startValue
517 \brief the optional start value of the animation
518
519 This property describes the optional start value of the animation. If
520 omitted, or if a null QVariant is assigned as the start value, the
521 animation will use the current position of the end when the animation
522 is started.
523
524 \sa endValue
525*/
526QVariant QVariantAnimation::startValue() const
527{
528 return keyValueAt(0);
529}
530
531void QVariantAnimation::setStartValue(const QVariant &value)
532{
533 setKeyValueAt(0, value);
534}
535
536/*!
537 \property QVariantAnimation::endValue
538 \brief the end value of the animation
539
540 This property describes the end value of the animation.
541
542 \sa startValue
543 */
544QVariant QVariantAnimation::endValue() const
545{
546 return keyValueAt(1);
547}
548
549void QVariantAnimation::setEndValue(const QVariant &value)
550{
551 setKeyValueAt(1, value);
552}
553
554
555/*!
556 Returns the key frame value for the given \a step. The given \a step
557 must be in the range 0 to 1. If there is no KeyValue for \a step,
558 it returns an invalid QVariant.
559
560 \sa keyValues(), setKeyValueAt()
561*/
562QVariant QVariantAnimation::keyValueAt(qreal step) const
563{
564 return d_func()->valueAt(step);
565}
566
567/*!
568 \typedef QVariantAnimation::KeyValue
569
570 This is a typedef for QPair<qreal, QVariant>.
571*/
572/*!
573 \typedef QVariantAnimation::KeyValues
574
575 This is a typedef for QList<KeyValue>
576*/
577
578/*!
579 Creates a key frame at the given \a step with the given \a value.
580 The given \a step must be in the range 0 to 1.
581
582 \sa setKeyValues(), keyValueAt()
583*/
584void QVariantAnimation::setKeyValueAt(qreal step, const QVariant &value)
585{
586 d_func()->setValueAt(step, value);
587}
588
589/*!
590 Returns the key frames of this animation.
591
592 \sa keyValueAt(), setKeyValues()
593*/
594QVariantAnimation::KeyValues QVariantAnimation::keyValues() const
595{
596 return d_func()->keyValues;
597}
598
599/*!
600 Replaces the current set of key frames with the given \a keyValues.
601 the step of the key frames must be in the range 0 to 1.
602
603 \sa keyValues(), keyValueAt()
604*/
605void QVariantAnimation::setKeyValues(const KeyValues &keyValues)
606{
607 Q_D(QVariantAnimation);
608 d->keyValues = keyValues;
609 std::sort(d->keyValues.begin(), d->keyValues.end(), animationValueLessThan);
610 d->recalculateCurrentInterval(/*force=*/true);
611}
612
613/*!
614 \property QVariantAnimation::currentValue
615 \brief the current value of the animation.
616
617 This property describes the current value; an interpolated value
618 between the \l{startValue}{start value} and the \l{endValue}{end
619 value}, using the current time for progress. The value itself is
620 obtained from interpolated(), which is called repeatedly as the
621 animation is running.
622
623 QVariantAnimation calls the virtual updateCurrentValue() function
624 when the current value changes. This is particularly useful for
625 subclasses that need to track updates. For example,
626 QPropertyAnimation uses this function to animate Qt \l{Qt's
627 Property System}{properties}.
628
629 \sa startValue, endValue
630*/
631QVariant QVariantAnimation::currentValue() const
632{
633 Q_D(const QVariantAnimation);
634 if (!d->currentValue.isValid())
635 const_cast<QVariantAnimationPrivate*>(d)->recalculateCurrentInterval();
636 return d->currentValue;
637}
638
639/*!
640 \reimp
641 */
642bool QVariantAnimation::event(QEvent *event)
643{
644 return QAbstractAnimation::event(event);
645}
646
647/*!
648 \reimp
649*/
650void QVariantAnimation::updateState(QAbstractAnimation::State newState,
651 QAbstractAnimation::State oldState)
652{
653 Q_UNUSED(oldState);
654 Q_UNUSED(newState);
655}
656
657/*!
658
659 This virtual function returns the linear interpolation between
660 variants \a from and \a to, at \a progress, usually a value
661 between 0 and 1. You can reimplement this function in a subclass
662 of QVariantAnimation to provide your own interpolation algorithm.
663
664 Note that in order for the interpolation to work with a
665 QEasingCurve that return a value smaller than 0 or larger than 1
666 (such as QEasingCurve::InBack) you should make sure that it can
667 extrapolate. If the semantic of the datatype does not allow
668 extrapolation this function should handle that gracefully.
669
670 You should call the QVariantAnimation implementation of this
671 function if you want your class to handle the types already
672 supported by Qt (see class QVariantAnimation description for a
673 list of supported types).
674
675 \sa QEasingCurve
676 */
677QVariant QVariantAnimation::interpolated(const QVariant &from, const QVariant &to, qreal progress) const
678{
679 return d_func()->interpolator(from.constData(), to.constData(), progress);
680}
681
682/*!
683 \reimp
684 */
685void QVariantAnimation::updateCurrentTime(int)
686{
687 d_func()->recalculateCurrentInterval();
688}
689
690QT_END_NAMESPACE
691
692#include "moc_qvariantanimation.cpp"
693