src/gui/widgets/qeffects.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/widgets/qeffects.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,628 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+#ifndef QT_NO_EFFECTS
+#include "qdatetime.h"
+#include "qdesktopwidget.h"
+#include "qeffects_p.h"
+#include "qevent.h"
+#include "qimage.h"
+#include "qpainter.h"
+#include "qpixmap.h"
+#include "qpointer.h"
+#include "qtimer.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+/*
+  Internal class to get access to protected QWidget-members
+*/
+
+class QAccessWidget : public QWidget
+{
+    friend class QAlphaWidget;
+    friend class QRollEffect;
+public:
+    QAccessWidget(QWidget* parent=0, Qt::WindowFlags f = 0)
+        : QWidget(parent, f) {}
+};
+
+/*
+  Internal class QAlphaWidget.
+
+  The QAlphaWidget is shown while the animation lasts
+  and displays the pixmap resulting from the alpha blending.
+*/
+
+class QAlphaWidget: public QWidget, private QEffects
+{
+    Q_OBJECT
+public:
+    QAlphaWidget(QWidget* w, Qt::WindowFlags f = 0);
+    ~QAlphaWidget();
+
+    void run(int time);
+
+protected:
+    void paintEvent(QPaintEvent* e);
+    void closeEvent(QCloseEvent*);
+    void alphaBlend();
+    bool eventFilter(QObject *, QEvent *);
+
+protected slots:
+    void render();
+
+private:
+    QPixmap pm;
+    double alpha;
+    QImage backImage;
+    QImage frontImage;
+    QImage mixedImage;
+    QPointer<QAccessWidget> widget;
+    int duration;
+    int elapsed;
+    bool showWidget;
+    QTimer anim;
+    QTime checkTime;
+    double windowOpacity;
+};
+
+static QAlphaWidget* q_blend = 0;
+
+/*
+  Constructs a QAlphaWidget.
+*/
+QAlphaWidget::QAlphaWidget(QWidget* w, Qt::WindowFlags f)
+    : QWidget(QApplication::desktop()->screen(QApplication::desktop()->screenNumber(w)), f)
+{
+#ifndef Q_WS_WIN
+    setEnabled(false);
+#endif
+    setAttribute(Qt::WA_NoSystemBackground, true);
+    widget = (QAccessWidget*)w;
+    windowOpacity = w->windowOpacity();
+    alpha = 0;
+}
+
+QAlphaWidget::~QAlphaWidget()
+{
+#if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
+    // Restore user-defined opacity value
+    if (widget)
+        widget->setWindowOpacity(windowOpacity);
+#endif
+}
+
+/*
+  \reimp
+*/
+void QAlphaWidget::paintEvent(QPaintEvent*)
+{
+    QPainter p(this);
+    p.drawPixmap(0, 0, pm);
+}
+
+/*
+  Starts the alphablending animation.
+  The animation will take about \a time ms
+*/
+void QAlphaWidget::run(int time)
+{
+    duration = time;
+
+    if (duration < 0)
+        duration = 150;
+
+    if (!widget)
+        return;
+
+    elapsed = 0;
+    checkTime.start();
+
+    showWidget = true;
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+    qApp->installEventFilter(this);
+    widget->setWindowOpacity(0.0);
+    widget->show();
+    connect(&anim, SIGNAL(timeout()), this, SLOT(render()));
+    anim.start(1);
+#else
+    //This is roughly equivalent to calling setVisible(true) without actually showing the widget
+    widget->setAttribute(Qt::WA_WState_ExplicitShowHide, true);
+    widget->setAttribute(Qt::WA_WState_Hidden, false);
+
+    qApp->installEventFilter(this);
+
+    move(widget->geometry().x(),widget->geometry().y());
+    resize(widget->size().width(), widget->size().height());
+
+    frontImage = QPixmap::grabWidget(widget).toImage();
+    backImage = QPixmap::grabWindow(QApplication::desktop()->winId(),
+                                widget->geometry().x(), widget->geometry().y(),
+                                widget->geometry().width(), widget->geometry().height()).toImage();
+
+    if (!backImage.isNull() && checkTime.elapsed() < duration / 2) {
+        mixedImage = backImage.copy();
+        pm = QPixmap::fromImage(mixedImage);
+        show();
+        setEnabled(false);
+
+        connect(&anim, SIGNAL(timeout()), this, SLOT(render()));
+        anim.start(1);
+    } else {
+       duration = 0;
+       render();
+    }
+#endif
+}
+
+/*
+  \reimp
+*/
+bool QAlphaWidget::eventFilter(QObject *o, QEvent *e)
+{
+    switch (e->type()) {
+    case QEvent::Move:
+	    if (o != widget)
+	        break;
+	    move(widget->geometry().x(),widget->geometry().y());
+	    update();
+	    break;
+    case QEvent::Hide:
+    case QEvent::Close:
+	    if (o != widget)
+	        break;
+    case QEvent::MouseButtonPress:
+	case QEvent::MouseButtonDblClick:
+	    showWidget = false;
+	    render();
+	    break;
+    case QEvent::KeyPress: {
+	        QKeyEvent *ke = (QKeyEvent*)e;
+            if (ke->key() == Qt::Key_Escape) {
+		        showWidget = false;
+            } else {
+		        duration = 0;
+            }
+	        render();
+	        break;
+	}
+    default:
+	    break;
+    }
+    return QWidget::eventFilter(o, e);
+}
+
+/*
+  \reimp
+*/
+void QAlphaWidget::closeEvent(QCloseEvent *e)
+{
+    e->accept();
+    if (!q_blend)
+        return;
+
+    showWidget = false;
+    render();
+
+    QWidget::closeEvent(e);
+}
+
+/*
+  Render alphablending for the time elapsed.
+
+  Show the blended widget and free all allocated source
+  if the blending is finished.
+*/
+void QAlphaWidget::render()
+{
+    int tempel = checkTime.elapsed();
+    if (elapsed >= tempel)
+        elapsed++;
+    else
+        elapsed = tempel;
+
+    if (duration != 0)
+        alpha = tempel / double(duration);
+    else
+        alpha = 1;
+
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+    if (alpha >= windowOpacity || !showWidget) {
+        anim.stop();
+        qApp->removeEventFilter(this);
+        widget->setWindowOpacity(windowOpacity);
+        q_blend = 0;
+        deleteLater();
+    } else {
+        widget->setWindowOpacity(alpha);
+    }
+#else
+    if (alpha >= 1 || !showWidget) {
+        anim.stop();
+        qApp->removeEventFilter(this);
+
+        if (widget) {
+            if (!showWidget) {
+#ifdef Q_WS_WIN
+                setEnabled(true);
+                setFocus();
+#endif // Q_WS_WIN
+                widget->hide();
+            } else {
+                //Since we are faking the visibility of the widget 
+                //we need to unset the hidden state on it before calling show
+                widget->setAttribute(Qt::WA_WState_Hidden, true);
+                widget->show();
+                lower();
+            }
+        }
+        q_blend = 0;
+        deleteLater();
+    } else {
+        alphaBlend();
+        pm = QPixmap::fromImage(mixedImage);
+        repaint();
+    }
+#endif // defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+}
+
+/*
+  Calculate an alphablended image.
+*/
+void QAlphaWidget::alphaBlend()
+{
+    const int a = qRound(alpha*256);
+    const int ia = 256 - a;
+
+    const int sw = frontImage.width();
+    const int sh = frontImage.height();
+    const int bpl = frontImage.bytesPerLine();
+    switch(frontImage.depth()) {
+    case 32:
+        {
+            uchar *mixed_data = mixedImage.bits();
+            const uchar *back_data = backImage.bits();
+            const uchar *front_data = frontImage.bits();
+
+            for (int sy = 0; sy < sh; sy++) {
+                quint32* mixed = (quint32*)mixed_data;
+                const quint32* back = (const quint32*)back_data;
+                const quint32* front = (const quint32*)front_data;
+                for (int sx = 0; sx < sw; sx++) {
+                    quint32 bp = back[sx];
+                    quint32 fp = front[sx];
+
+                    mixed[sx] =  qRgb((qRed(bp)*ia + qRed(fp)*a)>>8,
+                                      (qGreen(bp)*ia + qGreen(fp)*a)>>8,
+                                      (qBlue(bp)*ia + qBlue(fp)*a)>>8);
+                }
+                mixed_data += bpl;
+                back_data += bpl;
+                front_data += bpl;
+            }
+        }
+    default:
+        break;
+    }
+}
+
+/*
+  Internal class QRollEffect
+
+  The QRollEffect widget is shown while the animation lasts
+  and displays a scrolling pixmap.
+*/
+
+class QRollEffect : public QWidget, private QEffects
+{
+    Q_OBJECT
+public:
+    QRollEffect(QWidget* w, Qt::WindowFlags f, DirFlags orient);
+
+    void run(int time);
+
+protected:
+    void paintEvent(QPaintEvent*);
+    void closeEvent(QCloseEvent*);
+
+private slots:
+    void scroll();
+
+private:
+    QPointer<QAccessWidget> widget;
+
+    int currentHeight;
+    int currentWidth;
+    int totalHeight;
+    int totalWidth;
+
+    int duration;
+    int elapsed;
+    bool done;
+    bool showWidget;
+    int orientation;
+
+    QTimer anim;
+    QTime checkTime;
+
+    QPixmap pm;
+};
+
+static QRollEffect* q_roll = 0;
+
+/*
+  Construct a QRollEffect widget.
+*/
+QRollEffect::QRollEffect(QWidget* w, Qt::WindowFlags f, DirFlags orient)
+    : QWidget(0, f), orientation(orient)
+{
+#ifndef Q_WS_WIN
+    setEnabled(false);
+#endif
+
+    widget = (QAccessWidget*) w;
+    Q_ASSERT(widget);
+
+    setAttribute(Qt::WA_NoSystemBackground, true);
+
+    if (widget->testAttribute(Qt::WA_Resized)) {
+        totalWidth = widget->width();
+        totalHeight = widget->height();
+    } else {
+        totalWidth = widget->sizeHint().width();
+        totalHeight = widget->sizeHint().height();
+    }
+
+    currentHeight = totalHeight;
+    currentWidth = totalWidth;
+
+    if (orientation & (RightScroll|LeftScroll))
+        currentWidth = 0;
+    if (orientation & (DownScroll|UpScroll))
+        currentHeight = 0;
+
+    pm = QPixmap::grabWidget(widget);
+}
+
+/*
+  \reimp
+*/
+void QRollEffect::paintEvent(QPaintEvent*)
+{
+    int x = orientation & RightScroll ? qMin(0, currentWidth - totalWidth) : 0;
+    int y = orientation & DownScroll ? qMin(0, currentHeight - totalHeight) : 0;
+
+    QPainter p(this);
+    p.drawPixmap(x, y, pm);
+}
+
+/*
+  \reimp
+*/
+void QRollEffect::closeEvent(QCloseEvent *e)
+{
+    e->accept();
+    if (done)
+        return;
+
+    showWidget = false;
+    done = true;
+    scroll();
+
+    QWidget::closeEvent(e);
+}
+
+/*
+  Start the animation.
+
+  The animation will take about \a time ms, or is
+  calculated if \a time is negative
+*/
+void QRollEffect::run(int time)
+{
+    if (!widget)
+        return;
+
+    duration  = time;
+    elapsed = 0;
+
+    if (duration < 0) {
+        int dist = 0;
+        if (orientation & (RightScroll|LeftScroll))
+            dist += totalWidth - currentWidth;
+        if (orientation & (DownScroll|UpScroll))
+            dist += totalHeight - currentHeight;
+        duration = qMin(qMax(dist/3, 50), 120);
+    }
+
+    connect(&anim, SIGNAL(timeout()), this, SLOT(scroll()));
+
+    move(widget->geometry().x(),widget->geometry().y());
+    resize(qMin(currentWidth, totalWidth), qMin(currentHeight, totalHeight));
+
+    //This is roughly equivalent to calling setVisible(true) without actually showing the widget
+    widget->setAttribute(Qt::WA_WState_ExplicitShowHide, true);
+    widget->setAttribute(Qt::WA_WState_Hidden, false);
+
+    show();
+    setEnabled(false);
+
+    qApp->installEventFilter(this);
+
+    showWidget = true;
+    done = false;
+    anim.start(1);
+    checkTime.start();
+}
+
+/*
+  Roll according to the time elapsed.
+*/
+void QRollEffect::scroll()
+{
+    if (!done && widget) {
+        int tempel = checkTime.elapsed();
+        if (elapsed >= tempel)
+            elapsed++;
+        else
+            elapsed = tempel;
+
+        if (currentWidth != totalWidth) {
+            currentWidth = totalWidth * (elapsed/duration)
+                + (2 * totalWidth * (elapsed%duration) + duration)
+                / (2 * duration);
+            // equiv. to int((totalWidth*elapsed) / duration + 0.5)
+            done = (currentWidth >= totalWidth);
+        }
+        if (currentHeight != totalHeight) {
+            currentHeight = totalHeight * (elapsed/duration)
+                + (2 * totalHeight * (elapsed%duration) + duration)
+                / (2 * duration);
+            // equiv. to int((totalHeight*elapsed) / duration + 0.5)
+            done = (currentHeight >= totalHeight);
+        }
+        done = (currentHeight >= totalHeight) &&
+               (currentWidth >= totalWidth);
+
+        int w = totalWidth;
+        int h = totalHeight;
+        int x = widget->geometry().x();
+        int y = widget->geometry().y();
+
+        if (orientation & RightScroll || orientation & LeftScroll)
+            w = qMin(currentWidth, totalWidth);
+        if (orientation & DownScroll || orientation & UpScroll)
+            h = qMin(currentHeight, totalHeight);
+
+        setUpdatesEnabled(false);
+        if (orientation & UpScroll)
+            y = widget->geometry().y() + qMax(0, totalHeight - currentHeight);
+        if (orientation & LeftScroll)
+            x = widget->geometry().x() + qMax(0, totalWidth - currentWidth);
+        if (orientation & UpScroll || orientation & LeftScroll)
+            move(x, y);
+
+        resize(w, h);
+        setUpdatesEnabled(true);
+        repaint();
+    }
+    if (done) {
+        anim.stop();
+        qApp->removeEventFilter(this);
+        if (widget) {
+            if (!showWidget) {
+#ifdef Q_WS_WIN
+                setEnabled(true);
+                setFocus();
+#endif
+                widget->hide();
+            } else {
+                //Since we are faking the visibility of the widget 
+                //we need to unset the hidden state on it before calling show
+                widget->setAttribute(Qt::WA_WState_Hidden, true);
+                widget->show();
+                lower();
+            }
+        }
+        q_roll = 0;
+        deleteLater();
+    }
+}
+
+/*!
+    Scroll widget \a w in \a time ms. \a orient may be 1 (vertical), 2
+    (horizontal) or 3 (diagonal).
+*/
+void qScrollEffect(QWidget* w, QEffects::DirFlags orient, int time)
+{
+    if (q_roll) {
+        q_roll->deleteLater();
+        q_roll = 0;
+    }
+
+    if (!w)
+        return;
+
+    QApplication::sendPostedEvents(w, QEvent::Move);
+    QApplication::sendPostedEvents(w, QEvent::Resize);
+    Qt::WindowFlags flags = Qt::ToolTip;
+
+    // those can be popups - they would steal the focus, but are disabled
+    q_roll = new QRollEffect(w, flags, orient);
+    q_roll->run(time);
+}
+
+/*!
+    Fade in widget \a w in \a time ms.
+*/
+void qFadeEffect(QWidget* w, int time)
+{
+    if (q_blend) {
+        q_blend->deleteLater();
+        q_blend = 0;
+    }
+
+    if (!w)
+        return;
+
+    QApplication::sendPostedEvents(w, QEvent::Move);
+    QApplication::sendPostedEvents(w, QEvent::Resize);
+
+    Qt::WindowFlags flags = Qt::ToolTip;
+
+    // those can be popups - they would steal the focus, but are disabled
+    q_blend = new QAlphaWidget(w, flags);
+
+    q_blend->run(time);
+}
+
+QT_END_NAMESPACE
+
+/*
+  Delete this after timeout
+*/
+
+#include "qeffects.moc"
+
+#endif //QT_NO_EFFECTS