src/gui/widgets/qeffects.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtGui module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    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 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    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 
       
    55 QT_BEGIN_NAMESPACE
       
    56 
       
    57 /*
       
    58   Internal class to get access to protected QWidget-members
       
    59 */
       
    60 
       
    61 class QAccessWidget : public QWidget
       
    62 {
       
    63     friend class QAlphaWidget;
       
    64     friend class QRollEffect;
       
    65 public:
       
    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 
       
    77 class QAlphaWidget: public QWidget, private QEffects
       
    78 {
       
    79     Q_OBJECT
       
    80 public:
       
    81     QAlphaWidget(QWidget* w, Qt::WindowFlags f = 0);
       
    82     ~QAlphaWidget();
       
    83 
       
    84     void run(int time);
       
    85 
       
    86 protected:
       
    87     void paintEvent(QPaintEvent* e);
       
    88     void closeEvent(QCloseEvent*);
       
    89     void alphaBlend();
       
    90     bool eventFilter(QObject *, QEvent *);
       
    91 
       
    92 protected slots:
       
    93     void render();
       
    94 
       
    95 private:
       
    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 
       
   110 static QAlphaWidget* q_blend = 0;
       
   111 
       
   112 /*
       
   113   Constructs a QAlphaWidget.
       
   114 */
       
   115 QAlphaWidget::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 
       
   127 QAlphaWidget::~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 */
       
   139 void 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 */
       
   149 void 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 */
       
   202 bool 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 */
       
   239 void 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 */
       
   257 void 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 */
       
   313 void 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 
       
   357 class QRollEffect : public QWidget, private QEffects
       
   358 {
       
   359     Q_OBJECT
       
   360 public:
       
   361     QRollEffect(QWidget* w, Qt::WindowFlags f, DirFlags orient);
       
   362 
       
   363     void run(int time);
       
   364 
       
   365 protected:
       
   366     void paintEvent(QPaintEvent*);
       
   367     void closeEvent(QCloseEvent*);
       
   368 
       
   369 private slots:
       
   370     void scroll();
       
   371 
       
   372 private:
       
   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 
       
   392 static QRollEffect* q_roll = 0;
       
   393 
       
   394 /*
       
   395   Construct a QRollEffect widget.
       
   396 */
       
   397 QRollEffect::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 */
       
   431 void 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 */
       
   443 void 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 */
       
   462 void 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 */
       
   502 void 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 */
       
   577 void 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 */
       
   599 void 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 
       
   620 QT_END_NAMESPACE
       
   621 
       
   622 /*
       
   623   Delete this after timeout
       
   624 */
       
   625 
       
   626 #include "qeffects.moc"
       
   627 
       
   628 #endif //QT_NO_EFFECTS