From f7a4a79c62daeba9ab5545c5e0a1f36d5e1fce6b Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Sun, 10 Oct 2021 15:33:04 +0200 Subject: [PATCH] Take overshoot into account when laying out QAbstractScrollArea QAbstractScrollAreaPrivate::layoutChildren() positions the viewport, but did not take the overshoot from scrolling with a scroller into account. If the scroll area was resized during a scroll, then this resulted in the roll back overcompensating for the overshoot, placing the viewport outside the visible area. Fix this by taking the overshoot into account when positioning the viewport. Add a test case. We have to use QWindow-based mouse event simulation, as the QWidget based move events use QCursor::setPos, which doesn't reliably go through the gesture framework. Done-with: Volker Hilsheimer Done-with: Zhang Hao Fixes: QTBUG-94769 Pick-to: 5.15 6.2 Change-Id: Idf650c91e5a9cffa996e23e743939243b1d4fcc0 Reviewed-by: Volker Hilsheimer --- src/widgets/widgets/qabstractscrollarea.cpp | 4 +- .../tst_qabstractscrollarea.cpp | 39 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/widgets/widgets/qabstractscrollarea.cpp b/src/widgets/widgets/qabstractscrollarea.cpp index 23496a2e51..4ad8a3b411 100644 --- a/src/widgets/widgets/qabstractscrollarea.cpp +++ b/src/widgets/widgets/qabstractscrollarea.cpp @@ -442,8 +442,10 @@ void QAbstractScrollAreaPrivate::layoutChildren_helper(bool *needHorizontalScrol viewportRect.adjust(right, top, -left, -bottom); else viewportRect.adjust(left, top, -right, -bottom); + viewportRect = QStyle::visualRect(opt.direction, opt.rect, viewportRect); + viewportRect.translate(-overshoot); + viewport->setGeometry(viewportRect); // resize the viewport last - viewport->setGeometry(QStyle::visualRect(opt.direction, opt.rect, viewportRect)); // resize the viewport last *needHorizontalScrollbar = needh; *needVerticalScrollbar = needv; } diff --git a/tests/auto/widgets/widgets/qabstractscrollarea/tst_qabstractscrollarea.cpp b/tests/auto/widgets/widgets/qabstractscrollarea/tst_qabstractscrollarea.cpp index de57769793..9d7cc484cd 100644 --- a/tests/auto/widgets/widgets/qabstractscrollarea/tst_qabstractscrollarea.cpp +++ b/tests/auto/widgets/widgets/qabstractscrollarea/tst_qabstractscrollarea.cpp @@ -37,6 +37,7 @@ #include #include #include +#include class tst_QAbstractScrollArea : public QObject { @@ -57,6 +58,7 @@ private slots: void task214488_layoutDirection(); void margins(); + void resizeWithOvershoot(); }; tst_QAbstractScrollArea::tst_QAbstractScrollArea() @@ -394,5 +396,42 @@ void tst_QAbstractScrollArea::margins() QCOMPARE(area.viewportMargins(), margins); } +void tst_QAbstractScrollArea::resizeWithOvershoot() +{ + QWidget window; + + QScrollArea scrollArea(&window); + scrollArea.setWidget([]{ + QWidget *widget = new QWidget; + widget->setFixedSize(QSize(0, 200)); + return widget; + }()); + scrollArea.setGeometry(0, 20, 100, 100); + + QScroller::grabGesture(&scrollArea, QScroller::LeftMouseButtonGesture); + + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + const QPoint originAtRest = scrollArea.viewport()->pos(); + + QPoint center = scrollArea.viewport()->mapToGlobal(scrollArea.viewport()->rect().center()); + center = window.windowHandle()->mapFromGlobal(center); + QTest::mousePress(window.windowHandle(), Qt::LeftButton, {}, center); + QTest::mouseMove(window.windowHandle(), center + QPoint(0, 50)); + QTRY_COMPARE(scrollArea.viewport()->pos(), originAtRest + QPoint(0, 25)); + QPoint overshootPosition = scrollArea.viewport()->pos(); + + // trigger a layout of the scroll area while there's overshoot + scrollArea.setGeometry(0, 0, 100, 120); + QCOMPARE(scrollArea.viewport()->pos(), overshootPosition); + QTest::mouseRelease(window.windowHandle(), Qt::LeftButton, {}, center + QPoint(0, 50)); + QTRY_COMPARE(scrollArea.viewport()->pos(), originAtRest); + // Process a few more events and verify that the scroll area + // doesn't overcompensate for the overshoot. + QApplication::processEvents(); + QTRY_COMPARE(scrollArea.viewport()->pos(), originAtRest); +} + QTEST_MAIN(tst_QAbstractScrollArea) #include "tst_qabstractscrollarea.moc"