From 3adcd1d51c1e4724ae4b6205c55369c4737ea8d5 Mon Sep 17 00:00:00 2001 From: Devin Lin Date: Mon, 15 Jul 2024 22:32:01 -0400 Subject: [PATCH] swipearea: Add support for touchpad This adds support for touchpad scrolling in various shell components, such as the lockscreen, homescreen and action drawer. Currently TODO because it appears to be very buggy when there is a control underneath that also accepts touchpad input (ex. flickable). The touchpad scroll start appears to get called by Qt, but not the end event, so I am unable to "let go" of the flick. Not sure if it's a wayland issue. This also appears to not work in the nested KWin session, not sure if it's because of libinput or something --- .../mobileshell/components/swipearea.cpp | 53 +++++++++++++++++++ components/mobileshell/components/swipearea.h | 9 ++++ .../qml/actiondrawer/ActionDrawer.qml | 16 ++++-- .../actiondrawer/ActionDrawerOpenSurface.qml | 26 +++++---- .../folio/package/contents/ui/HomeScreen.qml | 4 ++ shell/contents/lockscreen/FlickContainer.qml | 18 +++++-- 6 files changed, 110 insertions(+), 16 deletions(-) diff --git a/components/mobileshell/components/swipearea.cpp b/components/mobileshell/components/swipearea.cpp index b9f96aa0..0c984baa 100644 --- a/components/mobileshell/components/swipearea.cpp +++ b/components/mobileshell/components/swipearea.cpp @@ -216,6 +216,52 @@ void SwipeArea::touchUngrabEvent() QQuickItem::touchUngrabEvent(); } +void SwipeArea::wheelEvent(QWheelEvent *event) +{ + if (!m_interactive) { + QQuickItem::wheelEvent(event); + return; + } + + event->setAccepted(false); + + switch (event->phase()) { + case Qt::ScrollBegin: + if (!m_touchpadScrolling) { + event->accept(); + + m_touchpadScrolling = true; + m_totalScrollDelta = QPointF{0, 0}; + Q_EMIT touchpadScrollStarted(event->points().first().position()); + } + break; + case Qt::ScrollEnd: + if (m_touchpadScrolling) { + m_touchpadScrolling = false; + m_totalScrollDelta = QPointF{0, 0}; + Q_EMIT touchpadScrollEnded(); + } + break; + default: + break; + } + + // HACK: if it isn't the touchpad, we never get the isBeginEvent() and isEndEvent() events + if (!m_touchpadScrolling) { + return; + } + + for (auto &point : event->points()) { + event->addPassiveGrabber(point, this); + } + + auto pixelDelta = event->pixelDelta(); + m_totalScrollDelta = QPointF{m_totalScrollDelta + pixelDelta}; + Q_EMIT touchpadScrollMove(m_totalScrollDelta.x(), m_totalScrollDelta.y(), pixelDelta.x(), pixelDelta.y()); + + event->accept(); +} + void SwipeArea::setMoving(bool moving) { m_moving = moving; @@ -242,6 +288,8 @@ void SwipeArea::resetSwipe() void SwipeArea::handlePressEvent(QPointerEvent *event, QPointF point) { + Q_UNUSED(event); + // ignore more touch events if (m_pressed) { return; @@ -255,6 +303,9 @@ void SwipeArea::handlePressEvent(QPointerEvent *event, QPointF point) void SwipeArea::handleReleaseEvent(QPointerEvent *event, QPointF point) { + Q_UNUSED(event); + Q_UNUSED(point); + // if we are in a swipe if (m_moving) { Q_EMIT swipeEnded(); @@ -265,6 +316,8 @@ void SwipeArea::handleReleaseEvent(QPointerEvent *event, QPointF point) void SwipeArea::handleMoveEvent(QPointerEvent *event, QPointF point) { + Q_UNUSED(event); + if (!m_stealMouse) { if (!m_skipSwipeThreshold) { // if we haven't reached the swipe registering threshold yet, don't start the swipe diff --git a/components/mobileshell/components/swipearea.h b/components/mobileshell/components/swipearea.h index 7b26c1c4..3de23451 100644 --- a/components/mobileshell/components/swipearea.h +++ b/components/mobileshell/components/swipearea.h @@ -58,6 +58,10 @@ Q_SIGNALS: // totalDeltaX, totalDeltaY - amount move since startedSwipe() void swipeMove(qreal totalDeltaX, qreal totalDeltaY, qreal deltaX, qreal deltaY); + void touchpadScrollStarted(QPointF point); + void touchpadScrollEnded(); + void touchpadScrollMove(qreal totalDeltaX, qreal totalDeltaY, qreal deltaX, qreal deltaY); + protected: bool childMouseEventFilter(QQuickItem *item, QEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; @@ -66,6 +70,7 @@ protected: void mouseUngrabEvent() override; void touchEvent(QTouchEvent *event) override; void touchUngrabEvent() override; + void wheelEvent(QWheelEvent *event) override; private: void setMoving(bool moving); @@ -82,6 +87,7 @@ private: Mode m_mode = Mode::BothAxis; bool m_interactive = true; bool m_pressed = false; + bool m_touchpadScrolling = false; // whether we have started a flick bool m_moving = false; @@ -100,6 +106,9 @@ private: // whether to skip trying to measure the swipe threshold bool m_skipSwipeThreshold; + + // the total amount of distance scrolled + QPointF m_totalScrollDelta; }; QML_DECLARE_TYPE(SwipeArea) diff --git a/components/mobileshell/qml/actiondrawer/ActionDrawer.qml b/components/mobileshell/qml/actiondrawer/ActionDrawer.qml index f4035530..1078b58e 100644 --- a/components/mobileshell/qml/actiondrawer/ActionDrawer.qml +++ b/components/mobileshell/qml/actiondrawer/ActionDrawer.qml @@ -267,18 +267,28 @@ Item { mode: MobileShell.SwipeArea.VerticalOnly anchors.fill: parent - onSwipeStarted: { + function startSwipe() { root.cancelAnimations(); root.dragging = true; } - onSwipeEnded: { + + function endSwipe() { root.dragging = false; root.updateState(); } - onSwipeMove: (totalDeltaX, totalDeltaY, deltaX, deltaY) => { + + function moveSwipe(totalDeltaX, totalDeltaY, deltaX, deltaY) { root.offset += deltaY; } + onSwipeStarted: startSwipe() + onSwipeEnded: endSwipe() + onSwipeMove: (totalDeltaX, totalDeltaY, deltaX, deltaY) => moveSwipe(totalDeltaX, totalDeltaY, deltaX, deltaY) + + onTouchpadScrollStarted: startSwipe() + onTouchpadScrollEnded: endSwipe() + onTouchpadScrollMove: (totalDeltaX, totalDeltaY, deltaX, deltaY) => moveSwipe(totalDeltaX, totalDeltaY, deltaX, deltaY) + Loader { id: contentContainerLoader anchors.fill: parent diff --git a/components/mobileshell/qml/actiondrawer/ActionDrawerOpenSurface.qml b/components/mobileshell/qml/actiondrawer/ActionDrawerOpenSurface.qml index 57b35198..167ba8f1 100644 --- a/components/mobileshell/qml/actiondrawer/ActionDrawerOpenSurface.qml +++ b/components/mobileshell/qml/actiondrawer/ActionDrawerOpenSurface.qml @@ -33,6 +33,17 @@ MobileShell.SwipeArea { actionDrawer.visible = true; } + function startSwipeWithPoint(point) { + // if the user swiped from the top left, otherwise it's from the top right + if (point.x < root.width / 2) { + actionDrawer.openToPinnedMode = ShellSettings.Settings.actionDrawerTopLeftMode == ShellSettings.Settings.Pinned; + } else { + actionDrawer.openToPinnedMode = ShellSettings.Settings.actionDrawerTopRightMode == ShellSettings.Settings.Pinned; + } + + startSwipe(); + } + function endSwipe() { actionDrawer.dragging = false; actionDrawer.updateState(); @@ -44,16 +55,11 @@ MobileShell.SwipeArea { anchors.fill: parent - onSwipeStarted: (point) => { - // if the user swiped from the top left, otherwise it's from the top right - if (point.x < root.width / 2) { - actionDrawer.openToPinnedMode = ShellSettings.Settings.actionDrawerTopLeftMode == ShellSettings.Settings.Pinned; - } else { - actionDrawer.openToPinnedMode = ShellSettings.Settings.actionDrawerTopRightMode == ShellSettings.Settings.Pinned; - } - - startSwipe(); - } + onSwipeStarted: (point) => startSwipeWithPoint(point) onSwipeEnded: endSwipe() onSwipeMove: (totalDeltaX, totalDeltaY, deltaX, deltaY) => updateOffset(deltaY); + + onTouchpadScrollStarted: (point) => startSwipeWithPoint(point) + onTouchpadScrollEnded: endSwipe() + onTouchpadScrollMove: (totalDeltaX, totalDeltaY, deltaX, deltaY) => updateOffset(deltaY); } diff --git a/containments/homescreens/folio/package/contents/ui/HomeScreen.qml b/containments/homescreens/folio/package/contents/ui/HomeScreen.qml index 69f4b464..41e57190 100644 --- a/containments/homescreens/folio/package/contents/ui/HomeScreen.qml +++ b/containments/homescreens/folio/package/contents/ui/HomeScreen.qml @@ -124,6 +124,10 @@ Item { homeScreenState.swipeMoved(totalDeltaX, totalDeltaY, deltaX, deltaY); } + onTouchpadScrollStarted: homeScreenState.swipeStarted(0, 0); + onTouchpadScrollEnded: homeScreenState.swipeEnded(); + onTouchpadScrollMove: (totalDeltaX, totalDeltaY, deltaX, deltaY) => homeScreenState.swipeMoved(totalDeltaX, totalDeltaY, deltaX, deltaY); + onPressedChanged: { if (pressed) { // ensures that components like the widget settings overlay close when swiping diff --git a/shell/contents/lockscreen/FlickContainer.qml b/shell/contents/lockscreen/FlickContainer.qml index f5c0c1e3..e84573cd 100644 --- a/shell/contents/lockscreen/FlickContainer.qml +++ b/shell/contents/lockscreen/FlickContainer.qml @@ -71,16 +71,28 @@ MobileShell.SwipeArea { } } - onSwipeStarted: cancelAnimations(); - onSwipeEnded: { + function __startSwipe() { + cancelAnimations(); + } + + function __endSwipe() { if (!positionAnim.running) { updateState(); } } - onSwipeMove: (totalDeltaX, totalDeltaY, deltaX, deltaY) => { + function __moveSwipe(totalDeltaX, totalDeltaY, deltaX, deltaY) { position = Math.max(0, Math.min(keypadHeight, position - deltaY)); } + + onSwipeStarted: __startSwipe() + onSwipeEnded: __endSwipe() + + onSwipeMove: __moveSwipe(totalDeltaX, totalDeltaY, deltaX, deltaY) + + onTouchpadScrollStarted: __startSwipe() + onTouchpadScrollEnded: __endSwipe() + onTouchpadScrollMove: __moveSwipe(totalDeltaX, totalDeltaY, deltaX, deltaY) }