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 \class QWidgetRepaintManager
601 \inmodule QtWidgets
602 \internal
603*/
604
605/*!
606 Synchronizes the \a exposedRegion of the \a exposedWidget with the backing store.
607
608 If there are dirty widgets, including but not limited to the \a exposedWidget,
609 these will be repainted first. The backingstore is then flushed to the screen,
610 regardless of whether or not there were any repaints.
611*/
612void QWidgetRepaintManager::sync(QWidget *exposedWidget, const QRegion &exposedRegion)
613{
614 qCInfo(lcWidgetPainting) << "Syncing" << exposedRegion << "of" << exposedWidget;
615
616 if (!tlw->isVisible())
617 return;
618
619 if (!exposedWidget || !hasPlatformWindow(exposedWidget)
620 || !exposedWidget->isVisible() || !exposedWidget->testAttribute(Qt::WA_Mapped)
621 || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) {
622 return;
623 }
624
625 // Nothing to repaint.
626 if (!isDirty() && store->size().isValid()) {
627 QPlatformTextureList *widgetTextures = widgetTexturesFor(tlw, exposedWidget);
628 flush(exposedWidget, widgetTextures ? QRegion() : exposedRegion, widgetTextures);
629 return;
630 }
631
632 // As requests to sync a specific widget typically comes from an expose event
633 // we can't rely solely on our own dirty tracking to decide what to flush, and
634 // need to respect the platform's request to at least flush the entire widget,
635 QPoint offset = exposedWidget != tlw ? exposedWidget->mapTo(tlw, QPoint()) : QPoint();
636 markNeedsFlush(exposedWidget, exposedRegion, offset);
637
638 if (syncAllowed())
639 paintAndFlush();
640}
641
642/*!
643 Synchronizes the backing store, i.e. dirty areas are repainted and flushed.
644*/
645void QWidgetRepaintManager::sync()
646{
647 qCInfo(lcWidgetPainting) << "Syncing dirty widgets";
648
649 updateRequestSent = false;
650 if (qt_widget_private(tlw)->shouldDiscardSyncRequest()) {
651 // If the top-level is minimized, it's not visible on the screen so we can delay the
652 // update until it's shown again. In order to do that we must keep the dirty states.
653 // These will be cleared when we receive the first expose after showNormal().
654 // However, if the widget is not visible (isVisible() returns false), everything will
655 // be invalidated once the widget is shown again, so clear all dirty states.
656 if (!tlw->isVisible()) {
657 dirty = QRegion();
658 for (int i = 0; i < dirtyWidgets.size(); ++i)
659 resetWidget(dirtyWidgets.at(i));
660 dirtyWidgets.clear();
661 }
662 return;
663 }
664
665 if (syncAllowed())
666 paintAndFlush();
667}
668
669bool QWidgetPrivate::shouldDiscardSyncRequest() const
670{
671 Q_Q(const QWidget);
672 return !maybeTopData() || !q->testAttribute(Qt::WA_Mapped) || !q->isVisible();
673}
674
675bool QWidgetRepaintManager::syncAllowed()
676{
677 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
678 if (textureListWatcher && !textureListWatcher->isLocked()) {
679 textureListWatcher->deleteLater();
680 textureListWatcher = nullptr;
681 } else if (!tlwExtra->widgetTextures.empty()) {
682 bool skipSync = false;
683 for (const auto &tl : tlwExtra->widgetTextures) {
684 if (tl->isLocked()) {
685 if (!textureListWatcher)
686 textureListWatcher = new QPlatformTextureListWatcher(this);
687 if (!textureListWatcher->isLocked())
688 textureListWatcher->watch(tl.get());
689 skipSync = true;
690 }
691 }
692 if (skipSync) // cannot compose due to widget textures being in use
693 return false;
694 }
695 return true;
696}
697
698static bool isDrawnInEffect(const QWidget *w)
699{
700#if QT_CONFIG(graphicseffect)
701 do {
702 if (w->graphicsEffect())
703 return true;
704 w = w->parentWidget();
705 } while (w);
706#endif
707 return false;
708}
709
710void QWidgetRepaintManager::paintAndFlush()
711{
712 qCInfo(lcWidgetPainting) << "Painting and flushing dirty"
713 << "top level" << dirty << "and dirty widgets" << dirtyWidgets;
714
715 const bool updatesDisabled = !tlw->updatesEnabled();
716 bool repaintAllWidgets = false;
717
718 const QRect tlwRect = tlw->data->crect;
719 if (!updatesDisabled && store->size() != tlwRect.size()) {
720 QPlatformIntegration *integration = QGuiApplicationPrivate::platformIntegration();
721 if (hasStaticContents() && !store->size().isEmpty()
722 && integration->hasCapability(QPlatformIntegration::BackingStoreStaticContents)) {
723 // Repaint existing dirty area and newly visible area.
724 const QRect clipRect(QPoint(0, 0), store->size());
725 const QRegion staticRegion(staticContents(nullptr, clipRect));
726 QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height());
727 newVisible -= staticRegion;
728 dirty += newVisible;
729 store->setStaticContents(staticRegion);
730 } else {
731 // Repaint everything.
732 dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height());
733 for (int i = 0; i < dirtyWidgets.size(); ++i)
734 resetWidget(dirtyWidgets.at(i));
735 dirtyWidgets.clear();
736 repaintAllWidgets = true;
737 }
738 }
739
740 if (store->size() != tlwRect.size())
741 store->resize(tlwRect.size());
742
743 if (updatesDisabled)
744 return;
745
746 // Contains everything that needs repaint.
747 QRegion toClean(dirty);
748
749 // Loop through all update() widgets and remove them from the list before they are
750 // painted (in case someone calls update() in paintEvent). If the widget is opaque
751 // and does not have transparent overlapping siblings, append it to the
752 // opaqueNonOverlappedWidgets list and paint it directly without composition.
753 QVarLengthArray<QWidget *, 32> opaqueNonOverlappedWidgets;
754 for (int i = 0; i < dirtyWidgets.size(); ++i) {
755 QWidget *w = dirtyWidgets.at(i);
756 QWidgetPrivate *wd = w->d_func();
757 if (wd->data.in_destructor)
758 continue;
759
760 // Clip with mask() and clipRect().
761 wd->dirty &= wd->clipRect();
762 wd->clipToEffectiveMask(wd->dirty);
763
764 // Subtract opaque siblings and children.
765 bool hasDirtySiblingsAbove = false;
766 // We know for sure that the widget isn't overlapped if 'isMoved' is true.
767 if (!wd->isMoved)
768 wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove);
769
770 // Make a copy of the widget's dirty region, to restore it in case there is an opaque
771 // render-to-texture child that completely covers the widget, because otherwise the
772 // render-to-texture child won't be visible, due to its parent widget not being redrawn
773 // with a proper blending mask.
774 const QRegion dirtyBeforeSubtractedOpaqueChildren = wd->dirty;
775
776 // Scrolled and moved widgets must draw all children.
777 if (!wd->isScrolled && !wd->isMoved)
778 wd->subtractOpaqueChildren(wd->dirty, w->rect());
779
780 if (wd->dirty.isEmpty() && wd->textureChildSeen)
781 wd->dirty = dirtyBeforeSubtractedOpaqueChildren;
782
783 if (wd->dirty.isEmpty()) {
784 resetWidget(w);
785 continue;
786 }
787
788 const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint()))
789 : wd->dirty);
790 toClean += widgetDirty;
791
792#if QT_CONFIG(graphicsview)
793 if (tlw->d_func()->extra->proxyWidget) {
794 resetWidget(w);
795 continue;
796 }
797#endif
798
799 if (!isDrawnInEffect(w) && !hasDirtySiblingsAbove && wd->isOpaque
800 && !dirty.intersects(widgetDirty.boundingRect())) {
801 opaqueNonOverlappedWidgets.append(w);
802 } else {
803 resetWidget(w);
804 dirty += widgetDirty;
805 }
806 }
807 dirtyWidgets.clear();
808
809 // Find all render-to-texture child widgets (including self).
810 // The search is cut at native widget boundaries, meaning that each native child widget
811 // has its own list for the subtree below it.
812 QTLWExtra *tlwExtra = tlw->d_func()->topData();
813 tlwExtra->widgetTextures.clear();
814 findAllTextureWidgetsRecursively(tlw, tlw);
815
816 if (toClean.isEmpty()) {
817 // Nothing to repaint. However renderToTexture widgets are handled
818 // specially, they are not in the regular dirty list, in order to
819 // prevent triggering unnecessary backingstore painting when only the
820 // texture content changes. Check if we have such widgets in the special
821 // dirty list.
822 QVarLengthArray<QWidget *, 16> paintPending;
823 const int numPaintPending = dirtyRenderToTextureWidgets.size();
824 paintPending.reserve(numPaintPending);
825 for (int i = 0; i < numPaintPending; ++i) {
826 QWidget *w = dirtyRenderToTextureWidgets.at(i);
827 paintPending << w;
828 resetWidget(w);
829 }
830 dirtyRenderToTextureWidgets.clear();
831 for (int i = 0; i < numPaintPending; ++i) {
832 QWidget *w = paintPending[i];
833 w->d_func()->sendPaintEvent(w->rect());
834 if (w != tlw) {
835 QWidget *npw = w->nativeParentWidget();
836 if (hasPlatformWindow(w) || (npw && npw != tlw)) {
837 if (!hasPlatformWindow(w))
838 w = npw;
839 markNeedsFlush(w);
840 }
841 }
842 }
843
844 // We might have newly exposed areas on the screen if this function was
845 // called from sync(QWidget *, QRegion)), so we have to make sure those
846 // are flushed. We also need to composite the renderToTexture widgets.
847 flush();
848
849 return;
850 }
851
852 for (const auto &tl : tlwExtra->widgetTextures) {
853 for (int i = 0; i < tl->count(); ++i) {
854 QWidget *w = static_cast<QWidget *>(tl->source(i));
855 if (dirtyRenderToTextureWidgets.contains(w)) {
856 const QRect rect = tl->geometry(i); // mapped to the tlw already
857 // Set a flag to indicate that the paint event for this
858 // render-to-texture widget must not to be optimized away.
859 w->d_func()->renderToTextureReallyDirty = 1;
860 dirty += rect;
861 toClean += rect;
862 }
863 }
864 }
865 for (int i = 0; i < dirtyRenderToTextureWidgets.size(); ++i)
866 resetWidget(dirtyRenderToTextureWidgets.at(i));
867 dirtyRenderToTextureWidgets.clear();
868
869#if QT_CONFIG(graphicsview)
870 if (tlw->d_func()->extra->proxyWidget) {
871 updateStaticContentsSize();
872 dirty = QRegion();
873 updateRequestSent = false;
874 for (const QRect &rect : toClean)
875 tlw->d_func()->extra->proxyWidget->update(rect);
876 return;
877 }
878#endif
879
880 store->beginPaint(toClean);
881
882 // Must do this before sending any paint events because
883 // the size may change in the paint event.
884 updateStaticContentsSize();
885 const QRegion dirtyCopy(dirty);
886 dirty = QRegion();
887 updateRequestSent = false;
888
889 // Paint opaque non overlapped widgets.
890 for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) {
891 QWidget *w = opaqueNonOverlappedWidgets[i];
892 QWidgetPrivate *wd = w->d_func();
893
894 QWidgetPrivate::DrawWidgetFlags flags = QWidgetPrivate::DrawRecursive;
895 // Scrolled and moved widgets must draw all children.
896 if (!wd->isScrolled && !wd->isMoved)
897 flags |= QWidgetPrivate::DontDrawOpaqueChildren;
898 if (w == tlw)
899 flags |= QWidgetPrivate::DrawAsRoot;
900
901 QRegion toBePainted(wd->dirty);
902 resetWidget(w);
903
904 QPoint offset;
905 if (w != tlw)
906 offset += w->mapTo(tlw, QPoint());
907 wd->drawWidget(store->paintDevice(), toBePainted, offset, flags, nullptr, this);
908 }
909
910 // Paint the rest with composition.
911 if (repaintAllWidgets || !dirtyCopy.isEmpty()) {
912 QWidgetPrivate::DrawWidgetFlags flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive
913 | QWidgetPrivate::UseEffectRegionBounds;
914 tlw->d_func()->drawWidget(store->paintDevice(), dirtyCopy, QPoint(), flags, nullptr, this);
915 }
916
917 store->endPaint();
918
919 flush();
920}
921
922/*!
923 Marks the \a region of the \a widget as needing a flush. The \a region will be copied from
924 the backing store to the \a widget's native parent next time flush() is called.
925
926 Paint on screen widgets are ignored.
927*/
928void QWidgetRepaintManager::markNeedsFlush(QWidget *widget, const QRegion &region, const QPoint &topLevelOffset)
929{
930 if (!widget || widget->d_func()->shouldPaintOnScreen() || region.isEmpty())
931 return;
932
933 if (widget == tlw) {
934 // Top-level (native)
935 qCInfo(lcWidgetPainting) << "Marking" << region << "of top level"
936 << widget << "as needing flush";
937 topLevelNeedsFlush += region;
938 } else if (!hasPlatformWindow(widget) && !widget->isWindow()) {
939 QWidget *nativeParent = widget->nativeParentWidget();
940 qCInfo(lcWidgetPainting) << "Marking" << region << "of"
941 << widget << "as needing flush in" << nativeParent
942 << "at offset" << topLevelOffset;
943 if (nativeParent == tlw) {
944 // Alien widgets with the top-level as the native parent (common case)
945 topLevelNeedsFlush += region.translated(topLevelOffset);
946 } else {
947 // Alien widgets with native parent != tlw
948 const QPoint nativeParentOffset = widget->mapTo(nativeParent, QPoint());
949 markNeedsFlush(nativeParent, region.translated(nativeParentOffset));
950 }
951 } else {
952 // Native child widgets
953 qCInfo(lcWidgetPainting) << "Marking" << region
954 << "of native child" << widget << "as needing flush";
955 markNeedsFlush(widget, region);
956 }
957}
958
959void QWidgetRepaintManager::markNeedsFlush(QWidget *widget, const QRegion &region)
960{
961 if (!widget)
962 return;
963
964 auto *widgetPrivate = qt_widget_private(widget);
965 if (!widgetPrivate->needsFlush)
966 widgetPrivate->needsFlush = new QRegion;
967
968 *widgetPrivate->needsFlush += region;
969
970 if (!needsFlushWidgets.contains(widget))
971 needsFlushWidgets.append(widget);
972}
973
974/*!
975 Flushes the contents of the backing store into the top-level widget.
976*/
977void QWidgetRepaintManager::flush()
978{
979 qCInfo(lcWidgetPainting) << "Flushing top level"
980 << topLevelNeedsFlush << "and children" << needsFlushWidgets;
981
982 const bool hasNeedsFlushWidgets = !needsFlushWidgets.isEmpty();
983 bool flushed = false;
984
985 // Flush the top level widget
986 if (!topLevelNeedsFlush.isEmpty()) {
987 flush(tlw, topLevelNeedsFlush, widgetTexturesFor(tlw, tlw));
988 topLevelNeedsFlush = QRegion();
989 flushed = true;
990 }
991
992 // Render-to-texture widgets are not in topLevelNeedsFlush so flush if we have not done it above.
993 if (!flushed && !hasNeedsFlushWidgets) {
994 if (!tlw->d_func()->topData()->widgetTextures.empty()) {
995 if (QPlatformTextureList *widgetTextures = widgetTexturesFor(tlw, tlw))
996 flush(tlw, QRegion(), widgetTextures);
997 }
998 }
999
1000 if (!hasNeedsFlushWidgets)
1001 return;
1002
1003 for (QWidget *w : std::exchange(needsFlushWidgets, {})) {
1004 QWidgetPrivate *wd = w->d_func();
1005 Q_ASSERT(wd->needsFlush);
1006 QPlatformTextureList *widgetTexturesForNative = wd->textureChildSeen ? widgetTexturesFor(tlw, w) : nullptr;
1007 flush(w, *wd->needsFlush, widgetTexturesForNative);
1008 *wd->needsFlush = QRegion();
1009 }
1010}
1011
1012/*
1013 Flushes the contents of the backingstore into the screen area of \a widget.
1014
1015 \a region is the region to be updated in \a widget coordinates.
1016 */
1017void QWidgetRepaintManager::flush(QWidget *widget, const QRegion &region, QPlatformTextureList *widgetTextures)
1018{
1019 Q_ASSERT(!region.isEmpty() || widgetTextures);
1020 Q_ASSERT(widget);
1021 Q_ASSERT(tlw);
1022
1023 if (tlw->testAttribute(Qt::WA_DontShowOnScreen) || widget->testAttribute(Qt::WA_DontShowOnScreen))
1024 return;
1025
1026 QWindow *window = widget->windowHandle();
1027 // We should only be flushing to native widgets
1028 Q_ASSERT(window);
1029
1030 // Foreign Windows do not have backing store content and must not be flushed
1031 if (window->type() == Qt::ForeignWindow)
1032 return;
1033
1034 static bool fpsDebug = qEnvironmentVariableIntValue("QT_DEBUG_FPS");
1035 if (fpsDebug) {
1036 if (!perfFrames++)
1037 perfTime.start();
1038 if (perfTime.elapsed() > 5000) {
1039 double fps = double(perfFrames * 1000) / perfTime.restart();
1040 qDebug("FPS: %.1f\n", fps);
1041 perfFrames = 0;
1042 }
1043 }
1044
1045 QPoint offset;
1046 if (widget != tlw)
1047 offset += widget->mapTo(tlw, QPoint());
1048
1049 // A widget uses RHI flush if itself, or one of its non-native children
1050 // uses RHI for its drawing. If so, we composite the backing store raster
1051 // data along with textures produced by the RHI widgets.
1052 const bool flushWithRhi = widget->d_func()->usesRhiFlush;
1053
1054 qCDebug(lcWidgetPainting) << "Flushing" << region << "of" << widget
1055 << "to" << window << (flushWithRhi ? "using RHI" : "");
1056
1057 // A widget uses RHI flush if itself, or one of its non-native children
1058 // uses RHI for its drawing. If so, we composite the backing store raster
1059 // data along with textures produced by the RHI widgets.
1060 if (flushWithRhi) {
1061 if (!widgetTextures)
1062 widgetTextures = qt_dummy_platformTextureList;
1063
1064 // We only need to let the widget sub-hierarchy that
1065 // we are flushing know that we're compositing.
1066 auto *widgetPrivate = QWidgetPrivate::get(widget);
1067 widgetPrivate->sendComposeStatus(widget, false);
1068
1069 // A window may have alpha even when the app did not request
1070 // WA_TranslucentBackground. Therefore the compositor needs to know whether the app intends
1071 // to rely on translucency, in order to decide if it should clear to transparent or opaque.
1072 const bool translucentBackground = widget->testAttribute(Qt::WA_TranslucentBackground);
1073
1074 QPlatformBackingStore::FlushResult flushResult;
1075 flushResult = store->handle()->rhiFlush(window,
1076 widget->devicePixelRatio(),
1077 region,
1078 offset,
1079 widgetTextures,
1080 translucentBackground);
1081
1082 widgetPrivate->sendComposeStatus(widget, true);
1083
1084 if (flushResult == QPlatformBackingStore::FlushFailedDueToLostDevice) {
1085 qSendWindowChangeToTextureChildrenRecursively(widget->window(),
1086 QEvent::WindowAboutToChangeInternal);
1087 store->handle()->graphicsDeviceReportedLost(window);
1088 qSendWindowChangeToTextureChildrenRecursively(widget->window(),
1089 QEvent::WindowChangeInternal);
1090 widget->update();
1091 }
1092 } else {
1093 store->flush(region, window, offset);
1094 }
1095}
1096
1097// ---------------------------------------------------------------------------
1098
1099void QWidgetRepaintManager::addStaticWidget(QWidget *widget)
1100{
1101 if (!widget)
1102 return;
1103
1104 Q_ASSERT(widget->testAttribute(Qt::WA_StaticContents));
1105 if (!staticWidgets.contains(widget))
1106 staticWidgets.append(widget);
1107}
1108
1109// Move the reparented widget and all its static children from this backing store
1110// to the new backing store if reparented into another top-level / backing store.
1111void QWidgetRepaintManager::moveStaticWidgets(QWidget *reparented)
1112{
1113 Q_ASSERT(reparented);
1114 QWidgetRepaintManager *newPaintManager = reparented->d_func()->maybeRepaintManager();
1115 if (newPaintManager == this)
1116 return;
1117
1118 int i = 0;
1119 while (i < staticWidgets.size()) {
1120 QWidget *w = staticWidgets.at(i);
1121 if (reparented == w || reparented->isAncestorOf(w)) {
1122 staticWidgets.removeAt(i);
1123 if (newPaintManager)
1124 newPaintManager->addStaticWidget(w);
1125 } else {
1126 ++i;
1127 }
1128 }
1129}
1130
1131void QWidgetRepaintManager::removeStaticWidget(QWidget *widget)
1132{
1133 staticWidgets.removeAll(widget);
1134}
1135
1136bool QWidgetRepaintManager::hasStaticContents() const
1137{
1138 return !staticWidgets.isEmpty();
1139}
1140
1141/*!
1142 Returns the static content inside the \a parent if non-zero; otherwise the static content
1143 for the entire backing store is returned. The content will be clipped to \a withinClipRect
1144 if non-empty.
1145*/
1146QRegion QWidgetRepaintManager::staticContents(QWidget *parent, const QRect &withinClipRect) const
1147{
1148 if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) {
1149 QRect backingstoreRect(QPoint(0, 0), store->size());
1150 if (!withinClipRect.isEmpty())
1151 backingstoreRect &= withinClipRect;
1152 return QRegion(backingstoreRect);
1153 }
1154
1155 QRegion region;
1156 if (parent && parent->d_func()->children.isEmpty())
1157 return region;
1158
1159 const bool clipToRect = !withinClipRect.isEmpty();
1160 const int count = staticWidgets.size();
1161 for (int i = 0; i < count; ++i) {
1162 QWidget *w = staticWidgets.at(i);
1163 QWidgetPrivate *wd = w->d_func();
1164 if (!wd->isOpaque || !wd->extra || wd->extra->staticContentsSize.isEmpty()
1165 || !w->isVisible() || (parent && !parent->isAncestorOf(w))) {
1166 continue;
1167 }
1168
1169 QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height());
1170 const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint());
1171 if (clipToRect)
1172 rect &= withinClipRect.translated(-offset);
1173 if (rect.isEmpty())
1174 continue;
1175
1176 rect &= wd->clipRect();
1177 if (rect.isEmpty())
1178 continue;
1179
1180 QRegion visible(rect);
1181 wd->clipToEffectiveMask(visible);
1182 if (visible.isEmpty())
1183 continue;
1184 wd->subtractOpaqueSiblings(visible, nullptr, /*alsoNonOpaque=*/true);
1185
1186 visible.translate(offset);
1187 region += visible;
1188 }
1189
1190 return region;
1191}
1192
1193void QWidgetRepaintManager::updateStaticContentsSize()
1194{
1195 for (int i = 0; i < staticWidgets.size(); ++i) {
1196 QWidgetPrivate *wd = staticWidgets.at(i)->d_func();
1197 if (!wd->extra)
1198 wd->createExtra();
1199 wd->extra->staticContentsSize = wd->data.crect.size();
1200 }
1201}
1202
1203// ---------------------------------------------------------------------------
1204
1205bool QWidgetRepaintManager::isDirty() const
1206{
1207 return !(dirtyWidgets.isEmpty() && dirty.isEmpty() && dirtyRenderToTextureWidgets.isEmpty());
1208}
1209
1210/*!
1211 \class QWidgetPrivate
1212 \inmodule QtWidgets
1213 \internal
1214*/
1215
1216/*!
1217 Invalidates the backing store when the widget is resized.
1218 Static areas are never invalidated unless absolutely needed.
1219*/
1220void QWidgetPrivate::invalidateBackingStore_resizeHelper(const QPoint &oldPos, const QSize &oldSize)
1221{
1222 Q_Q(QWidget);
1223 Q_ASSERT(!q->isWindow());
1224 Q_ASSERT(q->parentWidget());
1225
1226 const bool staticContents = q->testAttribute(Qt::WA_StaticContents);
1227 const bool sizeDecreased = (data.crect.width() < oldSize.width())
1228 || (data.crect.height() < oldSize.height());
1229
1230 const QPoint offset(data.crect.x() - oldPos.x(), data.crect.y() - oldPos.y());
1231 const bool parentAreaExposed = !offset.isNull() || sizeDecreased;
1232 const QRect newWidgetRect(q->rect());
1233 const QRect oldWidgetRect(0, 0, oldSize.width(), oldSize.height());
1234
1235 if (!staticContents || graphicsEffect) {
1236 QRegion staticChildren;
1237 QWidgetRepaintManager *bs = nullptr;
1238 if (offset.isNull() && (bs = maybeRepaintManager()))
1239 staticChildren = bs->staticContents(q, oldWidgetRect);
1240 const bool hasStaticChildren = !staticChildren.isEmpty();
1241
1242 if (hasStaticChildren) {
1243 QRegion dirty(newWidgetRect);
1244 dirty -= staticChildren;
1245 invalidateBackingStore(dirty);
1246 } else {
1247 // Entire widget needs repaint.
1248 invalidateBackingStore(newWidgetRect);
1249 }
1250
1251 if (!parentAreaExposed)
1252 return;
1253
1254 // Invalidate newly exposed area of the parent.
1255 if (!graphicsEffect && extra && extra->hasMask) {
1256 QRegion parentExpose(extra->mask.translated(oldPos));
1257 parentExpose &= QRect(oldPos, oldSize);
1258 if (hasStaticChildren)
1259 parentExpose -= data.crect; // Offset is unchanged, safe to do this.
1260 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1261 } else {
1262 if (hasStaticChildren && !graphicsEffect) {
1263 QRegion parentExpose(QRect(oldPos, oldSize));
1264 parentExpose -= data.crect; // Offset is unchanged, safe to do this.
1265 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1266 } else {
1267 q->parentWidget()->d_func()->invalidateBackingStore(effectiveRectFor(QRect(oldPos, oldSize)));
1268 }
1269 }
1270 return;
1271 }
1272
1273 // Move static content to its new position.
1274 if (!offset.isNull()) {
1275 if (sizeDecreased) {
1276 const QSize minSize(qMin(oldSize.width(), data.crect.width()),
1277 qMin(oldSize.height(), data.crect.height()));
1278 moveRect(QRect(oldPos, minSize), offset.x(), offset.y());
1279 } else {
1280 moveRect(QRect(oldPos, oldSize), offset.x(), offset.y());
1281 }
1282 }
1283
1284 // Invalidate newly visible area of the widget.
1285 if (!sizeDecreased || !oldWidgetRect.contains(newWidgetRect)) {
1286 QRegion newVisible(newWidgetRect);
1287 newVisible -= oldWidgetRect;
1288 invalidateBackingStore(newVisible);
1289 }
1290
1291 if (!parentAreaExposed)
1292 return;
1293
1294 // Invalidate newly exposed area of the parent.
1295 const QRect oldRect(oldPos, oldSize);
1296 if (extra && extra->hasMask) {
1297 QRegion parentExpose(oldRect);
1298 parentExpose &= extra->mask.translated(oldPos);
1299 parentExpose -= (extra->mask.translated(data.crect.topLeft()) & data.crect);
1300 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1301 } else {
1302 QRegion parentExpose(oldRect);
1303 parentExpose -= data.crect;
1304 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1305 }
1306}
1307
1308QT_END_NAMESPACE
1309
1310#include "qwidgetrepaintmanager.moc"
1311#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)