4#include "qplatformdefs.h"
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>
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>
24#include <QtGui/private/qwindow_p.h>
25#include <QtGui/private/qhighdpiscaling_p.h>
27#include <qpa/qplatformbackingstore.h>
31Q_GLOBAL_STATIC(QPlatformTextureList, qt_dummy_platformTextureList)
38class QPlatformTextureListWatcher :
public QObject
42 QPlatformTextureListWatcher(QWidgetRepaintManager *repaintManager)
43 : m_repaintManager(repaintManager) {}
45 void watch(QPlatformTextureList *textureList) {
46 connect(textureList, SIGNAL(locked(
bool)), SLOT(onLockStatusChanged(
bool)));
47 m_locked[textureList] = textureList->isLocked();
50 bool isLocked()
const {
51 for (
const auto &[_, v] : m_locked.asKeyValueRange()) {
59 void onLockStatusChanged(
bool locked) {
60 QPlatformTextureList *tl =
static_cast<QPlatformTextureList *>(sender());
61 m_locked[tl] = locked;
63 m_repaintManager->sync();
67 QHash<QPlatformTextureList *,
bool> m_locked;
68 QWidgetRepaintManager *m_repaintManager;
73QWidgetRepaintManager::QWidgetRepaintManager(QWidget *topLevel)
74 : tlw(topLevel), store(tlw->backingStore())
79 updateLists(topLevel);
82void QWidgetRepaintManager::updateLists(QWidget *cur)
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())
96 if (cur->testAttribute(Qt::WA_StaticContents))
100QWidgetRepaintManager::~QWidgetRepaintManager()
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));
109
110
111
112
113
115void QWidgetPrivate::invalidateBackingStore(
const T &r)
120 if (QCoreApplication::closingDown())
124 if (!q->isVisible() || !q->updatesEnabled())
127 QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
128 if (!tlwExtra || !tlwExtra->backingStore || !tlwExtra->repaintManager)
132 clipped &= clipRect();
133 if (clipped.isEmpty())
136 if (!graphicsEffect && extra && extra->hasMask) {
137 QRegion masked(extra->mask);
139 if (masked.isEmpty())
142 tlwExtra->repaintManager->markDirty(masked, q,
143 QWidgetRepaintManager::UpdateLater, QWidgetRepaintManager::BufferInvalid);
145 tlwExtra->repaintManager->markDirty(clipped, q,
146 QWidgetRepaintManager::UpdateLater, QWidgetRepaintManager::BufferInvalid);
150template Q_AUTOTEST_EXPORT
void QWidgetPrivate::invalidateBackingStore<QRect>(
const QRect &r);
156
157
158
159
160
161
162
163
164
165
166
168void QWidgetRepaintManager::markDirty(
const T &r, QWidget *widget, UpdateTime updateTime, BufferState bufferState)
170 qCInfo(lcWidgetPainting) <<
"Marking" << r <<
"of" << widget <<
"dirty"
171 <<
"with" << updateTime;
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());
179#if QT_CONFIG(graphicseffect)
180 widget->d_func()->invalidateGraphicsEffectsRecursively();
183 QRect widgetRect = widgetRectFor(widget, r);
187 if (widget->d_func()->shouldPaintOnScreen()) {
188 if (widget->d_func()->dirty.isEmpty()) {
189 widget->d_func()->dirty = r;
190 sendUpdateRequest(widget, updateTime);
192 }
else if (qt_region_strictContains(widget->d_func()->dirty, widgetRect)) {
193 if (updateTime == UpdateNow)
194 sendUpdateRequest(widget, updateTime);
198 const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
199 widget->d_func()->dirty += r;
200 if (!eventAlreadyPosted || updateTime == UpdateNow)
201 sendUpdateRequest(widget, updateTime);
207 if (QWidgetPrivate::get(widget)->renderToTexture) {
208 if (!widget->d_func()->inDirtyList)
209 addDirtyRenderToTextureWidget(widget);
210 if (!updateRequestSent || updateTime == UpdateNow)
211 sendUpdateRequest(tlw, updateTime);
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)
222 translatedRect = translatedRect.intersected(QRect(QPoint(), tlw->size()));
224 if (qt_region_strictContains(dirty, translatedRect)) {
225 if (updateTime == UpdateNow)
226 sendUpdateRequest(tlw, updateTime);
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);
239 dirty += r.translated(offset);
241 if (!eventAlreadyPosted || updateTime == UpdateNow)
242 sendUpdateRequest(tlw, updateTime);
248 if (dirtyWidgets.isEmpty()) {
249 addDirtyWidget(widget, r);
250 sendUpdateRequest(tlw, updateTime);
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);
263 widget->d_func()->dirty += r;
266 addDirtyWidget(widget, r);
271 if (updateTime == UpdateNow)
272 sendUpdateRequest(tlw, updateTime);
274template void QWidgetRepaintManager::markDirty<QRect>(
const QRect &, QWidget *, UpdateTime, BufferState);
275template void QWidgetRepaintManager::markDirty<QRegion>(
const QRegion &, QWidget *, UpdateTime, BufferState);
277void QWidgetRepaintManager::addDirtyWidget(QWidget *widget,
const QRegion &rgn)
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());
286 widgetPrivate->dirty = rgn;
287 dirtyWidgets.append(widget);
288 widgetPrivate->inDirtyList =
true;
292void QWidgetRepaintManager::removeDirtyWidget(QWidget *w)
297 dirtyWidgets.removeAll(w);
298 dirtyRenderToTextureWidgets.removeAll(w);
301 needsFlushWidgets.removeAll(w);
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);
311void QWidgetRepaintManager::resetWidget(QWidget *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();
321void QWidgetRepaintManager::addDirtyRenderToTextureWidget(QWidget *widget)
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;
331void QWidgetRepaintManager::sendUpdateRequest(QWidget *widget, UpdateTime updateTime)
336 qCInfo(lcWidgetPainting) <<
"Sending update request to" << widget <<
"with" << updateTime;
342 if (updateTime == UpdateNow && QWidgetPrivate::get(widget)->textureChildSeen) {
344 QWidget *w = widget->window();
345 QScreen *ws = w->windowHandle()->screen();
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;
356 switch (updateTime) {
361 if (!widget->d_func()->shouldPaintOnScreen())
362 updateRequestSent =
true;
363 QCoreApplication::postEvent(widget,
new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
366 QEvent event(QEvent::UpdateRequest);
367 QCoreApplication::sendEvent(widget, &event);
377 return widget && widget->windowHandle() && widget->windowHandle()->handle();
382void QWidgetPrivate::moveRect(
const QRect &rect,
int dx,
int dy)
385 if (!q->isVisible() || (dx == 0 && dy == 0))
388 QWidget *tlw = q->window();
390 static const bool accelEnv = qEnvironmentVariableIntValue(
"QT_NO_FAST_MOVE") == 0;
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);
404 bool accelerateMove = accelEnv && isOpaque && !nativeWithTextureChild && sourceRect.isValid()
405#if QT_CONFIG(graphicsview)
407 && !tlw->d_func()->extra->proxyWidget
409 && !isOverlapped(sourceRect) && !isOverlapped(destRect);
411 if (!accelerateMove) {
412 QRegion parentRegion(effectiveRectFor(parentRect));
413 if (!extra || !extra->hasMask) {
414 parentRegion -= newRect;
417 parentRegion += newRect & clipR;
419 parentPrivate->invalidateBackingStore(parentRegion);
420 invalidateBackingStore((newRect & clipR).translated(-data.crect.topLeft()));
422 QWidgetRepaintManager *repaintManager = QWidgetPrivate::get(tlw)->maybeRepaintManager();
423 Q_ASSERT(repaintManager);
424 QRegion childExpose(newRect & clipR);
426 if (repaintManager->bltRect(sourceRect, dx, dy, parentWidget))
427 childExpose -= destRect;
429 if (!parentWidget->updatesEnabled())
432 const bool childUpdatesEnabled = q->updatesEnabled();
434 if (childUpdatesEnabled && !childExpose.isEmpty()) {
435 childExpose.translate(-data.crect.topLeft());
436 repaintManager->markDirty(childExpose, q);
440 QRegion parentExpose(parentRect);
441 parentExpose -= newRect;
442 if (extra && extra->hasMask)
443 parentExpose += QRegion(newRect) - extra->mask.translated(data.crect.topLeft());
445 if (!parentExpose.isEmpty()) {
446 repaintManager->markDirty(parentExpose, parentWidget);
447 parentPrivate->isMoved =
true;
450 if (childUpdatesEnabled) {
451 QRegion needsFlush(sourceRect);
452 needsFlush += destRect;
453 repaintManager->markNeedsFlush(parentWidget, needsFlush, toplevelOffset);
459void QWidgetPrivate::scrollRect(
const QRect &rect,
int dx,
int dy)
462 QWidget *tlw = q->window();
464 QWidgetRepaintManager *repaintManager = QWidgetPrivate::get(tlw)->maybeRepaintManager();
468 static const bool accelEnv = qEnvironmentVariableIntValue(
"QT_NO_FAST_SCROLL") == 0;
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())));
475 if (!accelerateScroll) {
477 QRegion region(scrollRect);
478 subtractOpaqueSiblings(region);
479 invalidateBackingStore(region);
481 invalidateBackingStore(scrollRect);
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);
488 QRegion childExpose(scrollRect);
489 if (sourceRect.isValid()) {
490 if (repaintManager->bltRect(sourceRect, dx, dy, q))
491 childExpose -= destRect;
495 if (rect == q->rect()) {
496 dirty.translate(dx, dy);
498 QRegion dirtyScrollRegion = dirty.intersected(scrollRect);
499 if (!dirtyScrollRegion.isEmpty()) {
500 dirty -= dirtyScrollRegion;
501 dirtyScrollRegion.translate(dx, dy);
502 dirty += dirtyScrollRegion;
507 if (!q->updatesEnabled())
510 if (!childExpose.isEmpty()) {
511 repaintManager->markDirty(childExpose, q);
518 repaintManager->markNeedsFlush(q, destRect, toplevelOffset);
523
524
525
526bool QWidgetRepaintManager::bltRect(
const QRect &rect,
int dx,
int dy, QWidget *widget)
528 const QPoint pos(widget->mapTo(tlw, rect.topLeft()));
529 const QRect tlwRect(QRect(pos, rect.size()));
530 if (dirty.intersects(tlwRect))
532 return store->scroll(tlwRect, dx, dy);
538 QPlatformTextureList *widgetTextures,
539 QList<QWidget *> *nativeChildren)
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);
549 for (
int i = 0; i < wd->children.size(); ++i) {
550 QWidget *w = qobject_cast<QWidget *>(wd->children.at(i));
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);
562 if (QWidgetPrivate::get(widget)->textureChildSeen) {
563 QList<QWidget *> nativeChildren;
564 auto tl = std::make_unique<QPlatformTextureList>();
567 findTextureWidgetsRecursively(tlw, widget, tl.get(), &nativeChildren);
570 QWidgetPrivate::get(tlw)->topData()->widgetTextures.push_back(std::move(tl));
572 for (QWidget *ncw : std::as_const(nativeChildren)) {
573 if (QWidgetPrivate::get(ncw)->textureChildSeen)
574 findAllTextureWidgetsRecursively(tlw, ncw);
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))
590 if (QWidgetPrivate::get(widget)->textureChildSeen)
591 return qt_dummy_platformTextureList();
599
600
601
602
603
604
605void QWidgetRepaintManager::sync(QWidget *exposedWidget,
const QRegion &exposedRegion)
607 qCInfo(lcWidgetPainting) <<
"Syncing" << exposedRegion <<
"of" << exposedWidget;
609 if (!tlw->isVisible())
612 if (!exposedWidget || !hasPlatformWindow(exposedWidget)
613 || !exposedWidget->isVisible() || !exposedWidget->testAttribute(Qt::WA_Mapped)
614 || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) {
619 if (!isDirty() && store->size().isValid()) {
620 QPlatformTextureList *widgetTextures = widgetTexturesFor(tlw, exposedWidget);
621 flush(exposedWidget, widgetTextures ? QRegion() : exposedRegion, widgetTextures);
628 QPoint offset = exposedWidget != tlw ? exposedWidget->mapTo(tlw, QPoint()) : QPoint();
629 markNeedsFlush(exposedWidget, exposedRegion, offset);
636
637
638void QWidgetRepaintManager::sync()
640 qCInfo(lcWidgetPainting) <<
"Syncing dirty widgets";
642 updateRequestSent =
false;
643 if (qt_widget_private(tlw)->shouldDiscardSyncRequest()) {
649 if (!tlw->isVisible()) {
651 for (
int i = 0; i < dirtyWidgets.size(); ++i)
652 resetWidget(dirtyWidgets.at(i));
653 dirtyWidgets.clear();
662bool QWidgetPrivate::shouldDiscardSyncRequest()
const
665 return !maybeTopData() || !q->testAttribute(Qt::WA_Mapped) || !q->isVisible();
668bool QWidgetRepaintManager::syncAllowed()
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());
693#if QT_CONFIG(graphicseffect)
695 if (w->graphicsEffect())
697 w = w->parentWidget();
703void QWidgetRepaintManager::paintAndFlush()
705 qCInfo(lcWidgetPainting) <<
"Painting and flushing dirty"
706 <<
"top level" << dirty <<
"and dirty widgets" << dirtyWidgets;
708 const bool updatesDisabled = !tlw->updatesEnabled();
709 bool repaintAllWidgets =
false;
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)) {
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;
722 store->setStaticContents(staticRegion);
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;
733 if (store->size() != tlwRect.size())
734 store->resize(tlwRect.size());
740 QRegion toClean(dirty);
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)
754 wd->dirty &= wd->clipRect();
755 wd->clipToEffectiveMask(wd->dirty);
758 bool hasDirtySiblingsAbove =
false;
761 wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove);
767 const QRegion dirtyBeforeSubtractedOpaqueChildren = wd->dirty;
770 if (!wd->isScrolled && !wd->isMoved)
771 wd->subtractOpaqueChildren(wd->dirty, w->rect());
773 if (wd->dirty.isEmpty() && wd->textureChildSeen)
774 wd->dirty = dirtyBeforeSubtractedOpaqueChildren;
776 if (wd->dirty.isEmpty()) {
781 const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint()))
783 toClean += widgetDirty;
785#if QT_CONFIG(graphicsview)
786 if (tlw->d_func()->extra->proxyWidget) {
792 if (!isDrawnInEffect(w) && !hasDirtySiblingsAbove && wd->isOpaque
793 && !dirty.intersects(widgetDirty.boundingRect())) {
794 opaqueNonOverlappedWidgets.append(w);
797 dirty += widgetDirty;
800 dirtyWidgets.clear();
805 QTLWExtra *tlwExtra = tlw->d_func()->topData();
806 tlwExtra->widgetTextures.clear();
807 findAllTextureWidgetsRecursively(tlw, tlw);
809 if (toClean.isEmpty()) {
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);
823 dirtyRenderToTextureWidgets.clear();
824 for (
int i = 0; i < numPaintPending; ++i) {
825 QWidget *w = paintPending[i];
826 w->d_func()->sendPaintEvent(w->rect());
828 QWidget *npw = w->nativeParentWidget();
829 if (hasPlatformWindow(w) || (npw && npw != tlw)) {
830 if (!hasPlatformWindow(w))
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);
852 w->d_func()->renderToTextureReallyDirty = 1;
858 for (
int i = 0; i < dirtyRenderToTextureWidgets.size(); ++i)
859 resetWidget(dirtyRenderToTextureWidgets.at(i));
860 dirtyRenderToTextureWidgets.clear();
862#if QT_CONFIG(graphicsview)
863 if (tlw->d_func()->extra->proxyWidget) {
864 updateStaticContentsSize();
866 updateRequestSent =
false;
867 for (
const QRect &rect : toClean)
868 tlw->d_func()->extra->proxyWidget->update(rect);
873 store->beginPaint(toClean);
877 updateStaticContentsSize();
878 const QRegion dirtyCopy(dirty);
880 updateRequestSent =
false;
883 for (
int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) {
884 QWidget *w = opaqueNonOverlappedWidgets[i];
885 QWidgetPrivate *wd = w->d_func();
887 QWidgetPrivate::DrawWidgetFlags flags = QWidgetPrivate::DrawRecursive;
889 if (!wd->isScrolled && !wd->isMoved)
890 flags |= QWidgetPrivate::DontDrawOpaqueChildren;
892 flags |= QWidgetPrivate::DrawAsRoot;
894 QRegion toBePainted(wd->dirty);
899 offset += w->mapTo(tlw, QPoint());
900 wd->drawWidget(store->paintDevice(), toBePainted, offset, flags,
nullptr,
this);
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);
916
917
918
919
920
921void QWidgetRepaintManager::markNeedsFlush(QWidget *widget,
const QRegion ®ion,
const QPoint &topLevelOffset)
923 if (!widget || widget->d_func()->shouldPaintOnScreen() || region.isEmpty())
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) {
938 topLevelNeedsFlush += region.translated(topLevelOffset);
941 const QPoint nativeParentOffset = widget->mapTo(nativeParent, QPoint());
942 markNeedsFlush(nativeParent, region.translated(nativeParentOffset));
946 qCInfo(lcWidgetPainting) <<
"Marking" << region
947 <<
"of native child" << widget <<
"as needing flush";
948 markNeedsFlush(widget, region);
952void QWidgetRepaintManager::markNeedsFlush(QWidget *widget,
const QRegion ®ion)
957 auto *widgetPrivate = qt_widget_private(widget);
958 if (!widgetPrivate->needsFlush)
959 widgetPrivate->needsFlush =
new QRegion;
961 *widgetPrivate->needsFlush += region;
963 if (!needsFlushWidgets.contains(widget))
964 needsFlushWidgets.append(widget);
968
969
970void QWidgetRepaintManager::flush()
972 qCInfo(lcWidgetPainting) <<
"Flushing top level"
973 << topLevelNeedsFlush <<
"and children" << needsFlushWidgets;
975 const bool hasNeedsFlushWidgets = !needsFlushWidgets.isEmpty();
976 bool flushed =
false;
979 if (!topLevelNeedsFlush.isEmpty()) {
980 flush(tlw, topLevelNeedsFlush, widgetTexturesFor(tlw, tlw));
981 topLevelNeedsFlush = QRegion();
986 if (!flushed && !hasNeedsFlushWidgets) {
987 if (!tlw->d_func()->topData()->widgetTextures.empty()) {
988 if (QPlatformTextureList *widgetTextures = widgetTexturesFor(tlw, tlw))
989 flush(tlw, QRegion(), widgetTextures);
993 if (!hasNeedsFlushWidgets)
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();
1006
1007
1008
1009
1010void QWidgetRepaintManager::flush(QWidget *widget,
const QRegion ®ion, QPlatformTextureList *widgetTextures)
1012 Q_ASSERT(!region.isEmpty() || widgetTextures);
1016 if (tlw->testAttribute(Qt::WA_DontShowOnScreen) || widget->testAttribute(Qt::WA_DontShowOnScreen))
1019 QWindow *window = widget->windowHandle();
1024 if (window->type() == Qt::ForeignWindow)
1027 static bool fpsDebug = qEnvironmentVariableIntValue(
"QT_DEBUG_FPS");
1031 if (perfTime.elapsed() > 5000) {
1032 double fps =
double(perfFrames * 1000) / perfTime.restart();
1033 qDebug(
"FPS: %.1f\n", fps);
1040 offset += widget->mapTo(tlw, QPoint());
1045 const bool flushWithRhi = widget->d_func()->usesRhiFlush;
1047 qCDebug(lcWidgetPainting) <<
"Flushing" << region <<
"of" << widget
1048 <<
"to" << window << (flushWithRhi ?
"using RHI" :
"");
1054 if (!widgetTextures)
1055 widgetTextures = qt_dummy_platformTextureList;
1059 auto *widgetPrivate = QWidgetPrivate::get(widget);
1060 widgetPrivate->sendComposeStatus(widget,
false);
1065 const bool translucentBackground = widget->testAttribute(Qt::WA_TranslucentBackground);
1067 QPlatformBackingStore::FlushResult flushResult;
1068 flushResult = store->handle()->rhiFlush(window,
1069 widget->devicePixelRatio(),
1073 translucentBackground);
1075 widgetPrivate->sendComposeStatus(widget,
true);
1077 if (flushResult == QPlatformBackingStore::FlushFailedDueToLostDevice) {
1078 qSendWindowChangeToTextureChildrenRecursively(widget->window(),
1079 QEvent::WindowAboutToChangeInternal);
1080 store->handle()->graphicsDeviceReportedLost(window);
1081 qSendWindowChangeToTextureChildrenRecursively(widget->window(),
1082 QEvent::WindowChangeInternal);
1086 store->flush(region, window, offset);
1092void QWidgetRepaintManager::addStaticWidget(QWidget *widget)
1097 Q_ASSERT(widget->testAttribute(Qt::WA_StaticContents));
1098 if (!staticWidgets.contains(widget))
1099 staticWidgets.append(widget);
1104void QWidgetRepaintManager::moveStaticWidgets(QWidget *reparented)
1106 Q_ASSERT(reparented);
1107 QWidgetRepaintManager *newPaintManager = reparented->d_func()->maybeRepaintManager();
1108 if (newPaintManager ==
this)
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);
1124void QWidgetRepaintManager::removeStaticWidget(QWidget *widget)
1126 staticWidgets.removeAll(widget);
1129bool QWidgetRepaintManager::hasStaticContents()
const
1131 return !staticWidgets.isEmpty();
1135
1136
1137
1138
1139QRegion QWidgetRepaintManager::staticContents(QWidget *parent,
const QRect &withinClipRect)
const
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);
1149 if (parent && parent->d_func()->children.isEmpty())
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))) {
1162 QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height());
1163 const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint());
1165 rect &= withinClipRect.translated(-offset);
1169 rect &= wd->clipRect();
1173 QRegion visible(rect);
1174 wd->clipToEffectiveMask(visible);
1175 if (visible.isEmpty())
1177 wd->subtractOpaqueSiblings(visible,
nullptr,
true);
1179 visible.translate(offset);
1186void QWidgetRepaintManager::updateStaticContentsSize()
1188 for (
int i = 0; i < staticWidgets.size(); ++i) {
1189 QWidgetPrivate *wd = staticWidgets.at(i)->d_func();
1192 wd->extra->staticContentsSize = wd->data.crect.size();
1198bool QWidgetRepaintManager::isDirty()
const
1200 return !(dirtyWidgets.isEmpty() && dirty.isEmpty() && dirtyRenderToTextureWidgets.isEmpty());
1204
1205
1206
1207void QWidgetPrivate::invalidateBackingStore_resizeHelper(
const QPoint &oldPos,
const QSize &oldSize)
1210 Q_ASSERT(!q->isWindow());
1211 Q_ASSERT(q->parentWidget());
1213 const bool staticContents = q->testAttribute(Qt::WA_StaticContents);
1214 const bool sizeDecreased = (data.crect.width() < oldSize.width())
1215 || (data.crect.height() < oldSize.height());
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());
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();
1229 if (hasStaticChildren) {
1230 QRegion dirty(newWidgetRect);
1231 dirty -= staticChildren;
1232 invalidateBackingStore(dirty);
1235 invalidateBackingStore(newWidgetRect);
1238 if (!parentAreaExposed)
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;
1247 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1249 if (hasStaticChildren && !graphicsEffect) {
1250 QRegion parentExpose(QRect(oldPos, oldSize));
1251 parentExpose -= data.crect;
1252 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1254 q->parentWidget()->d_func()->invalidateBackingStore(effectiveRectFor(QRect(oldPos, oldSize)));
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());
1267 moveRect(QRect(oldPos, oldSize), offset.x(), offset.y());
1272 if (!sizeDecreased || !oldWidgetRect.contains(newWidgetRect)) {
1273 QRegion newVisible(newWidgetRect);
1274 newVisible -= oldWidgetRect;
1275 invalidateBackingStore(newVisible);
1278 if (!parentAreaExposed)
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);
1289 QRegion parentExpose(oldRect);
1290 parentExpose -= data.crect;
1291 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1297#include "qwidgetrepaintmanager.moc"
1298#include "moc_qwidgetrepaintmanager_p.cpp"
Combined button and popup list for selecting options.