* memory leak: delete all the outstanding movements

* add the ability to use Phase's coordinated tick for custom animations

svn path=/trunk/KDE/kdebase/workspace/libs/plasma/; revision=724392
This commit is contained in:
Aaron J. Seigo 2007-10-12 07:25:24 +00:00
parent 79d929e848
commit 7d29f75f4a
2 changed files with 147 additions and 17 deletions

131
phase.cpp
View File

@ -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<AnimId, CustomAnimationState*> 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<QGraphicsItem*, AnimationState*> animatedItems;
QMap<QGraphicsItem*, MovementState*> movingItems;
QMap<Phase::AnimId, ElementAnimationState*> animatedElements;
QMap<AnimId, CustomAnimationState*> customAnims;
};
class PhaseSingleton
@ -184,6 +214,18 @@ void Phase::appletDestroyed(QObject* o)
}
}
void Phase::customAnimReceiverDestroyed(QObject* o)
{
QMutableMapIterator<AnimId, CustomAnimationState*> 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<AnimId, CustomAnimationState*>::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;

33
phase.h
View File

@ -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();