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 double originalOpacity = 1.0;
337
338 int currentHeight;
339 int currentWidth;
340 int totalHeight;
341 int totalWidth;
342
343 int duration;
344 int elapsed;
345 bool done;
346 bool showWidget;
347 int orientation;
348
349 QTimer anim;
350 QElapsedTimer checkTime;
351
352 QPixmap pm;
353};
354
355static std::unique_ptr<QRollEffect, DeleteLater> q_roll;
356
357/*
358 Construct a QRollEffect widget.
359*/
360QRollEffect::QRollEffect(QWidget* w, Qt::WindowFlags f, DirFlags orient)
361 : QWidget(nullptr, f), orientation(orient)
362{
363 QWidgetPrivate::get(this)->setScreen(w->screen());
364#ifndef Q_OS_WIN
365 setEnabled(false);
366#endif
367
368 widget = w;
369 Q_ASSERT(widget);
370
371 setAttribute(Qt::WA_NoSystemBackground, true);
372
373 if (widget->testAttribute(Qt::WA_Resized)) {
374 totalWidth = widget->width();
375 totalHeight = widget->height();
376 } else {
377 totalWidth = widget->sizeHint().width();
378 totalHeight = widget->sizeHint().height();
379 }
380
381 currentHeight = totalHeight;
382 currentWidth = totalWidth;
383
384 if (orientation & (RightScroll|LeftScroll))
385 currentWidth = 0;
386 if (orientation & (DownScroll|UpScroll))
387 currentHeight = 0;
388
389 pm = widget->grab();
390 connect(w, &QWidget::destroyed, this, []() { q_roll.reset(); });
391}
392
393/*
394 \reimp
395*/
396void QRollEffect::paintEvent(QPaintEvent*)
397{
398 int x = orientation & RightScroll ? qMin(0, currentWidth - totalWidth) : 0;
399 int y = orientation & DownScroll ? qMin(0, currentHeight - totalHeight) : 0;
400
401 QPainter p(this);
402 p.drawPixmap(x, y, pm);
403}
404
405/*
406 \reimp
407*/
408void QRollEffect::closeEvent(QCloseEvent *e)
409{
410 e->accept();
411 if (done)
412 return;
413
414 showWidget = false;
415 done = true;
416 scroll();
417
418 QWidget::closeEvent(e);
419}
420
421/*
422 Start the animation.
423
424 The animation will take about \a time ms, or is
425 calculated if \a time is negative
426*/
427void QRollEffect::run(int time)
428{
429 if (!widget)
430 return;
431
432 duration = time;
433 elapsed = 0;
434
435 if (duration < 0) {
436 int dist = 0;
437 if (orientation & (RightScroll|LeftScroll))
438 dist += totalWidth - currentWidth;
439 if (orientation & (DownScroll|UpScroll))
440 dist += totalHeight - currentHeight;
441 duration = qMin(qMax(dist/3, 50), 120);
442 }
443
444 connect(&anim, &QTimer::timeout, this, &QRollEffect::scroll);
445
446 move(widget->geometry().x(),widget->geometry().y());
447 resize(qMin(currentWidth, totalWidth), qMin(currentHeight, totalHeight));
448
449 originalOpacity = widget->windowOpacity();
450 widget->setVisible(true);
451 widget->setWindowOpacity(0.0);
452
453 show();
454 setEnabled(false);
455
456 showWidget = true;
457 done = false;
458 anim.start(1);
459 checkTime.start();
460}
461
462/*
463 Roll according to the time elapsed.
464*/
465void QRollEffect::scroll()
466{
467 if (!done && widget) {
468 int tempel = checkTime.elapsed();
469 if (elapsed >= tempel)
470 elapsed++;
471 else
472 elapsed = tempel;
473
474 if (currentWidth != totalWidth) {
475 currentWidth = totalWidth * (elapsed/duration)
476 + (2 * totalWidth * (elapsed%duration) + duration)
477 / (2 * duration);
478 // equiv. to int((totalWidth*elapsed) / duration + 0.5)
479 }
480 if (currentHeight != totalHeight) {
481 currentHeight = totalHeight * (elapsed/duration)
482 + (2 * totalHeight * (elapsed%duration) + duration)
483 / (2 * duration);
484 // equiv. to int((totalHeight*elapsed) / duration + 0.5)
485 }
486 done = (currentHeight >= totalHeight) &&
487 (currentWidth >= totalWidth);
488
489 int w = totalWidth;
490 int h = totalHeight;
491 int x = widget->geometry().x();
492 int y = widget->geometry().y();
493
494 if (orientation & RightScroll || orientation & LeftScroll)
495 w = qMin(currentWidth, totalWidth);
496 if (orientation & DownScroll || orientation & UpScroll)
497 h = qMin(currentHeight, totalHeight);
498
499 setUpdatesEnabled(false);
500 if (orientation & UpScroll)
501 y = widget->geometry().y() + qMax(0, totalHeight - currentHeight);
502 if (orientation & LeftScroll)
503 x = widget->geometry().x() + qMax(0, totalWidth - currentWidth);
504 if (orientation & UpScroll || orientation & LeftScroll)
505 move(x, y);
506
507 resize(w, h);
508 setUpdatesEnabled(true);
509 repaint();
510 }
511 if (done || !widget) {
512 anim.stop();
513 if (widget) {
514 if (!showWidget) {
515#ifdef Q_OS_WIN
516 setEnabled(true);
517 setFocus();
518#endif
519 widget->hide();
520 } else {
521 lower();
522 }
523 widget->setWindowOpacity(originalOpacity);
524 }
525 q_roll.reset();
526 }
527}
528
529/*
530 Scroll widget \a w in \a time ms. \a orient may be 1 (vertical), 2
531 (horizontal) or 3 (diagonal).
532*/
533void qScrollEffect(QWidget* w, QEffects::DirFlags orient, int time)
534{
535 q_roll.reset();
536
537 if (!w)
538 return;
539
540 QCoreApplication::sendPostedEvents(w, QEvent::Move);
541 QCoreApplication::sendPostedEvents(w, QEvent::Resize);
542 Qt::WindowFlags flags = Qt::ToolTip;
543
544 // those can be popups - they would steal the focus, but are disabled
545 q_roll.reset(new QRollEffect(w, flags, orient));
546 q_roll->run(time);
547}
548
549/*
550 Fade in widget \a w in \a time ms.
551*/
552void qFadeEffect(QWidget* w, int time)
553{
554 q_blend.reset();
555
556 if (!w)
557 return;
558
559 QCoreApplication::sendPostedEvents(w, QEvent::Move);
560 QCoreApplication::sendPostedEvents(w, QEvent::Resize);
561
562 Qt::WindowFlags flags = Qt::ToolTip;
563
564 // those can be popups - they would steal the focus, but are disabled
565 q_blend.reset(new QAlphaWidget(w, flags));
566
567 q_blend->run(time);
568}
569
570QT_END_NAMESPACE
571
572/*
573 Delete this after timeout
574*/
575
576#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:396
void run(int time)
Definition qeffects.cpp:427
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:408
Combined button and popup list for selecting options.
#define qApp
static std::unique_ptr< QRollEffect, DeleteLater > q_roll
Definition qeffects.cpp:355
void qScrollEffect(QWidget *w, QEffects::DirFlags orient, int time)
Definition qeffects.cpp:533
void qFadeEffect(QWidget *w, int time)
Definition qeffects.cpp:552
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