diff --git a/phase.cpp b/phase.cpp index e5ecf1a4d..502368fb5 100644 --- a/phase.cpp +++ b/phase.cpp @@ -70,6 +70,18 @@ struct MovementState QPoint destination; }; +struct CustomAnimationState +{ + Phase::CurveShape curve; + int frames; + int currentFrame; + int interval; + int currentInterval; + Phase::AnimId id; + QObject* receiver; + char* slot; +}; + class Phase::Private { public: @@ -85,10 +97,27 @@ class Phase::Private { qDeleteAll(animatedItems); qDeleteAll(animatedElements); + qDeleteAll(movingItems); + + QMutableMapIterator it(customAnims); + while (it.hasNext()) { + delete it.value()->slot; + delete it.value(); + it.remove(); + } + // Animator is a QObject // and we don't own the items } + qreal calculateProgress(int frames, int currentFrame) + { + qreal progress = frames; + progress = currentFrame / progress; + progress = qMin(1.0, qMax(0.0, progress)); + return progress; + } + void performAnimation(qreal amount, const AnimationState* state) { switch (state->animation) { @@ -114,11 +143,11 @@ class Phase::Private { switch (state->movement) { case Phase::SlideIn: - kDebug() << "performMovement, SlideIn"; + //kDebug() << "performMovement, SlideIn"; animator->slideIn(amount, state->item, state->start, state->destination); break; case Phase::SlideOut: - kDebug() << "performMovement, SlideOut"; + //kDebug() << "performMovement, SlideOut"; animator->slideOut(amount, state->item, state->start, state->destination); break; } @@ -134,6 +163,7 @@ class Phase::Private QMap animatedItems; QMap movingItems; QMap animatedElements; + QMap customAnims; }; class PhaseSingleton @@ -184,6 +214,18 @@ void Phase::appletDestroyed(QObject* o) } } +void Phase::customAnimReceiverDestroyed(QObject* o) +{ + QMutableMapIterator it(d->customAnims); + while (it.hasNext()) { + if (it.next().value()->receiver == o) { + delete it.value()->slot; + delete it.value(); + it.remove(); + } + } +} + void Phase::animateItem(QGraphicsItem* item, Animation animation) { //kDebug(); @@ -261,6 +303,50 @@ void Phase::moveItem(QGraphicsItem* item, Movement movement, const QPoint &desti } } +Phase::AnimId Phase::customAnimation(int frames, int duration, Phase::CurveShape curve, + QObject* receiver, const char* slot) +{ + if (frames < 1 || duration < 1 || !receiver || !slot) { + return -1; + } + + CustomAnimationState *state = new CustomAnimationState; + state->id = ++d->animId; + state->frames = frames; + state->currentFrame = 0; + state->curve = curve; + state->interval = duration / qreal(state->frames); + state->interval = (state->interval / 40) * 40; + state->currentInterval = state->interval; + state->receiver = receiver; + state->slot = qstrdup(slot); + + d->customAnims[state->id] = state; + + connect(receiver, SLOT(objectDestroyed(QObject*)), + this, SLOT(customAnimReceiverDestroyed(QObject*))); + + QMetaObject::invokeMethod(receiver, slot, Q_ARG(qreal, 0)); + + if (!d->timerId) { + d->timerId = startTimer(40); + d->time.restart(); + } + + return state->id; +} + +void Phase::stopCustomAnimation(AnimId id) +{ + QMap::iterator it = d->customAnims.find(id); + if (it != d->customAnims.end()) { + delete it.value()->slot; + delete it.value(); + d->customAnims.erase(it); + } + //kDebug() << "stopCustomAnimation(AnimId " << id << ") done"; +} + Phase::AnimId Phase::animateElement(QGraphicsItem *item, ElementAnimation animation) { //kDebug() << "startElementAnimation(AnimId " << animation << ")"; @@ -364,9 +450,7 @@ void Phase::timerEvent(QTimerEvent *event) state->currentFrame += qMax(1, elapsed / state->interval); if (state->currentFrame < state->frames) { - qreal progress = state->frames; - progress = state->currentFrame / progress; - progress = qMin(1.0, qMax(0.0, progress)); + qreal progress = d->calculateProgress(state->frames, state->currentFrame); d->performAnimation(progress, state); state->currentInterval = state->interval; //TODO: calculate a proper interval based on the curve @@ -390,10 +474,8 @@ void Phase::timerEvent(QTimerEvent *event) state->currentFrame += qMax(1, elapsed / state->interval); if (state->currentFrame < state->frames) { - qreal progress = state->frames; - progress = state->currentFrame / progress; - progress = qMin(1.0, qMax(0.0, progress)); - d->performMovement(progress, state); + d->performMovement(d->calculateProgress(state->frames, state->currentFrame), state); + //TODO: calculate a proper interval based on the curve state->currentInterval = state->interval; animationsRemain = true; } else { @@ -428,10 +510,7 @@ void Phase::timerEvent(QTimerEvent *event) if (state->currentFrame < state->frames) { state->currentInterval = state->interval; //TODO: calculate a proper interval based on the curve - qreal progress = state->frames; - progress = state->currentFrame / progress; - progress = qMin(1.0, qMax(0.0, progress)); - state->interval *= 1 - progress; + state->interval *= 1 - d->calculateProgress(state->frames, state->currentFrame); animationsRemain = true; } } else { @@ -440,6 +519,32 @@ void Phase::timerEvent(QTimerEvent *event) } } + foreach (CustomAnimationState *state, d->customAnims) { + if (state->currentInterval <= elapsed) { + // advance the frame + state->currentFrame += qMax(1, elapsed / state->interval); + + + if (state->currentFrame < state->frames) { + //TODO: calculate a proper interval based on the curve + state->currentInterval = state->interval; + animationsRemain = true; + // signal the object + QMetaObject::invokeMethod(state->receiver, state->slot, + Q_ARG(qreal, + d->calculateProgress(state->frames, state->currentFrame))); + } else { + QMetaObject::invokeMethod(state->receiver, state->slot, Q_ARG(qreal, 1)); + d->customAnims.erase(d->customAnims.find(state->id)); + delete state->slot; + delete state; + } + } else { + state->currentInterval -= elapsed; + animationsRemain = true; + } + } + if (!animationsRemain && d->timerId) { killTimer(d->timerId); d->timerId = 0; diff --git a/phase.h b/phase.h index 0e645c7e0..2006e8942 100644 --- a/phase.h +++ b/phase.h @@ -84,10 +84,34 @@ public: Q_INVOKABLE void animateItem(QGraphicsItem* item, Animation anim); Q_INVOKABLE void moveItem(QGraphicsItem* item, Movement movement, const QPoint &destination); - AnimId animateElement(QGraphicsItem *obj, ElementAnimation); - void stopElementAnimation(AnimId id); - void setAnimationPixmap(AnimId id, const QPixmap &pixmap); - QPixmap animationResult(AnimId id); + /** + * Starts a custom animation, preventing the need to create a timeline + * with its own timer tick. + * + * @arg frames the number of frames this animation should persist for + * @arg duration the length, in milliseconds, the animation will take + * @arg curve the curve applied to the frame rate + * @arg receive the object that will handle the actual animation + * @arg slot the slot to be invoked on each update + * + * @return an id that can be used to identify this animation. + */ + Q_INVOKABLE AnimId customAnimation(int frames, int duration, Phase::CurveShape curve, + QObject* receiver, const char* slot); + + /** + * Stops a custom animation. Note that it is not necessary to call + * this on object destruction, as custom animations associated with + * a given QObject are cleaned up automatically on QObject destruction. + * + * @arg id the id of the animation as returned by customAnimation + */ + Q_INVOKABLE void stopCustomAnimation(AnimId id); + + Q_INVOKABLE AnimId animateElement(QGraphicsItem *obj, ElementAnimation); + Q_INVOKABLE void stopElementAnimation(AnimId id); + Q_INVOKABLE void setAnimationPixmap(AnimId id, const QPixmap &pixmap); + Q_INVOKABLE QPixmap animationResult(AnimId id); Q_SIGNALS: void animationComplete(QGraphicsItem *item, Animation anim); @@ -98,6 +122,7 @@ protected: protected Q_SLOTS: void appletDestroyed(QObject*); + void customAnimReceiverDestroyed(QObject*); private: void init();