From 2475cfed092858222f4c7d84dedd533d5e701e04 Mon Sep 17 00:00:00 2001 From: Robert Knight Date: Thu, 16 Aug 2007 15:56:00 +0000 Subject: [PATCH] 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 --- CMakeLists.txt | 2 + widgets/layoutanimator.cpp | 233 +++++++++++++++++++++++++++++++++++++ widgets/layoutanimator.h | 184 +++++++++++++++++++++++++++++ widgets/widget.cpp | 7 ++ widgets/widget.h | 1 + 5 files changed, 427 insertions(+) create mode 100644 widgets/layoutanimator.cpp create mode 100644 widgets/layoutanimator.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fb46d983..c0a0b5868 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/widgets/layoutanimator.cpp b/widgets/layoutanimator.cpp new file mode 100644 index 000000000..ee5ee4d4f --- /dev/null +++ b/widgets/layoutanimator.cpp @@ -0,0 +1,233 @@ +/* + 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" + diff --git a/widgets/layoutanimator.h b/widgets/layoutanimator.h new file mode 100644 index 000000000..962e22bd3 --- /dev/null +++ b/widgets/layoutanimator.h @@ -0,0 +1,184 @@ +/* + 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. +*/ + +#ifndef __LAYOUTANIMATOR__ +#define __LAYOUTANIMATOR__ + +#include +#include + +#include + +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__ + diff --git a/widgets/widget.cpp b/widgets/widget.cpp index 5ed3e0f5c..8c5c0131e 100644 --- a/widgets/widget.cpp +++ b/widgets/widget.cpp @@ -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()); diff --git a/widgets/widget.h b/widgets/widget.h index 263d23b8a..1ff02dffb 100644 --- a/widgets/widget.h +++ b/widgets/widget.h @@ -219,6 +219,7 @@ public: Q_INVOKABLE void addChild(Widget *widget); void setOpacity(qreal opacity); + qreal opacity() const; virtual QGraphicsItem* graphicsItem();