/
qquickitemanimation.cpp
1046 lines (909 loc) · 36.3 KB
1
2
/****************************************************************************
**
3
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/
6
** This file is part of the QtQml module of the Qt Toolkit.
7
8
9
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
10
11
12
13
14
15
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16
17
**
** In addition, as a special exception, Nokia gives you certain additional
18
** rights. These rights are described in the Nokia Qt LGPL Exception
19
20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
21
22
23
24
25
26
27
28
29
30
31
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
32
33
34
35
36
**
**
**
**
**
38
39
40
41
** $QT_END_LICENSE$
**
****************************************************************************/
42
43
#include "qquickitemanimation_p.h"
#include "qquickitemanimation_p_p.h"
44
#include "qquickstateoperations_p.h"
46
47
#include <private/qqmlproperty_p.h>
#include <private/qquickpath_p.h>
49
#include <QtQml/qqmlinfo.h>
50
#include <QtCore/qmath.h>
51
52
#include "private/qsequentialanimationgroupjob_p.h"
#include "private/qparallelanimationgroupjob_p.h"
53
#include <QtGui/qtransform.h>
54
55
56
QT_BEGIN_NAMESPACE
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/*!
\qmlclass ParentAnimation QQuickParentAnimation
\inqmlmodule QtQuick 2
\ingroup qml-animation-transition
\since QtQuick 2.0
\inherits Animation
\brief The ParentAnimation element animates changes in parent values.
ParentAnimation is used to animate a parent change for an \l Item.
For example, the following ParentChange changes \c blueRect to become
a child of \c redRect when it is clicked. The inclusion of the
ParentAnimation, which defines a NumberAnimation to be applied during
the transition, ensures the item animates smoothly as it moves to
its new parent:
73
\snippet doc/src/snippets/qml/parentanimation.qml 0
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
A ParentAnimation can contain any number of animations. These animations will
be run in parallel; to run them sequentially, define them within a
SequentialAnimation.
In some cases, such as when reparenting between items with clipping enabled, it is useful
to animate the parent change via another item that does not have clipping
enabled. Such an item can be set using the \l via property.
For convenience, when a ParentAnimation is used in a \l Transition, it will
animate any ParentChange that has occurred during the state change.
This can be overridden by setting a specific target item using the
\l target property.
Like any other animation element, a ParentAnimation can be applied in a
number of ways, including transitions, behaviors and property value
sources. The \l {QML Animation and Transitions} documentation shows a
variety of methods for creating animations.
\sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example}
*/
95
QQuickParentAnimation::QQuickParentAnimation(QObject *parent)
96
: QQuickAnimationGroup(*(new QQuickParentAnimationPrivate), parent)
97
98
99
{
}
100
QQuickParentAnimation::~QQuickParentAnimation()
101
102
103
{
}
104
105
106
107
108
109
110
/*!
\qmlproperty Item QtQuick2::ParentAnimation::target
The item to reparent.
When used in a transition, if no target is specified, all
ParentChange occurrences are animated by the ParentAnimation.
*/
111
QQuickItem *QQuickParentAnimation::target() const
113
Q_D(const QQuickParentAnimation);
114
115
116
return d->target;
}
117
void QQuickParentAnimation::setTargetObject(QQuickItem *target)
119
Q_D(QQuickParentAnimation);
120
121
122
123
124
125
126
if (target == d->target)
return;
d->target = target;
emit targetChanged();
}
127
128
129
130
131
132
133
134
135
/*!
\qmlproperty Item QtQuick2::ParentAnimation::newParent
The new parent to animate to.
If the ParentAnimation is defined within a \l Transition or \l Behavior,
this value defaults to the value defined in the end state of the
\l Transition, or the value of the property change that triggered the
\l Behavior.
*/
136
QQuickItem *QQuickParentAnimation::newParent() const
138
Q_D(const QQuickParentAnimation);
139
140
141
return d->newParent;
}
142
void QQuickParentAnimation::setNewParent(QQuickItem *newParent)
144
Q_D(QQuickParentAnimation);
145
146
147
148
149
150
151
if (newParent == d->newParent)
return;
d->newParent = newParent;
emit newParentChanged();
}
152
153
154
155
156
157
158
159
160
161
162
163
164
/*!
\qmlproperty Item QtQuick2::ParentAnimation::via
The item to reparent via. This provides a way to do an unclipped animation
when both the old parent and new parent are clipped.
\qml
ParentAnimation {
target: myItem
via: topLevelItem
// ...
}
\endqml
*/
165
QQuickItem *QQuickParentAnimation::via() const
167
Q_D(const QQuickParentAnimation);
168
169
170
return d->via;
}
171
void QQuickParentAnimation::setVia(QQuickItem *via)
173
Q_D(QQuickParentAnimation);
174
175
176
177
178
179
180
if (via == d->via)
return;
d->via = via;
emit viaChanged();
}
181
182
//### mirrors same-named function in QQuickItem
QPointF QQuickParentAnimationPrivate::computeTransformOrigin(QQuickItem::TransformOrigin origin, qreal width, qreal height) const
184
switch (origin) {
185
default:
186
case QQuickItem::TopLeft:
187
return QPointF(0, 0);
188
case QQuickItem::Top:
189
return QPointF(width / 2., 0);
190
case QQuickItem::TopRight:
191
return QPointF(width, 0);
192
case QQuickItem::Left:
193
return QPointF(0, height / 2.);
194
case QQuickItem::Center:
195
return QPointF(width / 2., height / 2.);
196
case QQuickItem::Right:
197
return QPointF(width, height / 2.);
198
case QQuickItem::BottomLeft:
199
return QPointF(0, height);
200
case QQuickItem::Bottom:
201
return QPointF(width / 2., height);
202
case QQuickItem::BottomRight:
203
204
205
206
return QPointF(width, height);
}
}
207
208
QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &actions,
QQmlProperties &modified,
209
210
TransitionDirection direction,
QObject *defaultTarget)
212
Q_D(QQuickParentAnimation);
214
struct QQuickParentAnimationData : public QAbstractAnimationAction
216
217
QQuickParentAnimationData() {}
~QQuickParentAnimationData() { qDeleteAll(pc); }
219
QQuickStateActions actions;
220
221
//### reverse should probably apply on a per-action basis
bool reverse;
222
QList<QQuickParentChange *> pc;
223
224
225
virtual void doAction()
{
for (int ii = 0; ii < actions.count(); ++ii) {
226
const QQuickAction &action = actions.at(ii);
227
228
229
230
231
232
233
234
if (reverse)
action.event->reverse();
else
action.event->execute();
}
}
};
235
236
QQuickParentAnimationData *data = new QQuickParentAnimationData;
QQuickParentAnimationData *viaData = new QQuickParentAnimationData;
237
238
239
240
bool hasExplicit = false;
if (d->target && d->newParent) {
data->reverse = false;
241
QQuickAction myAction;
242
QQuickParentChange *pc = new QQuickParentChange;
243
244
245
246
247
248
249
250
pc->setObject(d->target);
pc->setParent(d->newParent);
myAction.event = pc;
data->pc << pc;
data->actions << myAction;
hasExplicit = true;
if (d->via) {
viaData->reverse = false;
251
QQuickAction myVAction;
252
QQuickParentChange *vpc = new QQuickParentChange;
253
254
255
256
257
258
259
260
261
262
263
264
vpc->setObject(d->target);
vpc->setParent(d->via);
myVAction.event = vpc;
viaData->pc << vpc;
viaData->actions << myVAction;
}
//### once actions have concept of modified,
// loop to match appropriate ParentChanges and mark as modified
}
if (!hasExplicit)
for (int i = 0; i < actions.size(); ++i) {
265
266
QQuickAction &action = actions[i];
if (action.event && action.event->type() == QQuickActionEvent::ParentChange
267
&& (!d->target || static_cast<QQuickParentChange*>(action.event)->object() == d->target)) {
269
QQuickParentChange *pc = static_cast<QQuickParentChange*>(action.event);
270
QQuickAction myAction = action;
271
272
273
274
275
data->reverse = action.reverseEvent;
//### this logic differs from PropertyAnimation
// (probably a result of modified vs. done)
if (d->newParent) {
276
277
QQuickParentChange *epc = new QQuickParentChange;
epc->setObject(static_cast<QQuickParentChange*>(action.event)->object());
278
279
280
281
282
283
284
285
286
287
288
289
epc->setParent(d->newParent);
myAction.event = epc;
data->pc << epc;
data->actions << myAction;
pc = epc;
} else {
action.actionDone = true;
data->actions << myAction;
}
if (d->via) {
viaData->reverse = false;
290
QQuickAction myAction;
291
QQuickParentChange *vpc = new QQuickParentChange;
292
293
294
295
296
vpc->setObject(pc->object());
vpc->setParent(d->via);
myAction.event = vpc;
viaData->pc << vpc;
viaData->actions << myAction;
297
298
299
300
301
QQuickAction dummyAction;
QQuickAction &xAction = pc->xIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
QQuickAction &yAction = pc->yIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
QQuickAction &sAction = pc->scaleIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
QQuickAction &rAction = pc->rotationIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
302
303
QQuickItem *target = pc->object();
QQuickItem *targetParent = action.reverseEvent ? pc->originalParent() : pc->parent();
305
//### this mirrors the logic in QQuickParentChange.
306
307
308
bool ok;
const QTransform &transform = targetParent->itemTransform(d->via, &ok);
if (transform.type() >= QTransform::TxShear || !ok) {
309
qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under complex transform");
310
311
312
313
314
315
316
317
318
319
ok = false;
}
qreal scale = 1;
qreal rotation = 0;
bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0);
if (ok && !isRotate) {
if (transform.m11() == transform.m22())
scale = transform.m11();
else {
320
qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
321
322
323
324
325
326
ok = false;
}
} else if (ok && isRotate) {
if (transform.m11() == transform.m22())
scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12());
else {
327
qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
328
329
330
331
332
333
ok = false;
}
if (scale != 0)
rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI;
else {
334
qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under scale of 0");
335
336
337
338
339
340
341
ok = false;
}
}
const QPointF &point = transform.map(QPointF(xAction.toValue.toReal(),yAction.toValue.toReal()));
qreal x = point.x();
qreal y = point.y();
342
if (ok && target->transformOrigin() != QQuickItem::TopLeft) {
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
qreal w = target->width();
qreal h = target->height();
if (pc->widthIsSet() && i < actions.size() - 1)
w = actions[++i].toValue.toReal();
if (pc->heightIsSet() && i < actions.size() - 1)
h = actions[++i].toValue.toReal();
const QPointF &transformOrigin
= d->computeTransformOrigin(target->transformOrigin(), w,h);
qreal tempxt = transformOrigin.x();
qreal tempyt = transformOrigin.y();
QTransform t;
t.translate(-tempxt, -tempyt);
t.rotate(rotation);
t.scale(scale, scale);
t.translate(tempxt, tempyt);
const QPointF &offset = t.map(QPointF(0,0));
x += offset.x();
y += offset.y();
}
if (ok) {
//qDebug() << x << y << rotation << scale;
xAction.toValue = x;
yAction.toValue = y;
sAction.toValue = sAction.toValue.toReal() * scale;
rAction.toValue = rAction.toValue.toReal() + rotation;
}
}
}
}
374
375
376
377
378
379
QSequentialAnimationGroupJob *topLevelGroup = new QSequentialAnimationGroupJob;
QActionAnimation *viaAction = d->via ? new QActionAnimation : 0;
QActionAnimation *targetAction = new QActionAnimation;
//we'll assume the common case by far is to have children, and always create ag
QParallelAnimationGroupJob *ag = new QParallelAnimationGroupJob;
380
if (data->actions.count()) {
381
382
383
384
385
386
387
388
389
390
if (d->via)
viaAction->setAnimAction(viaData);
targetAction->setAnimAction(data);
//take care of any child animations
bool valid = d->defaultProperty.isValid();
QAbstractAnimationJob* anim;
for (int ii = 0; ii < d->animations.count(); ++ii) {
if (valid)
d->animations.at(ii)->setDefaultTarget(d->defaultProperty);
391
anim = d->animations.at(ii)->transition(actions, modified, direction, defaultTarget);
392
393
394
395
ag->appendAnimation(anim);
}
//TODO: simplify/clarify logic
396
bool forwards = direction == QQuickAbstractAnimation::Forward;
397
398
399
400
401
if (forwards) {
topLevelGroup->appendAnimation(d->via ? viaAction : targetAction);
topLevelGroup->appendAnimation(ag);
if (d->via)
topLevelGroup->appendAnimation(targetAction);
402
} else {
403
404
405
406
if (d->via)
topLevelGroup->appendAnimation(targetAction);
topLevelGroup->appendAnimation(ag);
topLevelGroup->appendAnimation(d->via ? viaAction : targetAction);
407
408
409
410
411
412
}
} else {
delete data;
delete viaData;
}
413
return initInstance(topLevelGroup);
416
417
418
419
420
421
422
423
424
425
426
/*!
\qmlclass AnchorAnimation QQuickAnchorAnimation
\inqmlmodule QtQuick 2
\ingroup qml-animation-transition
\inherits Animation
\brief The AnchorAnimation element animates changes in anchor values.
AnchorAnimation is used to animate an anchor change.
In the following snippet we animate the addition of a right anchor to a \l Rectangle:
427
\snippet doc/src/snippets/qml/anchoranimation.qml 0
428
429
430
431
432
433
434
435
436
437
438
439
440
For convenience, when an AnchorAnimation is used in a \l Transition, it will
animate any AnchorChanges that have occurred during the state change.
This can be overridden by setting a specific target item using the
\l target property.
Like any other animation element, an AnchorAnimation can be applied in a
number of ways, including transitions, behaviors and property value
sources. The \l {QML Animation and Transitions} documentation shows a
variety of methods for creating animations.
\sa {QML Animation and Transitions}, AnchorChanges
*/
441
QQuickAnchorAnimation::QQuickAnchorAnimation(QObject *parent)
442
: QQuickAbstractAnimation(*(new QQuickAnchorAnimationPrivate), parent)
443
444
445
{
}
446
QQuickAnchorAnimation::~QQuickAnchorAnimation()
447
448
449
{
}
450
451
452
453
454
455
456
/*!
\qmlproperty list<Item> QtQuick2::AnchorAnimation::targets
The items to reanchor.
If no targets are specified all AnchorChanges will be
animated by the AnchorAnimation.
*/
457
QQmlListProperty<QQuickItem> QQuickAnchorAnimation::targets()
459
Q_D(QQuickAnchorAnimation);
460
return QQmlListProperty<QQuickItem>(this, d->targets);
463
464
465
466
467
468
/*!
\qmlproperty int QtQuick2::AnchorAnimation::duration
This property holds the duration of the animation, in milliseconds.
The default value is 250.
*/
469
int QQuickAnchorAnimation::duration() const
471
Q_D(const QQuickAnchorAnimation);
472
return d->duration;
475
void QQuickAnchorAnimation::setDuration(int duration)
476
477
478
479
480
481
{
if (duration < 0) {
qmlInfo(this) << tr("Cannot set a duration of < 0");
return;
}
482
Q_D(QQuickAnchorAnimation);
483
if (d->duration == duration)
484
return;
485
d->duration = duration;
486
487
488
emit durationChanged(duration);
}
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
/*!
\qmlproperty enumeration QtQuick2::AnchorAnimation::easing.type
\qmlproperty real QtQuick2::AnchorAnimation::easing.amplitude
\qmlproperty real QtQuick2::AnchorAnimation::easing.overshoot
\qmlproperty real QtQuick2::AnchorAnimation::easing.period
\brief the easing curve used for the animation.
To specify an easing curve you need to specify at least the type. For some curves you can also specify
amplitude, period and/or overshoot. The default easing curve is
Linear.
\qml
AnchorAnimation { easing.type: Easing.InOutQuad }
\endqml
See the \l{PropertyAnimation::easing.type} documentation for information
about the different types of easing curves.
*/
507
QEasingCurve QQuickAnchorAnimation::easing() const
509
Q_D(const QQuickAnchorAnimation);
510
return d->easing;
513
void QQuickAnchorAnimation::setEasing(const QEasingCurve &e)
515
Q_D(QQuickAnchorAnimation);
516
if (d->easing == e)
517
518
return;
519
d->easing = e;
520
521
522
emit easingChanged(e);
}
523
524
QAbstractAnimationJob* QQuickAnchorAnimation::transition(QQuickStateActions &actions,
QQmlProperties &modified,
525
526
TransitionDirection direction,
QObject *defaultTarget)
527
528
{
Q_UNUSED(modified);
529
Q_UNUSED(defaultTarget);
530
Q_D(QQuickAnchorAnimation);
531
QQuickAnimationPropertyUpdater *data = new QQuickAnimationPropertyUpdater;
532
533
534
535
536
537
538
data->interpolatorType = QMetaType::QReal;
data->interpolator = d->interpolator;
data->reverse = direction == Backward ? true : false;
data->fromSourced = false;
data->fromDefined = false;
for (int ii = 0; ii < actions.count(); ++ii) {
539
540
QQuickAction &action = actions[ii];
if (action.event && action.event->type() == QQuickActionEvent::AnchorChanges
541
542
&& (d->targets.isEmpty() || d->targets.contains(static_cast<QQuickAnchorChanges*>(action.event)->object()))) {
data->actions << static_cast<QQuickAnchorChanges*>(action.event)->additionalActions();
543
544
545
}
}
546
QQuickBulkValueAnimator *animator = new QQuickBulkValueAnimator;
547
if (data->actions.count()) {
548
549
animator->setAnimValue(data);
animator->setFromSourcedValue(&data->fromSourced);
550
551
552
} else {
delete data;
}
554
555
animator->setDuration(d->duration);
animator->setEasingCurve(d->easing);
556
return initInstance(animator);
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
/*!
\qmlclass PathAnimation QQuickPathAnimation
\inqmlmodule QtQuick 2
\ingroup qml-animation-transition
\inherits Animation
\since QtQuick 2.0
\brief The PathAnimation element animates an item along a path.
When used in a transition, the path can be specified without start
or end points, for example:
\qml
PathAnimation {
path: Path {
//no startX, startY
PathCurve { x: 100; y: 100}
PathCurve {} //last element is empty with no end point specified
}
}
\endqml
In the above case, the path start will be the item's current position, and the
path end will be the item's target position in the target state.
\sa {QML Animation and Transitions}, PathInterpolator
*/
584
QQuickPathAnimation::QQuickPathAnimation(QObject *parent)
585
: QQuickAbstractAnimation(*(new QQuickPathAnimationPrivate), parent)
586
587
588
{
}
589
QQuickPathAnimation::~QQuickPathAnimation()
591
592
593
594
595
Q_D(QQuickPathAnimation);
QHash<QQuickItem*, QQuickPathAnimationAnimator* >::iterator it;
for (it = d->activeAnimations.begin(); it != d->activeAnimations.end(); ++it) {
it.value()->clearTemplate();
}
596
597
}
598
599
600
601
602
603
/*!
\qmlproperty int QtQuick2::PathAnimation::duration
This property holds the duration of the animation, in milliseconds.
The default value is 250.
*/
604
int QQuickPathAnimation::duration() const
606
Q_D(const QQuickPathAnimation);
607
return d->duration;
608
609
}
610
void QQuickPathAnimation::setDuration(int duration)
611
612
613
614
615
616
{
if (duration < 0) {
qmlInfo(this) << tr("Cannot set a duration of < 0");
return;
}
617
Q_D(QQuickPathAnimation);
618
if (d->duration == duration)
619
return;
620
d->duration = duration;
621
622
623
emit durationChanged(duration);
}
624
625
626
627
628
629
630
631
632
633
634
635
636
637
/*!
\qmlproperty enumeration QtQuick2::PathAnimation::easing.type
\qmlproperty real QtQuick2::PathAnimation::easing.amplitude
\qmlproperty list<real> QtQuick2::PathAnimation::easing.bezierCurve
\qmlproperty real QtQuick2::PathAnimation::easing.overshoot
\qmlproperty real QtQuick2::PathAnimation::easing.period
\brief the easing curve used for the animation.
To specify an easing curve you need to specify at least the type. For some curves you can also specify
amplitude, period, overshoot or custom bezierCurve data. The default easing curve is \c Easing.Linear.
See the \l{PropertyAnimation::easing.type} documentation for information
about the different types of easing curves.
*/
638
QEasingCurve QQuickPathAnimation::easing() const
640
Q_D(const QQuickPathAnimation);
641
return d->easingCurve;
642
643
}
644
void QQuickPathAnimation::setEasing(const QEasingCurve &e)
646
Q_D(QQuickPathAnimation);
647
if (d->easingCurve == e)
648
649
return;
650
d->easingCurve = e;
651
652
653
emit easingChanged(e);
}
654
655
656
657
658
659
/*!
\qmlproperty Path QtQuick2::PathAnimation::path
This property holds the path to animate along.
For more information on defining a path see the \l Path documentation.
*/
660
QQuickPath *QQuickPathAnimation::path() const
662
Q_D(const QQuickPathAnimation);
663
664
665
return d->path;
}
666
void QQuickPathAnimation::setPath(QQuickPath *path)
668
Q_D(QQuickPathAnimation);
669
670
671
672
673
674
675
if (d->path == path)
return;
d->path = path;
emit pathChanged();
}
676
677
678
679
/*!
\qmlproperty Item QtQuick2::PathAnimation::target
This property holds the item to animate.
*/
680
QQuickItem *QQuickPathAnimation::target() const
682
Q_D(const QQuickPathAnimation);
683
684
685
return d->target;
}
686
void QQuickPathAnimation::setTargetObject(QQuickItem *target)
688
Q_D(QQuickPathAnimation);
689
690
691
692
693
694
695
if (d->target == target)
return;
d->target = target;
emit targetChanged();
}
696
697
698
699
700
701
702
703
/*!
\qmlproperty enumeration QtQuick2::PathAnimation::orientation
This property controls the rotation of the item as it animates along the path.
If a value other than \c Fixed is specified, the PathAnimation will rotate the
item to achieve the specified orientation as it travels along the path.
\list
704
\li PathAnimation.Fixed (default) - the PathAnimation will not control
705
the rotation of the item.
706
707
708
709
\li PathAnimation.RightFirst - The right side of the item will lead along the path.
\li PathAnimation.LeftFirst - The left side of the item will lead along the path.
\li PathAnimation.BottomFirst - The bottom of the item will lead along the path.
\li PathAnimation.TopFirst - The top of the item will lead along the path.
710
711
\endlist
*/
712
QQuickPathAnimation::Orientation QQuickPathAnimation::orientation() const
714
Q_D(const QQuickPathAnimation);
715
716
717
return d->orientation;
}
718
void QQuickPathAnimation::setOrientation(Orientation orientation)
720
Q_D(QQuickPathAnimation);
721
722
723
724
725
726
727
if (d->orientation == orientation)
return;
d->orientation = orientation;
emit orientationChanged(d->orientation);
}
728
729
730
731
732
733
734
735
736
/*!
\qmlproperty point QtQuick2::PathAnimation::anchorPoint
This property holds the anchor point for the item being animated.
By default, the upper-left corner of the target (its 0,0 point)
will be anchored to (or follow) the path. The anchorPoint property can be used to
specify a different point for anchoring. For example, specifying an anchorPoint of
5,5 for a 10x10 item means the center of the item will follow the path.
*/
737
QPointF QQuickPathAnimation::anchorPoint() const
739
Q_D(const QQuickPathAnimation);
740
741
742
return d->anchorPoint;
}
743
void QQuickPathAnimation::setAnchorPoint(const QPointF &point)
745
Q_D(QQuickPathAnimation);
746
747
748
749
750
751
752
if (d->anchorPoint == point)
return;
d->anchorPoint = point;
emit anchorPointChanged(point);
}
753
754
755
756
757
758
759
760
761
762
/*!
\qmlproperty real QtQuick2::PathAnimation::orientationEntryDuration
This property holds the duration (in milliseconds) of the transition in to the orientation.
If an orientation has been specified for the PathAnimation, and the starting
rotation of the item does not match that given by the orientation,
orientationEntryDuration can be used to smoothly transition from the item's
starting rotation to the rotation given by the path orientation.
*/
int QQuickPathAnimation::orientationEntryDuration() const
764
Q_D(const QQuickPathAnimation);
765
return d->entryDuration;
766
767
}
768
void QQuickPathAnimation::setOrientationEntryDuration(int duration)
770
Q_D(QQuickPathAnimation);
771
if (d->entryDuration == duration)
772
return;
773
774
d->entryDuration = duration;
emit orientationEntryDurationChanged(duration);
775
776
}
777
778
779
780
781
782
783
784
785
/*!
\qmlproperty real QtQuick2::PathAnimation::orientationExitDuration
This property holds the duration (in milliseconds) of the transition out of the orientation.
If an orientation and endRotation have been specified for the PathAnimation,
orientationExitDuration can be used to smoothly transition from the rotation given
by the path orientation to the specified endRotation.
*/
int QQuickPathAnimation::orientationExitDuration() const
787
Q_D(const QQuickPathAnimation);
788
return d->exitDuration;
789
790
}
791
void QQuickPathAnimation::setOrientationExitDuration(int duration)
793
Q_D(QQuickPathAnimation);
794
if (d->exitDuration == duration)
795
return;
796
797
d->exitDuration = duration;
emit orientationExitDurationChanged(duration);
798
799
}
800
801
802
803
804
805
806
807
808
809
810
811
812
/*!
\qmlproperty real QtQuick2::PathAnimation::endRotation
This property holds the ending rotation for the target.
If an orientation has been specified for the PathAnimation,
and the path doesn't end with the item at the desired rotation,
the endRotation property can be used to manually specify an end
rotation.
This property is typically used with orientationExitDuration, as specifying
an endRotation without an orientationExitDuration may cause a jump to
the final rotation, rather than a smooth transition.
*/
813
qreal QQuickPathAnimation::endRotation() const
815
Q_D(const QQuickPathAnimation);
816
817
818
return d->endRotation.isNull ? qreal(0) : d->endRotation.value;
}
819
void QQuickPathAnimation::setEndRotation(qreal rotation)
821
Q_D(QQuickPathAnimation);
822
823
824
825
826
827
828
if (!d->endRotation.isNull && d->endRotation == rotation)
return;
d->endRotation = rotation;
emit endRotationChanged(d->endRotation);
}
829
830
QAbstractAnimationJob* QQuickPathAnimation::transition(QQuickStateActions &actions,
QQmlProperties &modified,
831
832
TransitionDirection direction,
QObject *defaultTarget)
834
Q_D(QQuickPathAnimation);
836
837
QQuickItem *target = d->target ? d->target : qobject_cast<QQuickItem*>(defaultTarget);
838
839
QQuickPathAnimationUpdater prevData;
bool havePrevData = false;
840
if (d->activeAnimations.contains(target)) {
841
havePrevData = true;
842
prevData = *d->activeAnimations[target]->pathUpdater();
843
844
845
846
847
848
849
850
851
852
853
854
855
856
}
QList<QQuickItem*> keys = d->activeAnimations.keys();
foreach (QQuickItem *item, keys) {
QQuickPathAnimationAnimator *anim = d->activeAnimations.value(item);
if (anim->state() == QAbstractAnimationJob::Stopped) {
anim->clearTemplate();
d->activeAnimations.remove(item);
}
}
QQuickPathAnimationUpdater *data = new QQuickPathAnimationUpdater();
QQuickPathAnimationAnimator *pa = new QQuickPathAnimationAnimator(d);
857
d->activeAnimations[target] = pa;
858
859
860
data->orientation = d->orientation;
data->anchorPoint = d->anchorPoint;
861
862
data->entryInterval = d->duration ? qreal(d->entryDuration) / d->duration : qreal(0);
data->exitInterval = d->duration ? qreal(d->exitDuration) / d->duration : qreal(0);
863
data->endRotation = d->endRotation;
864
865
data->reverse = direction == Backward ? true : false;
data->fromSourced = false;
866
867
data->fromDefined = (d->path && d->path->hasStartX() && d->path->hasStartY()) ? true : false;
data->toDefined = d->path ? d->path->hasEnd() : false;
868
869
870
int origModifiedSize = modified.count();
for (int i = 0; i < actions.count(); ++i) {
871
QQuickAction &action = actions[i];
872
873
if (action.event)
continue;
874
if (action.specifiedObject == target && action.property.name() == QLatin1String("x")) {
875
876
877
878
data->toX = action.toValue.toReal();
modified << action.property;
action.fromValue = action.toValue;
}
879
if (action.specifiedObject == target && action.property.name() == QLatin1String("y")) {
880
881
882
883
884
885
data->toY = action.toValue.toReal();
modified << action.property;
action.fromValue = action.toValue;
}
}
886
if (target && d->path &&
887
(modified.count() > origModifiedSize || data->toDefined)) {
888
data->target = target;
889
data->path = d->path;
890
data->path->invalidateSequentialHistory();
892
if (havePrevData) {
893
// get the original start angle that was used (so we can exactly reverse).
894
data->startRotation = prevData.startRotation;
895
896
// treat interruptions specially, otherwise we end up with strange paths
897
898
if ((data->reverse || prevData.reverse) && prevData.currentV > 0 && prevData.currentV < 1) {
if (!data->fromDefined && !data->toDefined && !prevData.painterPath.isEmpty()) {
899
QPointF pathPos = QQuickPath::sequentialPointAt(prevData.painterPath, prevData.pathLength, prevData.attributePoints, prevData.prevBez, prevData.currentV);
900
901
if (!prevData.anchorPoint.isNull())
pathPos -= prevData.anchorPoint;
902
if (pathPos == data->target->pos()) { //only treat as interruption if we interrupted ourself
903
data->painterPath = prevData.painterPath;
904
905
data->toDefined = data->fromDefined = data->fromSourced = true;
data->prevBez.isValid = false;
906
907
908
909
data->interruptStart = prevData.currentV;
data->startRotation = prevData.startRotation;
data->pathLength = prevData.pathLength;
data->attributePoints = prevData.attributePoints;
910
911
912
913
}
}
}
}
914
915
pa->setFromSourcedValue(&data->fromSourced);
pa->setAnimValue(data);
916
} else {
917
918
919
pa->setFromSourcedValue(0);
pa->setAnimValue(0);
delete pa;
920
921
delete data;
}
922
923
924
925
pa->setDuration(d->duration);
pa->setEasingCurve(d->easingCurve);
return initInstance(pa);
926
927
}
928
void QQuickPathAnimationUpdater::setValue(qreal v)
930
931
v = qMin(qMax(v, (qreal)0.0), (qreal)1.0);;
932
933
934
935
936
937
938
939
940
if (interruptStart.isValid()) {
if (reverse)
v = 1 - v;
qreal end = reverse ? 0.0 : 1.0;
v = interruptStart + v * (end-interruptStart);
}
currentV = v;
bool atStart = ((reverse && v == 1.0) || (!reverse && v == 0.0));
if (!fromSourced && (!fromDefined || !toDefined)) {
941
942
943
944
945
946
947
948
949
950
951
qreal startX = reverse ? toX + anchorPoint.x() : target->x() + anchorPoint.x();
qreal startY = reverse ? toY + anchorPoint.y() : target->y() + anchorPoint.y();
qreal endX = reverse ? target->x() + anchorPoint.x() : toX + anchorPoint.x();
qreal endY = reverse ? target->y() + anchorPoint.y() : toY + anchorPoint.y();
prevBez.isValid = false;
painterPath = path->createPath(QPointF(startX, startY), QPointF(endX, endY), QStringList(), pathLength, attributePoints);
fromSourced = true;
}
qreal angle;
952
bool fixed = orientation == QQuickPathAnimation::Fixed;
953
954
955
956
957
QPointF currentPos = !painterPath.isEmpty() ? path->sequentialPointAt(painterPath, pathLength, attributePoints, prevBez, v, fixed ? 0 : &angle) : path->sequentialPointAt(v, fixed ? 0 : &angle);
//adjust position according to anchor point
if (!anchorPoint.isNull()) {
currentPos -= anchorPoint;
958
if (atStart) {
959
960
961
962
963
if (!anchorPoint.isNull() && !fixed)
target->setTransformOriginPoint(anchorPoint);
}
}
964
target->setPos(currentPos);
965
966
967
968
//adjust angle according to orientation
if (!fixed) {
switch (orientation) {
969
case QQuickPathAnimation::RightFirst:
970
971
angle = -angle;
break;
972
case QQuickPathAnimation::TopFirst:
973
974
angle = -angle + 90;
break;
975
case QQuickPathAnimation::LeftFirst:
976
977
angle = -angle + 180;
break;
978
case QQuickPathAnimation::BottomFirst:
979
980
981
982
983
984
angle = -angle + 270;
break;
default:
angle = 0;
break;
}
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
if (atStart && !reverse) {
startRotation = target->rotation();
//shortest distance to correct orientation
qreal diff = angle - startRotation;
while (diff > 180.0) {
startRotation.value += 360.0;
diff -= 360.0;
}
while (diff < -180.0) {
startRotation.value -= 360.0;
diff += 360.0;
}
}