KIO
delegateanimationhandler.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
00042 class ProtectedAccessor : public QAbstractItemView
00043 {
00044 public:
00045 bool draggingState() const { return state() == DraggingState; }
00046 };
00047
00048
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);
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
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();
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
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();
00216 currentSequenceIndex = 0;
00217 iconSequenceTimer.stop();
00218 }
00219 }
00220
00221 void DelegateAnimationHandler::eventuallyStartIteration(QModelIndex index)
00222 {
00223
00225
00226 if (sequenceModelIndex.isValid())
00227 setSequenceIndex(0);
00228
00229
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
00239
00240
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
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
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
00282 if (index == sequenceModelIndex)
00283 {
00284 setSequenceIndex(0);
00285 sequenceModelIndex = QPersistentModelIndex();
00286 }
00287 }
00288 else if (hover && state->direction == QTimeLine::Backward)
00289 {
00290
00291
00292
00293
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
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
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);
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
00367
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
00381
00382
00383 if (state->direction == QTimeLine::Backward || !state->index.isValid())
00384 {
00385 delete state;
00386 i.remove();
00387 }
00388 }
00389
00390
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