blob: d813c27632d93b5794231238db56efeaa582ae9f [file] [log] [blame]
/****************************************************************************
**
** 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