/* Copyright 2007 by Robert Knight This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "layoutanimator.h" #include #include #include #include #include #include #include #include "layout.h" #include "widget.h" using namespace Plasma; class LayoutAnimator::Private { public: QHash effects; class ItemGeometry { public: QRectF startGeometry; QRectF endGeometry; }; QHash geometries; QHash states; QPointer timeLine; qreal lastValue; qreal delta(qreal currentValue) const { if ( currentValue > lastValue ) return currentValue - lastValue; else return (1.0-lastValue) + currentValue; } QRectF interpolateGeometry(LayoutItem* item,qreal value) const { QRectF newGeometry; const QRectF& current = geometries[item].startGeometry; const QRectF& next = geometries[item].endGeometry; newGeometry.setLeft(current.left() + (next.left()-current.left()) * value); newGeometry.setRight(current.right() + (next.right()-current.right()) * value); newGeometry.setTop(current.top() + (next.top()-current.top()) * value); newGeometry.setBottom(current.bottom() + (next.bottom()-current.bottom()) * value); return newGeometry; } }; LayoutAnimator::LayoutAnimator(QObject* parent) : QObject(parent), d(new Private) { d->lastValue = 0; } LayoutAnimator::~LayoutAnimator() { delete d; } void LayoutAnimator::setEffect( State action , int effect ) { d->effects[action] = effect; } int LayoutAnimator::effect(State action) const { return d->effects[action]; } void LayoutAnimator::setCurrentState( LayoutItem* item , State action ) { d->states[item] = action; } LayoutAnimator::State LayoutAnimator::state( LayoutItem* item ) const { return d->states[item]; } void LayoutAnimator::setTimeLine(QTimeLine* timeLine) { if ( d->timeLine ) { disconnect( d->timeLine , SIGNAL(valueChanged(qreal)) , this , SLOT(valueChanged(qreal)) ); disconnect( d->timeLine , SIGNAL(finished()) , this , SLOT(animationCompleted()) ); } d->timeLine = timeLine; connect( d->timeLine , SIGNAL(valueChanged(qreal)) , this , SLOT(valueChanged(qreal)) ); connect( d->timeLine , SIGNAL(finished()) , this , SLOT(animationCompleted()) ); } QTimeLine* LayoutAnimator::timeLine() const { return d->timeLine; } void LayoutAnimator::valueChanged(qreal value) { foreach( LayoutItem* item , d->geometries.keys() ) { updateItem(value,item); } d->lastValue = value; } void LayoutAnimator::setGeometry( LayoutItem* item , const QRectF& destGeometry ) { Q_ASSERT( item ); Private::ItemGeometry& itemGeometry = d->geometries[item]; itemGeometry.startGeometry = item->geometry(); itemGeometry.endGeometry = destGeometry; } void LayoutAnimator::moveEffectUpdateItem( qreal value , LayoutItem* item , Effect effect ) { Widget* widget = dynamic_cast(item->graphicsItem()); if ( widget && effect == FadeInMoveEffect ) widget->setOpacity( qMin(1.0,widget->opacity()+d->delta(value)) ); else if ( widget && effect == FadeOutMoveEffect ) widget->setOpacity( qMax(0.0,widget->opacity()-d->delta(value)) ); item->setGeometry( d->interpolateGeometry(item,value) ); } void LayoutAnimator::noEffectUpdateItem( qreal , LayoutItem* item ) { item->setGeometry( d->geometries[item].endGeometry ); } void LayoutAnimator::fadeEffectUpdateItem( qreal value , LayoutItem* item ) { Widget* widget = dynamic_cast(item->graphicsItem()); qreal threshold = 0; if ( widget != 0 && d->geometries[item].startGeometry != d->geometries[item].endGeometry ) { widget->setOpacity( qAbs( (value*2)-1.0 ) ); threshold = 0.5; } QRectF newGeometry; if ( value < threshold ) newGeometry = d->geometries[item].startGeometry; else newGeometry = d->geometries[item].endGeometry; item->setGeometry(newGeometry); } void LayoutAnimator::animationFinished(LayoutItem* item) { switch (d->states[item]) { case InsertedState: d->states[item] = NormalState; break; case RemovedState: d->states.remove(item); d->geometries.remove(item); break; case NormalState: // do nothing break; default: Q_ASSERT(false); } } void LayoutAnimator::updateItem( qreal value , LayoutItem* item ) { Q_ASSERT( value >= 0 && value <= 1.0 ); Q_ASSERT( item ); Q_ASSERT( d->geometries.contains(item) ); switch ( effect(d->states[item]) ) { case NoEffect: noEffectUpdateItem(value,item); break; case MoveEffect: moveEffectUpdateItem(value,item,MoveEffect); break; case FadeInMoveEffect: moveEffectUpdateItem(value,item,FadeInMoveEffect); break; case FadeOutMoveEffect: moveEffectUpdateItem(value,item,FadeOutMoveEffect); break; case FadeEffect: fadeEffectUpdateItem(value,item); break; default: Q_ASSERT(false); } } #include "layoutanimator.moc"