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

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
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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