22 #include "private/animator_p.h"
24 #include <QGraphicsItem>
26 #include <QTimerEvent>
29 #include <kconfiggroup.h>
32 #include <kservicetypetrader.h>
33 #include <kglobalsettings.h>
36 #include "private/kineticscroll_p.h"
44 AnimatorPrivate::AnimatorPrivate(
Animator *parent)
52 AnimatorPrivate::~AnimatorPrivate()
55 qDeleteAll(animatedItems);
56 qDeleteAll(animatedElements);
57 qDeleteAll(movingItems);
59 QMutableHashIterator<int, CustomAnimationState*> it(customAnims);
60 while (it.hasNext()) {
62 delete[] it.value()->slot;
71 qreal AnimatorPrivate::calculateProgress(
int time,
int duration, Animator::CurveShape curve)
73 if (!(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects)) {
77 timeline.setCurveShape(static_cast<QTimeLine::CurveShape>(curve));
78 timeline.setDuration(duration);
79 qreal progress = timeline.valueForTime(time);
83 void AnimatorPrivate::performAnimation(qreal amount,
const AnimationState *state)
87 switch (state->animation) {
88 case Animator::AppearAnimation:
89 driver->itemAppear(amount, state->item);
91 case Animator::DisappearAnimation:
92 driver->itemDisappear(amount, state->item);
97 case Animator::ActivateAnimation:
98 driver->itemActivated(amount, state->item);
101 kDebug() <<
"Unsupported animation type.";
106 void AnimatorPrivate::performMovement(qreal amount,
const MovementState *state)
108 switch (state->movement) {
109 case Animator::SlideInMovement:
110 case Animator::FastSlideInMovement:
112 driver->itemSlideIn(amount, state->item, state->start, state->destination);
114 case Animator::SlideOutMovement:
115 case Animator::FastSlideOutMovement:
117 driver->itemSlideOut(amount, state->item, state->start, state->destination);
122 void AnimatorPrivate::scrollStateChanged(QAbstractAnimation::State newState,
123 QAbstractAnimation::State oldState)
125 KineticScrolling *scroll = qobject_cast<KineticScrolling*>(q->sender());
127 kDebug() <<
"Could not find KineticScrolling object";
131 emit q->scrollStateChanged(scrollingManagers.key(scroll), newState, oldState);
134 class AnimatorSingleton
140 K_GLOBAL_STATIC(AnimatorSingleton, privateSelf)
144 return &privateSelf->
self;
147 Animator::Animator(
QObject *parent)
149 d(new AnimatorPrivate(this))
154 Animator::~Animator()
159 void AnimatorPrivate::animatedItemDestroyed(
QObject *o)
162 QMutableHashIterator<QGraphicsItem*, AnimationState*> it(animatedItems);
163 while (it.hasNext()) {
166 if (it.value()->qobj == o) {
167 kDebug() <<
"found deleted animated item";
169 animatedItemsToDelete.insert(it.value());
179 void AnimatorPrivate::movingItemDestroyed(
QObject *o)
181 QMutableHashIterator<QGraphicsItem*, MovementState*> it(movingItems);
182 while (it.hasNext()) {
184 if (it.value()->qobj == o) {
186 movingItemsToDelete.insert(it.value());
196 void AnimatorPrivate::animatedElementDestroyed(
QObject *o)
198 QMutableHashIterator<int, ElementAnimationState*> it(animatedElements);
199 while (it.hasNext()) {
201 if (it.value()->qobj == o) {
203 animatedElementsToDelete.insert(it.value());
213 void AnimatorPrivate::customAnimReceiverDestroyed(
QObject *o)
215 QMutableHashIterator<int, CustomAnimationState*> it(customAnims);
216 while (it.hasNext()) {
217 if (it.next().value()->receiver == o) {
219 customAnimsToDelete.insert(it.value());
221 delete[] it.value()->slot;
234 QHash<QGraphicsItem*, AnimationState*>::iterator it = d->animatedItems.find(item);
235 if (it != d->animatedItems.end()) {
237 d->animatedItemsToDelete.insert(it.value());
242 d->animatedItems.erase(it);
245 int frames = d->driver->animationFps(animation);
253 int duration = d->driver->animationDuration(animation);
255 AnimationState *state =
new AnimationState;
256 state->id = ++d->animId;
259 state->curve = d->driver->animationCurve(animation);
260 state->frames = qMax(1.0, frames * (duration / 1000.0));
261 state->currentFrame = 0;
262 state->interval = d->driver->animationDuration(animation) / qreal(state->frames);
264 state->currentInterval = state->interval;
265 state->qobj =
dynamic_cast<QObject*
>(item);
269 disconnect(state->qobj, SIGNAL(destroyed(
QObject*)),
270 this, SLOT(animatedItemDestroyed(
QObject*)));
271 connect(state->qobj, SIGNAL(destroyed(
QObject*)),
272 this, SLOT(animatedItemDestroyed(
QObject*)));
275 d->animatedItems[item] = state;
276 d->performAnimation(0, state);
289 QHash<QGraphicsItem*, MovementState*>::iterator it = d->movingItems.find(item);
290 if (it != d->movingItems.end()) {
292 d->movingItemsToDelete.insert(it.value());
297 d->movingItems.erase(it);
300 int frames = d->driver->movementAnimationFps(movement);
307 MovementState *state =
new MovementState;
308 state->id = ++d->animId;
309 state->destination = destination;
310 state->start = item->pos().toPoint();
312 state->movement = movement;
313 state->curve = d->driver->movementAnimationCurve(movement);
314 int duration = d->driver->movementAnimationDuration(movement);
315 state->frames = qMax(1.0, frames * (duration / 1000.0));
316 state->currentFrame = 0;
317 state->interval = duration / qreal(state->frames);
321 state->currentInterval = state->interval;
322 state->qobj =
dynamic_cast<QObject*
>(item);
325 disconnect(state->qobj, SIGNAL(destroyed(
QObject*)),
this, SLOT(movingItemDestroyed(
QObject*)));
326 connect(state->qobj, SIGNAL(destroyed(
QObject*)),
this, SLOT(movingItemDestroyed(
QObject*)));
329 d->movingItems[item] = state;
330 d->performMovement(0, state);
341 QObject *receiver,
const char *slot)
343 if (frames < 1 || duration < 1 || !receiver || !slot) {
347 CustomAnimationState *state =
new CustomAnimationState;
348 state->id = ++d->animId;
349 state->frames = frames;
350 state->currentFrame = 0;
351 state->curve = curve;
352 state->frameInterval = qMax(qreal(1.0), duration / qreal(state->frames));
354 state->currentInterval = state->interval;
355 state->receiver = receiver;
356 state->slot = qstrdup(slot);
358 d->customAnims[state->id] = state;
360 disconnect(receiver, SIGNAL(destroyed(
QObject*)),
361 this, SLOT(customAnimReceiverDestroyed(
QObject*)));
362 connect(receiver, SIGNAL(destroyed(
QObject*)),
363 this, SLOT(customAnimReceiverDestroyed(
QObject*)));
366 if (!QMetaObject::invokeMethod(receiver, slot, Q_ARG(qreal, 0))) {
368 QMetaObject::invokeMethod(receiver, slot, Q_ARG(qreal, 0), Q_ARG(
int, state->id));
381 QHash<int, CustomAnimationState*>::iterator it = d->customAnims.find(
id);
382 if (it != d->customAnims.end()) {
384 d->customAnimsToDelete.insert(it.value());
386 delete[] it.value()->slot;
390 d->customAnims.erase(it);
397 QMutableHashIterator<QGraphicsItem*, AnimationState*> it(d->animatedItems);
398 while (it.hasNext()) {
400 if (it.value()->id == id) {
402 d->animatedItemsToDelete.insert(it.value());
415 QMutableHashIterator<QGraphicsItem*, MovementState*> it(d->movingItems);
416 while (it.hasNext()) {
418 if (it.value()->id == id) {
420 d->movingItemsToDelete.insert(it.value());
434 int frames = d->driver->elementAnimationFps(animation);
435 int duration = d->driver->animationDuration(animation);
437 ElementAnimationState *state =
new ElementAnimationState;
439 state->curve = d->driver->elementAnimationCurve(animation);
441 state->frames = qMax(1.0, frames * (duration / 1000.0));
442 state->currentFrame = 0;
443 state->interval = duration / qreal(state->frames);
445 state->currentInterval = state->interval;
446 state->id = ++d->animId;
447 state->qobj =
dynamic_cast<QObject*
>(item);
450 disconnect(state->qobj, SIGNAL(destroyed(
QObject*)),
451 this, SLOT(animatedElementDestroyed(
QObject*)));
452 connect(state->qobj, SIGNAL(destroyed(
QObject*)),
453 this, SLOT(animatedElementDestroyed(
QObject*)));
458 bool needTimer =
true;
459 if (state->frames < 1) {
461 state->currentFrame = 1;
465 d->animatedElements[state->id] = state;
468 if (needTimer && !d->timerId) {
478 QHash<int, ElementAnimationState*>::iterator it = d->animatedElements.find(
id);
479 if (it != d->animatedElements.end()) {
481 d->animatedElementsToDelete.insert(it.value());
486 d->animatedElements.erase(it);
493 QHash<int, ElementAnimationState*>::iterator it = d->animatedElements.find(
id);
495 if (it == d->animatedElements.end()) {
496 kDebug() <<
"No entry found for id " << id;
500 it.value()->pixmap = pixmap;
505 QHash<int, ElementAnimationState*>::const_iterator it = d->animatedElements.constFind(
id);
507 if (it == d->animatedElements.constEnd()) {
512 ElementAnimationState *state = it.value();
513 qreal progress = d->calculateProgress(state->currentFrame * state->interval,
514 state->frames * state->interval,
518 switch (state->animation) {
520 return d->driver->elementAppear(progress, state->pixmap);
523 return d->driver->elementDisappear(progress, state->pixmap);
528 kDebug() <<
"Unsupported animation type.";
532 return state->pixmap;
537 return (!d->animatedItems.isEmpty() ||
538 !d->movingItems.isEmpty() ||
539 !d->animatedElements.isEmpty() ||
540 !d->customAnims.isEmpty());
545 if (event->timerId() != d->timerId) {
546 QObject::timerEvent(event);
551 bool animationsRemain =
false;
553 if (d->time.elapsed() > elapsed) {
554 elapsed = d->time.elapsed();
559 foreach (AnimationState *state, d->animatedItems) {
560 if (d->animatedItemsToDelete.contains(state)) {
564 if (state->currentInterval <= elapsed) {
566 state->currentFrame +=
567 (KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) ?
568 qMax(1, elapsed / state->interval) : state->frames - state->currentFrame;
570 if (state->currentFrame < state->frames) {
571 qreal progress = d->calculateProgress(state->currentFrame * state->interval,
572 state->frames * state->interval,
574 d->performAnimation(progress, state);
575 state->currentInterval = state->interval;
576 animationsRemain =
true;
578 d->performAnimation(1, state);
579 d->animatedItems.erase(d->animatedItems.find(state->item));
581 d->animatedItemsToDelete.insert(state);
584 state->currentInterval -= elapsed;
585 animationsRemain =
true;
589 foreach (MovementState *state, d->movingItems) {
590 if (d->movingItemsToDelete.contains(state)) {
594 if (state->currentInterval <= elapsed) {
596 state->currentFrame +=
597 (KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) ?
598 qMax(1, elapsed / state->interval) : state->frames - state->currentFrame;
600 if (state->currentFrame < state->frames) {
602 qreal progress = d->calculateProgress(state->currentFrame * state->interval,
603 state->frames * state->interval,
605 d->performMovement(progress, state);
606 animationsRemain =
true;
609 d->performMovement(1, state);
610 d->movingItems.erase(d->movingItems.find(state->item));
612 d->movingItemsToDelete.insert(state);
615 state->currentInterval -= elapsed;
616 animationsRemain =
true;
620 foreach (ElementAnimationState *state, d->animatedElements) {
621 if (d->animatedElementsToDelete.contains(state)) {
625 if (state->currentFrame == state->frames) {
634 if (state->currentInterval <= elapsed) {
640 state->currentFrame +=
641 (KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) ?
642 qMax(1, elapsed / state->interval) : state->frames - state->currentFrame;
644 state->item->update();
645 if (state->currentFrame < state->frames) {
646 state->currentInterval = state->interval;
647 animationsRemain =
true;
649 d->animatedElements.remove(state->id);
651 d->animatedElementsToDelete.insert(state);
654 state->currentInterval -= elapsed;
655 animationsRemain =
true;
659 foreach (CustomAnimationState *state, d->customAnims) {
660 if (d->customAnimsToDelete.contains(state)) {
664 if (state->currentInterval <= elapsed) {
666 state->currentFrame +=
667 (KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) ?
668 qMax(1, elapsed / state->frameInterval) : state->frames - state->currentFrame;
674 if (state->currentFrame < state->frames) {
676 state->currentInterval = state->interval;
677 animationsRemain =
true;
680 qreal progress = d->calculateProgress(state->currentFrame * state->interval,
681 state->frames * state->interval,
683 if (!QMetaObject::invokeMethod(state->receiver, state->slot, Q_ARG(qreal, progress))) {
685 QMetaObject::invokeMethod(state->receiver, state->slot, Q_ARG(qreal, progress),
686 Q_ARG(
int, state->id));
689 if (!QMetaObject::invokeMethod(state->receiver, state->slot, Q_ARG(qreal, 1))) {
690 QMetaObject::invokeMethod(state->receiver, state->slot, Q_ARG(qreal, 1), Q_ARG(
int, state->id));
692 d->customAnims.erase(d->customAnims.find(state->id));
694 d->customAnimsToDelete.insert(state);
697 state->currentInterval -= elapsed;
698 animationsRemain =
true;
702 if (!animationsRemain && d->timerId) {
703 killTimer(d->timerId);
710 void AnimatorPrivate::init(
Animator *q)
713 KConfig c(
"plasmarc");
714 KConfigGroup cg(&c,
"Animator");
715 QString pluginName = cg.readEntry(
"driver",
"default");
717 if (!pluginName.isEmpty()) {
718 QString constraint = QString(
"[X-KDE-PluginInfo-Name] == '%1'").arg(pluginName);
719 KService::List offers = KServiceTypeTrader::self()->query(
"Plasma/Animator", constraint);
721 if (!offers.isEmpty()) {
724 KPluginLoader plugin(*offers.first());
731 kDebug() <<
"Could not load requested animator "
732 << offers.first() <<
". Error given: " << error;
738 driver =
new AnimationDriver(q);
742 void AnimatorPrivate::cleanupStates()
748 qDeleteAll(animatedItemsToDelete);
749 animatedItemsToDelete.clear();
750 qDeleteAll(animatedElementsToDelete);
751 animatedElementsToDelete.clear();
752 qDeleteAll(movingItemsToDelete);
753 movingItemsToDelete.clear();
755 QSetIterator<CustomAnimationState*> it(customAnimsToDelete);
756 while (it.hasNext()) {
757 CustomAnimationState *state = it.next();
758 delete[] state->slot;
761 customAnimsToDelete.clear();
766 if (!d->scrollingManagers.contains(widget)) {
767 KineticScrolling *scroll =
new KineticScrolling(widget);
768 d->scrollingManagers.insert(widget, scroll);
770 SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)),
this,
777 if (d->scrollingManagers.contains(widget)) {
778 disconnect(d->scrollingManagers.value(widget),
779 SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)),
this,
781 d->scrollingManagers.value(widget)->deleteLater();
782 d->scrollingManagers.remove(widget);
A system for applying effects to Plasma elements.
static Animator * self()
Singleton accessor.
Q_INVOKABLE void stopItemAnimation(int id)
Stops an item animation before the animation is complete.
Q_INVOKABLE int customAnimation(int frames, int duration, Animator::CurveShape curve, QObject *receiver, const char *method)
Starts a custom animation, preventing the need to create a timeline with its own timer tick...
void customAnimationFinished(int id)
void animationFinished(QGraphicsItem *item, Plasma::Animator::Animation anim)
Q_INVOKABLE bool isAnimating() const
Can be used to query if there are other animations happening.
Q_INVOKABLE void stopElementAnimation(int id)
void movementFinished(QGraphicsItem *item)
QScriptValue animation(const QString &anim)
bool isPluginVersionCompatible(unsigned int version)
Verifies that a plugin is compatible with plasma.
Q_INVOKABLE int animateElement(QGraphicsItem *obj, Animation)
void timerEvent(QTimerEvent *event)
static const int MIN_TICK_RATE_INT
Q_INVOKABLE int animateItem(QGraphicsItem *item, Animation anim)
Starts a standard animation on a QGraphicsItem.
static const qreal MIN_TICK_RATE
Q_INVOKABLE int moveItem(QGraphicsItem *item, Movement movement, const QPoint &destination)
Starts a standard animation on a QGraphicsItem.
Q_INVOKABLE QPixmap currentPixmap(int id)
void scrollStateChanged(QGraphicsWidget *widget, QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
Q_INVOKABLE void stopCustomAnimation(int id)
Stops a custom animation.
Q_INVOKABLE void stopItemMovement(int id)
Stops an item movement before the animation is complete.
void unregisterScrollingManager(QGraphicsWidget *widget)
unregister the scrolling manager of a certain widget This function is deprecated: use ScrollWidget in...
void registerScrollingManager(QGraphicsWidget *widget)
Register a widget as a scrolling widget.
void elementAnimationFinished(int id)
Q_INVOKABLE void setInitialPixmap(int id, const QPixmap &pixmap)