QTabBar: Improve scrolling with high resolution mouse wheels

The current behavior for handling the angle delta of a wheel event
changes index the instant there is a change in angle delta. This works
fine for mouse wheels that send events with 120 angle delta units and
there is also already behavior defined for devices with pixel deltas,
but there is nothing good for handling events from high resolution mouse
wheels that don't have pixel deltas.

This patch makes it so that the current index doesn't change until the
accumulated angle delta for the X or Y axis reaches 120.

[ChangeLog][QtWidgets][QTabBar] Scrolling with a high resolution mouse
wheel changes the current index at a rate more like a normal mouse
wheel.

Task-number: QTBUG-97844
Pick-to: 6.3
Change-Id: I2e7fd88984a253f6ef8a0008deb7233e4cb4d84a
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
This commit is contained in:
Noah Davis 2022-01-15 14:51:34 -05:00 committed by Shawn Rutledge
parent 9ac323f7cd
commit f0371487ce
3 changed files with 62 additions and 2 deletions

View File

@ -1718,6 +1718,9 @@ bool QTabBar::event(QEvent *event)
update(d->hoverRect); update(d->hoverRect);
d->hoverIndex = -1; d->hoverIndex = -1;
d->hoverRect = QRect(); d->hoverRect = QRect();
#if QT_CONFIG(wheelevent)
d->accumulatedAngleDelta = QPoint();
#endif
return true; return true;
} }
#if QT_CONFIG(tooltip) #if QT_CONFIG(tooltip)
@ -2421,8 +2424,17 @@ void QTabBar::wheelEvent(QWheelEvent *event)
} }
} }
} else { } else {
const int delta = wheelVertical ? event->angleDelta().y() : event->angleDelta().x(); d->accumulatedAngleDelta += event->angleDelta();
const int offset = delta > 0 ? -1 : 1; const int xSteps = d->accumulatedAngleDelta.x() / QWheelEvent::DefaultDeltasPerStep;
const int ySteps = d->accumulatedAngleDelta.y() / QWheelEvent::DefaultDeltasPerStep;
int offset = 0;
if (xSteps > 0 || ySteps > 0) {
offset = -1;
d->accumulatedAngleDelta = QPoint();
} else if (xSteps < 0 || ySteps < 0) {
offset = 1;
d->accumulatedAngleDelta = QPoint();
}
const int oldCurrentIndex = d->currentIndex; const int oldCurrentIndex = d->currentIndex;
d->setCurrentNextEnabledIndex(offset); d->setCurrentNextEnabledIndex(offset);
if (oldCurrentIndex != d->currentIndex) { if (oldCurrentIndex != d->currentIndex) {

View File

@ -102,6 +102,9 @@ public:
QRect hoverRect; QRect hoverRect;
QPoint dragStartPosition; QPoint dragStartPosition;
QPoint mousePosition = {-1, -1}; QPoint mousePosition = {-1, -1};
#if QT_CONFIG(wheelevent)
QPoint accumulatedAngleDelta;
#endif
QSize iconSize; QSize iconSize;
QToolButton* rightB = nullptr; // right or bottom QToolButton* rightB = nullptr; // right or bottom
QToolButton* leftB = nullptr; // left or top QToolButton* leftB = nullptr; // left or top

View File

@ -103,6 +103,8 @@ private slots:
void mouseWheel(); void mouseWheel();
void kineticWheel_data(); void kineticWheel_data();
void kineticWheel(); void kineticWheel();
void highResolutionWheel_data();
void highResolutionWheel();
void scrollButtons_data(); void scrollButtons_data();
void scrollButtons(); void scrollButtons();
@ -1106,6 +1108,49 @@ void tst_QTabBar::kineticWheel()
} }
} }
void tst_QTabBar::highResolutionWheel_data()
{
QTest::addColumn<int>("angleDelta");
// Smallest angleDelta for a Logitech MX Master 3 with Linux/X11/Libinput
QTest::addRow("increment index") << -16;
QTest::addRow("decrement index") << 16;
}
void tst_QTabBar::highResolutionWheel()
{
TabBar tabbar;
TabBarScrollingProxyStyle proxyStyle;
tabbar.setStyle(&proxyStyle);
tabbar.addTab("tab1");
tabbar.addTab("tab2");
QFETCH(int, angleDelta);
// Negative values increment, positive values decrement
int startIndex = angleDelta < 0 ? 0 : 1;
tabbar.setCurrentIndex(startIndex);
const auto systemId = QPointingDevice::primaryPointingDevice()->systemId() + 1;
QPointingDevice hiResWheel(
"test high resolution wheel", systemId, QInputDevice::DeviceType::Mouse,
QPointingDevice::PointerType::Generic,
QInputDevice::Capability::Position | QInputDevice::Capability::Scroll, 1, 3);
const QPoint wheelPoint = tabbar.rect().bottomRight();
QWheelEvent event(wheelPoint, tabbar.mapToGlobal(wheelPoint), QPoint(),
QPoint(angleDelta, angleDelta), Qt::NoButton, Qt::NoModifier,
Qt::NoScrollPhase, false, Qt::MouseEventSynthesizedByApplication,
&hiResWheel);
proxyStyle.scrolling = true;
for (int accumulated = 0; accumulated < QWheelEvent::DefaultDeltasPerStep;
accumulated += qAbs(angleDelta)) {
// verify that nothing has changed until the threshold has been reached
QVERIFY(tabbar.currentIndex() == startIndex);
QVERIFY(QApplication::sendEvent(&tabbar, &event));
}
QVERIFY(tabbar.currentIndex() != startIndex);
}
#endif // QT_CONFIG(wheelevent) #endif // QT_CONFIG(wheelevent)
void tst_QTabBar::scrollButtons_data() void tst_QTabBar::scrollButtons_data()