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