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

KIO

  • kio
  • kio
delegateanimationhandler.cpp
Go to the documentation of this file.
1/*
2 This file is part of the KDE project
3
4 Copyright © 2007 Fredrik Höglund <fredrik@kde.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20*/
21
22#include "delegateanimationhandler_p.h"
23
24#include <QListView>
25#include <QAbstractItemView>
26#include <QPersistentModelIndex>
27#include <QTime>
28#include <QDebug>
29
30#include <cmath>
31#include "kdirmodel.h"
32#include <kdebug.h>
33#include <qabstractproxymodel.h>
34
35#include "delegateanimationhandler_p.moc"
36
37namespace KIO
38{
39
40// Needed because state() is a protected method
41class ProtectedAccessor : public QAbstractItemView
42{
43public:
44 bool draggingState() const { return state() == DraggingState; }
45};
46
47// Debug output is disabled by default, use kdebugdialog to enable it
48static int animationDebugArea() { static int s_area = KDebug::registerArea("kio (delegateanimationhandler)", false);
49 return s_area; }
50
51// ---------------------------------------------------------------------------
52
53
54
55CachedRendering::CachedRendering(QStyle::State state, const QSize &size, QModelIndex index)
56 : state(state), regular(QPixmap(size)), hover(QPixmap(size)), valid(true), validityIndex(index)
57{
58 regular.fill(Qt::transparent);
59 hover.fill(Qt::transparent);
60
61 if (index.model())
62 {
63 connect(index.model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
64 SLOT(dataChanged(QModelIndex,QModelIndex)));
65 connect(index.model(), SIGNAL(modelReset()), SLOT(modelReset()));
66 }
67}
68
69void CachedRendering::dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight)
70{
71 if (validityIndex.row() >= topLeft.row() && validityIndex.column() >= topLeft.column() &&
72 validityIndex.row() <= bottomRight.row() && validityIndex.column() <= bottomRight.column())
73 valid = false;
74}
75
76void CachedRendering::modelReset()
77{
78 valid = false;
79}
80
81// ---------------------------------------------------------------------------
82
83
84
85AnimationState::AnimationState(const QModelIndex &index)
86 : index(index), direction(QTimeLine::Forward),
87 animating(false), jobAnimation(false), progress(0.0), m_fadeProgress(1.0),
88 m_jobAnimationAngle(0.0), renderCache(NULL), fadeFromRenderCache(NULL)
89{
90 creationTime.start();
91}
92
93
94AnimationState::~AnimationState()
95{
96 delete renderCache;
97 delete fadeFromRenderCache;
98}
99
100
101bool AnimationState::update()
102{
103 const qreal runtime = (direction == QTimeLine::Forward ? 150 : 250); // milliseconds
104 const qreal increment = 1000. / runtime / 1000.;
105 const qreal delta = increment * time.restart();
106
107 if (direction == QTimeLine::Forward)
108 {
109 progress = qMin(qreal(1.0), progress + delta);
110 animating = (progress < 1.0);
111 }
112 else
113 {
114 progress = qMax(qreal(0.0), progress - delta);
115 animating = (progress > 0.0);
116 }
117
118
119 if (fadeFromRenderCache)
120 {
121 //Icon fading goes always forwards
122 m_fadeProgress = qMin(qreal(1.0), m_fadeProgress + delta);
123 animating |= (m_fadeProgress < 1.0);
124 if (m_fadeProgress == 1)
125 setCachedRenderingFadeFrom(0);
126 }
127
128 if (jobAnimation)
129 {
130 m_jobAnimationAngle += 1.0;
131 if (m_jobAnimationAngle == 360)
132 m_jobAnimationAngle = 0;
133
134 if (index.model()->data(index, KDirModel::HasJobRole).toBool())
135 {
136 animating = true;
137 //there is a job here still...
138 return false;
139 }
140 else
141 {
142 animating = false;
143 //there's no job here anymore, return true so we stop painting this.
144 return true;
145 }
146 }
147 else
148 {
149 return !animating;
150 }
151}
152
153qreal AnimationState::hoverProgress() const
154{
155#ifndef M_PI_2
156#define M_PI_2 1.57079632679489661923
157#endif
158 return qRound(255.0 * std::sin(progress * M_PI_2)) / 255.0;
159}
160
161qreal AnimationState::fadeProgress() const
162{
163 return qRound(255.0 * std::sin(m_fadeProgress * M_PI_2)) / 255.0;
164}
165
166qreal AnimationState::jobAnimationAngle() const
167{
168 return m_jobAnimationAngle;
169}
170
171bool AnimationState::hasJobAnimation() const
172{
173 return jobAnimation;
174}
175
176void AnimationState::setJobAnimation(bool value)
177{
178 jobAnimation = value;
179}
180
181// ---------------------------------------------------------------------------
182
183static const int switchIconInterval = 1000;
184
185DelegateAnimationHandler::DelegateAnimationHandler(QObject *parent)
186 : QObject(parent)
187{
188 iconSequenceTimer.setSingleShot(true);
189 iconSequenceTimer.setInterval(switchIconInterval);
190 connect(&iconSequenceTimer, SIGNAL(timeout()), SLOT(sequenceTimerTimeout()));;
191}
192
193DelegateAnimationHandler::~DelegateAnimationHandler()
194{
195 timer.stop();
196
197 QMapIterator<const QAbstractItemView*, AnimationList*> i(animationLists);
198 while (i.hasNext())
199 {
200 i.next();
201 qDeleteAll(*i.value());
202 delete i.value();
203 }
204 animationLists.clear();
205}
206
207void DelegateAnimationHandler::sequenceTimerTimeout()
208{
209 QAbstractItemModel* model = const_cast<QAbstractItemModel*>(sequenceModelIndex.model());
210 QAbstractProxyModel* proxy = qobject_cast<QAbstractProxyModel*>(model);
211 QModelIndex index = sequenceModelIndex;
212
213 if (proxy)
214 {
215 index = proxy->mapToSource(index);
216 model = proxy->sourceModel();
217 }
218
219 KDirModel* dirModel = dynamic_cast<KDirModel*>(model);
220 if (dirModel)
221 {
222 kDebug(animationDebugArea()) << "requesting" << currentSequenceIndex;
223 dirModel->requestSequenceIcon(index, currentSequenceIndex);
224 iconSequenceTimer.start(); // Some upper-bound interval is needed, in case items are not generated
225 }
226}
227
228void DelegateAnimationHandler::gotNewIcon(const QModelIndex& index)
229{
230 Q_UNUSED(index);
231
232 kDebug(animationDebugArea()) << currentSequenceIndex;
233 if (sequenceModelIndex.isValid() && currentSequenceIndex)
234 iconSequenceTimer.start();
235// if(index ==sequenceModelIndex) //Leads to problems
236 ++currentSequenceIndex;
237}
238
239void DelegateAnimationHandler::setSequenceIndex(int sequenceIndex)
240{
241 kDebug(animationDebugArea()) << sequenceIndex;
242
243 if (sequenceIndex > 0)
244 {
245 currentSequenceIndex = sequenceIndex;
246 iconSequenceTimer.start();
247 }
248 else
249 {
250 currentSequenceIndex = 0;
251 sequenceTimerTimeout(); //Set the icon back to the standard one
252 currentSequenceIndex = 0; //currentSequenceIndex was incremented, set it back to 0
253 iconSequenceTimer.stop();
254 }
255}
256
257void DelegateAnimationHandler::eventuallyStartIteration(QModelIndex index)
258{
259// if (KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) {
261
262 if (sequenceModelIndex.isValid())
263 setSequenceIndex(0); // Stop old iteration, and reset the icon for the old iteration
264
265 // Start sequence iteration
266 sequenceModelIndex = index;
267 setSequenceIndex(1);
268// }
269}
270
271AnimationState *DelegateAnimationHandler::animationState(const QStyleOption &option,
272 const QModelIndex &index,
273 const QAbstractItemView *view)
274{
275 // We can't do animations reliably when an item is being dragged, since that
276 // item will be drawn in two locations at the same time and hovered in one and
277 // not the other. We can't tell them apart because they both have the same index.
278 if (!view || static_cast<const ProtectedAccessor*>(view)->draggingState())
279 return NULL;
280
281 AnimationState *state = findAnimationState(view, index);
282 bool hover = option.state & QStyle::State_MouseOver;
283
284 // If the cursor has entered an item
285 if (!state && hover)
286 {
287 state = new AnimationState(index);
288 addAnimationState(state, view);
289
290 if (!fadeInAddTime.isValid() ||
291 (fadeInAddTime.isValid() && fadeInAddTime.elapsed() > 300))
292 {
293 startAnimation(state);
294 }
295 else
296 {
297 state->animating = false;
298 state->progress = 1.0;
299 state->direction = QTimeLine::Forward;
300 }
301
302 fadeInAddTime.restart();
303
304 eventuallyStartIteration(index);
305 }
306 else if (state)
307 {
308 // If the cursor has exited an item
309 if (!hover && (!state->animating || state->direction == QTimeLine::Forward))
310 {
311 state->direction = QTimeLine::Backward;
312
313 if (state->creationTime.elapsed() < 200)
314 state->progress = 0.0;
315
316 startAnimation(state);
317
318 // Stop sequence iteration
319 if (index == sequenceModelIndex)
320 {
321 setSequenceIndex(0);
322 sequenceModelIndex = QPersistentModelIndex();
323 }
324 }
325 else if (hover && state->direction == QTimeLine::Backward)
326 {
327 // This is needed to handle the case where an item is dragged within
328 // the view, and dropped in a different location. State_MouseOver will
329 // initially not be set causing a "hover out" animation to start.
330 // This reverses the direction as soon as we see the bit being set.
331 state->direction = QTimeLine::Forward;
332
333 if (!state->animating)
334 startAnimation(state);
335
336 eventuallyStartIteration(index);
337 }
338 }
339 else if (!state && index.model()->data(index, KDirModel::HasJobRole).toBool())
340 {
341 state = new AnimationState(index);
342 addAnimationState(state, view);
343 startAnimation(state);
344 state->setJobAnimation(true);
345 }
346
347 return state;
348}
349
350
351AnimationState *DelegateAnimationHandler::findAnimationState(const QAbstractItemView *view,
352 const QModelIndex &index) const
353{
354 // Try to find a list of animation states for the view
355 AnimationList *list = animationLists.value(view);
356
357 if (list)
358 {
359 foreach (AnimationState *state, *list)
360 if (state->index == index)
361 return state;
362 }
363
364 return NULL;
365}
366
367
368void DelegateAnimationHandler::addAnimationState(AnimationState *state, const QAbstractItemView *view)
369{
370 AnimationList *list = animationLists.value(view);
371
372 // If this is the first time we've seen this view
373 if (!list)
374 {
375 connect(view, SIGNAL(destroyed(QObject*)), SLOT(viewDeleted(QObject*)));
376
377 list = new AnimationList;
378 animationLists.insert(view, list);
379 }
380
381 list->append(state);
382}
383
384void DelegateAnimationHandler::restartAnimation(AnimationState *state)
385{
386 startAnimation(state);
387}
388
389void DelegateAnimationHandler::startAnimation(AnimationState *state)
390{
391 state->time.start();
392 state->animating = true;
393
394 if (!timer.isActive())
395 timer.start(1000 / 30, this); // 30 fps
396}
397
398int DelegateAnimationHandler::runAnimations(AnimationList *list, const QAbstractItemView *view)
399{
400 int activeAnimations = 0;
401 QRegion region;
402
403 QMutableLinkedListIterator<AnimationState*> i(*list);
404 while (i.hasNext())
405 {
406 AnimationState *state = i.next();
407
408 if (!state->animating)
409 continue;
410
411 // We need to make sure the index is still valid, since it could be removed
412 // while the animation is running.
413 if (state->index.isValid())
414 {
415 bool finished = state->update();
416 region += view->visualRect(state->index);
417
418 if (!finished)
419 {
420 activeAnimations++;
421 continue;
422 }
423 }
424
425 // If the direction is Forward, the state object needs to stick around
426 // after the animation has finished, so we know that we've already done
427 // a "hover in" for the index.
428 if (state->direction == QTimeLine::Backward || !state->index.isValid())
429 {
430 delete state;
431 i.remove();
432 }
433 }
434
435 // Trigger a repaint of the animated indexes
436 if (!region.isEmpty())
437 const_cast<QAbstractItemView*>(view)->viewport()->update(region);
438
439 return activeAnimations;
440}
441
442
443void DelegateAnimationHandler::viewDeleted(QObject *view)
444{
445 AnimationList *list = animationLists.take(static_cast<QAbstractItemView*>(view));
446 qDeleteAll(*list);
447 delete list;
448}
449
450
451void DelegateAnimationHandler::timerEvent(QTimerEvent *)
452{
453 int activeAnimations = 0;
454
455 AnimationListsIterator i(animationLists);
456 while (i.hasNext())
457 {
458 i.next();
459 AnimationList *list = i.value();
460 const QAbstractItemView *view = i.key();
461
462 activeAnimations += runAnimations(list, view);
463 }
464
465 if (activeAnimations == 0 && timer.isActive())
466 timer.stop();
467}
468
469}
470
KDirModel
A model for a KIO-based directory tree.
Definition: kdirmodel.h:49
KDirModel::HasJobRole
@ HasJobRole
returns whether or not there is a job on an item (file/directory)
Definition: kdirmodel.h:139
KDirModel::requestSequenceIcon
void requestSequenceIcon(const QModelIndex &index, int sequenceIndex)
This emits the needSequenceIcon signal, requesting another sequence icon.
Definition: kdirmodel.cpp:855
KIO::AnimationState
Definition: delegateanimationhandler_p.h:61
KIO::AnimationState::setCachedRenderingFadeFrom
void setCachedRenderingFadeFrom(CachedRendering *rendering)
Definition: delegateanimationhandler_p.h:84
KIO::AnimationState::hoverProgress
qreal hoverProgress() const
Definition: delegateanimationhandler.cpp:153
KIO::AnimationState::~AnimationState
~AnimationState()
Definition: delegateanimationhandler.cpp:94
KIO::AnimationState::hasJobAnimation
bool hasJobAnimation() const
Definition: delegateanimationhandler.cpp:171
KIO::AnimationState::jobAnimationAngle
qreal jobAnimationAngle() const
Definition: delegateanimationhandler.cpp:166
KIO::AnimationState::setJobAnimation
void setJobAnimation(bool value)
Definition: delegateanimationhandler.cpp:176
KIO::AnimationState::fadeProgress
qreal fadeProgress() const
Definition: delegateanimationhandler.cpp:161
KIO::CachedRendering::regular
QPixmap regular
Definition: delegateanimationhandler_p.h:49
KIO::CachedRendering::valid
bool valid
Definition: delegateanimationhandler_p.h:52
KIO::CachedRendering::validityIndex
QPersistentModelIndex validityIndex
Definition: delegateanimationhandler_p.h:53
KIO::CachedRendering::CachedRendering
CachedRendering(QStyle::State state, const QSize &size, QModelIndex validityIndex)
Definition: delegateanimationhandler.cpp:55
KIO::CachedRendering::hover
QPixmap hover
Definition: delegateanimationhandler_p.h:50
KIO::DelegateAnimationHandler::~DelegateAnimationHandler
~DelegateAnimationHandler()
Definition: delegateanimationhandler.cpp:193
KIO::DelegateAnimationHandler::animationState
AnimationState * animationState(const QStyleOption &option, const QModelIndex &index, const QAbstractItemView *view)
Definition: delegateanimationhandler.cpp:271
KIO::DelegateAnimationHandler::DelegateAnimationHandler
DelegateAnimationHandler(QObject *parent=0)
Definition: delegateanimationhandler.cpp:185
KIO::DelegateAnimationHandler::gotNewIcon
void gotNewIcon(const QModelIndex &index)
Definition: delegateanimationhandler.cpp:228
KIO::DelegateAnimationHandler::restartAnimation
void restartAnimation(AnimationState *state)
Definition: delegateanimationhandler.cpp:384
QAbstractItemModel
QAbstractProxyModel
QObject
M_PI_2
#define M_PI_2
delegateanimationhandler_p.h
kDebug
#define kDebug
kdebug.h
kdirmodel.h
timeout
int timeout
KIO
A namespace for KIO globals.
Definition: kbookmarkmenu.h:55
KIO::animationDebugArea
static int animationDebugArea()
Definition: delegateanimationhandler.cpp:48
KIO::switchIconInterval
static const int switchIconInterval
Definition: delegateanimationhandler.cpp:183
KRecentDirs::list
QStringList list(const QString &fileClass)
Returns a list of directories associated with this file-class.
Definition: krecentdirs.cpp:60
Forward
Forward
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KIO

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

kdelibs-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • 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
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal