• Skip to content
  • Skip to link menu
KDE 4.1 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 
00032 #include "delegateanimationhandler_p.moc"
00033 
00034 namespace KIO
00035 {
00036 
00037 // Needed because state() is a protected method
00038 class ProtectedAccessor : public QAbstractItemView
00039 {
00040 public:
00041     bool draggingState() const { return state() == DraggingState; }
00042 };
00043 
00044 
00045 
00046 // ---------------------------------------------------------------------------
00047 
00048 
00049 
00050 CachedRendering::CachedRendering(QStyle::State state, const QSize &size)
00051     : state(state), regular(QPixmap(size)), hover(QPixmap(size))
00052 {
00053     regular.fill(Qt::transparent);
00054     hover.fill(Qt::transparent);
00055 }
00056 
00057 
00058 
00059 // ---------------------------------------------------------------------------
00060 
00061 
00062 
00063 AnimationState::AnimationState(const QModelIndex &index)
00064         : index(index), direction(QTimeLine::Forward),
00065           animating(false), progress(0.0), renderCache(NULL)
00066 {
00067     creationTime.start();
00068 }
00069 
00070 
00071 AnimationState::~AnimationState()
00072 {
00073     delete renderCache;
00074 }
00075 
00076 
00077 bool AnimationState::update()
00078 {
00079     const qreal runtime = (direction == QTimeLine::Forward ? 150 : 250); // milliseconds
00080     const qreal increment = 1000. / runtime / 1000.;
00081     const qreal delta = increment * time.restart();
00082 
00083     if (direction == QTimeLine::Forward)
00084     {
00085         progress = qMin(qreal(1.0), progress + delta);
00086         animating = (progress < 1.0);
00087     }
00088     else
00089     {
00090         progress = qMax(qreal(0.0), progress - delta);
00091         animating = (progress > 0.0);
00092     }
00093 
00094     return !animating;
00095 }
00096 
00097 
00098 qreal AnimationState::hoverProgress() const
00099 {
00100 #ifndef M_PI_2
00101 #define M_PI_2 1.57079632679489661923
00102 #endif
00103     return qRound(255.0 * std::sin(progress * M_PI_2)) / 255.0;
00104 }
00105 
00106 
00107 
00108 // ---------------------------------------------------------------------------
00109 
00110 
00111 
00112 DelegateAnimationHandler::DelegateAnimationHandler(QObject *parent)
00113     : QObject(parent), timerId(0)
00114 {
00115 }
00116 
00117 
00118 AnimationState *DelegateAnimationHandler::animationState(const QStyleOption &option,
00119                                                          const QModelIndex &index,
00120                                                          const QAbstractItemView *view)
00121 {
00122    // We can't do animations reliably when an item is being dragged, since that
00123     // item will be drawn in two locations at the same time and hovered in one and
00124     // not the other. We can't tell them apart because they both have the same index.
00125     if (!view || static_cast<const ProtectedAccessor*>(view)->draggingState())
00126         return NULL;
00127 
00128     AnimationState *state = findAnimationState(view, index);
00129     bool hover = option.state & QStyle::State_MouseOver;
00130 
00131     // If the cursor has entered an item
00132     if (!state && hover)
00133     {
00134         state = new AnimationState(index);
00135         addAnimationState(state, view);
00136 
00137         if (!fadeInAddTime.isValid() ||
00138             (fadeInAddTime.isValid() && fadeInAddTime.elapsed() > 300))
00139         {
00140             startAnimation(state);
00141         }
00142         else
00143         {
00144             state->animating = false;
00145             state->progress  = 1.0;
00146             state->direction = QTimeLine::Forward;
00147         }
00148 
00149         fadeInAddTime.restart();
00150     }
00151     else if (state)
00152     {
00153         // If the cursor has exited an item
00154         if (!hover && (!state->animating || state->direction == QTimeLine::Forward))
00155         {
00156             state->direction = QTimeLine::Backward;
00157 
00158             if (state->creationTime.elapsed() < 200)
00159                 state->progress = 0.0;
00160 
00161             startAnimation(state);
00162         }
00163         else if (hover && state->direction == QTimeLine::Backward)
00164         {
00165             // This is needed to handle the case where an item is dragged within
00166             // the view, and dropped in a different location. State_MouseOver will
00167             // initially not be set causing a "hover out" animation to start.
00168             // This reverses the direction as soon as we see the bit being set.
00169             state->direction = QTimeLine::Forward;
00170 
00171             if (!state->animating)
00172                 startAnimation(state);
00173         }
00174     }
00175 
00176     return state;
00177 }
00178 
00179 
00180 AnimationState *DelegateAnimationHandler::findAnimationState(const QAbstractItemView *view,
00181                                                              const QModelIndex &index) const
00182 {
00183     // Try to find a list of animation states for the view
00184     AnimationList *list = animationLists.value(view);
00185 
00186     if (list)
00187     {
00188         foreach (AnimationState *state, *list)
00189             if (state->index == index)
00190                 return state;
00191     }
00192 
00193     return NULL;
00194 }
00195 
00196 
00197 void DelegateAnimationHandler::addAnimationState(AnimationState *state, const QAbstractItemView *view)
00198 {
00199     AnimationList *list = animationLists.value(view);
00200 
00201     // If this is the first time we've seen this view
00202     if (!list)
00203     {
00204         connect(view, SIGNAL(destroyed(QObject*)), SLOT(viewDeleted(QObject*)));
00205 
00206         list = new AnimationList;
00207         animationLists.insert(view, list);
00208     }
00209 
00210     list->append(state);
00211 }
00212 
00213 
00214 void DelegateAnimationHandler::startAnimation(AnimationState *state)
00215 {
00216     state->time.start();
00217     state->animating = true;
00218 
00219     if (!timerId)
00220     {
00221         timerId = startTimer(1000 / 30); // 30 fps
00222     }
00223 }
00224 
00225 
00226 int DelegateAnimationHandler::runAnimations(AnimationList *list, const QAbstractItemView *view)
00227 {
00228     int activeAnimations = 0;
00229     QRegion region;
00230 
00231     QMutableLinkedListIterator<AnimationState*> i(*list);
00232     while (i.hasNext())
00233     {
00234         AnimationState *state = i.next();
00235 
00236         if (!state->animating)
00237             continue;
00238 
00239         // We need to make sure the index is still valid, since it could be removed
00240         // while the animation is running.
00241         if (state->index.isValid())
00242         {
00243             bool finished = state->update();
00244             region += view->visualRect(state->index);
00245 
00246             if (!finished)
00247             {
00248                 activeAnimations++;
00249                 continue;
00250             }
00251         }
00252 
00253         // If the direction is Forward, the state object needs to stick around
00254         // after the animation has finished, so we know that we've already done
00255         // a "hover in" for the index.
00256         if (state->direction == QTimeLine::Backward || !state->index.isValid())
00257         {
00258             delete state;
00259             i.remove();
00260         }
00261     }
00262 
00263     // Trigger a repaint of the animated indexes
00264     if (!region.isEmpty())
00265         const_cast<QAbstractItemView*>(view)->viewport()->update(region);
00266 
00267     return activeAnimations;
00268 }
00269 
00270 
00271 void DelegateAnimationHandler::viewDeleted(QObject *view)
00272 {
00273     AnimationList *list = animationLists.take(static_cast<QAbstractItemView*>(view));
00274     qDeleteAll(*list);
00275     delete list;
00276 }
00277 
00278 
00279 void DelegateAnimationHandler::timerEvent(QTimerEvent *)
00280 {
00281     int activeAnimations = 0;
00282 
00283     AnimationListsIterator i(animationLists);
00284     while (i.hasNext())
00285     {
00286         i.next();
00287         AnimationList *list = i.value();
00288         const QAbstractItemView *view = i.key();
00289 
00290         activeAnimations += runAnimations(list, view);
00291     }
00292 
00293     if (activeAnimations == 0 && timerId)
00294     {
00295         killTimer(timerId);
00296         timerId = 0;
00297     }
00298 }
00299 
00300 }
00301 

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
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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