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
qwidgetrepaintmanager.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 "qplatformdefs.h"
6
8
9#include <QtCore/qglobal.h>
10#include <QtCore/qdebug.h>
11#include <QtCore/qvarlengtharray.h>
12#include <QtGui/qevent.h>
13#include <QtWidgets/qapplication.h>
14#include <QtGui/qpaintengine.h>
15#if QT_CONFIG(graphicsview)
16#include <QtWidgets/qgraphicsproxywidget.h>
17#endif
18
19#include <private/qwidget_p.h>
20#include <private/qapplication_p.h>
21#include <private/qpaintengine_raster_p.h>
22#if QT_CONFIG(graphicseffect)
23#include <private/qgraphicseffect_p.h>
24#endif
25#include <QtGui/private/qwindow_p.h>
26
27#include <qpa/qplatformbackingstore.h>
28
30
31Q_GLOBAL_STATIC(QPlatformTextureList, qt_dummy_platformTextureList)
32
33// Watches one or more QPlatformTextureLists for changes in the lock state and
34// triggers a backingstore sync when all the registered lists turn into
35// unlocked state. This is essential when a custom rhiFlush()
36// implementation in a platform plugin is not synchronous and keeps
37// holding on to the textures for some time even after returning from there.
38class QPlatformTextureListWatcher : public QObject
39{
40 Q_OBJECT
41public:
42 QPlatformTextureListWatcher(QWidgetRepaintManager *repaintManager)
43 : m_repaintManager(repaintManager) {}
44
45 void watch(QPlatformTextureList *textureList) {
46 connect(textureList, SIGNAL(locked(bool)), SLOT(onLockStatusChanged(bool)));
47 m_locked[textureList] = textureList->isLocked();
48 }
49
50 bool isLocked() const {
51 for (const auto &[_, v] : m_locked.asKeyValueRange()) {
52 if (v)
53 return true;
54 }
55 return false;
56 }
57
58private slots:
59 void onLockStatusChanged(bool locked) {
60 QPlatformTextureList *tl = static_cast<QPlatformTextureList *>(sender());
61 m_locked[tl] = locked;
62 if (!isLocked())
63 m_repaintManager->sync();
64 }
65
66private:
67 QHash<QPlatformTextureList *, bool> m_locked;
68 QWidgetRepaintManager *m_repaintManager;
69};
70
71// ---------------------------------------------------------------------------
72
73QWidgetRepaintManager::QWidgetRepaintManager(QWidget *topLevel)
74 : tlw(topLevel), store(tlw->backingStore())
75{
76 Q_ASSERT(store);
77
78 // Ensure all existing subsurfaces and static widgets are added to their respective lists.
79 updateLists(topLevel);
80}
81
82void QWidgetRepaintManager::updateLists(QWidget *cur)
83{
84 if (!cur)
85 return;
86
87 QList<QObject*> children = cur->children();
88 for (int i = 0; i < children.size(); ++i) {
89 QWidget *child = qobject_cast<QWidget*>(children.at(i));
90 if (!child || child->isWindow())
91 continue;
92
93 updateLists(child);
94 }
95
96 if (cur->testAttribute(Qt::WA_StaticContents))
97 addStaticWidget(cur);
98}
99
100QWidgetRepaintManager::~QWidgetRepaintManager()
101{
102 for (int c = 0; c < dirtyWidgets.size(); ++c)
103 resetWidget(dirtyWidgets.at(c));
104 for (int c = 0; c < dirtyRenderToTextureWidgets.size(); ++c)
105 resetWidget(dirtyRenderToTextureWidgets.at(c));
106}
107
108/*!
109 \internal
110 Invalidates the \a r (in widget's coordinates) of the backing store, i.e.
111 all widgets intersecting with the region will be repainted when the backing
112 store is synced.
113*/
114template <class T>
115void QWidgetPrivate::invalidateBackingStore(const T &r)
116{
117 if (r.isEmpty())
118 return;
119
120 if (QCoreApplication::closingDown())
121 return;
122
123 Q_Q(QWidget);
124 if (!q->isVisible() || !q->updatesEnabled())
125 return;
126
127 QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
128 if (!tlwExtra || !tlwExtra->backingStore || !tlwExtra->repaintManager)
129 return;
130
131 T clipped(r);
132 clipped &= clipRect();
133 if (clipped.isEmpty())
134 return;
135
136 if (!graphicsEffect && extra && extra->hasMask) {
137 QRegion masked(extra->mask);
138 masked &= clipped;
139 if (masked.isEmpty())
140 return;
141
142 tlwExtra->repaintManager->markDirty(masked, q,
143 QWidgetRepaintManager::UpdateLater, QWidgetRepaintManager::BufferInvalid);
144 } else {
145 tlwExtra->repaintManager->markDirty(clipped, q,
146 QWidgetRepaintManager::UpdateLater, QWidgetRepaintManager::BufferInvalid);
147 }
148}
149// Needed by tst_QWidget
150template Q_AUTOTEST_EXPORT void QWidgetPrivate::invalidateBackingStore<QRect>(const QRect &r);
151
152static inline QRect widgetRectFor(QWidget *, const QRect &r) { return r; }
153static inline QRect widgetRectFor(QWidget *widget, const QRegion &) { return widget->rect(); }
154
155/*!
156 \internal
157 Marks the region of the widget as dirty (if not already marked as dirty) and
158 posts an UpdateRequest event to the top-level widget (if not already posted).
159
160 If updateTime is UpdateNow, the event is sent immediately instead of posted.
161
162 If bufferState is BufferInvalid, all widgets intersecting with the region will be dirty.
163
164 If the widget paints directly on screen, the event is sent to the widget
165 instead of the top-level widget, and bufferState is completely ignored.
166*/
167template <class T>
168void QWidgetRepaintManager::markDirty(const T &r, QWidget *widget, UpdateTime updateTime, BufferState bufferState)
169{
170 qCInfo(lcWidgetPainting) << "Marking" << r << "of" << widget << "dirty"
171 << "with" << updateTime;
172
173 Q_ASSERT(tlw->d_func()->extra);
174 Q_ASSERT(tlw->d_func()->extra->topextra);
175 Q_ASSERT(widget->isVisible() && widget->updatesEnabled());
176 Q_ASSERT(widget->window() == tlw);
177 Q_ASSERT(!r.isEmpty());
178
179#if QT_CONFIG(graphicseffect)
180 widget->d_func()->invalidateGraphicsEffectsRecursively();
181#endif
182
183 QRect widgetRect = widgetRectFor(widget, r);
184
185 // ---------------------------------------------------------------------------
186
187 if (widget->d_func()->shouldPaintOnScreen()) {
188 if (widget->d_func()->dirty.isEmpty()) {
189 widget->d_func()->dirty = r;
190 sendUpdateRequest(widget, updateTime);
191 return;
192 } else if (qt_region_strictContains(widget->d_func()->dirty, widgetRect)) {
193 if (updateTime == UpdateNow)
194 sendUpdateRequest(widget, updateTime);
195 return; // Already dirty
196 }
197
198 const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
199 widget->d_func()->dirty += r;
200 if (!eventAlreadyPosted || updateTime == UpdateNow)
201 sendUpdateRequest(widget, updateTime);
202 return;
203 }
204
205 // ---------------------------------------------------------------------------
206
207 if (QWidgetPrivate::get(widget)->renderToTexture) {
208 if (!widget->d_func()->inDirtyList)
209 addDirtyRenderToTextureWidget(widget);
210 if (!updateRequestSent || updateTime == UpdateNow)
211 sendUpdateRequest(tlw, updateTime);
212 return;
213 }
214
215 // ---------------------------------------------------------------------------
216
217 QRect effectiveWidgetRect = widget->d_func()->effectiveRectFor(widgetRect);
218 const QPoint offset = widget->mapTo(tlw, QPoint());
219 QRect translatedRect = effectiveWidgetRect.translated(offset);
220#if QT_CONFIG(graphicseffect)
221 // Graphics effects may exceed window size, clamp
222 translatedRect = translatedRect.intersected(QRect(QPoint(), tlw->size()));
223#endif
224 if (qt_region_strictContains(dirty, translatedRect)) {
225 if (updateTime == UpdateNow)
226 sendUpdateRequest(tlw, updateTime);
227 return; // Already dirty
228 }
229
230 // ---------------------------------------------------------------------------
231
232 if (bufferState == BufferInvalid) {
233 const bool eventAlreadyPosted = !dirty.isEmpty() || updateRequestSent;
234#if QT_CONFIG(graphicseffect)
235 if (widget->d_func()->graphicsEffect)
236 dirty += widget->d_func()->effectiveRectFor(r).translated(offset);
237 else
238#endif
239 dirty += r.translated(offset);
240
241 if (!eventAlreadyPosted || updateTime == UpdateNow)
242 sendUpdateRequest(tlw, updateTime);
243 return;
244 }
245
246 // ---------------------------------------------------------------------------
247
248 if (dirtyWidgets.isEmpty()) {
249 addDirtyWidget(widget, r);
250 sendUpdateRequest(tlw, updateTime);
251 return;
252 }
253
254 // ---------------------------------------------------------------------------
255
256 if (widget->d_func()->inDirtyList) {
257 if (!qt_region_strictContains(widget->d_func()->dirty, effectiveWidgetRect)) {
258#if QT_CONFIG(graphicseffect)
259 if (widget->d_func()->graphicsEffect)
260 widget->d_func()->dirty += widget->d_func()->effectiveRectFor(r);
261 else
262#endif
263 widget->d_func()->dirty += r;
264 }
265 } else {
266 addDirtyWidget(widget, r);
267 }
268
269 // ---------------------------------------------------------------------------
270
271 if (updateTime == UpdateNow)
272 sendUpdateRequest(tlw, updateTime);
273}
274template void QWidgetRepaintManager::markDirty<QRect>(const QRect &, QWidget *, UpdateTime, BufferState);
275template void QWidgetRepaintManager::markDirty<QRegion>(const QRegion &, QWidget *, UpdateTime, BufferState);
276
277void QWidgetRepaintManager::addDirtyWidget(QWidget *widget, const QRegion &rgn)
278{
279 if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) {
280 QWidgetPrivate *widgetPrivate = widget->d_func();
281#if QT_CONFIG(graphicseffect)
282 if (widgetPrivate->graphicsEffect)
283 widgetPrivate->dirty = widgetPrivate->effectiveRectFor(rgn.boundingRect());
284 else
285#endif // QT_CONFIG(graphicseffect)
286 widgetPrivate->dirty = rgn;
287 dirtyWidgets.append(widget);
288 widgetPrivate->inDirtyList = true;
289 }
290}
291
292void QWidgetRepaintManager::removeDirtyWidget(QWidget *w)
293{
294 if (!w)
295 return;
296
297 dirtyWidgets.removeAll(w);
298 dirtyRenderToTextureWidgets.removeAll(w);
299 resetWidget(w);
300
301 needsFlushWidgets.removeAll(w);
302
303 QWidgetPrivate *wd = w->d_func();
304 const int n = wd->children.size();
305 for (int i = 0; i < n; ++i) {
306 if (QWidget *child = qobject_cast<QWidget*>(wd->children.at(i)))
307 removeDirtyWidget(child);
308 }
309}
310
311void QWidgetRepaintManager::resetWidget(QWidget *widget)
312{
313 if (widget) {
314 widget->d_func()->inDirtyList = false;
315 widget->d_func()->isScrolled = false;
316 widget->d_func()->isMoved = false;
317 widget->d_func()->dirty = QRegion();
318 }
319}
320
321void QWidgetRepaintManager::addDirtyRenderToTextureWidget(QWidget *widget)
322{
323 if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) {
324 QWidgetPrivate *widgetPrivate = widget->d_func();
325 Q_ASSERT(widgetPrivate->renderToTexture);
326 dirtyRenderToTextureWidgets.append(widget);
327 widgetPrivate->inDirtyList = true;
328 }
329}
330
331void QWidgetRepaintManager::sendUpdateRequest(QWidget *widget, UpdateTime updateTime)
332{
333 if (!widget)
334 return;
335
336 qCInfo(lcWidgetPainting) << "Sending update request to" << widget << "with" << updateTime;
337
338 // Having every repaint() leading to a sync/flush is bad as it causes
339 // compositing and waiting for vsync each and every time. Change to
340 // UpdateLater, except for approx. once per frame to prevent starvation in
341 // case the control does not get back to the event loop.
342 if (updateTime == UpdateNow && QWidgetPrivate::get(widget)->textureChildSeen) {
343 int refresh = 60;
344 QWidget *w = widget->window();
345 QScreen *ws = w->windowHandle()->screen();
346 if (ws)
347 refresh = ws->refreshRate();
348 QWindowPrivate *wd = QWindowPrivate::get(w->windowHandle());
349 if (wd->lastComposeTime.isValid()) {
350 const qint64 elapsed = wd->lastComposeTime.elapsed();
351 if (elapsed <= qint64(1000.0f / refresh))
352 updateTime = UpdateLater;
353 }
354 }
355
356 switch (updateTime) {
357 case UpdateLater:
358 // Prevent redundant update request events, unless it's a
359 // paint on screen widget, as these don't go through the
360 // normal backingstore sync machinery.
361 if (!widget->d_func()->shouldPaintOnScreen())
362 updateRequestSent = true;
363 QCoreApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
364 break;
365 case UpdateNow: {
366 QEvent event(QEvent::UpdateRequest);
367 QCoreApplication::sendEvent(widget, &event);
368 break;
369 }
370 }
371}
372
373// ---------------------------------------------------------------------------
374
375static bool hasPlatformWindow(QWidget *widget)
376{
377 return widget && widget->windowHandle() && widget->windowHandle()->handle();
378}
379
380//parent's coordinates; move whole rect; update parent and widget
381//assume the screen blt has already been done, so we don't need to refresh that part
382void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy)
383{
384 Q_Q(QWidget);
385 if (!q->isVisible() || (dx == 0 && dy == 0))
386 return;
387
388 QWidget *tlw = q->window();
389
390 static const bool accelEnv = qEnvironmentVariableIntValue("QT_NO_FAST_MOVE") == 0;
391
392 QWidget *parentWidget = q->parentWidget();
393 QPoint toplevelOffset = parentWidget->mapTo(tlw, QPoint());
394 QWidgetPrivate *parentPrivate = parentWidget->d_func();
395 const QRect clipR(parentPrivate->clipRect());
396 const QRect newRect(rect.translated(dx, dy));
397 QRect destRect = rect.intersected(clipR);
398 if (destRect.isValid())
399 destRect = destRect.translated(dx, dy).intersected(clipR);
400 const QRect sourceRect(destRect.translated(-dx, -dy));
401 const QRect parentRect(rect & clipR);
402 const bool nativeWithTextureChild = textureChildSeen && hasPlatformWindow(q);
403
404 bool accelerateMove = accelEnv && isOpaque && !nativeWithTextureChild && sourceRect.isValid()
405#if QT_CONFIG(graphicsview)
406 // No accelerate move for proxy widgets.
407 && !tlw->d_func()->extra->proxyWidget
408#endif
409 && !isOverlapped(sourceRect) && !isOverlapped(destRect);
410
411 if (!accelerateMove) {
412 QRegion parentRegion(effectiveRectFor(parentRect));
413 if (!extra || !extra->hasMask) {
414 parentRegion -= newRect;
415 } else {
416 // invalidateBackingStore() excludes anything outside the mask
417 parentRegion += newRect & clipR;
418 }
419 parentPrivate->invalidateBackingStore(parentRegion);
420 invalidateBackingStore((newRect & clipR).translated(-data.crect.topLeft()));
421 } else {
422 QWidgetRepaintManager *repaintManager = QWidgetPrivate::get(tlw)->maybeRepaintManager();
423 Q_ASSERT(repaintManager);
424 QRegion childExpose(newRect & clipR);
425
426 if (repaintManager->bltRect(sourceRect, dx, dy, parentWidget))
427 childExpose -= destRect;
428
429 if (!parentWidget->updatesEnabled())
430 return;
431
432 const bool childUpdatesEnabled = q->updatesEnabled();
433
434 if (childUpdatesEnabled && !childExpose.isEmpty()) {
435 childExpose.translate(-data.crect.topLeft());
436 repaintManager->markDirty(childExpose, q);
437 isMoved = true;
438 }
439
440 QRegion parentExpose(parentRect);
441 parentExpose -= newRect;
442 if (extra && extra->hasMask)
443 parentExpose += QRegion(newRect) - extra->mask.translated(data.crect.topLeft());
444
445 if (!parentExpose.isEmpty()) {
446 repaintManager->markDirty(parentExpose, parentWidget);
447 parentPrivate->isMoved = true;
448 }
449
450 if (childUpdatesEnabled) {
451 QRegion needsFlush(sourceRect);
452 needsFlush += destRect;
453 repaintManager->markNeedsFlush(parentWidget, needsFlush, toplevelOffset);
454 }
455 }
456}
457
458//widget's coordinates; scroll within rect; only update widget
459void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy)
460{
461 Q_Q(QWidget);
462 QWidget *tlw = q->window();
463
464 QWidgetRepaintManager *repaintManager = QWidgetPrivate::get(tlw)->maybeRepaintManager();
465 if (!repaintManager)
466 return;
467
468 static const bool accelEnv = qEnvironmentVariableIntValue("QT_NO_FAST_SCROLL") == 0;
469
470 const QRect scrollRect = rect & clipRect();
471 bool overlapped = false;
472 bool accelerateScroll = accelEnv && isOpaque && !q_func()->testAttribute(Qt::WA_WState_InPaintEvent)
473 && !(overlapped = isOverlapped(scrollRect.translated(data.crect.topLeft())));
474
475 if (!accelerateScroll) {
476 if (overlapped) {
477 QRegion region(scrollRect);
478 subtractOpaqueSiblings(region);
479 invalidateBackingStore(region);
480 }else {
481 invalidateBackingStore(scrollRect);
482 }
483 } else {
484 const QPoint toplevelOffset = q->mapTo(tlw, QPoint());
485 const QRect destRect = scrollRect.translated(dx, dy) & scrollRect;
486 const QRect sourceRect = destRect.translated(-dx, -dy);
487
488 QRegion childExpose(scrollRect);
489 if (sourceRect.isValid()) {
490 if (repaintManager->bltRect(sourceRect, dx, dy, q))
491 childExpose -= destRect;
492 }
493
494 if (inDirtyList) {
495 if (rect == q->rect()) {
496 dirty.translate(dx, dy);
497 } else {
498 QRegion dirtyScrollRegion = dirty.intersected(scrollRect);
499 if (!dirtyScrollRegion.isEmpty()) {
500 dirty -= dirtyScrollRegion;
501 dirtyScrollRegion.translate(dx, dy);
502 dirty += dirtyScrollRegion;
503 }
504 }
505 }
506
507 if (!q->updatesEnabled())
508 return;
509
510 if (!childExpose.isEmpty()) {
511 repaintManager->markDirty(childExpose, q);
512 isScrolled = true;
513 }
514
515 // Instead of using native scroll-on-screen, we copy from
516 // backingstore, giving only one screen update for each
517 // scroll, and a solid appearance
518 repaintManager->markNeedsFlush(q, destRect, toplevelOffset);
519 }
520}
521
522/*
523 Moves the whole rect by (dx, dy) in widget's coordinate system.
524 Doesn't generate any updates.
525*/
526bool QWidgetRepaintManager::bltRect(const QRect &rect, int dx, int dy, QWidget *widget)
527{
528 const QPoint pos(widget->mapTo(tlw, rect.topLeft()));
529 const QRect tlwRect(QRect(pos, rect.size()));
530 if (dirty.intersects(tlwRect))
531 return false; // We don't want to scroll junk.
532 return store->scroll(tlwRect, dx, dy);
533}
534
535// ---------------------------------------------------------------------------
536
537static void findTextureWidgetsRecursively(QWidget *tlw, QWidget *widget,
538 QPlatformTextureList *widgetTextures,
539 QList<QWidget *> *nativeChildren)
540{
541 QWidgetPrivate *wd = QWidgetPrivate::get(widget);
542 if (wd->renderToTexture) {
543 QPlatformTextureList::Flags flags = wd->textureListFlags();
544 const QRect rect(widget->mapTo(tlw, QPoint()), widget->size());
545 QWidgetPrivate::TextureData data = wd->texture();
546 widgetTextures->appendTexture(widget, data.textureLeft, data.textureRight, rect, wd->clipRect(), flags);
547 }
548
549 for (int i = 0; i < wd->children.size(); ++i) {
550 QWidget *w = qobject_cast<QWidget *>(wd->children.at(i));
551 // Stop at native widgets but store them. Stop at hidden widgets too.
552 if (w && !w->isWindow() && hasPlatformWindow(w))
553 nativeChildren->append(w);
554 if (w && !w->isWindow() && !hasPlatformWindow(w) && !w->isHidden() && QWidgetPrivate::get(w)->textureChildSeen)
555 findTextureWidgetsRecursively(tlw, w, widgetTextures, nativeChildren);
556 }
557}
558
559static void findAllTextureWidgetsRecursively(QWidget *tlw, QWidget *widget)
560{
561 // textureChildSeen does not take native child widgets into account and that's good.
562 if (QWidgetPrivate::get(widget)->textureChildSeen) {
563 QList<QWidget *> nativeChildren;
564 auto tl = std::make_unique<QPlatformTextureList>();
565 // Look for texture widgets (incl. widget itself) from 'widget' down,
566 // but skip subtrees with a parent of a native child widget.
567 findTextureWidgetsRecursively(tlw, widget, tl.get(), &nativeChildren);
568 // tl may be empty regardless of textureChildSeen if we have native or hidden children.
569 if (!tl->isEmpty())
570 QWidgetPrivate::get(tlw)->topData()->widgetTextures.push_back(std::move(tl));
571 // Native child widgets, if there was any, get their own separate QPlatformTextureList.
572 for (QWidget *ncw : std::as_const(nativeChildren)) {
573 if (QWidgetPrivate::get(ncw)->textureChildSeen)
574 findAllTextureWidgetsRecursively(tlw, ncw);
575 }
576 }
577}
578
579static QPlatformTextureList *widgetTexturesFor(QWidget *tlw, QWidget *widget)
580{
581 for (const auto &tl : QWidgetPrivate::get(tlw)->topData()->widgetTextures) {
582 Q_ASSERT(!tl->isEmpty());
583 for (int i = 0; i < tl->count(); ++i) {
584 QWidget *w = static_cast<QWidget *>(tl->source(i));
585 if ((hasPlatformWindow(w) && w == widget) || (!hasPlatformWindow(w) && w->nativeParentWidget() == widget))
586 return tl.get();
587 }
588 }
589
590 if (QWidgetPrivate::get(widget)->textureChildSeen)
591 return qt_dummy_platformTextureList();
592
593 return nullptr;
594}
595
596// ---------------------------------------------------------------------------
597
598/*!
599 \class QWidgetRepaintManager
600 \inmodule QtWidgets
601 \internal
602*/
603
604/*!
605 Synchronizes the \a exposedRegion of the \a exposedWidget with the backing store.
606
607 If there are dirty widgets, including but not limited to the \a exposedWidget,
608 these will be repainted first. The backingstore is then flushed to the screen,
609 regardless of whether or not there were any repaints.
610*/
611void QWidgetRepaintManager::sync(QWidget *exposedWidget, const QRegion &exposedRegion)
612{
613 qCInfo(lcWidgetPainting) << "Syncing" << exposedRegion << "of" << exposedWidget;
614
615 if (!tlw->isVisible())
616 return;
617
618 if (!exposedWidget || !hasPlatformWindow(exposedWidget)
619 || !exposedWidget->isVisible() || !exposedWidget->testAttribute(Qt::WA_Mapped)
620 || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) {
621 return;
622 }
623
624 // Nothing to repaint.
625 if (!isDirty() && store->size().isValid()) {
626 QPlatformTextureList *widgetTextures = widgetTexturesFor(tlw, exposedWidget);
627 flush(exposedWidget, widgetTextures ? QRegion() : exposedRegion, widgetTextures);
628 return;
629 }
630
631 // As requests to sync a specific widget typically comes from an expose event
632 // we can't rely solely on our own dirty tracking to decide what to flush, and
633 // need to respect the platform's request to at least flush the entire widget,
634 QPoint offset = exposedWidget != tlw ? exposedWidget->mapTo(tlw, QPoint()) : QPoint();
635 markNeedsFlush(exposedWidget, exposedRegion, offset);
636
637 if (syncAllowed())
638 paintAndFlush();
639}
640
641/*!
642 Synchronizes the backing store, i.e. dirty areas are repainted and flushed.
643*/
644void QWidgetRepaintManager::sync()
645{
646 qCInfo(lcWidgetPainting) << "Syncing dirty widgets";
647
648 updateRequestSent = false;
649 if (qt_widget_private(tlw)->shouldDiscardSyncRequest()) {
650 // If the top-level is minimized, it's not visible on the screen so we can delay the
651 // update until it's shown again. In order to do that we must keep the dirty states.
652 // These will be cleared when we receive the first expose after showNormal().
653 // However, if the widget is not visible (isVisible() returns false), everything will
654 // be invalidated once the widget is shown again, so clear all dirty states.
655 if (!tlw->isVisible()) {
656 dirty = QRegion();
657 for (int i = 0; i < dirtyWidgets.size(); ++i)
658 resetWidget(dirtyWidgets.at(i));
659 dirtyWidgets.clear();
660 }
661 return;
662 }
663
664 if (syncAllowed())
665 paintAndFlush();
666}
667
668bool QWidgetPrivate::shouldDiscardSyncRequest() const
669{
670 Q_Q(const QWidget);
671 return !maybeTopData() || !q->testAttribute(Qt::WA_Mapped) || !q->isVisible();
672}
673
674bool QWidgetRepaintManager::syncAllowed()
675{
676 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
677 if (textureListWatcher && !textureListWatcher->isLocked()) {
678 textureListWatcher->deleteLater();
679 textureListWatcher = nullptr;
680 } else if (!tlwExtra->widgetTextures.empty()) {
681 bool skipSync = false;
682 for (const auto &tl : tlwExtra->widgetTextures) {
683 if (tl->isLocked()) {
684 if (!textureListWatcher)
685 textureListWatcher = new QPlatformTextureListWatcher(this);
686 if (!textureListWatcher->isLocked())
687 textureListWatcher->watch(tl.get());
688 skipSync = true;
689 }
690 }
691 if (skipSync) // cannot compose due to widget textures being in use
692 return false;
693 }
694 return true;
695}
696
697static bool isDrawnInEffect(const QWidget *w)
698{
699#if QT_CONFIG(graphicseffect)
700 do {
701 if (w->graphicsEffect())
702 return true;
703 w = w->parentWidget();
704 } while (w);
705#endif
706 return false;
707}
708
709void QWidgetRepaintManager::paintAndFlush()
710{
711 qCInfo(lcWidgetPainting) << "Painting and flushing dirty"
712 << "top level" << dirty << "and dirty widgets" << dirtyWidgets;
713
714 const bool updatesDisabled = !tlw->updatesEnabled();
715 bool repaintAllWidgets = false;
716
717 const QRect tlwRect = tlw->data->crect;
718 if (!updatesDisabled && store->size() != tlwRect.size()) {
719 QPlatformIntegration *integration = QGuiApplicationPrivate::platformIntegration();
720 if (hasStaticContents() && !store->size().isEmpty()
721 && integration->hasCapability(QPlatformIntegration::BackingStoreStaticContents)) {
722 // Repaint existing dirty area and newly visible area.
723 const QRect clipRect(QPoint(0, 0), store->size());
724 const QRegion staticRegion(staticContents(nullptr, clipRect));
725 QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height());
726 newVisible -= staticRegion;
727 dirty += newVisible;
728 store->setStaticContents(staticRegion);
729 } else {
730 // Repaint everything.
731 dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height());
732 for (int i = 0; i < dirtyWidgets.size(); ++i)
733 resetWidget(dirtyWidgets.at(i));
734 dirtyWidgets.clear();
735 repaintAllWidgets = true;
736 }
737 }
738
739 if (store->size() != tlwRect.size())
740 store->resize(tlwRect.size());
741
742 if (updatesDisabled)
743 return;
744
745 // Contains everything that needs repaint.
746 QRegion toClean(dirty);
747
748 // Loop through all update() widgets and remove them from the list before they are
749 // painted (in case someone calls update() in paintEvent). If the widget is opaque
750 // and does not have transparent overlapping siblings, append it to the
751 // opaqueNonOverlappedWidgets list and paint it directly without composition.
752 QVarLengthArray<QWidget *, 32> opaqueNonOverlappedWidgets;
753 for (int i = 0; i < dirtyWidgets.size(); ++i) {
754 QWidget *w = dirtyWidgets.at(i);
755 QWidgetPrivate *wd = w->d_func();
756 if (wd->data.in_destructor)
757 continue;
758
759 // Clip with mask() and clipRect().
760 wd->dirty &= wd->clipRect();
761 wd->clipToEffectiveMask(wd->dirty);
762
763 // Subtract opaque siblings and children.
764 bool hasDirtySiblingsAbove = false;
765 // We know for sure that the widget isn't overlapped if 'isMoved' is true.
766 if (!wd->isMoved)
767 wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove);
768
769 // Make a copy of the widget's dirty region, to restore it in case there is an opaque
770 // render-to-texture child that completely covers the widget, because otherwise the
771 // render-to-texture child won't be visible, due to its parent widget not being redrawn
772 // with a proper blending mask.
773 const QRegion dirtyBeforeSubtractedOpaqueChildren = wd->dirty;
774
775 // Scrolled and moved widgets must draw all children.
776 if (!wd->isScrolled && !wd->isMoved)
777 wd->subtractOpaqueChildren(wd->dirty, w->rect());
778
779 if (wd->dirty.isEmpty() && wd->textureChildSeen)
780 wd->dirty = dirtyBeforeSubtractedOpaqueChildren;
781
782 if (wd->dirty.isEmpty()) {
783 resetWidget(w);
784 continue;
785 }
786
787 const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint()))
788 : wd->dirty);
789 toClean += widgetDirty;
790
791#if QT_CONFIG(graphicsview)
792 if (tlw->d_func()->extra->proxyWidget) {
793 resetWidget(w);
794 continue;
795 }
796#endif
797
798 if (!isDrawnInEffect(w) && !hasDirtySiblingsAbove && wd->isOpaque
799 && !dirty.intersects(widgetDirty.boundingRect())) {
800 opaqueNonOverlappedWidgets.append(w);
801 } else {
802 resetWidget(w);
803 dirty += widgetDirty;
804 }
805 }
806 dirtyWidgets.clear();
807
808 // Find all render-to-texture child widgets (including self).
809 // The search is cut at native widget boundaries, meaning that each native child widget
810 // has its own list for the subtree below it.
811 QTLWExtra *tlwExtra = tlw->d_func()->topData();
812 tlwExtra->widgetTextures.clear();
813 findAllTextureWidgetsRecursively(tlw, tlw);
814
815 if (toClean.isEmpty()) {
816 // Nothing to repaint. However renderToTexture widgets are handled
817 // specially, they are not in the regular dirty list, in order to
818 // prevent triggering unnecessary backingstore painting when only the
819 // texture content changes. Check if we have such widgets in the special
820 // dirty list.
821 QVarLengthArray<QWidget *, 16> paintPending;
822 const int numPaintPending = dirtyRenderToTextureWidgets.size();
823 paintPending.reserve(numPaintPending);
824 for (int i = 0; i < numPaintPending; ++i) {
825 QWidget *w = dirtyRenderToTextureWidgets.at(i);
826 paintPending << w;
827 resetWidget(w);
828 }
829 dirtyRenderToTextureWidgets.clear();
830 for (int i = 0; i < numPaintPending; ++i) {
831 QWidget *w = paintPending[i];
832 w->d_func()->sendPaintEvent(w->rect());
833 if (w != tlw) {
834 QWidget *npw = w->nativeParentWidget();
835 if (hasPlatformWindow(w) || (npw && npw != tlw)) {
836 if (!hasPlatformWindow(w))
837 w = npw;
838 markNeedsFlush(w);
839 }
840 }
841 }
842
843 // We might have newly exposed areas on the screen if this function was
844 // called from sync(QWidget *, QRegion)), so we have to make sure those
845 // are flushed. We also need to composite the renderToTexture widgets.
846 flush();
847
848 return;
849 }
850
851 for (const auto &tl : tlwExtra->widgetTextures) {
852 for (int i = 0; i < tl->count(); ++i) {
853 QWidget *w = static_cast<QWidget *>(tl->source(i));
854 if (dirtyRenderToTextureWidgets.contains(w)) {
855 const QRect rect = tl->geometry(i); // mapped to the tlw already
856 // Set a flag to indicate that the paint event for this
857 // render-to-texture widget must not to be optimized away.
858 w->d_func()->renderToTextureReallyDirty = 1;
859 dirty += rect;
860 toClean += rect;
861 }
862 }
863 }
864 for (int i = 0; i < dirtyRenderToTextureWidgets.size(); ++i)
865 resetWidget(dirtyRenderToTextureWidgets.at(i));
866 dirtyRenderToTextureWidgets.clear();
867
868#if QT_CONFIG(graphicsview)
869 if (tlw->d_func()->extra->proxyWidget) {
870 updateStaticContentsSize();
871 dirty = QRegion();
872 updateRequestSent = false;
873 for (const QRect &rect : toClean)
874 tlw->d_func()->extra->proxyWidget->update(rect);
875 return;
876 }
877#endif
878
879 store->beginPaint(toClean);
880
881 // Must do this before sending any paint events because
882 // the size may change in the paint event.
883 updateStaticContentsSize();
884 const QRegion dirtyCopy(dirty);
885 dirty = QRegion();
886 updateRequestSent = false;
887
888 // Paint opaque non overlapped widgets.
889 for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) {
890 QWidget *w = opaqueNonOverlappedWidgets[i];
891 QWidgetPrivate *wd = w->d_func();
892
893 QWidgetPrivate::DrawWidgetFlags flags = QWidgetPrivate::DrawRecursive;
894 // Scrolled and moved widgets must draw all children.
895 if (!wd->isScrolled && !wd->isMoved)
896 flags |= QWidgetPrivate::DontDrawOpaqueChildren;
897 if (w == tlw)
898 flags |= QWidgetPrivate::DrawAsRoot;
899
900 QRegion toBePainted(wd->dirty);
901 resetWidget(w);
902
903 QPoint offset;
904 if (w != tlw)
905 offset += w->mapTo(tlw, QPoint());
906 wd->drawWidget(store->paintDevice(), toBePainted, offset, flags, nullptr, this);
907 }
908
909 // Paint the rest with composition.
910 if (repaintAllWidgets || !dirtyCopy.isEmpty()) {
911 QWidgetPrivate::DrawWidgetFlags flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive
912 | QWidgetPrivate::UseEffectRegionBounds;
913 tlw->d_func()->drawWidget(store->paintDevice(), dirtyCopy, QPoint(), flags, nullptr, this);
914 }
915
916 store->endPaint();
917
918 flush();
919}
920
921/*!
922 Marks the \a region of the \a widget as needing a flush. The \a region will be copied from
923 the backing store to the \a widget's native parent next time flush() is called.
924
925 Paint on screen widgets are ignored.
926*/
927void QWidgetRepaintManager::markNeedsFlush(QWidget *widget, const QRegion &region, const QPoint &topLevelOffset)
928{
929 if (!widget || widget->d_func()->shouldPaintOnScreen() || region.isEmpty())
930 return;
931
932 if (widget == tlw) {
933 // Top-level (native)
934 qCInfo(lcWidgetPainting) << "Marking" << region << "of top level"
935 << widget << "as needing flush";
936 topLevelNeedsFlush += region;
937 } else if (!hasPlatformWindow(widget) && !widget->isWindow()) {
938 QWidget *nativeParent = widget->nativeParentWidget();
939 qCInfo(lcWidgetPainting) << "Marking" << region << "of"
940 << widget << "as needing flush in" << nativeParent
941 << "at offset" << topLevelOffset;
942 if (nativeParent == tlw) {
943 // Alien widgets with the top-level as the native parent (common case)
944 topLevelNeedsFlush += region.translated(topLevelOffset);
945 } else {
946 // Alien widgets with native parent != tlw
947 const QPoint nativeParentOffset = widget->mapTo(nativeParent, QPoint());
948 markNeedsFlush(nativeParent, region.translated(nativeParentOffset));
949 }
950 } else {
951 // Native child widgets
952 qCInfo(lcWidgetPainting) << "Marking" << region
953 << "of native child" << widget << "as needing flush";
954 markNeedsFlush(widget, region);
955 }
956}
957
958void QWidgetRepaintManager::markNeedsFlush(QWidget *widget, const QRegion &region)
959{
960 if (!widget)
961 return;
962
963 auto *widgetPrivate = qt_widget_private(widget);
964 if (!widgetPrivate->needsFlush)
965 widgetPrivate->needsFlush = new QRegion;
966
967 *widgetPrivate->needsFlush += region;
968
969 if (!needsFlushWidgets.contains(widget))
970 needsFlushWidgets.append(widget);
971}
972
973/*!
974 Flushes the contents of the backing store into the top-level widget.
975*/
976void QWidgetRepaintManager::flush()
977{
978 qCInfo(lcWidgetPainting) << "Flushing top level"
979 << topLevelNeedsFlush << "and children" << needsFlushWidgets;
980
981 const bool hasNeedsFlushWidgets = !needsFlushWidgets.isEmpty();
982 bool flushed = false;
983
984 // Flush the top level widget
985 if (!topLevelNeedsFlush.isEmpty()) {
986 flush(tlw, topLevelNeedsFlush, widgetTexturesFor(tlw, tlw));
987 topLevelNeedsFlush = QRegion();
988 flushed = true;
989 }
990
991 // Render-to-texture widgets are not in topLevelNeedsFlush so flush if we have not done it above.
992 if (!flushed && !hasNeedsFlushWidgets) {
993 if (!tlw->d_func()->topData()->widgetTextures.empty()) {
994 if (QPlatformTextureList *widgetTextures = widgetTexturesFor(tlw, tlw))
995 flush(tlw, QRegion(), widgetTextures);
996 }
997 }
998
999 if (!hasNeedsFlushWidgets)
1000 return;
1001
1002 for (QWidget *w : std::exchange(needsFlushWidgets, {})) {
1003 QWidgetPrivate *wd = w->d_func();
1004 Q_ASSERT(wd->needsFlush);
1005 QPlatformTextureList *widgetTexturesForNative = wd->textureChildSeen ? widgetTexturesFor(tlw, w) : nullptr;
1006 flush(w, *wd->needsFlush, widgetTexturesForNative);
1007 *wd->needsFlush = QRegion();
1008 }
1009}
1010
1011/*
1012 Flushes the contents of the backingstore into the screen area of \a widget.
1013
1014 \a region is the region to be updated in \a widget coordinates.
1015 */
1016void QWidgetRepaintManager::flush(QWidget *widget, const QRegion &region, QPlatformTextureList *widgetTextures)
1017{
1018 Q_ASSERT(!region.isEmpty() || widgetTextures);
1019 Q_ASSERT(widget);
1020 Q_ASSERT(tlw);
1021
1022 if (tlw->testAttribute(Qt::WA_DontShowOnScreen) || widget->testAttribute(Qt::WA_DontShowOnScreen))
1023 return;
1024
1025 QWindow *window = widget->windowHandle();
1026 // We should only be flushing to native widgets
1027 Q_ASSERT(window);
1028
1029 // Foreign Windows do not have backing store content and must not be flushed
1030 if (window->type() == Qt::ForeignWindow)
1031 return;
1032
1033 static bool fpsDebug = qEnvironmentVariableIntValue("QT_DEBUG_FPS");
1034 if (fpsDebug) {
1035 if (!perfFrames++)
1036 perfTime.start();
1037 if (perfTime.elapsed() > 5000) {
1038 double fps = double(perfFrames * 1000) / perfTime.restart();
1039 qDebug("FPS: %.1f\n", fps);
1040 perfFrames = 0;
1041 }
1042 }
1043
1044 QPoint offset;
1045 if (widget != tlw)
1046 offset += widget->mapTo(tlw, QPoint());
1047
1048 // A widget uses RHI flush if itself, or one of its non-native children
1049 // uses RHI for its drawing. If so, we composite the backing store raster
1050 // data along with textures produced by the RHI widgets.
1051 const bool flushWithRhi = widget->d_func()->usesRhiFlush;
1052
1053 qCDebug(lcWidgetPainting) << "Flushing" << region << "of" << widget
1054 << "to" << window << (flushWithRhi ? "using RHI" : "");
1055
1056 // A widget uses RHI flush if itself, or one of its non-native children
1057 // uses RHI for its drawing. If so, we composite the backing store raster
1058 // data along with textures produced by the RHI widgets.
1059 if (flushWithRhi) {
1060 if (!widgetTextures)
1061 widgetTextures = qt_dummy_platformTextureList;
1062
1063 // We only need to let the widget sub-hierarchy that
1064 // we are flushing know that we're compositing.
1065 auto *widgetPrivate = QWidgetPrivate::get(widget);
1066 widgetPrivate->sendComposeStatus(widget, false);
1067
1068 // A window may have alpha even when the app did not request
1069 // WA_TranslucentBackground. Therefore the compositor needs to know whether the app intends
1070 // to rely on translucency, in order to decide if it should clear to transparent or opaque.
1071 const bool translucentBackground = widget->testAttribute(Qt::WA_TranslucentBackground);
1072
1073 QPlatformBackingStore::FlushResult flushResult;
1074 flushResult = store->handle()->rhiFlush(window,
1075 widget->devicePixelRatio(),
1076 region,
1077 offset,
1078 widgetTextures,
1079 translucentBackground);
1080
1081 widgetPrivate->sendComposeStatus(widget, true);
1082
1083 if (flushResult == QPlatformBackingStore::FlushFailedDueToLostDevice) {
1084 qSendWindowChangeToTextureChildrenRecursively(widget->window(),
1085 QEvent::WindowAboutToChangeInternal);
1086 store->handle()->graphicsDeviceReportedLost(window);
1087 qSendWindowChangeToTextureChildrenRecursively(widget->window(),
1088 QEvent::WindowChangeInternal);
1089 widget->update();
1090 }
1091 } else {
1092 store->flush(region, window, offset);
1093 }
1094}
1095
1096// ---------------------------------------------------------------------------
1097
1098void QWidgetRepaintManager::addStaticWidget(QWidget *widget)
1099{
1100 if (!widget)
1101 return;
1102
1103 Q_ASSERT(widget->testAttribute(Qt::WA_StaticContents));
1104 if (!staticWidgets.contains(widget))
1105 staticWidgets.append(widget);
1106}
1107
1108// Move the reparented widget and all its static children from this backing store
1109// to the new backing store if reparented into another top-level / backing store.
1110void QWidgetRepaintManager::moveStaticWidgets(QWidget *reparented)
1111{
1112 Q_ASSERT(reparented);
1113 QWidgetRepaintManager *newPaintManager = reparented->d_func()->maybeRepaintManager();
1114 if (newPaintManager == this)
1115 return;
1116
1117 int i = 0;
1118 while (i < staticWidgets.size()) {
1119 QWidget *w = staticWidgets.at(i);
1120 if (reparented == w || reparented->isAncestorOf(w)) {
1121 staticWidgets.removeAt(i);
1122 if (newPaintManager)
1123 newPaintManager->addStaticWidget(w);
1124 } else {
1125 ++i;
1126 }
1127 }
1128}
1129
1130void QWidgetRepaintManager::removeStaticWidget(QWidget *widget)
1131{
1132 staticWidgets.removeAll(widget);
1133}
1134
1135bool QWidgetRepaintManager::hasStaticContents() const
1136{
1137 return !staticWidgets.isEmpty();
1138}
1139
1140/*!
1141 Returns the static content inside the \a parent if non-zero; otherwise the static content
1142 for the entire backing store is returned. The content will be clipped to \a withinClipRect
1143 if non-empty.
1144*/
1145QRegion QWidgetRepaintManager::staticContents(QWidget *parent, const QRect &withinClipRect) const
1146{
1147 if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) {
1148 QRect backingstoreRect(QPoint(0, 0), store->size());
1149 if (!withinClipRect.isEmpty())
1150 backingstoreRect &= withinClipRect;
1151 return QRegion(backingstoreRect);
1152 }
1153
1154 QRegion region;
1155 if (parent && parent->d_func()->children.isEmpty())
1156 return region;
1157
1158 const bool clipToRect = !withinClipRect.isEmpty();
1159 const int count = staticWidgets.size();
1160 for (int i = 0; i < count; ++i) {
1161 QWidget *w = staticWidgets.at(i);
1162 QWidgetPrivate *wd = w->d_func();
1163 if (!wd->isOpaque || !wd->extra || wd->extra->staticContentsSize.isEmpty()
1164 || !w->isVisible() || (parent && !parent->isAncestorOf(w))) {
1165 continue;
1166 }
1167
1168 QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height());
1169 const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint());
1170 if (clipToRect)
1171 rect &= withinClipRect.translated(-offset);
1172 if (rect.isEmpty())
1173 continue;
1174
1175 rect &= wd->clipRect();
1176 if (rect.isEmpty())
1177 continue;
1178
1179 QRegion visible(rect);
1180 wd->clipToEffectiveMask(visible);
1181 if (visible.isEmpty())
1182 continue;
1183 wd->subtractOpaqueSiblings(visible, nullptr, /*alsoNonOpaque=*/true);
1184
1185 visible.translate(offset);
1186 region += visible;
1187 }
1188
1189 return region;
1190}
1191
1192void QWidgetRepaintManager::updateStaticContentsSize()
1193{
1194 for (int i = 0; i < staticWidgets.size(); ++i) {
1195 QWidgetPrivate *wd = staticWidgets.at(i)->d_func();
1196 if (!wd->extra)
1197 wd->createExtra();
1198 wd->extra->staticContentsSize = wd->data.crect.size();
1199 }
1200}
1201
1202// ---------------------------------------------------------------------------
1203
1204bool QWidgetRepaintManager::isDirty() const
1205{
1206 return !(dirtyWidgets.isEmpty() && dirty.isEmpty() && dirtyRenderToTextureWidgets.isEmpty());
1207}
1208
1209/*!
1210 \class QWidgetPrivate
1211 \inmodule QtWidgets
1212 \internal
1213*/
1214
1215/*!
1216 Invalidates the backing store when the widget is resized.
1217 Static areas are never invalidated unless absolutely needed.
1218*/
1219void QWidgetPrivate::invalidateBackingStore_resizeHelper(const QPoint &oldPos, const QSize &oldSize)
1220{
1221 Q_Q(QWidget);
1222 Q_ASSERT(!q->isWindow());
1223 Q_ASSERT(q->parentWidget());
1224
1225 const bool staticContents = q->testAttribute(Qt::WA_StaticContents);
1226 const bool sizeDecreased = (data.crect.width() < oldSize.width())
1227 || (data.crect.height() < oldSize.height());
1228
1229 const QPoint offset(data.crect.x() - oldPos.x(), data.crect.y() - oldPos.y());
1230 const bool parentAreaExposed = !offset.isNull() || sizeDecreased;
1231 const QRect newWidgetRect(q->rect());
1232 const QRect oldWidgetRect(0, 0, oldSize.width(), oldSize.height());
1233
1234 if (!staticContents || graphicsEffect) {
1235 QRegion staticChildren;
1236 QWidgetRepaintManager *bs = nullptr;
1237 if (offset.isNull() && (bs = maybeRepaintManager()))
1238 staticChildren = bs->staticContents(q, oldWidgetRect);
1239 const bool hasStaticChildren = !staticChildren.isEmpty();
1240
1241 if (hasStaticChildren) {
1242 QRegion dirty(newWidgetRect);
1243 dirty -= staticChildren;
1244 invalidateBackingStore(dirty);
1245 } else {
1246 // Entire widget needs repaint.
1247 invalidateBackingStore(newWidgetRect);
1248 }
1249
1250 if (!parentAreaExposed)
1251 return;
1252
1253 // Invalidate newly exposed area of the parent.
1254 if (!graphicsEffect && extra && extra->hasMask) {
1255 QRegion parentExpose(extra->mask.translated(oldPos));
1256 parentExpose &= QRect(oldPos, oldSize);
1257 if (hasStaticChildren)
1258 parentExpose -= data.crect; // Offset is unchanged, safe to do this.
1259 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1260 } else {
1261 if (hasStaticChildren && !graphicsEffect) {
1262 QRegion parentExpose(QRect(oldPos, oldSize));
1263 parentExpose -= data.crect; // Offset is unchanged, safe to do this.
1264 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1265 } else {
1266 q->parentWidget()->d_func()->invalidateBackingStore(effectiveRectFor(QRect(oldPos, oldSize)));
1267 }
1268 }
1269 return;
1270 }
1271
1272 // Move static content to its new position.
1273 if (!offset.isNull()) {
1274 if (sizeDecreased) {
1275 const QSize minSize(qMin(oldSize.width(), data.crect.width()),
1276 qMin(oldSize.height(), data.crect.height()));
1277 moveRect(QRect(oldPos, minSize), offset.x(), offset.y());
1278 } else {
1279 moveRect(QRect(oldPos, oldSize), offset.x(), offset.y());
1280 }
1281 }
1282
1283 // Invalidate newly visible area of the widget.
1284 if (!sizeDecreased || !oldWidgetRect.contains(newWidgetRect)) {
1285 QRegion newVisible(newWidgetRect);
1286 newVisible -= oldWidgetRect;
1287 invalidateBackingStore(newVisible);
1288 }
1289
1290 if (!parentAreaExposed)
1291 return;
1292
1293 // Invalidate newly exposed area of the parent.
1294 const QRect oldRect(oldPos, oldSize);
1295 if (extra && extra->hasMask) {
1296 QRegion parentExpose(oldRect);
1297 parentExpose &= extra->mask.translated(oldPos);
1298 parentExpose -= (extra->mask.translated(data.crect.topLeft()) & data.crect);
1299 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1300 } else {
1301 QRegion parentExpose(oldRect);
1302 parentExpose -= data.crect;
1303 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1304 }
1305}
1306
1307QT_END_NAMESPACE
1308
1309#include "qwidgetrepaintmanager.moc"
1310#include "moc_qwidgetrepaintmanager_p.cpp"
Combined button and popup list for selecting options.
static void findAllTextureWidgetsRecursively(QWidget *tlw, QWidget *widget)
static QPlatformTextureList * widgetTexturesFor(QWidget *tlw, QWidget *widget)
static bool isDrawnInEffect(const QWidget *w)
static void findTextureWidgetsRecursively(QWidget *tlw, QWidget *widget, QPlatformTextureList *widgetTextures, QList< QWidget * > *nativeChildren)
static QRect widgetRectFor(QWidget *, const QRect &r)
static bool hasPlatformWindow(QWidget *widget)