summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBłażej Szczygieł <spaz16@wp.pl>2017-11-10 23:52:17 +0100
committerBłażej Szczygieł <spaz16@wp.pl>2018-04-17 06:32:52 +0000
commit5b09346cf4322704a866f253b911d467c40df3ba (patch)
tree0b61b5548ac8dae08609bd349d241b2bc1f16f35
parent98ad498a46471d43a745cc61fd53e08e8a6b56dc (diff)
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>
-rw-r--r--src/widgets/kernel/qwidget.cpp18
-rw-r--r--src/widgets/kernel/qwidget_p.h2
-rw-r--r--src/widgets/kernel/qwidgetbackingstore.cpp84
3 files changed, 81 insertions, 23 deletions
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
index 8e3263ad92..6b3fad6b5f 100644
--- a/src/widgets/kernel/qwidget.cpp
+++ b/src/widgets/kernel/qwidget.cpp
@@ -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()
diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h
index c39333161b..fae9e5ff4e 100644
--- a/src/widgets/kernel/qwidget_p.h
+++ b/src/widgets/kernel/qwidget_p.h
@@ -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 &region);
diff --git a/src/widgets/kernel/qwidgetbackingstore.cpp b/src/widgets/kernel/qwidgetbackingstore.cpp
index 3b093283cd..768406f361 100644
--- a/src/widgets/kernel/qwidgetbackingstore.cpp
+++ b/src/widgets/kernel/qwidgetbackingstore.cpp
@@ -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 &region, 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;