Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qeffects.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qapplication.h"
6#include "qdebug.h"
7#include "qeffects_p.h"
9#include "qevent.h"
10#include "qimage.h"
11#include "qpainter.h"
12#include "qscreen.h"
13#include "qpixmap.h"
14#include "qpointer.h"
15#include "qtimer.h"
16#include "qwidget.h"
17#include "private/qwidget_p.h"
18#include "qwindow.h"
19
20
22
24{
25 void operator()(QObject *o) const
26 {
27 if (o)
28 o->deleteLater();
29 }
30};
31
32/*
33 Internal class QAlphaWidget.
34
35 The QAlphaWidget is shown while the animation lasts
36 and displays the pixmap resulting from the alpha blending.
37*/
38
39class QAlphaWidget: public QWidget, private QEffects
40{
42public:
45
46 void run(int time);
47
48protected:
49 void paintEvent(QPaintEvent* e) override;
50 void closeEvent(QCloseEvent*) override;
51 void alphaBlend();
52 bool eventFilter(QObject *, QEvent *) override;
53
54protected slots:
55 void render();
56
57private:
58 QPixmap pm;
59 double alpha;
60 QImage backImage;
61 QImage frontImage;
62 QImage mixedImage;
63 QPointer<QWidget> widget;
64 int duration;
65 int elapsed;
66 bool showWidget;
67 QTimer anim;
68 QElapsedTimer checkTime;
69};
70
71static std::unique_ptr<QAlphaWidget, DeleteLater> q_blend;
72
73/*
74 Constructs a QAlphaWidget.
75*/
76QAlphaWidget::QAlphaWidget(QWidget* w, Qt::WindowFlags f)
77 : QWidget(nullptr, f)
78{
79 QWidgetPrivate::get(this)->setScreen(w->screen());
80#ifndef Q_OS_WIN
81 setEnabled(false);
82#endif
83 setAttribute(Qt::WA_NoSystemBackground, true);
84 widget = w;
85 alpha = 0;
86 connect(w, &QWidget::destroyed, this, []() { q_blend.reset(); });
87}
88
90{
91#if defined(Q_OS_WIN)
92 // Restore user-defined opacity value
93 if (widget)
94 widget->setWindowOpacity(1);
95#endif
96}
97
98/*
99 \reimp
100*/
101void QAlphaWidget::paintEvent(QPaintEvent*)
102{
103 QPainter p(this);
104 p.drawPixmap(0, 0, pm);
105}
106
107/*
108 Starts the alphablending animation.
109 The animation will take about \a time ms
110*/
111void QAlphaWidget::run(int time)
112{
113 duration = time;
114
115 if (duration < 0)
116 duration = 150;
117
118 if (!widget)
119 return;
120
121 elapsed = 0;
122 checkTime.start();
123
124 showWidget = true;
125#if defined(Q_OS_WIN)
126 qApp->installEventFilter(this);
127 widget->setWindowOpacity(0.0);
128 widget->show();
129 connect(&anim, &QTimer::timeout, this, &QAlphaWidget::render);
130 anim.start(1);
131#else
132 //This is roughly equivalent to calling setVisible(true) without actually showing the widget
133 widget->setAttribute(Qt::WA_WState_ExplicitShowHide, true);
134 widget->setAttribute(Qt::WA_WState_Hidden, false);
135
136 qApp->installEventFilter(this);
137
138 move(widget->geometry().x(),widget->geometry().y());
139 resize(widget->size().width(), widget->size().height());
140
141 frontImage = widget->grab().toImage();
142 backImage = QGuiApplication::primaryScreen()->grabWindow(0,
143 widget->geometry().x(), widget->geometry().y(),
144 widget->geometry().width(), widget->geometry().height()).toImage();
145
146 if (!backImage.isNull() && checkTime.elapsed() < duration / 2) {
147 mixedImage = backImage.copy();
148 pm = QPixmap::fromImage(mixedImage);
149 show();
150 setEnabled(false);
151
152 connect(&anim, &QTimer::timeout, this, &QAlphaWidget::render);
153 anim.start(1);
154 } else {
155 duration = 0;
156 render();
157 }
158#endif
159}
160
161/*
162 \reimp
163*/
164bool QAlphaWidget::eventFilter(QObject *o, QEvent *e)
165{
166 switch (e->type()) {
167 case QEvent::Move:
168 if (o != widget)
169 break;
170 move(widget->geometry().x(),widget->geometry().y());
171 update();
172 break;
173 case QEvent::Hide:
174 case QEvent::Close:
175 if (o != widget)
176 break;
177 Q_FALLTHROUGH();
178 case QEvent::MouseButtonPress:
179 case QEvent::MouseButtonDblClick:
180 showWidget = false;
181 render();
182 break;
183 case QEvent::KeyPress: {
184#ifndef QT_NO_SHORTCUT
185 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
186 if (ke->matches(QKeySequence::Cancel)) {
187 showWidget = false;
188 } else
189#endif
190 {
191 duration = 0;
192 }
193 render();
194 break;
195 }
196 default:
197 break;
198 }
199 return QWidget::eventFilter(o, e);
200}
201
202/*
203 \reimp
204*/
205void QAlphaWidget::closeEvent(QCloseEvent *e)
206{
207 e->accept();
208 if (!q_blend)
209 return;
210
211 showWidget = false;
212 render();
213
214 QWidget::closeEvent(e);
215}
216
217/*
218 Render alphablending for the time elapsed.
219
220 Show the blended widget and free all allocated source
221 if the blending is finished.
222*/
223void QAlphaWidget::render()
224{
225 int tempel = checkTime.elapsed();
226 if (elapsed >= tempel)
227 elapsed++;
228 else
229 elapsed = tempel;
230
231 if (duration != 0)
232 alpha = tempel / double(duration);
233 else
234 alpha = 1;
235
236#if defined(Q_OS_WIN)
237 if (alpha >= 1 || !showWidget) {
238 anim.stop();
239 qApp->removeEventFilter(this);
240 widget->setWindowOpacity(1);
241 q_blend.reset();
242 } else {
243 widget->setWindowOpacity(alpha);
244 }
245#else
246 if (alpha >= 1 || !showWidget) {
247 anim.stop();
248 qApp->removeEventFilter(this);
249
250 if (widget) {
251 if (!showWidget) {
252 widget->hide();
253 } else {
254 //Since we are faking the visibility of the widget
255 //we need to unset the hidden state on it before calling show
256 widget->setAttribute(Qt::WA_WState_Hidden, true);
257 widget->show();
258 lower();
259 }
260 }
261 q_blend.reset();
262 } else {
264 pm = QPixmap::fromImage(mixedImage);
265 repaint();
266 }
267#endif // defined(Q_OS_WIN)
268}
269
270/*
271 Calculate an alphablended image.
272*/
274{
275 const int a = qRound(alpha*256);
276 const int ia = 256 - a;
277
278 const int sw = frontImage.width();
279 const int sh = frontImage.height();
280 const qsizetype bpl = frontImage.bytesPerLine();
281 switch(frontImage.depth()) {
282 case 32:
283 {
284 uchar *mixed_data = mixedImage.bits();
285 const uchar *back_data = backImage.bits();
286 const uchar *front_data = frontImage.bits();
287
288 for (int sy = 0; sy < sh; sy++) {
289 quint32* mixed = (quint32*)mixed_data;
290 const quint32* back = (const quint32*)back_data;
291 const quint32* front = (const quint32*)front_data;
292 for (int sx = 0; sx < sw; sx++) {
293 quint32 bp = back[sx];
294 quint32 fp = front[sx];
295
296 mixed[sx] = qRgb((qRed(bp)*ia + qRed(fp)*a)>>8,
297 (qGreen(bp)*ia + qGreen(fp)*a)>>8,
298 (qBlue(bp)*ia + qBlue(fp)*a)>>8);
299 }
300 mixed_data += bpl;
301 back_data += bpl;
302 front_data += bpl;
303 }
304 break;
305 }
306 default:
307 break;
308 }
309}
310
311/*
312 Internal class QRollEffect
313
314 The QRollEffect widget is shown while the animation lasts
315 and displays a scrolling pixmap.
316*/
317
318class QRollEffect : public QWidget, private QEffects
319{
321public:
323
324 void run(int time);
325
326protected:
327 void paintEvent(QPaintEvent*) override;
328 void closeEvent(QCloseEvent*) override;
329
330private slots:
331 void scroll();
332
333private:
334 QPointer<QWidget> widget;
335
336 int currentHeight;
337 int currentWidth;
338 int totalHeight;
339 int totalWidth;
340
341 int duration;
342 int elapsed;
343 bool done;
344 bool showWidget;
345 int orientation;
346
347 QTimer anim;
348 QElapsedTimer checkTime;
349
350 QPixmap pm;
351};
352
353static std::unique_ptr<QRollEffect, DeleteLater> q_roll;
354
355/*
356 Construct a QRollEffect widget.
357*/
358QRollEffect::QRollEffect(QWidget* w, Qt::WindowFlags f, DirFlags orient)
359 : QWidget(nullptr, f), orientation(orient)
360{
361 QWidgetPrivate::get(this)->setScreen(w->screen());
362#ifndef Q_OS_WIN
363 setEnabled(false);
364#endif
365
366 widget = w;
367 Q_ASSERT(widget);
368
369 setAttribute(Qt::WA_NoSystemBackground, true);
370
371 if (widget->testAttribute(Qt::WA_Resized)) {
372 totalWidth = widget->width();
373 totalHeight = widget->height();
374 } else {
375 totalWidth = widget->sizeHint().width();
376 totalHeight = widget->sizeHint().height();
377 }
378
379 currentHeight = totalHeight;
380 currentWidth = totalWidth;
381
382 if (orientation & (RightScroll|LeftScroll))
383 currentWidth = 0;
384 if (orientation & (DownScroll|UpScroll))
385 currentHeight = 0;
386
387 pm = widget->grab();
388 connect(w, &QWidget::destroyed, this, []() { q_roll.reset(); });
389}
390
391/*
392 \reimp
393*/
394void QRollEffect::paintEvent(QPaintEvent*)
395{
396 int x = orientation & RightScroll ? qMin(0, currentWidth - totalWidth) : 0;
397 int y = orientation & DownScroll ? qMin(0, currentHeight - totalHeight) : 0;
398
399 QPainter p(this);
400 p.drawPixmap(x, y, pm);
401}
402
403/*
404 \reimp
405*/
406void QRollEffect::closeEvent(QCloseEvent *e)
407{
408 e->accept();
409 if (done)
410 return;
411
412 showWidget = false;
413 done = true;
414 scroll();
415
416 QWidget::closeEvent(e);
417}
418
419/*
420 Start the animation.
421
422 The animation will take about \a time ms, or is
423 calculated if \a time is negative
424*/
425void QRollEffect::run(int time)
426{
427 if (!widget)
428 return;
429
430 duration = time;
431 elapsed = 0;
432
433 if (duration < 0) {
434 int dist = 0;
435 if (orientation & (RightScroll|LeftScroll))
436 dist += totalWidth - currentWidth;
437 if (orientation & (DownScroll|UpScroll))
438 dist += totalHeight - currentHeight;
439 duration = qMin(qMax(dist/3, 50), 120);
440 }
441
442 connect(&anim, &QTimer::timeout, this, &QRollEffect::scroll);
443
444 move(widget->geometry().x(),widget->geometry().y());
445 resize(qMin(currentWidth, totalWidth), qMin(currentHeight, totalHeight));
446
447 //This is roughly equivalent to calling setVisible(true) without actually showing the widget
448 widget->setAttribute(Qt::WA_WState_ExplicitShowHide, true);
449 widget->setAttribute(Qt::WA_WState_Hidden, false);
450
451 show();
452 setEnabled(false);
453
454 showWidget = true;
455 done = false;
456 anim.start(1);
457 checkTime.start();
458}
459
460/*
461 Roll according to the time elapsed.
462*/
463void QRollEffect::scroll()
464{
465 if (!done && widget) {
466 int tempel = checkTime.elapsed();
467 if (elapsed >= tempel)
468 elapsed++;
469 else
470 elapsed = tempel;
471
472 if (currentWidth != totalWidth) {
473 currentWidth = totalWidth * (elapsed/duration)
474 + (2 * totalWidth * (elapsed%duration) + duration)
475 / (2 * duration);
476 // equiv. to int((totalWidth*elapsed) / duration + 0.5)
477 }
478 if (currentHeight != totalHeight) {
479 currentHeight = totalHeight * (elapsed/duration)
480 + (2 * totalHeight * (elapsed%duration) + duration)
481 / (2 * duration);
482 // equiv. to int((totalHeight*elapsed) / duration + 0.5)
483 }
484 done = (currentHeight >= totalHeight) &&
485 (currentWidth >= totalWidth);
486
487 int w = totalWidth;
488 int h = totalHeight;
489 int x = widget->geometry().x();
490 int y = widget->geometry().y();
491
492 if (orientation & RightScroll || orientation & LeftScroll)
493 w = qMin(currentWidth, totalWidth);
494 if (orientation & DownScroll || orientation & UpScroll)
495 h = qMin(currentHeight, totalHeight);
496
497 setUpdatesEnabled(false);
498 if (orientation & UpScroll)
499 y = widget->geometry().y() + qMax(0, totalHeight - currentHeight);
500 if (orientation & LeftScroll)
501 x = widget->geometry().x() + qMax(0, totalWidth - currentWidth);
502 if (orientation & UpScroll || orientation & LeftScroll)
503 move(x, y);
504
505 resize(w, h);
506 setUpdatesEnabled(true);
507 repaint();
508 }
509 if (done || !widget) {
510 anim.stop();
511 if (widget) {
512 if (!showWidget) {
513#ifdef Q_OS_WIN
514 setEnabled(true);
515 setFocus();
516#endif
517 widget->hide();
518 } else {
519 //Since we are faking the visibility of the widget
520 //we need to unset the hidden state on it before calling show
521 widget->setAttribute(Qt::WA_WState_Hidden, true);
522 widget->show();
523 lower();
524 }
525 }
526 q_roll.reset();
527 }
528}
529
530/*
531 Scroll widget \a w in \a time ms. \a orient may be 1 (vertical), 2
532 (horizontal) or 3 (diagonal).
533*/
534void qScrollEffect(QWidget* w, QEffects::DirFlags orient, int time)
535{
536 q_roll.reset();
537
538 if (!w)
539 return;
540
541 QCoreApplication::sendPostedEvents(w, QEvent::Move);
542 QCoreApplication::sendPostedEvents(w, QEvent::Resize);
543 Qt::WindowFlags flags = Qt::ToolTip;
544
545 // those can be popups - they would steal the focus, but are disabled
546 q_roll.reset(new QRollEffect(w, flags, orient));
547 q_roll->run(time);
548}
549
550/*
551 Fade in widget \a w in \a time ms.
552*/
553void qFadeEffect(QWidget* w, int time)
554{
555 q_blend.reset();
556
557 if (!w)
558 return;
559
560 QCoreApplication::sendPostedEvents(w, QEvent::Move);
561 QCoreApplication::sendPostedEvents(w, QEvent::Resize);
562
563 Qt::WindowFlags flags = Qt::ToolTip;
564
565 // those can be popups - they would steal the focus, but are disabled
566 q_blend.reset(new QAlphaWidget(w, flags));
567
568 q_blend->run(time);
569}
570
571QT_END_NAMESPACE
572
573/*
574 Delete this after timeout
575*/
576
577#include "qeffects.moc"
void paintEvent(QPaintEvent *e) override
This event handler can be reimplemented in a subclass to receive paint events passed in event.
Definition qeffects.cpp:101
bool eventFilter(QObject *, QEvent *) override
Filters events if this object has been installed as an event filter for the watched object.
Definition qeffects.cpp:164
void alphaBlend()
Definition qeffects.cpp:273
void closeEvent(QCloseEvent *) override
This event handler is called with the given event when Qt receives a window close request for a top-l...
Definition qeffects.cpp:205
void run(int time)
Definition qeffects.cpp:111
QPainter(QPaintDevice *)
Constructs a painter that begins painting the paint device immediately.
friend class QWidget
Definition qpainter.h:432
void paintEvent(QPaintEvent *) override
This event handler can be reimplemented in a subclass to receive paint events passed in event.
Definition qeffects.cpp:394
void run(int time)
Definition qeffects.cpp:425
void closeEvent(QCloseEvent *) override
This event handler is called with the given event when Qt receives a window close request for a top-l...
Definition qeffects.cpp:406
Combined button and popup list for selecting options.
#define qApp
static std::unique_ptr< QRollEffect, DeleteLater > q_roll
Definition qeffects.cpp:353
void qScrollEffect(QWidget *w, QEffects::DirFlags orient, int time)
Definition qeffects.cpp:534
void qFadeEffect(QWidget *w, int time)
Definition qeffects.cpp:553
static std::unique_ptr< QAlphaWidget, DeleteLater > q_blend
Definition qeffects.cpp:71
void operator()(QObject *o) const
Definition qeffects.cpp:25
@ RightScroll
Definition qeffects_p.h:33
uint DirFlags
Definition qeffects_p.h:38