source: trunk/src/gui/widgets/qeffects.cpp@ 651

Last change on this file since 651 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

File size: 15.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qapplication.h"
43#ifndef QT_NO_EFFECTS
44#include "qdatetime.h"
45#include "qdesktopwidget.h"
46#include "qeffects_p.h"
47#include "qevent.h"
48#include "qimage.h"
49#include "qpainter.h"
50#include "qpixmap.h"
51#include "qpointer.h"
52#include "qtimer.h"
53#include "qdebug.h"
54
55QT_BEGIN_NAMESPACE
56
57/*
58 Internal class to get access to protected QWidget-members
59*/
60
61class QAccessWidget : public QWidget
62{
63 friend class QAlphaWidget;
64 friend class QRollEffect;
65public:
66 QAccessWidget(QWidget* parent=0, Qt::WindowFlags f = 0)
67 : QWidget(parent, f) {}
68};
69
70/*
71 Internal class QAlphaWidget.
72
73 The QAlphaWidget is shown while the animation lasts
74 and displays the pixmap resulting from the alpha blending.
75*/
76
77class QAlphaWidget: public QWidget, private QEffects
78{
79 Q_OBJECT
80public:
81 QAlphaWidget(QWidget* w, Qt::WindowFlags f = 0);
82 ~QAlphaWidget();
83
84 void run(int time);
85
86protected:
87 void paintEvent(QPaintEvent* e);
88 void closeEvent(QCloseEvent*);
89 void alphaBlend();
90 bool eventFilter(QObject *, QEvent *);
91
92protected slots:
93 void render();
94
95private:
96 QPixmap pm;
97 double alpha;
98 QImage backImage;
99 QImage frontImage;
100 QImage mixedImage;
101 QPointer<QAccessWidget> widget;
102 int duration;
103 int elapsed;
104 bool showWidget;
105 QTimer anim;
106 QTime checkTime;
107 double windowOpacity;
108};
109
110static QAlphaWidget* q_blend = 0;
111
112/*
113 Constructs a QAlphaWidget.
114*/
115QAlphaWidget::QAlphaWidget(QWidget* w, Qt::WindowFlags f)
116 : QWidget(QApplication::desktop()->screen(QApplication::desktop()->screenNumber(w)), f)
117{
118#ifndef Q_WS_WIN
119 setEnabled(false);
120#endif
121 setAttribute(Qt::WA_NoSystemBackground, true);
122 widget = (QAccessWidget*)w;
123 windowOpacity = w->windowOpacity();
124 alpha = 0;
125}
126
127QAlphaWidget::~QAlphaWidget()
128{
129#if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
130 // Restore user-defined opacity value
131 if (widget)
132 widget->setWindowOpacity(windowOpacity);
133#endif
134}
135
136/*
137 \reimp
138*/
139void QAlphaWidget::paintEvent(QPaintEvent*)
140{
141 QPainter p(this);
142 p.drawPixmap(0, 0, pm);
143}
144
145/*
146 Starts the alphablending animation.
147 The animation will take about \a time ms
148*/
149void QAlphaWidget::run(int time)
150{
151 duration = time;
152
153 if (duration < 0)
154 duration = 150;
155
156 if (!widget)
157 return;
158
159 elapsed = 0;
160 checkTime.start();
161
162 showWidget = true;
163#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
164 qApp->installEventFilter(this);
165 widget->setWindowOpacity(0.0);
166 widget->show();
167 connect(&anim, SIGNAL(timeout()), this, SLOT(render()));
168 anim.start(1);
169#else
170 //This is roughly equivalent to calling setVisible(true) without actually showing the widget
171 widget->setAttribute(Qt::WA_WState_ExplicitShowHide, true);
172 widget->setAttribute(Qt::WA_WState_Hidden, false);
173
174 qApp->installEventFilter(this);
175
176 move(widget->geometry().x(),widget->geometry().y());
177 resize(widget->size().width(), widget->size().height());
178
179 frontImage = QPixmap::grabWidget(widget).toImage();
180 backImage = QPixmap::grabWindow(QApplication::desktop()->winId(),
181 widget->geometry().x(), widget->geometry().y(),
182 widget->geometry().width(), widget->geometry().height()).toImage();
183
184 if (!backImage.isNull() && checkTime.elapsed() < duration / 2) {
185 mixedImage = backImage.copy();
186 pm = QPixmap::fromImage(mixedImage);
187 show();
188 setEnabled(false);
189
190 connect(&anim, SIGNAL(timeout()), this, SLOT(render()));
191 anim.start(1);
192 } else {
193 duration = 0;
194 render();
195 }
196#endif
197}
198
199/*
200 \reimp
201*/
202bool QAlphaWidget::eventFilter(QObject *o, QEvent *e)
203{
204 switch (e->type()) {
205 case QEvent::Move:
206 if (o != widget)
207 break;
208 move(widget->geometry().x(),widget->geometry().y());
209 update();
210 break;
211 case QEvent::Hide:
212 case QEvent::Close:
213 if (o != widget)
214 break;
215 case QEvent::MouseButtonPress:
216 case QEvent::MouseButtonDblClick:
217 showWidget = false;
218 render();
219 break;
220 case QEvent::KeyPress: {
221 QKeyEvent *ke = (QKeyEvent*)e;
222 if (ke->key() == Qt::Key_Escape) {
223 showWidget = false;
224 } else {
225 duration = 0;
226 }
227 render();
228 break;
229 }
230 default:
231 break;
232 }
233 return QWidget::eventFilter(o, e);
234}
235
236/*
237 \reimp
238*/
239void QAlphaWidget::closeEvent(QCloseEvent *e)
240{
241 e->accept();
242 if (!q_blend)
243 return;
244
245 showWidget = false;
246 render();
247
248 QWidget::closeEvent(e);
249}
250
251/*
252 Render alphablending for the time elapsed.
253
254 Show the blended widget and free all allocated source
255 if the blending is finished.
256*/
257void QAlphaWidget::render()
258{
259 int tempel = checkTime.elapsed();
260 if (elapsed >= tempel)
261 elapsed++;
262 else
263 elapsed = tempel;
264
265 if (duration != 0)
266 alpha = tempel / double(duration);
267 else
268 alpha = 1;
269
270#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
271 if (alpha >= windowOpacity || !showWidget) {
272 anim.stop();
273 qApp->removeEventFilter(this);
274 widget->setWindowOpacity(windowOpacity);
275 q_blend = 0;
276 deleteLater();
277 } else {
278 widget->setWindowOpacity(alpha);
279 }
280#else
281 if (alpha >= 1 || !showWidget) {
282 anim.stop();
283 qApp->removeEventFilter(this);
284
285 if (widget) {
286 if (!showWidget) {
287#ifdef Q_WS_WIN
288 setEnabled(true);
289 setFocus();
290#endif // Q_WS_WIN
291 widget->hide();
292 } else {
293 //Since we are faking the visibility of the widget
294 //we need to unset the hidden state on it before calling show
295 widget->setAttribute(Qt::WA_WState_Hidden, true);
296 widget->show();
297 lower();
298 }
299 }
300 q_blend = 0;
301 deleteLater();
302 } else {
303 alphaBlend();
304 pm = QPixmap::fromImage(mixedImage);
305 repaint();
306 }
307#endif // defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
308}
309
310/*
311 Calculate an alphablended image.
312*/
313void QAlphaWidget::alphaBlend()
314{
315 const int a = qRound(alpha*256);
316 const int ia = 256 - a;
317
318 const int sw = frontImage.width();
319 const int sh = frontImage.height();
320 const int bpl = frontImage.bytesPerLine();
321 switch(frontImage.depth()) {
322 case 32:
323 {
324 uchar *mixed_data = mixedImage.bits();
325 const uchar *back_data = backImage.bits();
326 const uchar *front_data = frontImage.bits();
327
328 for (int sy = 0; sy < sh; sy++) {
329 quint32* mixed = (quint32*)mixed_data;
330 const quint32* back = (const quint32*)back_data;
331 const quint32* front = (const quint32*)front_data;
332 for (int sx = 0; sx < sw; sx++) {
333 quint32 bp = back[sx];
334 quint32 fp = front[sx];
335
336 mixed[sx] = qRgb((qRed(bp)*ia + qRed(fp)*a)>>8,
337 (qGreen(bp)*ia + qGreen(fp)*a)>>8,
338 (qBlue(bp)*ia + qBlue(fp)*a)>>8);
339 }
340 mixed_data += bpl;
341 back_data += bpl;
342 front_data += bpl;
343 }
344 }
345 default:
346 break;
347 }
348}
349
350/*
351 Internal class QRollEffect
352
353 The QRollEffect widget is shown while the animation lasts
354 and displays a scrolling pixmap.
355*/
356
357class QRollEffect : public QWidget, private QEffects
358{
359 Q_OBJECT
360public:
361 QRollEffect(QWidget* w, Qt::WindowFlags f, DirFlags orient);
362
363 void run(int time);
364
365protected:
366 void paintEvent(QPaintEvent*);
367 void closeEvent(QCloseEvent*);
368
369private slots:
370 void scroll();
371
372private:
373 QPointer<QAccessWidget> widget;
374
375 int currentHeight;
376 int currentWidth;
377 int totalHeight;
378 int totalWidth;
379
380 int duration;
381 int elapsed;
382 bool done;
383 bool showWidget;
384 int orientation;
385
386 QTimer anim;
387 QTime checkTime;
388
389 QPixmap pm;
390};
391
392static QRollEffect* q_roll = 0;
393
394/*
395 Construct a QRollEffect widget.
396*/
397QRollEffect::QRollEffect(QWidget* w, Qt::WindowFlags f, DirFlags orient)
398 : QWidget(0, f), orientation(orient)
399{
400#ifndef Q_WS_WIN
401 setEnabled(false);
402#endif
403
404 widget = (QAccessWidget*) w;
405 Q_ASSERT(widget);
406
407 setAttribute(Qt::WA_NoSystemBackground, true);
408
409 if (widget->testAttribute(Qt::WA_Resized)) {
410 totalWidth = widget->width();
411 totalHeight = widget->height();
412 } else {
413 totalWidth = widget->sizeHint().width();
414 totalHeight = widget->sizeHint().height();
415 }
416
417 currentHeight = totalHeight;
418 currentWidth = totalWidth;
419
420 if (orientation & (RightScroll|LeftScroll))
421 currentWidth = 0;
422 if (orientation & (DownScroll|UpScroll))
423 currentHeight = 0;
424
425 pm = QPixmap::grabWidget(widget);
426}
427
428/*
429 \reimp
430*/
431void QRollEffect::paintEvent(QPaintEvent*)
432{
433 int x = orientation & RightScroll ? qMin(0, currentWidth - totalWidth) : 0;
434 int y = orientation & DownScroll ? qMin(0, currentHeight - totalHeight) : 0;
435
436 QPainter p(this);
437 p.drawPixmap(x, y, pm);
438}
439
440/*
441 \reimp
442*/
443void QRollEffect::closeEvent(QCloseEvent *e)
444{
445 e->accept();
446 if (done)
447 return;
448
449 showWidget = false;
450 done = true;
451 scroll();
452
453 QWidget::closeEvent(e);
454}
455
456/*
457 Start the animation.
458
459 The animation will take about \a time ms, or is
460 calculated if \a time is negative
461*/
462void QRollEffect::run(int time)
463{
464 if (!widget)
465 return;
466
467 duration = time;
468 elapsed = 0;
469
470 if (duration < 0) {
471 int dist = 0;
472 if (orientation & (RightScroll|LeftScroll))
473 dist += totalWidth - currentWidth;
474 if (orientation & (DownScroll|UpScroll))
475 dist += totalHeight - currentHeight;
476 duration = qMin(qMax(dist/3, 50), 120);
477 }
478
479 connect(&anim, SIGNAL(timeout()), this, SLOT(scroll()));
480
481 move(widget->geometry().x(),widget->geometry().y());
482 resize(qMin(currentWidth, totalWidth), qMin(currentHeight, totalHeight));
483
484 //This is roughly equivalent to calling setVisible(true) without actually showing the widget
485 widget->setAttribute(Qt::WA_WState_ExplicitShowHide, true);
486 widget->setAttribute(Qt::WA_WState_Hidden, false);
487
488 show();
489 setEnabled(false);
490
491 qApp->installEventFilter(this);
492
493 showWidget = true;
494 done = false;
495 anim.start(1);
496 checkTime.start();
497}
498
499/*
500 Roll according to the time elapsed.
501*/
502void QRollEffect::scroll()
503{
504 if (!done && widget) {
505 int tempel = checkTime.elapsed();
506 if (elapsed >= tempel)
507 elapsed++;
508 else
509 elapsed = tempel;
510
511 if (currentWidth != totalWidth) {
512 currentWidth = totalWidth * (elapsed/duration)
513 + (2 * totalWidth * (elapsed%duration) + duration)
514 / (2 * duration);
515 // equiv. to int((totalWidth*elapsed) / duration + 0.5)
516 done = (currentWidth >= totalWidth);
517 }
518 if (currentHeight != totalHeight) {
519 currentHeight = totalHeight * (elapsed/duration)
520 + (2 * totalHeight * (elapsed%duration) + duration)
521 / (2 * duration);
522 // equiv. to int((totalHeight*elapsed) / duration + 0.5)
523 done = (currentHeight >= totalHeight);
524 }
525 done = (currentHeight >= totalHeight) &&
526 (currentWidth >= totalWidth);
527
528 int w = totalWidth;
529 int h = totalHeight;
530 int x = widget->geometry().x();
531 int y = widget->geometry().y();
532
533 if (orientation & RightScroll || orientation & LeftScroll)
534 w = qMin(currentWidth, totalWidth);
535 if (orientation & DownScroll || orientation & UpScroll)
536 h = qMin(currentHeight, totalHeight);
537
538 setUpdatesEnabled(false);
539 if (orientation & UpScroll)
540 y = widget->geometry().y() + qMax(0, totalHeight - currentHeight);
541 if (orientation & LeftScroll)
542 x = widget->geometry().x() + qMax(0, totalWidth - currentWidth);
543 if (orientation & UpScroll || orientation & LeftScroll)
544 move(x, y);
545
546 resize(w, h);
547 setUpdatesEnabled(true);
548 repaint();
549 }
550 if (done) {
551 anim.stop();
552 qApp->removeEventFilter(this);
553 if (widget) {
554 if (!showWidget) {
555#ifdef Q_WS_WIN
556 setEnabled(true);
557 setFocus();
558#endif
559 widget->hide();
560 } else {
561 //Since we are faking the visibility of the widget
562 //we need to unset the hidden state on it before calling show
563 widget->setAttribute(Qt::WA_WState_Hidden, true);
564 widget->show();
565 lower();
566 }
567 }
568 q_roll = 0;
569 deleteLater();
570 }
571}
572
573/*!
574 Scroll widget \a w in \a time ms. \a orient may be 1 (vertical), 2
575 (horizontal) or 3 (diagonal).
576*/
577void qScrollEffect(QWidget* w, QEffects::DirFlags orient, int time)
578{
579 if (q_roll) {
580 q_roll->deleteLater();
581 q_roll = 0;
582 }
583
584 if (!w)
585 return;
586
587 QApplication::sendPostedEvents(w, QEvent::Move);
588 QApplication::sendPostedEvents(w, QEvent::Resize);
589 Qt::WindowFlags flags = Qt::ToolTip;
590
591 // those can be popups - they would steal the focus, but are disabled
592 q_roll = new QRollEffect(w, flags, orient);
593 q_roll->run(time);
594}
595
596/*!
597 Fade in widget \a w in \a time ms.
598*/
599void qFadeEffect(QWidget* w, int time)
600{
601 if (q_blend) {
602 q_blend->deleteLater();
603 q_blend = 0;
604 }
605
606 if (!w)
607 return;
608
609 QApplication::sendPostedEvents(w, QEvent::Move);
610 QApplication::sendPostedEvents(w, QEvent::Resize);
611
612 Qt::WindowFlags flags = Qt::ToolTip;
613
614 // those can be popups - they would steal the focus, but are disabled
615 q_blend = new QAlphaWidget(w, flags);
616
617 q_blend->run(time);
618}
619
620QT_END_NAMESPACE
621
622/*
623 Delete this after timeout
624*/
625
626#include "qeffects.moc"
627
628#endif //QT_NO_EFFECTS
Note: See TracBrowser for help on using the repository browser.