Add LayoutAnimator class. This provides a way to animate changes in layouts. eg. Fading in new items, fading out removed items, moving and resizing items smoothly as the layout changes. The layouts themselves need some small changes to make use of this class, and the client code must create the animator, attach it to the layout and choose the animation effects to use.

svn path=/trunk/KDE/kdebase/workspace/libs/plasma/; revision=700844
This commit is contained in:
Robert Knight 2007-08-16 15:56:00 +00:00
parent cee77d79d9
commit 2475cfed09
5 changed files with 427 additions and 0 deletions

View File

@ -34,10 +34,12 @@ set(plasma_LIB_SRCS
widgets/boxlayout.cpp
widgets/checkbox.cpp
widgets/hboxlayout.cpp
# widgets/flowlayout.cpp
widgets/flash.cpp
widgets/icon.cpp
widgets/label.cpp
widgets/layout.cpp
widgets/layoutanimator.cpp
widgets/layoutitem.cpp
widgets/lineedit.cpp
widgets/pushbutton.cpp

233
widgets/layoutanimator.cpp Normal file
View File

@ -0,0 +1,233 @@
/*
Copyright 2007 by Robert Knight <robertknight@gmail.com>
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 <QtCore/QHash>
#include <QtCore/QList>
#include <QtCore/QPointer>
#include <QtCore/QTimeLine>
#include <QtGui/QGraphicsItem>
#include <QtGui/QGraphicsScene>
#include <QtDebug>
#include "layout.h"
#include "widget.h"
using namespace Plasma;
class LayoutAnimator::Private
{
public:
QHash<LayoutAnimator::State,int> effects;
class ItemGeometry
{
public:
QRectF startGeometry;
QRectF endGeometry;
};
QHash<LayoutItem*,ItemGeometry> geometries;
QHash<LayoutItem*,LayoutAnimator::State> states;
QPointer<QTimeLine> 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<Widget*>(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<Widget*>(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"

184
widgets/layoutanimator.h Normal file
View File

@ -0,0 +1,184 @@
/*
Copyright 2007 by Robert Knight <robertknight@gmail.com>
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.
*/
#ifndef __LAYOUTANIMATOR__
#define __LAYOUTANIMATOR__
#include <QtCore/QRectF>
#include <QtCore/QObject>
#include <plasma/plasma_export.h>
class QTimeLine;
namespace Plasma
{
class Layout;
class LayoutItem;
/**
* LayoutAnimator can be used to animate changes
* in Layouts.
*
* @code
*
* LayoutAnimator* animator = new LayoutAnimator;
* QTimeLine* timeLine = new QTimeLine;
*
* animator->setTimeLine(timeLine);
* animator->setEffect( LayoutAnimator::InsertedState , LayoutAnimator::FadeInMoveEffect );
* animator->setEffect( LayoutAnimator::NormalState , LayoutAnimator::MoveEffect );
* animator->setEffect( LayoutAnimator::RemovedState , LayoutAnimator::FadeOutMoveEffect );
* myLayout->setAnimator(animator);
*
* @endcode
*/
class PLASMA_EXPORT LayoutAnimator : public QObject
{
Q_OBJECT
public:
/** Constructs a new layout animator with the specified parent. */
explicit LayoutAnimator(QObject* parent = 0);
~LayoutAnimator();
/**
* This enum describes the possible states which a layout item may be in.
* Different effects can be defined for items which are being inserted,
* moved or resized or removed from layouts.
*/
enum State
{
/**
* State for an item which has recently been added to a layout.
* When the animation completes, the item's state will change to
* NormalState
*/
InsertedState,
/**
* Normal state for items in the layout.
* Items will remain in this state until it is explicitly changed
* via setCurrentState()
*/
NormalState,
/**
* State for an item which is being removed from a layout.
* When the animation completes, the item will be removed from the
* animator and its state will be undefined.
*/
RemovedState
};
/**
* This enum describes the available effects which can be used
* to animate changes in a layout.
*/
enum Effect
{
/**
* No effect. When the animation begins, the item immediately appears
* in its final position and size.
*/
NoEffect,
/**
* The item is smoothly moved and resized from its initial geometry to its final
* geometry as the animation progresses.
*/
MoveEffect,
/**
* The item fades out during the first half of the animation in its initial geometry
* and then fades in at its final position and size during the second half of
* the animation.
*/
FadeEffect,
/**
* The item is initially invisible and fades in whilst moving and resizing to
* its final position as the animation progresses.
*/
FadeInMoveEffect,
/**
* The item is initially fully opqaue and fades out whilst moving and resizing
* to its final position as the animation progresses.
*/
FadeOutMoveEffect
};
/**
* Sets the @p effect for items in the layout which are under-going a change
* specified by @p action.
*
* This allows different effects to be defined for items which are being added to,
* removed from, or repositioned inside layouts.
*/
void setEffect( State state , int effect );
/** See setEffect() */
int effect( State state ) const;
/**
* Sets the current action for a particular layout item. The Layout class
* should call this before changing an item so that the animator can apply the correct
* animation.
*
* When the current animation completes, depending on the current @p state, the item
* may advance into a new state.
*/
void setCurrentState( LayoutItem* item , State state );
/** See setCurrentState() */
State state( LayoutItem* item ) const;
/**
* Sets the new geometry for a layout item.
* The item will animate from its current geometry to @p geometry, using
* the effect specified in setEffect()
*/
virtual void setGeometry( LayoutItem* item , const QRectF& geometry );
/**
* Sets the time line used by this animator.
*
* The duration of the animation can be changed by altering @p timeLine 's duration
*/
void setTimeLine( QTimeLine* timeLine );
/** Returns the QTimeLine used by this animator. */
QTimeLine* timeLine() const;
protected:
virtual void updateItem( qreal value , LayoutItem* item );
private slots:
void valueChanged(qreal value);
private:
void moveEffectUpdateItem(qreal value,LayoutItem* item,Effect effect);
void noEffectUpdateItem(qreal value,LayoutItem* item);
void fadeEffectUpdateItem(qreal value,LayoutItem* item);
void animationFinished(LayoutItem* item);
private:
class Private;
Private* const d;
};
}
#endif // __LAYOUTANIMATOR__

View File

@ -89,6 +89,10 @@ void Widget::setOpacity(qreal opacity)
{
d->opacity = opacity;
}
qreal Widget::opacity() const
{
return d->opacity;
}
Qt::Orientations Widget::expandingDirections() const
{
@ -188,6 +192,9 @@ QSizeF Widget::sizeHint() const
void Widget::setSize(const QSizeF &newSize)
{
if ( newSize != d->size )
return;
prepareGeometryChange();
qreal width = qBound(d->minimumSize.width(), newSize.width(), d->maximumSize.width());
qreal height = qBound(d->minimumSize.height(), newSize.height(), d->maximumSize.height());

View File

@ -219,6 +219,7 @@ public:
Q_INVOKABLE void addChild(Widget *widget);
void setOpacity(qreal opacity);
qreal opacity() const;
virtual QGraphicsItem* graphicsItem();