Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qvariantanimation.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qvariantanimation.h"
6
7#include <QtCore/qrect.h>
8#include <QtCore/qline.h>
9#include <QtCore/qmutex.h>
10#include <QtCore/private/qlocking_p.h>
11
12#include <algorithm>
13
15
111
113{
114 return p1.first < p2.first;
115}
116
117static QVariant defaultInterpolator(const void *, const void *, qreal)
118{
119 return QVariant();
120}
121
122template<> Q_INLINE_TEMPLATE QRect _q_interpolate(const QRect &f, const QRect &t, qreal progress)
123{
124 QRect ret;
125 ret.setCoords(_q_interpolate(f.left(), t.left(), progress),
126 _q_interpolate(f.top(), t.top(), progress),
127 _q_interpolate(f.right(), t.right(), progress),
128 _q_interpolate(f.bottom(), t.bottom(), progress));
129 return ret;
130}
131
132template<> Q_INLINE_TEMPLATE QRectF _q_interpolate(const QRectF &f, const QRectF &t, qreal progress)
133{
134 qreal x1, y1, w1, h1;
135 f.getRect(&x1, &y1, &w1, &h1);
136 qreal x2, y2, w2, h2;
137 t.getRect(&x2, &y2, &w2, &h2);
138 return QRectF(_q_interpolate(x1, x2, progress), _q_interpolate(y1, y2, progress),
139 _q_interpolate(w1, w2, progress), _q_interpolate(h1, h2, progress));
140}
141
142template<> Q_INLINE_TEMPLATE QLine _q_interpolate(const QLine &f, const QLine &t, qreal progress)
143{
144 return QLine( _q_interpolate(f.p1(), t.p1(), progress), _q_interpolate(f.p2(), t.p2(), progress));
145}
146
147template<> Q_INLINE_TEMPLATE QLineF _q_interpolate(const QLineF &f, const QLineF &t, qreal progress)
148{
149 return QLineF( _q_interpolate(f.p1(), t.p1(), progress), _q_interpolate(f.p2(), t.p2(), progress));
150}
151
154
156{
157 auto type = QMetaType(t);
158 //this ensures that all the keyValues are of type t
159 for (int i = 0; i < keyValues.size(); ++i) {
160 QVariantAnimation::KeyValue &pair = keyValues[i];
161 pair.second.convert(type);
162 }
163 //we also need update to the current interval if needed
164 currentInterval.start.second.convert(type);
165 currentInterval.end.second.convert(type);
166
167 //... and the interpolator
169}
170
172{
173 int type = currentInterval.start.second.userType();
174 if (type == currentInterval.end.second.userType())
176 else
177 interpolator = nullptr;
178
179 //we make sure that the interpolator is always set to something
180 if (!interpolator)
182}
183
191{
192 // can't interpolate if we don't have at least 2 values
193 if ((keyValues.size() + (defaultStartEndValue.isValid() ? 1 : 0)) < 2)
194 return;
195
196 const qreal endProgress = (direction == QAbstractAnimation::Forward) ? qreal(1) : qreal(0);
197 const qreal progress = easing.value().valueForProgress(
198 duration == 0 ? endProgress : qreal(currentTime) / qreal(duration));
199
200 //0 and 1 are still the boundaries
201 if (force || (currentInterval.start.first > 0 && progress < currentInterval.start.first)
202 || (currentInterval.end.first < 1 && progress > currentInterval.end.first)) {
203 //let's update currentInterval
204 QVariantAnimation::KeyValues::const_iterator it = std::lower_bound(keyValues.constBegin(),
205 keyValues.constEnd(),
206 std::pair{progress, QVariant{}},
208 if (it == keyValues.constBegin()) {
209 //the item pointed to by it is the start element in the range
210 if (it->first == 0 && keyValues.size() > 1) {
211 currentInterval.start = *it;
212 currentInterval.end = *(it+1);
213 } else {
215 currentInterval.end = *it;
216 }
217 } else if (it == keyValues.constEnd()) {
218 --it; //position the iterator on the last item
219 if (it->first == 1 && keyValues.size() > 1) {
220 //we have an end value (item with progress = 1)
221 currentInterval.start = *(it-1);
222 currentInterval.end = *it;
223 } else {
224 //we use the default end value here
225 currentInterval.start = *it;
227 }
228 } else {
229 currentInterval.start = *(it-1);
230 currentInterval.end = *it;
231 }
232
233 // update all the values of the currentInterval
235 }
236 setCurrentValueForProgress(progress);
237}
238
240{
242
243 const qreal startProgress = currentInterval.start.first;
244 const qreal endProgress = currentInterval.end.first;
245 const qreal localProgress =
246 qIsNull(progress - startProgress) ? 0.0 // avoid 0/0 below
247 /* else */ : (progress - startProgress) / (endProgress - startProgress);
248
249 QVariant ret = q->interpolated(currentInterval.start.second,
250 currentInterval.end.second,
251 localProgress);
253 q->updateCurrentValue(currentValue);
254 Q_CONSTINIT static QBasicAtomicInt changedSignalIndex = Q_BASIC_ATOMIC_INITIALIZER(0);
255 if (!changedSignalIndex.loadRelaxed()) {
256 //we keep the mask so that we emit valueChanged only when needed (for performance reasons)
257 changedSignalIndex.testAndSetRelaxed(0, signalIndex("valueChanged(QVariant)"));
258 }
259 if (isSignalConnected(changedSignalIndex.loadRelaxed()) && currentValue != ret) {
260 //the value has changed
261 emit q->valueChanged(currentValue);
262 }
263}
264
266{
267 const auto sought = std::pair{step, QVariant()};
268 const auto result = std::lower_bound(keyValues.cbegin(), keyValues.cend(), sought,
270 if (result != keyValues.cend() && !animationValueLessThan(sought, *result))
271 return result->second;
272
273 return QVariant();
274}
275
277{
278 if (step < qreal(0.0) || step > qreal(1.0)) {
279 qWarning("QVariantAnimation::setValueAt: invalid step = %f", step);
280 return;
281 }
282
284
285 QVariantAnimation::KeyValues::iterator result = std::lower_bound(keyValues.begin(), keyValues.end(), pair, animationValueLessThan);
286 if (result == keyValues.end() || result->first != step) {
287 keyValues.insert(result, pair);
288 } else {
289 if (value.isValid())
290 result->second = value; // replaces the previous value
291 else
292 keyValues.erase(result); // removes the previous value
293 }
294
295 recalculateCurrentInterval(/*force=*/true);
296}
297
303
311
318
325
349{
350 Q_D(const QVariantAnimation);
351 return d->easing;
352}
353
355{
357 d->easing.removeBindingUnlessInWrapper();
358 const bool valueChanged = easing != d->easing.valueBypassingBindings();
359 d->easing.setValueBypassingBindings(easing);
360 d->recalculateCurrentInterval();
361 if (valueChanged)
362 d->easing.notify();
363}
364
366{
368 return &d->easing;
369}
370
371typedef QList<QVariantAnimation::Interpolator> QInterpolatorVector;
372Q_GLOBAL_STATIC(QInterpolatorVector, registeredInterpolators)
374
401void QVariantAnimation::registerInterpolator(QVariantAnimation::Interpolator func, int interpolationType)
402{
403 // will override any existing interpolators
404 QInterpolatorVector *interpolators = registeredInterpolators();
405 // When built on solaris with GCC, the destructors can be called
406 // in such an order that we get here with interpolators == NULL,
407 // to continue causes the app to crash on exit with a SEGV
408 if (interpolators) {
409 const auto locker = qt_scoped_lock(registeredInterpolatorsMutex);
410 if (interpolationType >= interpolators->size())
411 interpolators->resize(interpolationType + 1);
412 interpolators->replace(interpolationType, func);
413 }
414}
415
416
417template<typename T> static inline QVariantAnimation::Interpolator castToInterpolator(QVariant (*func)(const T &from, const T &to, qreal progress))
418{
419 return reinterpret_cast<QVariantAnimation::Interpolator>(reinterpret_cast<void(*)()>(func));
420}
421
423{
424 {
425 QInterpolatorVector *interpolators = registeredInterpolators();
426 const auto locker = qt_scoped_lock(registeredInterpolatorsMutex);
428 if (interpolationType < interpolators->size()) {
429 ret = interpolators->at(interpolationType);
430 if (ret) return ret;
431 }
432 }
433
434 switch(interpolationType)
435 {
436 case QMetaType::Int:
437 return castToInterpolator(_q_interpolateVariant<int>);
438 case QMetaType::UInt:
439 return castToInterpolator(_q_interpolateVariant<uint>);
440 case QMetaType::Double:
441 return castToInterpolator(_q_interpolateVariant<double>);
442 case QMetaType::Float:
443 return castToInterpolator(_q_interpolateVariant<float>);
444 case QMetaType::QLine:
445 return castToInterpolator(_q_interpolateVariant<QLine>);
446 case QMetaType::QLineF:
447 return castToInterpolator(_q_interpolateVariant<QLineF>);
448 case QMetaType::QPoint:
449 return castToInterpolator(_q_interpolateVariant<QPoint>);
450 case QMetaType::QPointF:
451 return castToInterpolator(_q_interpolateVariant<QPointF>);
452 case QMetaType::QSize:
453 return castToInterpolator(_q_interpolateVariant<QSize>);
454 case QMetaType::QSizeF:
455 return castToInterpolator(_q_interpolateVariant<QSizeF>);
456 case QMetaType::QRect:
457 return castToInterpolator(_q_interpolateVariant<QRect>);
458 case QMetaType::QRectF:
459 return castToInterpolator(_q_interpolateVariant<QRectF>);
460 default:
461 return nullptr; //this type is not handled
462 }
463}
464
475{
476 Q_D(const QVariantAnimation);
477 return d->duration;
478}
479
481{
483 if (msecs < 0) {
484 qWarning("QVariantAnimation::setDuration: cannot set a negative duration");
485 return;
486 }
487 d->duration.removeBindingUnlessInWrapper();
488 if (d->duration.valueBypassingBindings() != msecs) {
489 d->duration.setValueBypassingBindings(msecs);
490 d->recalculateCurrentInterval();
491 d->duration.notify();
492 }
493}
494
496{
498 return &d->duration;
499}
500
513{
514 return keyValueAt(0);
515}
516
521
531{
532 return keyValueAt(1);
533}
534
539
540
549{
550 return d_func()->valueAt(step);
551}
552
571{
572 d_func()->setValueAt(step, value);
573}
574
581{
582 return d_func()->keyValues;
583}
584
592{
594 d->keyValues = keyValues;
595 std::sort(d->keyValues.begin(), d->keyValues.end(), animationValueLessThan);
596 d->recalculateCurrentInterval(/*force=*/true);
597}
598
618{
619 Q_D(const QVariantAnimation);
620 if (!d->currentValue.isValid())
621 const_cast<QVariantAnimationPrivate*>(d)->recalculateCurrentInterval();
622 return d->currentValue;
623}
624
632
642
663QVariant QVariantAnimation::interpolated(const QVariant &from, const QVariant &to, qreal progress) const
664{
665 return d_func()->interpolator(from.constData(), to.constData(), progress);
666}
667
672{
673 d_func()->recalculateCurrentInterval();
674}
675
677
678#include "moc_qvariantanimation.cpp"
State
This enum describes the state of the animation.
bool event(QEvent *event) override
\reimp
\inmodule QtCore
qreal valueForProgress(qreal progress) const
Return the effective progress for the easing curve at progress.
\inmodule QtCore
Definition qcoreevent.h:45
\inmodule QtCore\compares equality \compareswith equality QLine \endcompareswith
Definition qline.h:192
\inmodule QtCore\compares equality \compareswith equality QLineF \endcompareswith
Definition qline.h:18
Definition qlist.h:75
\inmodule QtCore
Definition qmetatype.h:341
\inmodule QtCore
Definition qmutex.h:281
bool isSignalConnected(uint signalIdx, bool checkDeclarative=true) const
Definition qobject.cpp:420
\inmodule QtCore
Definition qobject.h:103
\inmodule QtCore\reentrant
Definition qrect.h:484
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr void setCoords(int x1, int y1, int x2, int y2) noexcept
Sets the coordinates of the rectangle's top-left corner to (x1, y1), and the coordinates of its botto...
Definition qrect.h:362
qsizetype size() const
Definition qset.h:50
const_iterator constBegin() const noexcept
Definition qset.h:139
const_iterator constEnd() const noexcept
Definition qset.h:143
void setCurrentValueForProgress(const qreal progress)
Q_OBJECT_COMPAT_PROPERTY(QVariantAnimationPrivate, int, duration, &QVariantAnimationPrivate::setDuration) QVariantAnimation QVariantAnimation::Interpolato interpolator)
void recalculateCurrentInterval(bool force=false)
void setValueAt(qreal, const QVariant &)
struct QVariantAnimationPrivate::@3 currentInterval
QVariant valueAt(qreal step) const
void setDefaultStartEndValue(const QVariant &value)
static Q_CORE_EXPORT QVariantAnimation::Interpolator getInterpolator(int interpolationType)
\inmodule QtCore
int duration
the duration of the animation
KeyValues keyValues() const
Returns the key frames of this animation.
void setStartValue(const QVariant &value)
void setEasingCurve(const QEasingCurve &easing)
void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) override
\reimp
QVariant(* Interpolator)(const void *from, const void *to, qreal progress)
QEasingCurve easingCurve
the easing curve of the animation
QVariant currentValue
the current value of the animation.
virtual void updateCurrentValue(const QVariant &value)
This virtual function is called every time the animation's current value changes.
QVariant keyValueAt(qreal step) const
Returns the key frame value for the given step.
void setKeyValues(const KeyValues &values)
Replaces the current set of key frames with the given keyValues.
void valueChanged(const QVariant &value)
QVariantAnimation emits this signal whenever the current value changes.
QVariantAnimation(QObject *parent=nullptr)
Construct a QVariantAnimation object.
QVariant startValue
the optional start value of the animation
QBindable< int > bindableDuration()
void updateCurrentTime(int) override
\reimp
void setDuration(int msecs)
std::pair< qreal, QVariant > KeyValue
This is a typedef for std::pair<qreal, QVariant>.
virtual QVariant interpolated(const QVariant &from, const QVariant &to, qreal progress) const
This virtual function returns the linear interpolation between variants from and to,...
QVariant endValue
the end value of the animation
bool event(QEvent *event) override
\reimp
void setKeyValueAt(qreal step, const QVariant &value)
Creates a key frame at the given step with the given value.
QBindable< QEasingCurve > bindableEasingCurve()
~QVariantAnimation()
Destroys the animation.
void setEndValue(const QVariant &value)
\inmodule QtCore
Definition qvariant.h:65
bool isValid() const
Returns true if the storage type of this variant is not QMetaType::UnknownType; otherwise returns fal...
Definition qvariant.h:714
const void * constData() const
Definition qvariant.h:451
QPixmap p2
QPixmap p1
[0]
QSet< QString >::iterator it
auto signalIndex
direction
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
Combined button and popup list for selecting options.
#define Q_BASIC_ATOMIC_INITIALIZER(a)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
bool qIsNull(qfloat16 f) noexcept
Definition qfloat16.h:354
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qWarning
Definition qlogging.h:166
return ret
GLuint GLfloat GLfloat GLfloat GLfloat y1
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLfloat GLfloat GLfloat x1
GLfloat GLfloat f
GLenum type
struct _cl_event * event
GLenum func
Definition qopenglext.h:663
GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint GLdouble GLdouble w2
GLfixed GLfixed GLfixed y2
GLfixed GLfixed x2
GLdouble GLdouble t
Definition qopenglext.h:243
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint GLdouble w1
QT_BEGIN_NAMESPACE constexpr void qSwap(T &value1, T &value2) noexcept(std::is_nothrow_swappable_v< T >)
Definition qswap.h:20
#define emit
#define Q_UNUSED(x)
double qreal
Definition qtypes.h:187
static double currentTime()
Q_INLINE_TEMPLATE QRect _q_interpolate(const QRect &f, const QRect &t, qreal progress)
static bool animationValueLessThan(const QVariantAnimation::KeyValue &p1, const QVariantAnimation::KeyValue &p2)
static QVariant defaultInterpolator(const void *, const void *, qreal)
static Q_CONSTINIT QBasicMutex registeredInterpolatorsMutex
static QVariantAnimation::Interpolator castToInterpolator(QVariant(*func)(const T &from, const T &to, qreal progress))
QList< QVariantAnimation::Interpolator > QInterpolatorVector
QEasingCurve easing(QEasingCurve::InOutQuad)
[typedef]