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