/**************************************************************************** | |
** | |
** Copyright (C) 2011 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$ | |
** GNU Lesser General Public License Usage | |
** 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. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "qapplication.h" | |
#ifndef QT_NO_EFFECTS | |
#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 "qelapsedtimer.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; | |
QElapsedTimer 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; | |
QElapsedTimer 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 |