• Skip to content
  • Skip to link menu
KDE 4.4 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • 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), progress(0.0), m_fadeProgress(1.0), renderCache(NULL), fadeFromRenderCache(NULL)
00089 {
00090     creationTime.start();
00091 }
00092 
00093 
00094 AnimationState::~AnimationState()
00095 {
00096     delete renderCache;
00097     delete fadeFromRenderCache;
00098 }
00099 
00100 
00101 bool AnimationState::update()
00102 {
00103     const qreal runtime = (direction == QTimeLine::Forward ? 150 : 250); // milliseconds
00104     const qreal increment = 1000. / runtime / 1000.;
00105     const qreal delta = increment * time.restart();
00106 
00107     if (direction == QTimeLine::Forward)
00108     {
00109         progress = qMin(qreal(1.0), progress + delta);
00110         animating = (progress < 1.0);
00111     }
00112     else
00113     {
00114         progress = qMax(qreal(0.0), progress - delta);
00115         animating = (progress > 0.0);
00116     }
00117 
00118 
00119     if (fadeFromRenderCache)
00120     {
00121         //Icon fading goes always forwards
00122         m_fadeProgress = qMin(qreal(1.0), m_fadeProgress + delta);
00123         animating |= (m_fadeProgress < 1.0);
00124         if (m_fadeProgress == 1)
00125             setCachedRenderingFadeFrom(0);
00126     }
00127 
00128     return !animating;
00129 }
00130 
00131 
00132 qreal AnimationState::hoverProgress() const
00133 {
00134 #ifndef M_PI_2
00135 #define M_PI_2 1.57079632679489661923
00136 #endif
00137     return qRound(255.0 * std::sin(progress * M_PI_2)) / 255.0;
00138 }
00139 
00140 qreal AnimationState::fadeProgress() const
00141 {
00142     return qRound(255.0 * std::sin(m_fadeProgress * M_PI_2)) / 255.0;
00143 }
00144 
00145 // ---------------------------------------------------------------------------
00146 
00147 static const int switchIconInterval = 1000; 
00148 
00149 DelegateAnimationHandler::DelegateAnimationHandler(QObject *parent)
00150     : QObject(parent)
00151 {
00152     iconSequenceTimer.setSingleShot(true);
00153     iconSequenceTimer.setInterval(switchIconInterval);
00154     connect(&iconSequenceTimer, SIGNAL(timeout()), SLOT(sequenceTimerTimeout()));;
00155 }
00156 
00157 DelegateAnimationHandler::~DelegateAnimationHandler()
00158 {
00159     timer.stop();
00160 
00161     QMapIterator<const QAbstractItemView*, AnimationList*> i(animationLists);
00162     while (i.hasNext())
00163     {
00164         i.next();
00165         qDeleteAll(*i.value());
00166         delete i.value();
00167     }
00168     animationLists.clear();
00169 }
00170 
00171 void DelegateAnimationHandler::sequenceTimerTimeout()
00172 {
00173     QAbstractItemModel* model = const_cast<QAbstractItemModel*>(sequenceModelIndex.model());
00174     QAbstractProxyModel* proxy = qobject_cast<QAbstractProxyModel*>(model);
00175     QModelIndex index = sequenceModelIndex;
00176 
00177     if (proxy)
00178     {
00179         index = proxy->mapToSource(index);
00180         model = proxy->sourceModel();
00181     }
00182 
00183     KDirModel* dirModel = dynamic_cast<KDirModel*>(model);
00184     if (dirModel)
00185     {
00186         kDebug(animationDebugArea()) << "requesting" << currentSequenceIndex;
00187         dirModel->requestSequenceIcon(index, currentSequenceIndex);
00188         iconSequenceTimer.start(); // Some upper-bound interval is needed, in case items are not generated
00189     }
00190 }
00191 
00192 void DelegateAnimationHandler::gotNewIcon(const QModelIndex& index)
00193 {
00194     Q_UNUSED(index);
00195 
00196     kDebug(animationDebugArea()) << currentSequenceIndex;
00197     if (sequenceModelIndex.isValid() && currentSequenceIndex)
00198         iconSequenceTimer.start();
00199 //   if(index ==sequenceModelIndex) //Leads to problems
00200     ++currentSequenceIndex;
00201 }
00202 
00203 void DelegateAnimationHandler::setSequenceIndex(int sequenceIndex)
00204 {
00205     kDebug(animationDebugArea()) << sequenceIndex;
00206 
00207     if (sequenceIndex > 0)
00208     {
00209         currentSequenceIndex = sequenceIndex;
00210         iconSequenceTimer.start();
00211     }
00212     else
00213     {
00214         currentSequenceIndex = 0;
00215         sequenceTimerTimeout(); //Set the icon back to the standard one
00216         currentSequenceIndex = 0; //currentSequenceIndex was incremented, set it back to 0
00217         iconSequenceTimer.stop();
00218     }
00219 }
00220 
00221 void DelegateAnimationHandler::eventuallyStartIteration(QModelIndex index)
00222 {
00223 //      if (KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) {
00225 
00226     if (sequenceModelIndex.isValid())
00227         setSequenceIndex(0); // Stop old iteration, and reset the icon for the old iteration
00228 
00229     // Start sequence iteration
00230     sequenceModelIndex = index;
00231     setSequenceIndex(1);
00232 //      }
00233 }
00234 AnimationState *DelegateAnimationHandler::animationState(const QStyleOption &option,
00235                                                          const QModelIndex &index,
00236                                                          const QAbstractItemView *view)
00237 {
00238     // We can't do animations reliably when an item is being dragged, since that
00239     // item will be drawn in two locations at the same time and hovered in one and
00240     // not the other. We can't tell them apart because they both have the same index.
00241     if (!view || static_cast<const ProtectedAccessor*>(view)->draggingState())
00242         return NULL;
00243 
00244     AnimationState *state = findAnimationState(view, index);
00245     bool hover = option.state & QStyle::State_MouseOver;
00246 
00247     // If the cursor has entered an item
00248     if (!state && hover)
00249     {
00250         state = new AnimationState(index);
00251         addAnimationState(state, view);
00252 
00253         if (!fadeInAddTime.isValid() ||
00254             (fadeInAddTime.isValid() && fadeInAddTime.elapsed() > 300))
00255         {
00256             startAnimation(state);
00257         }
00258         else
00259         {
00260             state->animating = false;
00261             state->progress  = 1.0;
00262             state->direction = QTimeLine::Forward;
00263         }
00264 
00265         fadeInAddTime.restart();
00266 
00267         eventuallyStartIteration(index);
00268     }
00269     else if (state)
00270     {
00271         // If the cursor has exited an item
00272         if (!hover && (!state->animating || state->direction == QTimeLine::Forward))
00273         {
00274             state->direction = QTimeLine::Backward;
00275 
00276             if (state->creationTime.elapsed() < 200)
00277                 state->progress = 0.0;
00278 
00279             startAnimation(state);
00280 
00281             // Stop sequence iteration
00282             if (index == sequenceModelIndex)
00283             {
00284                 setSequenceIndex(0);
00285                 sequenceModelIndex = QPersistentModelIndex();
00286             }
00287         }
00288         else if (hover && state->direction == QTimeLine::Backward)
00289         {
00290             // This is needed to handle the case where an item is dragged within
00291             // the view, and dropped in a different location. State_MouseOver will
00292             // initially not be set causing a "hover out" animation to start.
00293             // This reverses the direction as soon as we see the bit being set.
00294             state->direction = QTimeLine::Forward;
00295 
00296             if (!state->animating)
00297                 startAnimation(state);
00298 
00299             eventuallyStartIteration(index);
00300         }
00301     }
00302     return state;
00303 }
00304 
00305 
00306 AnimationState *DelegateAnimationHandler::findAnimationState(const QAbstractItemView *view,
00307                                                              const QModelIndex &index) const
00308 {
00309     // Try to find a list of animation states for the view
00310     AnimationList *list = animationLists.value(view);
00311 
00312     if (list)
00313     {
00314         foreach (AnimationState *state, *list)
00315             if (state->index == index)
00316                 return state;
00317     }
00318 
00319     return NULL;
00320 }
00321 
00322 
00323 void DelegateAnimationHandler::addAnimationState(AnimationState *state, const QAbstractItemView *view)
00324 {
00325     AnimationList *list = animationLists.value(view);
00326 
00327     // If this is the first time we've seen this view
00328     if (!list)
00329     {
00330         connect(view, SIGNAL(destroyed(QObject*)), SLOT(viewDeleted(QObject*)));
00331 
00332         list = new AnimationList;
00333         animationLists.insert(view, list);
00334     }
00335 
00336     list->append(state);
00337 }
00338 
00339 void DelegateAnimationHandler::restartAnimation(AnimationState *state)
00340 {
00341     startAnimation(state);
00342 }
00343 
00344 void DelegateAnimationHandler::startAnimation(AnimationState *state)
00345 {
00346     state->time.start();
00347     state->animating = true;
00348 
00349     if (!timer.isActive())
00350         timer.start(1000 / 30, this); // 30 fps
00351 }
00352 
00353 int DelegateAnimationHandler::runAnimations(AnimationList *list, const QAbstractItemView *view)
00354 {
00355     int activeAnimations = 0;
00356     QRegion region;
00357 
00358     QMutableLinkedListIterator<AnimationState*> i(*list);
00359     while (i.hasNext())
00360     {
00361         AnimationState *state = i.next();
00362 
00363         if (!state->animating)
00364             continue;
00365 
00366         // We need to make sure the index is still valid, since it could be removed
00367         // while the animation is running.
00368         if (state->index.isValid())
00369         {
00370             bool finished = state->update();
00371             region += view->visualRect(state->index);
00372 
00373             if (!finished)
00374             {
00375                 activeAnimations++;
00376                 continue;
00377             }
00378         }
00379 
00380         // If the direction is Forward, the state object needs to stick around
00381         // after the animation has finished, so we know that we've already done
00382         // a "hover in" for the index.
00383         if (state->direction == QTimeLine::Backward || !state->index.isValid())
00384         {
00385             delete state;
00386             i.remove();
00387         }
00388     }
00389 
00390     // Trigger a repaint of the animated indexes
00391     if (!region.isEmpty())
00392         const_cast<QAbstractItemView*>(view)->viewport()->update(region);
00393 
00394     return activeAnimations;
00395 }
00396 
00397 
00398 void DelegateAnimationHandler::viewDeleted(QObject *view)
00399 {
00400     AnimationList *list = animationLists.take(static_cast<QAbstractItemView*>(view));
00401     qDeleteAll(*list);
00402     delete list;
00403 }
00404 
00405 
00406 void DelegateAnimationHandler::timerEvent(QTimerEvent *)
00407 {
00408     int activeAnimations = 0;
00409 
00410     AnimationListsIterator i(animationLists);
00411     while (i.hasNext())
00412     {
00413         i.next();
00414         AnimationList *list = i.value();
00415         const QAbstractItemView *view = i.key();
00416 
00417         activeAnimations += runAnimations(list, view);
00418     }
00419 
00420     if (activeAnimations == 0 && timer.isActive())
00421         timer.stop();
00422 }
00423 
00424 }
00425 

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
  • KUtils
  • Nepomuk
  • Plasma
  •     Sodep
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.9-20090814
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