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