1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qquickkeyframe_p.h"
5
6#include "qquicktimeline_p.h"
7#include "qquickkeyframedatautils_p.h"
8
9#include <QtCore/qdebug.h>
10#include <QtCore/QVariantAnimation>
11#include <QtCore/qmath.h>
12#include <QtGui/qpainter.h>
13#include <QtQml/QQmlProperty>
14#include <QtQml/QQmlFile>
15#include <QtQml/QQmlContext>
16#include <QtQml/QQmlEngine>
17#include <QtCore/QFile>
18#include <QtCore/QCborStreamReader>
19
20#include <private/qvariantanimation_p.h>
21#include <private/qqmlproperty_p.h>
22
23#include <algorithm>
24
25QT_BEGIN_NAMESPACE
26
27class QQuickKeyframeGroupPrivate : public QObjectPrivate
28{
29 Q_DECLARE_PUBLIC(QQuickKeyframeGroup)
30public:
31 QQuickKeyframeGroupPrivate() = default;
32
33 QObject *target = nullptr;
34 QString propertyName;
35 QUrl keyframeSource;
36 QByteArray keyframeData;
37 bool componentComplete = false;
38 int userType = -1;
39
40protected:
41 void setupKeyframes();
42 void loadKeyframes(bool fromBinary = false);
43
44 static void append_keyframe(QQmlListProperty<QQuickKeyframe> *list, QQuickKeyframe *a);
45 static qsizetype keyframe_count(QQmlListProperty<QQuickKeyframe> *list);
46 static QQuickKeyframe* keyframe_at(QQmlListProperty<QQuickKeyframe> *list, qsizetype pos);
47 static void clear_keyframes(QQmlListProperty<QQuickKeyframe> *list);
48
49 QList<QQuickKeyframe *> keyframes;
50 QList<QQuickKeyframe *> sortedKeyframes;
51
52 QVariant originalValue;
53 QVariant lastValue;
54 QQmlAnyBinding originalBinding;
55};
56
57void QQuickKeyframeGroupPrivate::setupKeyframes()
58{
59 sortedKeyframes = keyframes;
60 std::sort(first: sortedKeyframes.begin(), last: sortedKeyframes.end(), comp: [](const QQuickKeyframe *first, const QQuickKeyframe *second) {
61 return first->frame() < second->frame();
62 });
63}
64
65void QQuickKeyframeGroupPrivate::loadKeyframes(bool fromBinary)
66{
67 Q_Q(QQuickKeyframeGroup);
68
69 QCborStreamReader reader;
70 QFile dataFile;
71 if (!fromBinary) {
72 // Resolve URL similar to QQuickImage source
73 QUrl loadUrl = keyframeSource;
74 QQmlContext *context = qmlContext(q);
75 if (context)
76 loadUrl = context->resolvedUrl(keyframeSource);
77 QString dataFilePath = QQmlFile::urlToLocalFileOrQrc(loadUrl);
78
79 dataFile.setFileName(dataFilePath);
80 if (!dataFile.open(flags: QIODevice::ReadOnly)) {
81 // Invalid file
82 qWarning() << "Unable to open keyframeSource:" << dataFilePath;
83 qDeleteAll(c: keyframes);
84 keyframes.clear();
85 return;
86 }
87 reader.setDevice(&dataFile);
88 } else {
89 reader.addData(data: keyframeData);
90 }
91
92 auto cleanup = qScopeGuard(f: [&dataFile] { dataFile.close(); });
93
94 // Check that file is standard keyframes CBOR and get the version
95 int version = readKeyframesHeader(reader);
96
97 if (version == -1) {
98 // Invalid file
99 qWarning() << "Invalid keyframeSource version:" << version;
100 return;
101 }
102
103 QMetaType::Type propertyType = QMetaType::UnknownType;
104 if (reader.isInteger()) {
105 propertyType = static_cast<QMetaType::Type>(reader.toInteger());
106 reader.next();
107 }
108
109 // Start keyframes array
110 reader.enterContainer();
111
112 while (reader.lastError() == QCborError::NoError && reader.hasNext()) {
113 auto keyframe = new QQuickKeyframe(q);
114 keyframe->setFrame(readReal(reader));
115 keyframe->setEasing(QEasingCurve(static_cast<QEasingCurve::Type>(reader.toInteger())));
116 reader.next();
117 keyframe->setValue(readValue(reader, type: propertyType));
118 keyframes.append(t: keyframe);
119 }
120 // Leave keyframes array
121 reader.leaveContainer();
122
123 // Leave root array
124 reader.leaveContainer();
125}
126
127void QQuickKeyframeGroupPrivate::append_keyframe(QQmlListProperty<QQuickKeyframe> *list, QQuickKeyframe *a)
128{
129 auto q = static_cast<QQuickKeyframeGroup *>(list->object);
130 q->d_func()->keyframes.append(t: a);
131 q->d_func()->setupKeyframes();
132 q->reset();
133}
134
135qsizetype QQuickKeyframeGroupPrivate::keyframe_count(QQmlListProperty<QQuickKeyframe> *list)
136{
137 auto q = static_cast<QQuickKeyframeGroup *>(list->object);
138 return q->d_func()->keyframes.size();
139}
140
141QQuickKeyframe* QQuickKeyframeGroupPrivate::keyframe_at(QQmlListProperty<QQuickKeyframe> *list, qsizetype pos)
142{
143 auto q = static_cast<QQuickKeyframeGroup *>(list->object);
144 return q->d_func()->keyframes.at(i: pos);
145}
146
147void QQuickKeyframeGroupPrivate::clear_keyframes(QQmlListProperty<QQuickKeyframe> *list)
148{
149 auto q = static_cast<QQuickKeyframeGroup *>(list->object);
150 q->d_func()->keyframes.clear();
151 q->d_func()->setupKeyframes();
152}
153
154class QQuickKeyframePrivate : public QObjectPrivate
155{
156 Q_DECLARE_PUBLIC(QQuickKeyframe)
157public:
158 QQuickKeyframePrivate() = default;
159
160 qreal frame = 0;
161 QEasingCurve easingCurve;
162 QVariant value;
163};
164
165/*!
166 \qmltype Keyframe
167 \inherits QtObject
168 \nativetype QQuickKeyframe
169 \inqmlmodule QtQuick.Timeline
170 \ingroup qtqmltypes
171
172 \brief A keyframe.
173
174 The value of a keyframe on a timeline.
175
176 An easing curve can be attached to the keyframe.
177*/
178
179/*!
180 \qmlproperty double Keyframe::frame
181
182 The position of the keyframe on the timeline.
183*/
184
185/*!
186 \qmlproperty var Keyframe::easing
187
188 The easing curve attached to the keyframe.
189*/
190
191/*!
192 \qmlproperty var Keyframe::value
193
194 The value of the keyframe.
195*/
196
197/*!
198 \qmlsignal Keyframe::easingCurveChanged
199
200 This signal is emitted when the easing curve attached to the keyframe
201 changes.
202*/
203
204QQuickKeyframe::QQuickKeyframe(QObject *parent)
205 : QObject(*(new QQuickKeyframePrivate), parent)
206{
207}
208
209qreal QQuickKeyframe::frame() const
210{
211 Q_D(const QQuickKeyframe);
212 return d->frame;
213}
214
215void QQuickKeyframe::setFrame(qreal f)
216{
217 Q_D(QQuickKeyframe);
218 if (d->frame == f)
219 return;
220 d->frame = f;
221
222 reset();
223
224 emit frameChanged();
225}
226
227void QQuickKeyframe::reset()
228{
229 auto keyframes = qobject_cast<QQuickKeyframeGroup*>(object: parent());
230 if (keyframes)
231 keyframes->reset();
232}
233
234QQuickKeyframe::QQuickKeyframe(QQuickKeyframePrivate &dd, QObject *parent)
235 : QObject(dd, parent)
236{
237}
238
239/*!
240 \qmltype KeyframeGroup
241 \inherits QtObject
242 \nativetype QQuickKeyframeGroup
243 \inqmlmodule QtQuick.Timeline
244 \ingroup qtqmltypes
245
246 \brief A keyframe group.
247
248 A keyframe group contains all keyframes for a specific property of an item
249 and always belongs to a timeline.
250*/
251
252/*!
253 \qmlproperty var KeyframeGroup::target
254
255 The item that is targeted by the keyframe group.
256*/
257
258/*!
259 \qmlproperty string KeyframeGroup::property
260
261 The property that is targeted by the keyframe group.
262*/
263
264/*!
265 \qmlproperty list KeyframeGroup::keyframes
266 \readonly
267
268 A list of keyframes that belong to the keyframe group.
269*/
270
271/*!
272 \qmlproperty url KeyframeGroup::keyframeSource
273
274 The URL to a file containing binary keyframe data.
275
276 \note KeyframeGroup should either set this property or contain
277 Keyframe child elements. Using both can lead to an undefined behavior.
278
279 \since 1.1
280*/
281
282QQuickKeyframeGroup::QQuickKeyframeGroup(QObject *parent)
283 : QObject(*(new QQuickKeyframeGroupPrivate), parent)
284{
285
286}
287
288QQmlListProperty<QQuickKeyframe> QQuickKeyframeGroup::keyframes()
289{
290 Q_D(QQuickKeyframeGroup);
291
292 return { this, &d->keyframes, QQuickKeyframeGroupPrivate::append_keyframe,
293 QQuickKeyframeGroupPrivate::keyframe_count,
294 QQuickKeyframeGroupPrivate::keyframe_at,
295 QQuickKeyframeGroupPrivate::clear_keyframes };
296}
297
298QObject *QQuickKeyframeGroup::target() const
299{
300 Q_D(const QQuickKeyframeGroup);
301 return d->target;
302}
303
304void QQuickKeyframeGroup::setTargetObject(QObject *o)
305{
306 Q_D(QQuickKeyframeGroup);
307 if (d->target == o)
308 return;
309 d->target = o;
310
311 if (!property().isEmpty())
312 init();
313
314 emit targetChanged();
315}
316
317QString QQuickKeyframeGroup::property() const
318{
319 Q_D(const QQuickKeyframeGroup);
320 return d->propertyName;
321}
322
323void QQuickKeyframeGroup::setProperty(const QString &n)
324{
325 Q_D(QQuickKeyframeGroup);
326 if (d->propertyName == n)
327 return;
328 d->propertyName = n;
329
330 if (target())
331 init();
332
333 emit propertyChanged();
334}
335
336QUrl QQuickKeyframeGroup::keyframeSource() const
337{
338 Q_D(const QQuickKeyframeGroup);
339 return d->keyframeSource;
340}
341
342void QQuickKeyframeGroup::setKeyframeSource(const QUrl &source)
343{
344 Q_D(QQuickKeyframeGroup);
345 if (d->keyframeSource == source)
346 return;
347
348 if (d->keyframes.size() > 0) {
349 // Remove possible previously loaded keyframes
350 qDeleteAll(c: d->keyframes);
351 d->keyframes.clear();
352 d->keyframeData.clear();
353 }
354
355 d->keyframeSource = source;
356 d->loadKeyframes();
357 d->setupKeyframes();
358 reset();
359
360 emit keyframeSourceChanged();
361}
362
363const QByteArray QQuickKeyframeGroup::keyframeData() const
364{
365 Q_D(const QQuickKeyframeGroup);
366 return d->keyframeData;
367}
368
369void QQuickKeyframeGroup::setKeyframeData(const QByteArray &data)
370{
371 Q_D(QQuickKeyframeGroup);
372 if (d->keyframeData == data)
373 return;
374
375 if (d->keyframes.size() > 0) {
376 // Remove possible previously loaded keyframes
377 qDeleteAll(c: d->keyframes);
378 d->keyframes.clear();
379 d->keyframeSource.clear();
380 }
381
382 d->keyframeData = data;
383 d->loadKeyframes(fromBinary: true);
384 d->setupKeyframes();
385 reset();
386
387 emit keyframeSourceChanged();
388}
389
390QVariant QQuickKeyframeGroup::evaluate(qreal frame) const
391{
392 Q_D(const QQuickKeyframeGroup);
393
394 if (d->sortedKeyframes.isEmpty())
395 return QVariant();
396
397 static QQuickKeyframe dummy;
398 auto timeline = qobject_cast<QQuickTimeline*>(object: parent());
399 if (timeline)
400 dummy.setFrame(timeline->startFrame() - 0.0001);
401 dummy.setValue(d->originalValue);
402
403 QQuickKeyframe *lastFrame = &dummy;
404
405 for (auto keyFrame : std::as_const(t: d->sortedKeyframes)) {
406 if (qFuzzyCompare(p1: frame, p2: keyFrame->frame()) || frame < keyFrame->frame())
407 return keyFrame->evaluate(pre: lastFrame, frame, userType: d->userType);
408 lastFrame = keyFrame;
409 }
410
411 return lastFrame->value();
412}
413
414void QQuickKeyframeGroup::setProperty(qreal frame)
415{
416 Q_D(QQuickKeyframeGroup);
417
418 if (target()) {
419 QQmlProperty qmlProperty(target(), property());
420
421 d->lastValue = evaluate(frame);
422
423 if (!qmlProperty.write(d->lastValue))
424 qWarning() << "Cannot set property" << property();
425 }
426}
427
428void QQuickKeyframeGroup::init()
429{
430 Q_D(QQuickKeyframeGroup);
431 if (target()) {
432 QQmlProperty qmlProperty(target(), property());
433 d->originalValue = QQmlProperty::read(target(), property());
434 d->userType = qmlProperty.property().userType();
435 if (d->originalBinding)
436 d->originalBinding = nullptr;
437 d->originalBinding = QQmlAnyBinding::ofProperty(prop: qmlProperty);
438 if (property().contains(c: QLatin1Char('.'))) {
439 if (d->userType == QMetaType::QVector2D
440 || d->userType == QMetaType::QVector3D
441 || d->userType == QMetaType::QVector4D
442 || d->userType == QMetaType::QQuaternion)
443 d->userType = QMetaType::Double;
444 }
445 }
446}
447
448void QQuickKeyframeGroup::resetDefaultValue()
449{
450 Q_D(QQuickKeyframeGroup);
451
452 if (QQmlProperty::read(target(), property()) == d->lastValue) {
453 if (d->originalBinding) {
454 QQmlProperty qmlProperty(target(), property());
455 d->originalBinding.installOn(target: qmlProperty);
456 d->originalBinding = nullptr;
457 } else {
458 QQmlProperty::write(target(), property(), d->originalValue);
459 }
460 }
461}
462
463void QQuickKeyframeGroup::reset()
464{
465 Q_D(QQuickKeyframeGroup);
466 if (!d->componentComplete)
467 return;
468
469 auto *timeline = qobject_cast<QQuickTimeline*>(object: parent());
470 if (timeline)
471 timeline->reevaluate();
472}
473
474void QQuickKeyframeGroup::setupKeyframes()
475{
476 Q_D(QQuickKeyframeGroup);
477
478 if (d->componentComplete)
479 d->setupKeyframes();
480}
481
482void QQuickKeyframeGroup::classBegin()
483{
484 Q_D(QQuickKeyframeGroup);
485 d->componentComplete = false;
486}
487
488void QQuickKeyframeGroup::componentComplete()
489{
490 Q_D(QQuickKeyframeGroup);
491 d->componentComplete = true;
492 setupKeyframes();
493}
494
495QEasingCurve QQuickKeyframe::easing() const
496{
497 Q_D(const QQuickKeyframe);
498 return d->easingCurve;
499}
500
501void QQuickKeyframe::setEasing(const QEasingCurve &e)
502{
503 Q_D(QQuickKeyframe);
504 if (d->easingCurve == e)
505 return;
506
507 d->easingCurve = e;
508
509 reset();
510
511 emit easingCurveChanged();
512}
513
514QVariant QQuickKeyframe::value() const
515{
516 Q_D(const QQuickKeyframe);
517 return d->value;
518}
519
520void QQuickKeyframe::setValue(const QVariant &v)
521{
522 Q_D(QQuickKeyframe);
523 if (d->value == v)
524 return;
525 d->value = v;
526
527 reset();
528
529 emit valueChanged();
530}
531
532QVariant QQuickKeyframe::evaluate(QQuickKeyframe *pre, qreal frametime, int userType) const
533{
534 QVariantAnimation::Interpolator interpolator = QVariantAnimationPrivate::getInterpolator(interpolationType: userType);
535 if (!pre)
536 return value();
537
538 QVariant preValue = pre->value();
539 qreal preFrame = pre->frame();
540
541 qreal duration = frame() - preFrame;
542 qreal offset = frametime - preFrame;
543
544 qreal progress = easing().valueForProgress(progress: offset / duration);
545
546 const QMetaType targetType(userType);
547 preValue.convert(type: targetType);
548 QVariant convertedValue = value();
549 convertedValue.convert(type: targetType);
550
551 if (!interpolator) {
552 if (progress < 1.0)
553 return preValue;
554
555 return convertedValue;
556 }
557
558 if (preValue.isValid() && convertedValue.isValid())
559 return interpolator(preValue.constData(), convertedValue.constData(), progress);
560
561 qWarning() << "invalid keyframe target" << preValue << convertedValue << userType;
562
563 return QVariant();
564}
565
566QT_END_NAMESPACE
567
568
569

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtquicktimeline/src/timeline/qquickkeyframe.cpp