Revert optimizations and fixes for moving/scrolling overlapped widgets

This reverts the QtWidgets changes made in commits
22634e00794e72d68e7578e1962f9f2023870749 and
5b09346cf4322704a866f253b911d467c40df3ba while keeping the auto tests
introduced in the former commit.

Both commits introduced rendering errors when moving widgets out of
or into areas in which they are obscured. Before we apply any further
optimizations to this code we need thorough auto test coverage.

Task-number: QTBUG-98151
Task-number: QTBUG-26269
Pick-to: 6.2
Change-Id: I9cb82b73776daed59ea0e9f51ff7ddef1c7265b6
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
Volker Hilsheimer 2021-12-05 15:13:24 +01:00
parent ab1180a2b0
commit c001216eed
2 changed files with 28 additions and 80 deletions

View File

@ -390,6 +390,7 @@ public:
void invalidateBackingStore(const T &); void invalidateBackingStore(const T &);
QRegion overlappedRegion(const QRect &rect, bool breakAfterFirst = false) const; QRegion overlappedRegion(const QRect &rect, bool breakAfterFirst = false) const;
bool isOverlapped(const QRect &rect) const { return !overlappedRegion(rect, true).isEmpty(); }
void syncBackingStore(); void syncBackingStore();
void syncBackingStore(const QRegion &region); void syncBackingStore(const QRegion &region);

View File

@ -414,25 +414,6 @@ static bool hasPlatformWindow(QWidget *widget)
return widget && widget->windowHandle() && widget->windowHandle()->handle(); return widget && widget->windowHandle() && widget->windowHandle()->handle();
} }
static QList<QRect> getSortedRectsToScroll(const QRegion &region, int dx, int dy)
{
QList<QRect> rects;
std::copy(region.begin(), region.end(), std::back_inserter(rects));
if (rects.count() > 1) {
std::sort(rects.begin(), rects.end(), [=](const QRect &r1, const QRect &r2) {
if (r1.y() == r2.y()) {
if (dx > 0)
return r1.x() > r2.x();
return r1.x() < r2.x();
}
if (dy > 0)
return r1.y() > r2.y();
return r1.y() < r2.y();
});
}
return rects;
}
//parent's coordinates; move whole rect; update parent and widget //parent's coordinates; move whole rect; update parent and widget
//assume the screen blt has already been done, so we don't need to refresh that part //assume the screen blt has already been done, so we don't need to refresh that part
void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy) void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy)
@ -449,17 +430,21 @@ void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy)
QWidget *pw = q->parentWidget(); QWidget *pw = q->parentWidget();
QPoint toplevelOffset = pw->mapTo(tlw, QPoint()); QPoint toplevelOffset = pw->mapTo(tlw, QPoint());
QWidgetPrivate *pd = pw->d_func(); QWidgetPrivate *pd = pw->d_func();
const QRect clipR(pd->clipRect()); QRect clipR(pd->clipRect());
const QRect newRect(rect.translated(dx, dy)); const QRect newRect(rect.translated(dx, dy));
QRect destRect = rect.intersected(clipR);
if (destRect.isValid())
destRect = destRect.translated(dx, dy).intersected(clipR);
const QRect sourceRect(destRect.translated(-dx, -dy));
const QRect parentRect(rect & clipR); const QRect parentRect(rect & clipR);
const bool nativeWithTextureChild = textureChildSeen && hasPlatformWindow(q); const bool nativeWithTextureChild = textureChildSeen && hasPlatformWindow(q);
const bool accelerateMove = accelEnv && isOpaque && !nativeWithTextureChild bool accelerateMove = accelEnv && isOpaque && !nativeWithTextureChild
#if QT_CONFIG(graphicsview) #if QT_CONFIG(graphicsview)
// No accelerate move for proxy widgets. // No accelerate move for proxy widgets.
&& !tlw->d_func()->extra->proxyWidget && !tlw->d_func()->extra->proxyWidget
#endif #endif
; && !isOverlapped(sourceRect) && !isOverlapped(destRect);
if (!accelerateMove) { if (!accelerateMove) {
QRegion parentR(effectiveRectFor(parentRect)); QRegion parentR(effectiveRectFor(parentRect));
@ -472,50 +457,22 @@ void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy)
pd->invalidateBackingStore(parentR); pd->invalidateBackingStore(parentR);
invalidateBackingStore((newRect & clipR).translated(-data.crect.topLeft())); invalidateBackingStore((newRect & clipR).translated(-data.crect.topLeft()));
} else { } else {
QRect destRect = rect.intersected(clipR);
if (destRect.isValid())
destRect = destRect.translated(dx, dy).intersected(clipR);
const QRect sourceRect(destRect.translated(-dx, -dy));
QWidgetRepaintManager *repaintManager = x->repaintManager.get(); QWidgetRepaintManager *repaintManager = x->repaintManager.get();
QRegion childExpose = QRegion(newRect) & clipR; QRegion childExpose(newRect & clipR);
QRegion overlappedExpose;
if (sourceRect.isValid()) { if (sourceRect.isValid() && repaintManager->bltRect(sourceRect, dx, dy, pw))
overlappedExpose = (overlappedRegion(sourceRect) | overlappedRegion(destRect)) & clipR; childExpose -= destRect;
const qreal factor = QHighDpiScaling::factor(q->windowHandle());
if (overlappedExpose.isEmpty() || qFloor(factor) == factor) {
const QList<QRect> rectsToScroll =
getSortedRectsToScroll(QRegion(sourceRect) - overlappedExpose, dx, dy);
for (QRect r : rectsToScroll) {
if (repaintManager->bltRect(r, dx, dy, pw)) {
childExpose -= r.translated(dx, dy);
}
}
isMoved = true;
}
childExpose -= overlappedExpose;
}
if (!pw->updatesEnabled()) if (!pw->updatesEnabled())
return; return;
const bool childUpdatesEnabled = q->updatesEnabled(); const bool childUpdatesEnabled = q->updatesEnabled();
if (childUpdatesEnabled) {
// As per paintAndFlush, reset isMoved if we have overlapping if (childUpdatesEnabled && !childExpose.isEmpty()) {
// or child regions that need to be painted. childExpose.translate(-data.crect.topLeft());
if (!overlappedExpose.isEmpty()) { repaintManager->markDirty(childExpose, q);
overlappedExpose.translate(-data.crect.topLeft()); isMoved = true;
invalidateBackingStore(overlappedExpose);
isMoved = false;
}
if (!childExpose.isEmpty()) {
childExpose.translate(-data.crect.topLeft());
repaintManager->markDirty(childExpose, q);
isMoved = false;
}
} }
QRegion parentExpose(parentRect); QRegion parentExpose(parentRect);
@ -523,10 +480,12 @@ void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy)
if (extra && extra->hasMask) if (extra && extra->hasMask)
parentExpose += QRegion(newRect) - extra->mask.translated(data.crect.topLeft()); parentExpose += QRegion(newRect) - extra->mask.translated(data.crect.topLeft());
if (!parentExpose.isEmpty()) if (!parentExpose.isEmpty()) {
repaintManager->markDirty(parentExpose, pw); repaintManager->markDirty(parentExpose, pw);
pd->isMoved = true;
}
if (childUpdatesEnabled && sourceRect.isValid()) { if (childUpdatesEnabled) {
QRegion needsFlush(sourceRect); QRegion needsFlush(sourceRect);
needsFlush += destRect; needsFlush += destRect;
repaintManager->markNeedsFlush(pw, needsFlush, toplevelOffset); repaintManager->markNeedsFlush(pw, needsFlush, toplevelOffset);
@ -547,12 +506,13 @@ void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy)
static const bool accelEnv = qEnvironmentVariableIntValue("QT_NO_FAST_SCROLL") == 0; static const bool accelEnv = qEnvironmentVariableIntValue("QT_NO_FAST_SCROLL") == 0;
const QRect clipR = clipRect(); QRect scrollRect = rect & clipRect();
const QRect scrollRect = rect & clipR; bool overlapped = false;
const bool accelerateScroll = accelEnv && isOpaque && !q_func()->testAttribute(Qt::WA_WState_InPaintEvent); bool accelerateScroll = accelEnv && isOpaque && !q_func()->testAttribute(Qt::WA_WState_InPaintEvent)
&& !(overlapped = isOverlapped(scrollRect.translated(data.crect.topLeft())));
if (!accelerateScroll) { if (!accelerateScroll) {
if (!overlappedRegion(scrollRect.translated(data.crect.topLeft()), true).isEmpty()) { if (overlapped) {
QRegion region(scrollRect); QRegion region(scrollRect);
subtractOpaqueSiblings(region); subtractOpaqueSiblings(region);
invalidateBackingStore(region); invalidateBackingStore(region);
@ -564,23 +524,12 @@ void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy)
const QRect destRect = scrollRect.translated(dx, dy) & scrollRect; const QRect destRect = scrollRect.translated(dx, dy) & scrollRect;
const QRect sourceRect = destRect.translated(-dx, -dy); const QRect sourceRect = destRect.translated(-dx, -dy);
const QRegion overlappedExpose = (overlappedRegion(scrollRect.translated(data.crect.topLeft())))
.translated(-data.crect.topLeft()) & clipR;
QRegion childExpose(scrollRect); QRegion childExpose(scrollRect);
if (sourceRect.isValid()) {
const qreal factor = QHighDpiScaling::factor(q->windowHandle()); if (repaintManager->bltRect(sourceRect, dx, dy, q))
if (overlappedExpose.isEmpty() || qFloor(factor) == factor) { childExpose -= destRect;
const QList<QRect> rectsToScroll =
getSortedRectsToScroll(QRegion(sourceRect) - overlappedExpose, dx, dy);
for (const QRect &r : rectsToScroll) {
if (repaintManager->bltRect(r, dx, dy, q)) {
childExpose -= r.translated(dx, dy);
}
}
} }
childExpose -= overlappedExpose;
if (inDirtyList) { if (inDirtyList) {
if (rect == q->rect()) { if (rect == q->rect()) {
dirty.translate(dx, dy); dirty.translate(dx, dy);
@ -597,8 +546,6 @@ void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy)
if (!q->updatesEnabled()) if (!q->updatesEnabled())
return; return;
if (!overlappedExpose.isEmpty())
invalidateBackingStore(overlappedExpose);
if (!childExpose.isEmpty()) { if (!childExpose.isEmpty()) {
repaintManager->markDirty(childExpose, q); repaintManager->markDirty(childExpose, q);
isScrolled = true; isScrolled = true;