• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KIO

delegateanimationhandler.cpp

Go to the documentation of this file.
00001 /*
00002    This file is part of the KDE project
00003 
00004    Copyright © 2007 Fredrik Höglund <fredrik@kde.org>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License as published by the Free Software Foundation; either
00009    version 2 of the License, or (at your option) any later version.
00010 
00011    This library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public License
00017    along with this library; see the file COPYING.LIB.  If not, write to
00018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019    Boston, MA 02110-1301, USA.
00020 */
00021 
00022 #include "delegateanimationhandler_p.h"
00023 
00024 #include <QListView>
00025 #include <QAbstractItemView>
00026 #include <QPersistentModelIndex>
00027 #include <QTime>
00028 #include <QDebug>
00029 
00030 #include <cmath>
00031 #include "kdirmodel.h"
00032 #include <kglobalsettings.h>
00033 #include <kdebug.h>
00034 #include <qabstractproxymodel.h>
00035 
00036 #include "delegateanimationhandler_p.moc"
00037 
00038 namespace KIO
00039 {
00040 
00041 // Needed because state() is a protected method
00042 class ProtectedAccessor : public QAbstractItemView
00043 {
00044 public:
00045     bool draggingState() const { return state() == DraggingState; }
00046 };
00047 
00048 // Debug output is disabled by default, use kdebugdialog to enable it
00049 static int animationDebugArea() { static int s_area = KDebug::registerArea("kio (delegateanimationhandler)", false);
00050                                   return s_area; }
00051 
00052 // ---------------------------------------------------------------------------
00053 
00054 
00055 
00056 CachedRendering::CachedRendering(QStyle::State state, const QSize &size, QModelIndex index)
00057     : state(state), regular(QPixmap(size)), hover(QPixmap(size)), valid(true), validityIndex(index)
00058 {
00059     regular.fill(Qt::transparent);
00060     hover.fill(Qt::transparent);
00061 
00062     if (index.model())
00063     {
00064       connect(index.model(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
00065               SLOT(dataChanged(const QModelIndex &, const QModelIndex &)));
00066       connect(index.model(), SIGNAL(modelReset()), SLOT(modelReset()));
00067     }
00068 }
00069 
00070 void CachedRendering::dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight)
00071 {
00072     if (validityIndex.row() >= topLeft.row() && validityIndex.column() >= topLeft.column() &&
00073         validityIndex.row() <= bottomRight.row() && validityIndex.column() <= bottomRight.column())
00074         valid = false;
00075 }
00076 
00077 void CachedRendering::modelReset()
00078 {
00079     valid = false;
00080 }
00081 
00082 // ---------------------------------------------------------------------------
00083 
00084 
00085 
00086 AnimationState::AnimationState(const QModelIndex &index)
00087         : index(index), direction(QTimeLine::Forward),
00088           animating(false), jobAnimation(false), progress(0.0), m_fadeProgress(1.0),
00089           m_jobAnimationAngle(0.0), renderCache(NULL), fadeFromRenderCache(NULL)
00090 {
00091     creationTime.start();
00092 }
00093 
00094 
00095 AnimationState::~AnimationState()
00096 {
00097     delete renderCache;
00098     delete fadeFromRenderCache;
00099 }
00100 
00101 
00102 bool AnimationState::update()
00103 {
00104     const qreal runtime = (direction == QTimeLine::Forward ? 150 : 250); // milliseconds
00105     const qreal increment = 1000. / runtime / 1000.;
00106     const qreal delta = increment * time.restart();
00107 
00108     if (direction == QTimeLine::Forward)
00109     {
00110         progress = qMin(qreal(1.0), progress + delta);
00111         animating = (progress < 1.0);
00112     }
00113     else
00114     {
00115         progress = qMax(qreal(0.0), progress - delta);
00116         animating = (progress > 0.0);
00117     }
00118 
00119 
00120     if (fadeFromRenderCache)
00121     {
00122         //Icon fading goes always forwards
00123         m_fadeProgress = qMin(qreal(1.0), m_fadeProgress + delta);
00124         animating |= (m_fadeProgress < 1.0);
00125         if (m_fadeProgress == 1)
00126             setCachedRenderingFadeFrom(0);
00127     }
00128 
00129     if (jobAnimation)
00130     {
00131         m_jobAnimationAngle += 1.0;
00132         if (m_jobAnimationAngle == 360)
00133             m_jobAnimationAngle = 0;
00134 
00135         if (index.model()->data(index, KDirModel::HasJobRole).toBool())
00136         {
00137             animating = true;
00138             //there is a job here still...
00139             return false;
00140         }
00141         else
00142         {
00143             animating = false;
00144             //there's no job here anymore, return true so we stop painting this.
00145             return true;
00146         }
00147     }
00148     else
00149     {
00150         return !animating;
00151     }
00152 }
00153 
00154 qreal AnimationState::hoverProgress() const
00155 {
00156 #ifndef M_PI_2
00157 #define M_PI_2 1.57079632679489661923
00158 #endif
00159     return qRound(255.0 * std::sin(progress * M_PI_2)) / 255.0;
00160 }
00161 
00162 qreal AnimationState::fadeProgress() const
00163 {
00164     return qRound(255.0 * std::sin(m_fadeProgress * M_PI_2)) / 255.0;
00165 }
00166 
00167 qreal AnimationState::jobAnimationAngle() const
00168 {
00169     return m_jobAnimationAngle;
00170 }
00171 
00172 bool AnimationState::hasJobAnimation() const
00173 {
00174     return jobAnimation;
00175 }
00176 
00177 void AnimationState::setJobAnimation(bool value)
00178 {
00179     jobAnimation = value;
00180 }
00181 
00182 // ---------------------------------------------------------------------------
00183 
00184 static const int switchIconInterval = 1000; 
00185 
00186 DelegateAnimationHandler::DelegateAnimationHandler(QObject *parent)
00187     : QObject(parent)
00188 {
00189     iconSequenceTimer.setSingleShot(true);
00190     iconSequenceTimer.setInterval(switchIconInterval);
00191     connect(&iconSequenceTimer, SIGNAL(timeout()), SLOT(sequenceTimerTimeout()));;
00192 }
00193 
00194 DelegateAnimationHandler::~DelegateAnimationHandler()
00195 {
00196     timer.stop();
00197 
00198     QMapIterator<const QAbstractItemView*, AnimationList*> i(animationLists);
00199     while (i.hasNext())
00200     {
00201         i.next();
00202         qDeleteAll(*i.value());
00203         delete i.value();
00204     }
00205     animationLists.clear();
00206 }
00207 
00208 void DelegateAnimationHandler::sequenceTimerTimeout()
00209 {
00210     QAbstractItemModel* model = const_cast<QAbstractItemModel*>(sequenceModelIndex.model());
00211     QAbstractProxyModel* proxy = qobject_cast<QAbstractProxyModel*>(model);
00212     QModelIndex index = sequenceModelIndex;
00213 
00214     if (proxy)
00215     {
00216         index = proxy->mapToSource(index);
00217         model = proxy->sourceModel();
00218     }
00219 
00220     KDirModel* dirModel = dynamic_cast<KDirModel*>(model);
00221     if (dirModel)
00222     {
00223         kDebug(animationDebugArea()) << "requesting" << currentSequenceIndex;
00224         dirModel->requestSequenceIcon(index, currentSequenceIndex);
00225         iconSequenceTimer.start(); // Some upper-bound interval is needed, in case items are not generated
00226     }
00227 }
00228 
00229 void DelegateAnimationHandler::gotNewIcon(const QModelIndex& index)
00230 {
00231     Q_UNUSED(index);
00232 
00233     kDebug(animationDebugArea()) << currentSequenceIndex;
00234     if (sequenceModelIndex.isValid() && currentSequenceIndex)
00235         iconSequenceTimer.start();
00236 //   if(index ==sequenceModelIndex) //Leads to problems
00237     ++currentSequenceIndex;
00238 }
00239 
00240 void DelegateAnimationHandler::setSequenceIndex(int sequenceIndex)
00241 {
00242     kDebug(animationDebugArea()) << sequenceIndex;
00243 
00244     if (sequenceIndex > 0)
00245     {
00246         currentSequenceIndex = sequenceIndex;
00247         iconSequenceTimer.start();
00248     }
00249     else
00250     {
00251         currentSequenceIndex = 0;
00252         sequenceTimerTimeout(); //Set the icon back to the standard one
00253         currentSequenceIndex = 0; //currentSequenceIndex was incremented, set it back to 0
00254         iconSequenceTimer.stop();
00255     }
00256 }
00257 
00258 void DelegateAnimationHandler::eventuallyStartIteration(QModelIndex index)
00259 {
00260 //      if (KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) {
00262 
00263     if (sequenceModelIndex.isValid())
00264         setSequenceIndex(0); // Stop old iteration, and reset the icon for the old iteration
00265 
00266     // Start sequence iteration
00267     sequenceModelIndex = index;
00268     setSequenceIndex(1);
00269 //      }
00270 }
00271 
00272 AnimationState *DelegateAnimationHandler::animationState(const QStyleOption &option,
00273                                                          const QModelIndex &index,
00274                                                          const QAbstractItemView *view)
00275 {
00276     // We can't do animations reliably when an item is being dragged, since that
00277     // item will be drawn in two locations at the same time and hovered in one and
00278     // not the other. We can't tell them apart because they both have the same index.
00279     if (!view || static_cast<const ProtectedAccessor*>(view)->draggingState())
00280         return NULL;
00281 
00282     AnimationState *state = findAnimationState(view, index);
00283     bool hover = option.state & QStyle::State_MouseOver;
00284 
00285     // If the cursor has entered an item
00286     if (!state && hover)
00287     {
00288         state = new AnimationState(index);
00289         addAnimationState(state, view);
00290 
00291         if (!fadeInAddTime.isValid() ||
00292             (fadeInAddTime.isValid() && fadeInAddTime.elapsed() > 300))
00293         {
00294             startAnimation(state);
00295         }
00296         else
00297         {
00298             state->animating = false;
00299             state->progress  = 1.0;
00300             state->direction = QTimeLine::Forward;
00301         }
00302 
00303         fadeInAddTime.restart();
00304 
00305         eventuallyStartIteration(index);
00306     }
00307     else if (state)
00308     {
00309         // If the cursor has exited an item
00310         if (!hover && (!state->animating || state->direction == QTimeLine::Forward))
00311         {
00312             state->direction = QTimeLine::Backward;
00313 
00314             if (state->creationTime.elapsed() < 200)
00315                 state->progress = 0.0;
00316 
00317             startAnimation(state);
00318 
00319             // Stop sequence iteration
00320             if (index == sequenceModelIndex)
00321             {
00322                 setSequenceIndex(0);
00323                 sequenceModelIndex = QPersistentModelIndex();
00324             }
00325         }
00326         else if (hover && state->direction == QTimeLine::Backward)
00327         {
00328             // This is needed to handle the case where an item is dragged within
00329             // the view, and dropped in a different location. State_MouseOver will
00330             // initially not be set causing a "hover out" animation to start.
00331             // This reverses the direction as soon as we see the bit being set.
00332             state->direction = QTimeLine::Forward;
00333 
00334             if (!state->animating)
00335                 startAnimation(state);
00336 
00337             eventuallyStartIteration(index);
00338         }
00339     }
00340     else if (!state && index.model()->data(index, KDirModel::HasJobRole).toBool())
00341     {
00342         state = new AnimationState(index);
00343         addAnimationState(state, view);
00344         startAnimation(state);
00345         state->setJobAnimation(true);
00346     }
00347 
00348     return state;
00349 }
00350 
00351 
00352 AnimationState *DelegateAnimationHandler::findAnimationState(const QAbstractItemView *view,
00353                                                              const QModelIndex &index) const
00354 {
00355     // Try to find a list of animation states for the view
00356     AnimationList *list = animationLists.value(view);
00357 
00358     if (list)
00359     {
00360         foreach (AnimationState *state, *list)
00361             if (state->index == index)
00362                 return state;
00363     }
00364 
00365     return NULL;
00366 }
00367 
00368 
00369 void DelegateAnimationHandler::addAnimationState(AnimationState *state, const QAbstractItemView *view)
00370 {
00371     AnimationList *list = animationLists.value(view);
00372 
00373     // If this is the first time we've seen this view
00374     if (!list)
00375     {
00376         connect(view, SIGNAL(destroyed(QObject*)), SLOT(viewDeleted(QObject*)));
00377 
00378         list = new AnimationList;
00379         animationLists.insert(view, list);
00380     }
00381 
00382     list->append(state);
00383 }
00384 
00385 void DelegateAnimationHandler::restartAnimation(AnimationState *state)
00386 {
00387     startAnimation(state);
00388 }
00389 
00390 void DelegateAnimationHandler::startAnimation(AnimationState *state)
00391 {
00392     state->time.start();
00393     state->animating = true;
00394 
00395     if (!timer.isActive())
00396         timer.start(1000 / 30, this); // 30 fps
00397 }
00398 
00399 int DelegateAnimationHandler::runAnimations(AnimationList *list, const QAbstractItemView *view)
00400 {
00401     int activeAnimations = 0;
00402     QRegion region;
00403 
00404     QMutableLinkedListIterator<AnimationState*> i(*list);
00405     while (i.hasNext())
00406     {
00407         AnimationState *state = i.next();
00408 
00409         if (!state->animating)
00410             continue;
00411 
00412         // We need to make sure the index is still valid, since it could be removed
00413         // while the animation is running.
00414         if (state->index.isValid())
00415         {
00416             bool finished = state->update();
00417             region += view->visualRect(state->index);
00418 
00419             if (!finished)
00420             {
00421                 activeAnimations++;
00422                 continue;
00423             }
00424         }
00425 
00426         // If the direction is Forward, the state object needs to stick around
00427         // after the animation has finished, so we know that we've already done
00428         // a "hover in" for the index.
00429         if (state->direction == QTimeLine::Backward || !state->index.isValid())
00430         {
00431             delete state;
00432             i.remove();
00433         }
00434     }
00435 
00436     // Trigger a repaint of the animated indexes
00437     if (!region.isEmpty())
00438         const_cast<QAbstractItemView*>(view)->viewport()->update(region);
00439 
00440     return activeAnimations;
00441 }
00442 
00443 
00444 void DelegateAnimationHandler::viewDeleted(QObject *view)
00445 {
00446     AnimationList *list = animationLists.take(static_cast<QAbstractItemView*>(view));
00447     qDeleteAll(*list);
00448     delete list;
00449 }
00450 
00451 
00452 void DelegateAnimationHandler::timerEvent(QTimerEvent *)
00453 {
00454     int activeAnimations = 0;
00455 
00456     AnimationListsIterator i(animationLists);
00457     while (i.hasNext())
00458     {
00459         i.next();
00460         AnimationList *list = i.value();
00461         const QAbstractItemView *view = i.key();
00462 
00463         activeAnimations += runAnimations(list, view);
00464     }
00465 
00466     if (activeAnimations == 0 && timer.isActive())
00467         timer.stop();
00468 }
00469 
00470 }
00471 

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal