Widgets: Use accelerated scroll when scrolled widget is overlapped
Get region of overlapped widgets and scroll only non-overlapped parts of image. Next, schedule an update for overlapped widgets region. This patch improves scrolling performance when scrolled widget has overlapped widgets. Common use cases: - faster scrolling when using "StyleHint::SH_ScrollBar_Transient", - faster scrolling of zoomed image with semi-transparent thumbnail. Accelerated scrolling with overlapped widgets is not available when scale factor is non-integer. Task-number: QTBUG-64504 Change-Id: I8337d3bc756e50f7d31cdc7979ccf86dc5c3695f Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
This commit is contained in:
parent
98ad498a46
commit
5b09346cf4
@ -1904,19 +1904,21 @@ void QWidgetPrivate::deleteTLSysExtra()
|
||||
}
|
||||
|
||||
/*
|
||||
Returns \c true if there are widgets above this which overlap with
|
||||
Returns \c region of widgets above this which overlap with
|
||||
\a rect, which is in parent's coordinate system (same as crect).
|
||||
*/
|
||||
|
||||
bool QWidgetPrivate::isOverlapped(const QRect &rect) const
|
||||
QRegion QWidgetPrivate::overlappedRegion(const QRect &rect, bool breakAfterFirst) const
|
||||
{
|
||||
Q_Q(const QWidget);
|
||||
|
||||
const QWidget *w = q;
|
||||
QRect r = rect;
|
||||
QPoint p;
|
||||
QRegion region;
|
||||
while (w) {
|
||||
if (w->isWindow())
|
||||
return false;
|
||||
break;
|
||||
QWidgetPrivate *pd = w->parentWidget()->d_func();
|
||||
bool above = false;
|
||||
for (int i = 0; i < pd->children.size(); ++i) {
|
||||
@ -1928,19 +1930,23 @@ bool QWidgetPrivate::isOverlapped(const QRect &rect) const
|
||||
continue;
|
||||
}
|
||||
|
||||
if (qRectIntersects(sibling->d_func()->effectiveRectFor(sibling->data->crect), r)) {
|
||||
const QRect siblingRect = sibling->d_func()->effectiveRectFor(sibling->data->crect);
|
||||
if (qRectIntersects(siblingRect, r)) {
|
||||
const QWExtra *siblingExtra = sibling->d_func()->extra;
|
||||
if (siblingExtra && siblingExtra->hasMask && !sibling->d_func()->graphicsEffect
|
||||
&& !siblingExtra->mask.translated(sibling->data->crect.topLeft()).intersects(r)) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
region += siblingRect.translated(-p);
|
||||
if (breakAfterFirst)
|
||||
break;
|
||||
}
|
||||
}
|
||||
w = w->parentWidget();
|
||||
r.translate(pd->data.crect.topLeft());
|
||||
p += pd->data.crect.topLeft();
|
||||
}
|
||||
return false;
|
||||
return region;
|
||||
}
|
||||
|
||||
void QWidgetPrivate::syncBackingStore()
|
||||
|
@ -453,7 +453,7 @@ public:
|
||||
// ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
|
||||
void invalidateBuffer(const QRegion &);
|
||||
void invalidateBuffer(const QRect &);
|
||||
bool isOverlapped(const QRect&) const;
|
||||
QRegion overlappedRegion(const QRect &rect, bool breakAfterFirst = false) const;
|
||||
void syncBackingStore();
|
||||
void syncBackingStore(const QRegion ®ion);
|
||||
|
||||
|
@ -59,6 +59,7 @@
|
||||
#include <private/qgraphicseffect_p.h>
|
||||
#endif
|
||||
#include <QtGui/private/qwindow_p.h>
|
||||
#include <QtGui/private/qhighdpiscaling_p.h>
|
||||
|
||||
#include <qpa/qplatformbackingstore.h>
|
||||
|
||||
@ -793,6 +794,24 @@ QWidgetBackingStore::~QWidgetBackingStore()
|
||||
delete dirtyOnScreenWidgets;
|
||||
}
|
||||
|
||||
static QVector<QRect> getSortedRectsToScroll(const QRegion ®ion, int dx, int dy)
|
||||
{
|
||||
QVector<QRect> rects = region.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
|
||||
//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)
|
||||
@ -820,12 +839,12 @@ void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy)
|
||||
const QRect parentRect(rect & clipR);
|
||||
const bool nativeWithTextureChild = textureChildSeen && q->internalWinId();
|
||||
|
||||
bool accelerateMove = accelEnv && isOpaque && !nativeWithTextureChild
|
||||
const bool accelerateMove = accelEnv && isOpaque && !nativeWithTextureChild
|
||||
#if QT_CONFIG(graphicsview)
|
||||
// No accelerate move for proxy widgets.
|
||||
&& !tlw->d_func()->extra->proxyWidget
|
||||
#endif
|
||||
&& !isOverlapped(sourceRect) && !isOverlapped(destRect);
|
||||
;
|
||||
|
||||
if (!accelerateMove) {
|
||||
QRegion parentR(effectiveRectFor(parentRect));
|
||||
@ -841,18 +860,39 @@ void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy)
|
||||
|
||||
QWidgetBackingStore *wbs = x->backingStoreTracker.data();
|
||||
QRegion childExpose(newRect & clipR);
|
||||
QRegion overlappedExpose;
|
||||
|
||||
if (sourceRect.isValid() && wbs->bltRect(sourceRect, dx, dy, pw))
|
||||
childExpose -= destRect;
|
||||
if (sourceRect.isValid()) {
|
||||
overlappedExpose = (overlappedRegion(sourceRect) | overlappedRegion(destRect)) & clipR;
|
||||
|
||||
const qreal factor = QHighDpiScaling::factor(q->windowHandle());
|
||||
if (overlappedExpose.isEmpty() || qFloor(factor) == factor) {
|
||||
const QVector<QRect> rectsToScroll
|
||||
= getSortedRectsToScroll(QRegion(sourceRect) - overlappedExpose, dx, dy);
|
||||
for (QRect rect : rectsToScroll) {
|
||||
if (wbs->bltRect(rect, dx, dy, pw)) {
|
||||
childExpose -= rect.translated(dx, dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
childExpose -= overlappedExpose;
|
||||
}
|
||||
|
||||
if (!pw->updatesEnabled())
|
||||
return;
|
||||
|
||||
const bool childUpdatesEnabled = q->updatesEnabled();
|
||||
if (childUpdatesEnabled && !childExpose.isEmpty()) {
|
||||
childExpose.translate(-data.crect.topLeft());
|
||||
wbs->markDirty(childExpose, q);
|
||||
isMoved = true;
|
||||
if (childUpdatesEnabled) {
|
||||
if (!overlappedExpose.isEmpty()) {
|
||||
overlappedExpose.translate(-data.crect.topLeft());
|
||||
invalidateBuffer(overlappedExpose);
|
||||
}
|
||||
if (!childExpose.isEmpty()) {
|
||||
childExpose.translate(-data.crect.topLeft());
|
||||
wbs->markDirty(childExpose, q);
|
||||
isMoved = true;
|
||||
}
|
||||
}
|
||||
|
||||
QRegion parentExpose(parentRect);
|
||||
@ -888,13 +928,12 @@ void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy)
|
||||
|
||||
static const bool accelEnv = qEnvironmentVariableIntValue("QT_NO_FAST_SCROLL") == 0;
|
||||
|
||||
QRect scrollRect = rect & clipRect();
|
||||
bool overlapped = false;
|
||||
bool accelerateScroll = accelEnv && isOpaque && !q_func()->testAttribute(Qt::WA_WState_InPaintEvent)
|
||||
&& !(overlapped = isOverlapped(scrollRect.translated(data.crect.topLeft())));
|
||||
const QRect clipR = clipRect();
|
||||
const QRect scrollRect = rect & clipR;
|
||||
const bool accelerateScroll = accelEnv && isOpaque && !q_func()->testAttribute(Qt::WA_WState_InPaintEvent);
|
||||
|
||||
if (!accelerateScroll) {
|
||||
if (overlapped) {
|
||||
if (!overlappedRegion(scrollRect.translated(data.crect.topLeft()), true).isEmpty()) {
|
||||
QRegion region(scrollRect);
|
||||
subtractOpaqueSiblings(region);
|
||||
invalidateBuffer(region);
|
||||
@ -906,12 +945,23 @@ void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy)
|
||||
const QRect destRect = scrollRect.translated(dx, dy) & scrollRect;
|
||||
const QRect sourceRect = destRect.translated(-dx, -dy);
|
||||
|
||||
const QRegion overlappedExpose = (overlappedRegion(scrollRect.translated(data.crect.topLeft())))
|
||||
.translated(-data.crect.topLeft()) & clipR;
|
||||
QRegion childExpose(scrollRect);
|
||||
if (sourceRect.isValid()) {
|
||||
if (wbs->bltRect(sourceRect, dx, dy, q))
|
||||
childExpose -= destRect;
|
||||
|
||||
const qreal factor = QHighDpiScaling::factor(q->windowHandle());
|
||||
if (overlappedExpose.isEmpty() || qFloor(factor) == factor) {
|
||||
const QVector<QRect> rectsToScroll
|
||||
= getSortedRectsToScroll(QRegion(sourceRect) - overlappedExpose, dx, dy);
|
||||
for (const QRect &rect : rectsToScroll) {
|
||||
if (wbs->bltRect(rect, dx, dy, q)) {
|
||||
childExpose -= rect.translated(dx, dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
childExpose -= overlappedExpose;
|
||||
|
||||
if (inDirtyList) {
|
||||
if (rect == q->rect()) {
|
||||
dirty.translate(dx, dy);
|
||||
@ -928,6 +978,8 @@ void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy)
|
||||
if (!q->updatesEnabled())
|
||||
return;
|
||||
|
||||
if (!overlappedExpose.isEmpty())
|
||||
invalidateBuffer(overlappedExpose);
|
||||
if (!childExpose.isEmpty()) {
|
||||
wbs->markDirty(childExpose, q);
|
||||
isScrolled = true;
|
||||
|
Loading…
Reference in New Issue
Block a user