blob: 2e9596bb977ef3780d326001e00b09a3dbca1a19 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Quick Designer Components.
**
** $QT_BEGIN_LICENSE:GPL$
** 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 General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) 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.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-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qquickkeyframe_p.h"
#include "qquicktimeline_p.h"
#include <QtCore/qdebug.h>
#include <QtCore/QVariantAnimation>
#include <QtCore/qmath.h>
#include <QtGui/qpainter.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQml/QQmlProperty>
#include <private/qvariantanimation_p.h>
#include <algorithm>
QT_BEGIN_NAMESPACE
class QQuickKeyframeGroupPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QQuickKeyframeGroup)
public:
QQuickKeyframeGroupPrivate() = default;
QObject *target = nullptr;
QString propertyName;
bool componentComplete = false;
int userType = -1;
protected:
void setupKeyframes();
static void append_keyframe(QQmlListProperty<QQuickKeyframe> *list, QQuickKeyframe *a);
static int keyframe_count(QQmlListProperty<QQuickKeyframe> *list);
static QQuickKeyframe* keyframe_at(QQmlListProperty<QQuickKeyframe> *list, int pos);
static void clear_keyframes(QQmlListProperty<QQuickKeyframe> *list);
QList<QQuickKeyframe *> keyframes;
QList<QQuickKeyframe *> sortedKeyframes;
QVariant originalValue;
};
void QQuickKeyframeGroupPrivate::setupKeyframes()
{
sortedKeyframes = keyframes;
std::sort(sortedKeyframes.begin(), sortedKeyframes.end(), [](const QQuickKeyframe *first, const QQuickKeyframe *second) {
return first->frame() < second->frame();
});
}
void QQuickKeyframeGroupPrivate::append_keyframe(QQmlListProperty<QQuickKeyframe> *list, QQuickKeyframe *a)
{
auto q = static_cast<QQuickKeyframeGroup *>(list->object);
q->d_func()->keyframes.append(a);
q->d_func()->setupKeyframes();
q->reset();
}
int QQuickKeyframeGroupPrivate::keyframe_count(QQmlListProperty<QQuickKeyframe> *list)
{
auto q = static_cast<QQuickKeyframeGroup *>(list->object);
return q->d_func()->keyframes.count();
}
QQuickKeyframe* QQuickKeyframeGroupPrivate::keyframe_at(QQmlListProperty<QQuickKeyframe> *list, int pos)
{
auto q = static_cast<QQuickKeyframeGroup *>(list->object);
return q->d_func()->keyframes.at(pos);
}
void QQuickKeyframeGroupPrivate::clear_keyframes(QQmlListProperty<QQuickKeyframe> *list)
{
auto q = static_cast<QQuickKeyframeGroup *>(list->object);
while (q->d_func()->keyframes.count()) {
QQuickKeyframe *firstKeyframe = q->d_func()->keyframes.at(0);
q->d_func()->keyframes.removeAll(firstKeyframe);
}
}
class QQuickKeyframePrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QQuickKeyframe)
public:
QQuickKeyframePrivate() = default;
qreal frame = 0;
QEasingCurve easingCurve;
QVariant value;
};
/*!
\qmltype Keyframe
\inherits QObject
\instantiates QQuickKeyframe
\inqmlmodule QtQuick.Timeline
\ingroup qtqmltypes
\brief A keyframe.
The value of a keyframe on a timeline.
An easing curve can be attached to the keyframe.
*/
/*!
\qmlproperty double Keyframe::frame
The position of the keyframe on the timeline.
*/
/*!
\qmlproperty var Keyframe::easing
The easing curve attached to the keyframe.
*/
/*!
\qmlproperty var Keyframe::value
The value of the keyframe.
*/
/*!
\qmlsignal Keyframe::easingCurveChanged
This signal is emitted when the easing curve attached to the keyframe
changes.
*/
QQuickKeyframe::QQuickKeyframe(QObject *parent)
: QObject(*(new QQuickKeyframePrivate), parent)
{
}
qreal QQuickKeyframe::frame() const
{
Q_D(const QQuickKeyframe);
return d->frame;
}
void QQuickKeyframe::setFrame(qreal f)
{
Q_D(QQuickKeyframe);
if (d->frame == f)
return;
d->frame = f;
reset();
emit frameChanged();
}
void QQuickKeyframe::reset()
{
auto keyframes = qobject_cast<QQuickKeyframeGroup*>(parent());
if (keyframes)
keyframes->reset();
}
QQuickKeyframe::QQuickKeyframe(QQuickKeyframePrivate &dd, QObject *parent)
: QObject(dd, parent)
{
}
/*!
\qmltype KeyframeGroup
\inherits QObject
\instantiates QQuickKeyframeGroup
\inqmlmodule QtQuick.Timeline
\ingroup qtqmltypes
\brief A keyframe group.
A keyframe group contains all keyframes for a specific property of an item
and always belongs to a timeline.
*/
/*!
\qmlproperty var KeyframeGroup::target
The item that is targeted by the keyframe group.
*/
/*!
\qmlproperty string KeyframeGroup::property
The property that is targeted by the keyframe group.
*/
/*!
\qmlproperty list KeyframeGroup::keyframes
\readonly
A list of keyframes that belong to the keyframe group.
*/
QQuickKeyframeGroup::QQuickKeyframeGroup(QObject *parent)
: QObject(*(new QQuickKeyframeGroupPrivate), parent)
{
}
QQmlListProperty<QQuickKeyframe> QQuickKeyframeGroup::keyframes()
{
Q_D(QQuickKeyframeGroup);
return { this, &d->keyframes, QQuickKeyframeGroupPrivate::append_keyframe,
QQuickKeyframeGroupPrivate::keyframe_count,
QQuickKeyframeGroupPrivate::keyframe_at,
QQuickKeyframeGroupPrivate::clear_keyframes };
}
QObject *QQuickKeyframeGroup::target() const
{
Q_D(const QQuickKeyframeGroup);
return d->target;
}
void QQuickKeyframeGroup::setTargetObject(QObject *o)
{
Q_D(QQuickKeyframeGroup);
if (d->target == o)
return;
d->target = o;
if (!property().isEmpty())
init();
emit targetChanged();
}
QString QQuickKeyframeGroup::property() const
{
Q_D(const QQuickKeyframeGroup);
return d->propertyName;
}
void QQuickKeyframeGroup::setProperty(const QString &n)
{
Q_D(QQuickKeyframeGroup);
if (d->propertyName == n)
return;
d->propertyName = n;
if (target())
init();
emit propertyChanged();
}
QVariant QQuickKeyframeGroup::evaluate(qreal frame) const
{
Q_D(const QQuickKeyframeGroup);
if (d->sortedKeyframes.isEmpty())
return QVariant();
static QQuickKeyframe dummy;
auto timeline = qobject_cast<QQuickTimeline*>(parent());
if (timeline)
dummy.setFrame(timeline->startFrame() - 0.0001);
dummy.setValue(d->originalValue);
QQuickKeyframe *lastFrame = &dummy;
for (auto keyFrame : qAsConst(d->sortedKeyframes)) {
if (qFuzzyCompare(frame, keyFrame->frame()) || frame < keyFrame->frame())
return keyFrame->evaluate(lastFrame, frame, d->userType);
lastFrame = keyFrame;
}
return lastFrame->value();
}
void QQuickKeyframeGroup::setProperty(qreal frame)
{
if (target()) {
QQmlProperty qmlProperty(target(), property());
if (!qmlProperty.write(evaluate(frame)))
qWarning() << "Cannot set property" << property();
}
}
void QQuickKeyframeGroup::init()
{
Q_D(QQuickKeyframeGroup);
if (target()) {
d->originalValue = QQmlProperty::read(target(), property());
d->userType = QQmlProperty(target(), property()).property().userType();
if (property().contains(QLatin1Char('.'))) {
if (d->userType == QMetaType::QVector2D
|| d->userType == QMetaType::QVector3D
|| d->userType == QMetaType::QVector4D
|| d->userType == QMetaType::QQuaternion)
d->userType = QMetaType::Double;
}
}
}
void QQuickKeyframeGroup::resetDefaultValue()
{
Q_D(QQuickKeyframeGroup);
QQmlProperty::write(target(), property(), d->originalValue);
}
void QQuickKeyframeGroup::reset()
{
Q_D(QQuickKeyframeGroup);
if (!d->componentComplete)
return;
auto *timeline = qobject_cast<QQuickTimeline*>(parent());
if (timeline)
timeline->reevaulate();
}
void QQuickKeyframeGroup::setupKeyframes()
{
Q_D(QQuickKeyframeGroup);
if (d->componentComplete)
d->setupKeyframes();
}
void QQuickKeyframeGroup::classBegin()
{
Q_D(QQuickKeyframeGroup);
d->componentComplete = false;
}
void QQuickKeyframeGroup::componentComplete()
{
Q_D(QQuickKeyframeGroup);
d->componentComplete = true;
setupKeyframes();
}
QEasingCurve QQuickKeyframe::easing() const
{
Q_D(const QQuickKeyframe);
return d->easingCurve;
}
void QQuickKeyframe::setEasing(const QEasingCurve &e)
{
Q_D(QQuickKeyframe);
if (d->easingCurve == e)
return;
d->easingCurve = e;
reset();
emit easingCurveChanged();
}
QVariant QQuickKeyframe::value() const
{
Q_D(const QQuickKeyframe);
return d->value;
}
void QQuickKeyframe::setValue(const QVariant &v)
{
Q_D(QQuickKeyframe);
if (d->value == v)
return;
d->value = v;
reset();
emit valueChanged();
}
QVariant QQuickKeyframe::evaluate(QQuickKeyframe *pre, qreal frametime, int userType) const
{
QVariantAnimation::Interpolator interpolator = QVariantAnimationPrivate::getInterpolator(userType);
if (!pre)
return value();
QVariant preValue = pre->value();
qreal preFrame = pre->frame();
qreal duration = frame() - preFrame;
qreal offset = frametime - preFrame;
qreal progress = easing().valueForProgress(offset / duration);
preValue.convert(userType);
QVariant convertedValue = value();
convertedValue.convert(userType);
if (!interpolator) {
if (progress < 1.0)
return preValue;
return convertedValue;
}
if (preValue.isValid() && convertedValue.isValid())
return interpolator(preValue.constData(), convertedValue.constData(), progress);
qWarning() << "invalid keyframe target" << preValue << convertedValue << userType;
return QVariant();
}
QT_END_NAMESPACE