diff --git a/ui/widgets/elastic_scroll.cpp b/ui/widgets/elastic_scroll.cpp index 532fabb..79fe18d 100644 --- a/ui/widgets/elastic_scroll.cpp +++ b/ui/widgets/elastic_scroll.cpp @@ -22,6 +22,7 @@ constexpr auto kOverscrollReturnDuration = crl::time(250); constexpr auto kOverscrollPower = 0.6; constexpr auto kOverscrollFromThreshold = -(1 << 30); constexpr auto kOverscrollTillThreshold = (1 << 30); +constexpr auto kTouchOverscrollMultiplier = 2; [[nodiscard]] int OverscrollFromAccumulated(int accumulated) { if (!accumulated) { @@ -56,6 +57,7 @@ ElasticScrollBar::ElasticScrollBar( , _hideTimer([=] { toggle(false); }) , _shown(!_st.hiding) , _vertical(orientation == Qt::Vertical) { + setAttribute(Qt::WA_NoMousePropagation); } void ElasticScrollBar::refreshGeometry() { @@ -329,6 +331,11 @@ void ElasticScrollBar::resizeEvent(QResizeEvent *e) { refreshGeometry(); } +bool ElasticScrollBar::eventHook(QEvent *e) { + setAttribute(Qt::WA_NoMousePropagation, e->type() != QEvent::Wheel); + return RpWidget::eventHook(e); +} + ElasticScroll::ElasticScroll( QWidget *parent, const style::ScrollArea &st, @@ -371,8 +378,17 @@ void ElasticScroll::setHandleTouch(bool handle) { } bool ElasticScroll::viewportEvent(QEvent *e) { - return (e->type() == QEvent::Wheel) - && handleWheelEvent(static_cast(e)); + const auto type = e->type(); + if (type == QEvent::Wheel) { + return handleWheelEvent(static_cast(e)); + } else if (type == QEvent::TouchBegin + || type == QEvent::TouchUpdate + || type == QEvent::TouchEnd + || type == QEvent::TouchCancel) { + handleTouchEvent(static_cast(e)); + return true; + } + return false; } void ElasticScroll::touchDeaccelerate(int32 elapsed) { @@ -496,14 +512,18 @@ void ElasticScroll::touchScrollTimer() { auto nowTime = crl::now(); if (_touchScrollState == TouchScrollState::Acceleration && _touchWaitingAcceleration && (nowTime - _touchAccelerationTime) > 40) { _touchScrollState = TouchScrollState::Manual; + sendWheelEvent(Qt::ScrollEnd); touchResetSpeed(); } else if (_touchScrollState == TouchScrollState::Auto || _touchScrollState == TouchScrollState::Acceleration) { int32 elapsed = int32(nowTime - _touchTime); QPoint delta = _touchSpeed * elapsed / 1000; - bool hasScrolled = touchScroll(delta); + sendWheelEvent( + _touchPress ? Qt::ScrollUpdate : Qt::ScrollMomentum, + delta); - if (_touchSpeed.isNull() || !hasScrolled) { + if (_touchSpeed.isNull()) { _touchScrollState = TouchScrollState::Manual; + sendWheelEvent(Qt::ScrollEnd); _touchScroll = false; _touchScrollTimer.cancel(); } else { @@ -602,7 +622,7 @@ void ElasticScroll::paintEvent(QPaintEvent *e) { } } -bool ElasticScroll::handleWheelEvent(not_null e) { +bool ElasticScroll::handleWheelEvent(not_null e, bool touch) { if (_customWheelProcess && _customWheelProcess(static_cast(e.get()))) { return true; @@ -652,6 +672,9 @@ bool ElasticScroll::handleWheelEvent(not_null e) { if (!delta) { return true; } + if (touch) { + delta *= kTouchOverscrollMultiplier; + } const auto accumulated = _overscrollAccumulated + delta; const auto type = (accumulated < 0) ? _overscrollTypeFrom @@ -726,7 +749,9 @@ void ElasticScroll::handleTouchEvent(QTouchEvent *e) { switch (e->type()) { case QEvent::TouchBegin: { - if (_touchPress || e->touchPoints().isEmpty()) return; + if (_touchPress || e->touchPoints().isEmpty()) { + return; + } _touchPress = true; if (_touchScrollState == TouchScrollState::Auto) { _touchScrollState = TouchScrollState::Acceleration; @@ -740,18 +765,23 @@ void ElasticScroll::handleTouchEvent(QTouchEvent *e) { } _touchStart = _touchPreviousPosition = _touchPosition; _touchRightButton = false; + sendWheelEvent(Qt::ScrollBegin); } break; case QEvent::TouchUpdate: { - if (!_touchPress) return; - if (!_touchScroll && (_touchPosition - _touchStart).manhattanLength() >= QApplication::startDragDistance()) { + if (!_touchPress) { + return; + } + if (!_touchScroll + && ((_touchPosition - _touchStart).manhattanLength() + >= QApplication::startDragDistance())) { _touchTimer.cancel(); _touchScroll = true; touchUpdateSpeed(); } if (_touchScroll) { if (_touchScrollState == TouchScrollState::Manual) { - touchScrollUpdated(_touchPosition); + touchScrollUpdated(); } else if (_touchScrollState == TouchScrollState::Acceleration) { touchUpdateSpeed(); _touchAccelerationTime = crl::now(); @@ -763,7 +793,9 @@ void ElasticScroll::handleTouchEvent(QTouchEvent *e) { } break; case QEvent::TouchEnd: { - if (!_touchPress) return; + if (!_touchPress) { + return; + } _touchPress = false; auto weak = MakeWeak(this); if (_touchScroll) { @@ -811,9 +843,12 @@ void ElasticScroll::handleTouchEvent(QTouchEvent *e) { } } -void ElasticScroll::touchScrollUpdated(const QPoint &screenPos) { - _touchPosition = screenPos; - touchScroll(_touchPosition - _touchPreviousPosition); +void ElasticScroll::touchScrollUpdated() { + //touchScroll(_touchPosition - _touchPreviousPosition); + const auto phase = !_touchPress + ? Qt::ScrollMomentum + : Qt::ScrollUpdate; + sendWheelEvent(phase, _touchPosition - _touchPreviousPosition); touchUpdateSpeed(); } @@ -958,15 +993,18 @@ void ElasticScroll::tryScrollTo(int position, bool synthMouseMove) { applyScrollTo(willScrollTo(position), synthMouseMove); } -bool ElasticScroll::touchScroll(const QPoint &delta) { - const auto scTop = scrollTop(); - const auto scMax = scrollTopMax(); - const auto scNew = std::clamp(scTop - delta.y(), 0, scMax); - if (scNew == scTop) { - return false; - } - scrollToY(scNew); - return true; +void ElasticScroll::sendWheelEvent(Qt::ScrollPhase phase, QPoint delta) { + auto e = QWheelEvent( + mapFromGlobal(_touchPosition), + _touchPosition, + delta, + delta, + Qt::NoButton, + QGuiApplication::keyboardModifiers(), + phase, + false, + Qt::MouseEventSynthesizedByApplication); + handleWheelEvent(&e, true); } void ElasticScroll::resizeEvent(QResizeEvent *e) { diff --git a/ui/widgets/elastic_scroll.h b/ui/widgets/elastic_scroll.h index 4a3434b..50d6111 100644 --- a/ui/widgets/elastic_scroll.h +++ b/ui/widgets/elastic_scroll.h @@ -55,6 +55,7 @@ private: void enterEventHook(QEnterEvent *e) override; void leaveEventHook(QEvent *e) override; void resizeEvent(QResizeEvent *e) override; + bool eventHook(QEvent *e) override; [[nodiscard]] int scaleToBar(int change) const; [[nodiscard]] bool barHighlighted() const; @@ -187,7 +188,7 @@ private: void enterEventHook(QEnterEvent *e) override; void leaveEventHook(QEvent *e) override; void keyPressEvent(QKeyEvent *e) override; - bool handleWheelEvent(not_null e); + bool handleWheelEvent(not_null e, bool touch = false); void handleTouchEvent(QTouchEvent *e); void updateState(); @@ -202,8 +203,8 @@ private: bool filterOutTouchEvent(QEvent *e); void touchScrollTimer(); - bool touchScroll(const QPoint &delta); - void touchScrollUpdated(const QPoint &screenPos); + void touchScrollUpdated(); + void sendWheelEvent(Qt::ScrollPhase phase, QPoint delta = {}); void touchResetSpeed(); void touchUpdateSpeed();