5#include "qplatformdefs.h"
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>
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>
25#include <QtGui/private/qwindow_p.h>
26#include <QtGui/private/qhighdpiscaling_p.h>
28#include <qpa/qplatformbackingstore.h>
32Q_GLOBAL_STATIC(QPlatformTextureList, qt_dummy_platformTextureList)
39class QPlatformTextureListWatcher :
public QObject
43 QPlatformTextureListWatcher(QWidgetRepaintManager *repaintManager)
44 : m_repaintManager(repaintManager) {}
46 void watch(QPlatformTextureList *textureList) {
47 connect(textureList, SIGNAL(locked(
bool)), SLOT(onLockStatusChanged(
bool)));
48 m_locked[textureList] = textureList->isLocked();
51 bool isLocked()
const {
52 for (
const auto &[_, v] : m_locked.asKeyValueRange()) {
60 void onLockStatusChanged(
bool locked) {
61 QPlatformTextureList *tl =
static_cast<QPlatformTextureList *>(sender());
62 m_locked[tl] = locked;
64 m_repaintManager->sync();
68 QHash<QPlatformTextureList *,
bool> m_locked;
69 QWidgetRepaintManager *m_repaintManager;
74QWidgetRepaintManager::QWidgetRepaintManager(QWidget *topLevel)
75 : tlw(topLevel), store(tlw->backingStore())
80 updateLists(topLevel);
83void QWidgetRepaintManager::updateLists(QWidget *cur)
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())
97 if (cur->testAttribute(Qt::WA_StaticContents))
101QWidgetRepaintManager::~QWidgetRepaintManager()
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));
110
111
112
113
114
116void QWidgetPrivate::invalidateBackingStore(
const T &r)
121 if (QCoreApplication::closingDown())
125 if (!q->isVisible() || !q->updatesEnabled())
128 QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
129 if (!tlwExtra || !tlwExtra->backingStore || !tlwExtra->repaintManager)
133 clipped &= clipRect();
134 if (clipped.isEmpty())
137 if (!graphicsEffect && extra && extra->hasMask) {
138 QRegion masked(extra->mask);
140 if (masked.isEmpty())
143 tlwExtra->repaintManager->markDirty(masked, q,
144 QWidgetRepaintManager::UpdateLater, QWidgetRepaintManager::BufferInvalid);
146 tlwExtra->repaintManager->markDirty(clipped, q,
147 QWidgetRepaintManager::UpdateLater, QWidgetRepaintManager::BufferInvalid);
151template Q_AUTOTEST_EXPORT
void QWidgetPrivate::invalidateBackingStore<QRect>(
const QRect &r);
157
158
159
160
161
162
163
164
165
166
167
169void QWidgetRepaintManager::markDirty(
const T &r, QWidget *widget, UpdateTime updateTime, BufferState bufferState)
171 qCInfo(lcWidgetPainting) <<
"Marking" << r <<
"of" << widget <<
"dirty"
172 <<
"with" << updateTime;
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());
180#if QT_CONFIG(graphicseffect)
181 widget->d_func()->invalidateGraphicsEffectsRecursively();
184 QRect widgetRect = widgetRectFor(widget, r);
188 if (widget->d_func()->shouldPaintOnScreen()) {
189 if (widget->d_func()->dirty.isEmpty()) {
190 widget->d_func()->dirty = r;
191 sendUpdateRequest(widget, updateTime);
193 }
else if (qt_region_strictContains(widget->d_func()->dirty, widgetRect)) {
194 if (updateTime == UpdateNow)
195 sendUpdateRequest(widget, updateTime);
199 const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
200 widget->d_func()->dirty += r;
201 if (!eventAlreadyPosted || updateTime == UpdateNow)
202 sendUpdateRequest(widget, updateTime);
208 if (QWidgetPrivate::get(widget)->renderToTexture) {
209 if (!widget->d_func()->inDirtyList)
210 addDirtyRenderToTextureWidget(widget);
211 if (!updateRequestSent || updateTime == UpdateNow)
212 sendUpdateRequest(tlw, updateTime);
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)
223 translatedRect = translatedRect.intersected(QRect(QPoint(), tlw->size()));
225 if (qt_region_strictContains(dirty, translatedRect)) {
226 if (updateTime == UpdateNow)
227 sendUpdateRequest(tlw, updateTime);
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);
240 dirty += r.translated(offset);
242 if (!eventAlreadyPosted || updateTime == UpdateNow)
243 sendUpdateRequest(tlw, updateTime);
249 if (dirtyWidgets.isEmpty()) {
250 addDirtyWidget(widget, r);
251 sendUpdateRequest(tlw, updateTime);
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);
264 widget->d_func()->dirty += r;
267 addDirtyWidget(widget, r);
272 if (updateTime == UpdateNow)
273 sendUpdateRequest(tlw, updateTime);
275template void QWidgetRepaintManager::markDirty<QRect>(
const QRect &, QWidget *, UpdateTime, BufferState);
276template void QWidgetRepaintManager::markDirty<QRegion>(
const QRegion &, QWidget *, UpdateTime, BufferState);
278void QWidgetRepaintManager::addDirtyWidget(QWidget *widget,
const QRegion &rgn)
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());
287 widgetPrivate->dirty = rgn;
288 dirtyWidgets.append(widget);
289 widgetPrivate->inDirtyList =
true;
293void QWidgetRepaintManager::removeDirtyWidget(QWidget *w)
298 dirtyWidgets.removeAll(w);
299 dirtyRenderToTextureWidgets.removeAll(w);
302 needsFlushWidgets.removeAll(w);
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);
312void QWidgetRepaintManager::resetWidget(QWidget *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();
322void QWidgetRepaintManager::addDirtyRenderToTextureWidget(QWidget *widget)
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;
332void QWidgetRepaintManager::sendUpdateRequest(QWidget *widget, UpdateTime updateTime)
337 qCInfo(lcWidgetPainting) <<
"Sending update request to" << widget <<
"with" << updateTime;
343 if (updateTime == UpdateNow && QWidgetPrivate::get(widget)->textureChildSeen) {
345 QWidget *w = widget->window();
346 QScreen *ws = w->windowHandle()->screen();
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;
357 switch (updateTime) {
362 if (!widget->d_func()->shouldPaintOnScreen())
363 updateRequestSent =
true;
364 QCoreApplication::postEvent(widget,
new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
367 QEvent event(QEvent::UpdateRequest);
368 QCoreApplication::sendEvent(widget, &event);
378 return widget && widget->windowHandle() && widget->windowHandle()->handle();
383void QWidgetPrivate::moveRect(
const QRect &rect,
int dx,
int dy)
386 if (!q->isVisible() || (dx == 0 && dy == 0))
389 QWidget *tlw = q->window();
391 static const bool accelEnv = qEnvironmentVariableIntValue(
"QT_NO_FAST_MOVE") == 0;
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);
405 bool accelerateMove = accelEnv && isOpaque && !nativeWithTextureChild && sourceRect.isValid()
406#if QT_CONFIG(graphicsview)
408 && !tlw->d_func()->extra->proxyWidget
410 && !isOverlapped(sourceRect) && !isOverlapped(destRect);
412 if (!accelerateMove) {
413 QRegion parentRegion(effectiveRectFor(parentRect));
414 if (!extra || !extra->hasMask) {
415 parentRegion -= newRect;
418 parentRegion += newRect & clipR;
420 parentPrivate->invalidateBackingStore(parentRegion);
421 invalidateBackingStore((newRect & clipR).translated(-data.crect.topLeft()));
423 QWidgetRepaintManager *repaintManager = QWidgetPrivate::get(tlw)->maybeRepaintManager();
424 Q_ASSERT(repaintManager);
425 QRegion childExpose(newRect & clipR);
427 if (repaintManager->bltRect(sourceRect, dx, dy, parentWidget))
428 childExpose -= destRect;
430 if (!parentWidget->updatesEnabled())
433 const bool childUpdatesEnabled = q->updatesEnabled();
435 if (childUpdatesEnabled && !childExpose.isEmpty()) {
436 childExpose.translate(-data.crect.topLeft());
437 repaintManager->markDirty(childExpose, q);
441 QRegion parentExpose(parentRect);
442 parentExpose -= newRect;
443 if (extra && extra->hasMask)
444 parentExpose += QRegion(newRect) - extra->mask.translated(data.crect.topLeft());
446 if (!parentExpose.isEmpty()) {
447 repaintManager->markDirty(parentExpose, parentWidget);
448 parentPrivate->isMoved =
true;
451 if (childUpdatesEnabled) {
452 QRegion needsFlush(sourceRect);
453 needsFlush += destRect;
454 repaintManager->markNeedsFlush(parentWidget, needsFlush, toplevelOffset);
460void QWidgetPrivate::scrollRect(
const QRect &rect,
int dx,
int dy)
463 QWidget *tlw = q->window();
465 QWidgetRepaintManager *repaintManager = QWidgetPrivate::get(tlw)->maybeRepaintManager();
469 static const bool accelEnv = qEnvironmentVariableIntValue(
"QT_NO_FAST_SCROLL") == 0;
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())));
476 if (!accelerateScroll) {
478 QRegion region(scrollRect);
479 subtractOpaqueSiblings(region);
480 invalidateBackingStore(region);
482 invalidateBackingStore(scrollRect);
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);
489 QRegion childExpose(scrollRect);
490 if (sourceRect.isValid()) {
491 if (repaintManager->bltRect(sourceRect, dx, dy, q))
492 childExpose -= destRect;
496 if (rect == q->rect()) {
497 dirty.translate(dx, dy);
499 QRegion dirtyScrollRegion = dirty.intersected(scrollRect);
500 if (!dirtyScrollRegion.isEmpty()) {
501 dirty -= dirtyScrollRegion;
502 dirtyScrollRegion.translate(dx, dy);
503 dirty += dirtyScrollRegion;
508 if (!q->updatesEnabled())
511 if (!childExpose.isEmpty()) {
512 repaintManager->markDirty(childExpose, q);
519 repaintManager->markNeedsFlush(q, destRect, toplevelOffset);
524
525
526
527bool QWidgetRepaintManager::bltRect(
const QRect &rect,
int dx,
int dy, QWidget *widget)
529 const QPoint pos(widget->mapTo(tlw, rect.topLeft()));
530 const QRect tlwRect(QRect(pos, rect.size()));
531 if (dirty.intersects(tlwRect))
533 return store->scroll(tlwRect, dx, dy);
539 QPlatformTextureList *widgetTextures,
540 QList<QWidget *> *nativeChildren)
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);
550 for (
int i = 0; i < wd->children.size(); ++i) {
551 QWidget *w = qobject_cast<QWidget *>(wd->children.at(i));
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);
563 if (QWidgetPrivate::get(widget)->textureChildSeen) {
564 QList<QWidget *> nativeChildren;
565 auto tl = std::make_unique<QPlatformTextureList>();
568 findTextureWidgetsRecursively(tlw, widget, tl.get(), &nativeChildren);
571 QWidgetPrivate::get(tlw)->topData()->widgetTextures.push_back(std::move(tl));
573 for (QWidget *ncw : std::as_const(nativeChildren)) {
574 if (QWidgetPrivate::get(ncw)->textureChildSeen)
575 findAllTextureWidgetsRecursively(tlw, ncw);
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))
591 if (QWidgetPrivate::get(widget)->textureChildSeen)
592 return qt_dummy_platformTextureList();
600
601
602
603
606
607
608
609
610
611
612void QWidgetRepaintManager::sync(QWidget *exposedWidget,
const QRegion &exposedRegion)
614 qCInfo(lcWidgetPainting) <<
"Syncing" << exposedRegion <<
"of" << exposedWidget;
616 if (!tlw->isVisible())
619 if (!exposedWidget || !hasPlatformWindow(exposedWidget)
620 || !exposedWidget->isVisible() || !exposedWidget->testAttribute(Qt::WA_Mapped)
621 || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) {
626 if (!isDirty() && store->size().isValid()) {
627 QPlatformTextureList *widgetTextures = widgetTexturesFor(tlw, exposedWidget);
628 flush(exposedWidget, widgetTextures ? QRegion() : exposedRegion, widgetTextures);
635 QPoint offset = exposedWidget != tlw ? exposedWidget->mapTo(tlw, QPoint()) : QPoint();
636 markNeedsFlush(exposedWidget, exposedRegion, offset);
643
644
645void QWidgetRepaintManager::sync()
647 qCInfo(lcWidgetPainting) <<
"Syncing dirty widgets";
649 updateRequestSent =
false;
650 if (qt_widget_private(tlw)->shouldDiscardSyncRequest()) {
656 if (!tlw->isVisible()) {
658 for (
int i = 0; i < dirtyWidgets.size(); ++i)
659 resetWidget(dirtyWidgets.at(i));
660 dirtyWidgets.clear();
669bool QWidgetPrivate::shouldDiscardSyncRequest()
const
672 return !maybeTopData() || !q->testAttribute(Qt::WA_Mapped) || !q->isVisible();
675bool QWidgetRepaintManager::syncAllowed()
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());
700#if QT_CONFIG(graphicseffect)
702 if (w->graphicsEffect())
704 w = w->parentWidget();
710void QWidgetRepaintManager::paintAndFlush()
712 qCInfo(lcWidgetPainting) <<
"Painting and flushing dirty"
713 <<
"top level" << dirty <<
"and dirty widgets" << dirtyWidgets;
715 const bool updatesDisabled = !tlw->updatesEnabled();
716 bool repaintAllWidgets =
false;
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)) {
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;
729 store->setStaticContents(staticRegion);
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;
740 if (store->size() != tlwRect.size())
741 store->resize(tlwRect.size());
747 QRegion toClean(dirty);
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)
761 wd->dirty &= wd->clipRect();
762 wd->clipToEffectiveMask(wd->dirty);
765 bool hasDirtySiblingsAbove =
false;
768 wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove);
774 const QRegion dirtyBeforeSubtractedOpaqueChildren = wd->dirty;
777 if (!wd->isScrolled && !wd->isMoved)
778 wd->subtractOpaqueChildren(wd->dirty, w->rect());
780 if (wd->dirty.isEmpty() && wd->textureChildSeen)
781 wd->dirty = dirtyBeforeSubtractedOpaqueChildren;
783 if (wd->dirty.isEmpty()) {
788 const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint()))
790 toClean += widgetDirty;
792#if QT_CONFIG(graphicsview)
793 if (tlw->d_func()->extra->proxyWidget) {
799 if (!isDrawnInEffect(w) && !hasDirtySiblingsAbove && wd->isOpaque
800 && !dirty.intersects(widgetDirty.boundingRect())) {
801 opaqueNonOverlappedWidgets.append(w);
804 dirty += widgetDirty;
807 dirtyWidgets.clear();
812 QTLWExtra *tlwExtra = tlw->d_func()->topData();
813 tlwExtra->widgetTextures.clear();
814 findAllTextureWidgetsRecursively(tlw, tlw);
816 if (toClean.isEmpty()) {
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);
830 dirtyRenderToTextureWidgets.clear();
831 for (
int i = 0; i < numPaintPending; ++i) {
832 QWidget *w = paintPending[i];
833 w->d_func()->sendPaintEvent(w->rect());
835 QWidget *npw = w->nativeParentWidget();
836 if (hasPlatformWindow(w) || (npw && npw != tlw)) {
837 if (!hasPlatformWindow(w))
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);
859 w->d_func()->renderToTextureReallyDirty = 1;
865 for (
int i = 0; i < dirtyRenderToTextureWidgets.size(); ++i)
866 resetWidget(dirtyRenderToTextureWidgets.at(i));
867 dirtyRenderToTextureWidgets.clear();
869#if QT_CONFIG(graphicsview)
870 if (tlw->d_func()->extra->proxyWidget) {
871 updateStaticContentsSize();
873 updateRequestSent =
false;
874 for (
const QRect &rect : toClean)
875 tlw->d_func()->extra->proxyWidget->update(rect);
880 store->beginPaint(toClean);
884 updateStaticContentsSize();
885 const QRegion dirtyCopy(dirty);
887 updateRequestSent =
false;
890 for (
int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) {
891 QWidget *w = opaqueNonOverlappedWidgets[i];
892 QWidgetPrivate *wd = w->d_func();
894 QWidgetPrivate::DrawWidgetFlags flags = QWidgetPrivate::DrawRecursive;
896 if (!wd->isScrolled && !wd->isMoved)
897 flags |= QWidgetPrivate::DontDrawOpaqueChildren;
899 flags |= QWidgetPrivate::DrawAsRoot;
901 QRegion toBePainted(wd->dirty);
906 offset += w->mapTo(tlw, QPoint());
907 wd->drawWidget(store->paintDevice(), toBePainted, offset, flags,
nullptr,
this);
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);
923
924
925
926
927
928void QWidgetRepaintManager::markNeedsFlush(QWidget *widget,
const QRegion ®ion,
const QPoint &topLevelOffset)
930 if (!widget || widget->d_func()->shouldPaintOnScreen() || region.isEmpty())
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) {
945 topLevelNeedsFlush += region.translated(topLevelOffset);
948 const QPoint nativeParentOffset = widget->mapTo(nativeParent, QPoint());
949 markNeedsFlush(nativeParent, region.translated(nativeParentOffset));
953 qCInfo(lcWidgetPainting) <<
"Marking" << region
954 <<
"of native child" << widget <<
"as needing flush";
955 markNeedsFlush(widget, region);
959void QWidgetRepaintManager::markNeedsFlush(QWidget *widget,
const QRegion ®ion)
964 auto *widgetPrivate = qt_widget_private(widget);
965 if (!widgetPrivate->needsFlush)
966 widgetPrivate->needsFlush =
new QRegion;
968 *widgetPrivate->needsFlush += region;
970 if (!needsFlushWidgets.contains(widget))
971 needsFlushWidgets.append(widget);
975
976
977void QWidgetRepaintManager::flush()
979 qCInfo(lcWidgetPainting) <<
"Flushing top level"
980 << topLevelNeedsFlush <<
"and children" << needsFlushWidgets;
982 const bool hasNeedsFlushWidgets = !needsFlushWidgets.isEmpty();
983 bool flushed =
false;
986 if (!topLevelNeedsFlush.isEmpty()) {
987 flush(tlw, topLevelNeedsFlush, widgetTexturesFor(tlw, tlw));
988 topLevelNeedsFlush = QRegion();
993 if (!flushed && !hasNeedsFlushWidgets) {
994 if (!tlw->d_func()->topData()->widgetTextures.empty()) {
995 if (QPlatformTextureList *widgetTextures = widgetTexturesFor(tlw, tlw))
996 flush(tlw, QRegion(), widgetTextures);
1000 if (!hasNeedsFlushWidgets)
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();
1013
1014
1015
1016
1017void QWidgetRepaintManager::flush(QWidget *widget,
const QRegion ®ion, QPlatformTextureList *widgetTextures)
1019 Q_ASSERT(!region.isEmpty() || widgetTextures);
1023 if (tlw->testAttribute(Qt::WA_DontShowOnScreen) || widget->testAttribute(Qt::WA_DontShowOnScreen))
1026 QWindow *window = widget->windowHandle();
1031 if (window->type() == Qt::ForeignWindow)
1034 static bool fpsDebug = qEnvironmentVariableIntValue(
"QT_DEBUG_FPS");
1038 if (perfTime.elapsed() > 5000) {
1039 double fps =
double(perfFrames * 1000) / perfTime.restart();
1040 qDebug(
"FPS: %.1f\n", fps);
1047 offset += widget->mapTo(tlw, QPoint());
1052 const bool flushWithRhi = widget->d_func()->usesRhiFlush;
1054 qCDebug(lcWidgetPainting) <<
"Flushing" << region <<
"of" << widget
1055 <<
"to" << window << (flushWithRhi ?
"using RHI" :
"");
1061 if (!widgetTextures)
1062 widgetTextures = qt_dummy_platformTextureList;
1066 auto *widgetPrivate = QWidgetPrivate::get(widget);
1067 widgetPrivate->sendComposeStatus(widget,
false);
1072 const bool translucentBackground = widget->testAttribute(Qt::WA_TranslucentBackground);
1074 QPlatformBackingStore::FlushResult flushResult;
1075 flushResult = store->handle()->rhiFlush(window,
1076 widget->devicePixelRatio(),
1080 translucentBackground);
1082 widgetPrivate->sendComposeStatus(widget,
true);
1084 if (flushResult == QPlatformBackingStore::FlushFailedDueToLostDevice) {
1085 qSendWindowChangeToTextureChildrenRecursively(widget->window(),
1086 QEvent::WindowAboutToChangeInternal);
1087 store->handle()->graphicsDeviceReportedLost(window);
1088 qSendWindowChangeToTextureChildrenRecursively(widget->window(),
1089 QEvent::WindowChangeInternal);
1093 store->flush(region, window, offset);
1099void QWidgetRepaintManager::addStaticWidget(QWidget *widget)
1104 Q_ASSERT(widget->testAttribute(Qt::WA_StaticContents));
1105 if (!staticWidgets.contains(widget))
1106 staticWidgets.append(widget);
1111void QWidgetRepaintManager::moveStaticWidgets(QWidget *reparented)
1113 Q_ASSERT(reparented);
1114 QWidgetRepaintManager *newPaintManager = reparented->d_func()->maybeRepaintManager();
1115 if (newPaintManager ==
this)
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);
1131void QWidgetRepaintManager::removeStaticWidget(QWidget *widget)
1133 staticWidgets.removeAll(widget);
1136bool QWidgetRepaintManager::hasStaticContents()
const
1138 return !staticWidgets.isEmpty();
1142
1143
1144
1145
1146QRegion QWidgetRepaintManager::staticContents(QWidget *parent,
const QRect &withinClipRect)
const
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);
1156 if (parent && parent->d_func()->children.isEmpty())
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))) {
1169 QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height());
1170 const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint());
1172 rect &= withinClipRect.translated(-offset);
1176 rect &= wd->clipRect();
1180 QRegion visible(rect);
1181 wd->clipToEffectiveMask(visible);
1182 if (visible.isEmpty())
1184 wd->subtractOpaqueSiblings(visible,
nullptr,
true);
1186 visible.translate(offset);
1193void QWidgetRepaintManager::updateStaticContentsSize()
1195 for (
int i = 0; i < staticWidgets.size(); ++i) {
1196 QWidgetPrivate *wd = staticWidgets.at(i)->d_func();
1199 wd->extra->staticContentsSize = wd->data.crect.size();
1205bool QWidgetRepaintManager::isDirty()
const
1207 return !(dirtyWidgets.isEmpty() && dirty.isEmpty() && dirtyRenderToTextureWidgets.isEmpty());
1211
1212
1213
1214
1217
1218
1219
1220void QWidgetPrivate::invalidateBackingStore_resizeHelper(
const QPoint &oldPos,
const QSize &oldSize)
1223 Q_ASSERT(!q->isWindow());
1224 Q_ASSERT(q->parentWidget());
1226 const bool staticContents = q->testAttribute(Qt::WA_StaticContents);
1227 const bool sizeDecreased = (data.crect.width() < oldSize.width())
1228 || (data.crect.height() < oldSize.height());
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());
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();
1242 if (hasStaticChildren) {
1243 QRegion dirty(newWidgetRect);
1244 dirty -= staticChildren;
1245 invalidateBackingStore(dirty);
1248 invalidateBackingStore(newWidgetRect);
1251 if (!parentAreaExposed)
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;
1260 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1262 if (hasStaticChildren && !graphicsEffect) {
1263 QRegion parentExpose(QRect(oldPos, oldSize));
1264 parentExpose -= data.crect;
1265 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1267 q->parentWidget()->d_func()->invalidateBackingStore(effectiveRectFor(QRect(oldPos, oldSize)));
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());
1280 moveRect(QRect(oldPos, oldSize), offset.x(), offset.y());
1285 if (!sizeDecreased || !oldWidgetRect.contains(newWidgetRect)) {
1286 QRegion newVisible(newWidgetRect);
1287 newVisible -= oldWidgetRect;
1288 invalidateBackingStore(newVisible);
1291 if (!parentAreaExposed)
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);
1302 QRegion parentExpose(oldRect);
1303 parentExpose -= data.crect;
1304 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1310#include "qwidgetrepaintmanager.moc"
1311#include "moc_qwidgetrepaintmanager_p.cpp"
Combined button and popup list for selecting options.