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
This commit is contained in:
Devin Lin 2024-07-15 22:32:01 -04:00
parent cf915cae4a
commit 3adcd1d51c
6 changed files with 110 additions and 16 deletions

View file

@ -216,6 +216,52 @@ void SwipeArea::touchUngrabEvent()
QQuickItem::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) void SwipeArea::setMoving(bool moving)
{ {
m_moving = moving; m_moving = moving;
@ -242,6 +288,8 @@ void SwipeArea::resetSwipe()
void SwipeArea::handlePressEvent(QPointerEvent *event, QPointF point) void SwipeArea::handlePressEvent(QPointerEvent *event, QPointF point)
{ {
Q_UNUSED(event);
// ignore more touch events // ignore more touch events
if (m_pressed) { if (m_pressed) {
return; return;
@ -255,6 +303,9 @@ void SwipeArea::handlePressEvent(QPointerEvent *event, QPointF point)
void SwipeArea::handleReleaseEvent(QPointerEvent *event, QPointF point) void SwipeArea::handleReleaseEvent(QPointerEvent *event, QPointF point)
{ {
Q_UNUSED(event);
Q_UNUSED(point);
// if we are in a swipe // if we are in a swipe
if (m_moving) { if (m_moving) {
Q_EMIT swipeEnded(); Q_EMIT swipeEnded();
@ -265,6 +316,8 @@ void SwipeArea::handleReleaseEvent(QPointerEvent *event, QPointF point)
void SwipeArea::handleMoveEvent(QPointerEvent *event, QPointF point) void SwipeArea::handleMoveEvent(QPointerEvent *event, QPointF point)
{ {
Q_UNUSED(event);
if (!m_stealMouse) { if (!m_stealMouse) {
if (!m_skipSwipeThreshold) { if (!m_skipSwipeThreshold) {
// if we haven't reached the swipe registering threshold yet, don't start the swipe // if we haven't reached the swipe registering threshold yet, don't start the swipe

View file

@ -58,6 +58,10 @@ Q_SIGNALS:
// totalDeltaX, totalDeltaY - amount move since startedSwipe() // totalDeltaX, totalDeltaY - amount move since startedSwipe()
void swipeMove(qreal totalDeltaX, qreal totalDeltaY, qreal deltaX, qreal deltaY); 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: protected:
bool childMouseEventFilter(QQuickItem *item, QEvent *event) override; bool childMouseEventFilter(QQuickItem *item, QEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;
@ -66,6 +70,7 @@ protected:
void mouseUngrabEvent() override; void mouseUngrabEvent() override;
void touchEvent(QTouchEvent *event) override; void touchEvent(QTouchEvent *event) override;
void touchUngrabEvent() override; void touchUngrabEvent() override;
void wheelEvent(QWheelEvent *event) override;
private: private:
void setMoving(bool moving); void setMoving(bool moving);
@ -82,6 +87,7 @@ private:
Mode m_mode = Mode::BothAxis; Mode m_mode = Mode::BothAxis;
bool m_interactive = true; bool m_interactive = true;
bool m_pressed = false; bool m_pressed = false;
bool m_touchpadScrolling = false;
// whether we have started a flick // whether we have started a flick
bool m_moving = false; bool m_moving = false;
@ -100,6 +106,9 @@ private:
// whether to skip trying to measure the swipe threshold // whether to skip trying to measure the swipe threshold
bool m_skipSwipeThreshold; bool m_skipSwipeThreshold;
// the total amount of distance scrolled
QPointF m_totalScrollDelta;
}; };
QML_DECLARE_TYPE(SwipeArea) QML_DECLARE_TYPE(SwipeArea)

View file

@ -267,18 +267,28 @@ Item {
mode: MobileShell.SwipeArea.VerticalOnly mode: MobileShell.SwipeArea.VerticalOnly
anchors.fill: parent anchors.fill: parent
onSwipeStarted: { function startSwipe() {
root.cancelAnimations(); root.cancelAnimations();
root.dragging = true; root.dragging = true;
} }
onSwipeEnded: {
function endSwipe() {
root.dragging = false; root.dragging = false;
root.updateState(); root.updateState();
} }
onSwipeMove: (totalDeltaX, totalDeltaY, deltaX, deltaY) => {
function moveSwipe(totalDeltaX, totalDeltaY, deltaX, deltaY) {
root.offset += 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 { Loader {
id: contentContainerLoader id: contentContainerLoader
anchors.fill: parent anchors.fill: parent

View file

@ -33,6 +33,17 @@ MobileShell.SwipeArea {
actionDrawer.visible = true; 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() { function endSwipe() {
actionDrawer.dragging = false; actionDrawer.dragging = false;
actionDrawer.updateState(); actionDrawer.updateState();
@ -44,16 +55,11 @@ MobileShell.SwipeArea {
anchors.fill: parent anchors.fill: parent
onSwipeStarted: (point) => { onSwipeStarted: (point) => 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();
}
onSwipeEnded: endSwipe() onSwipeEnded: endSwipe()
onSwipeMove: (totalDeltaX, totalDeltaY, deltaX, deltaY) => updateOffset(deltaY); onSwipeMove: (totalDeltaX, totalDeltaY, deltaX, deltaY) => updateOffset(deltaY);
onTouchpadScrollStarted: (point) => startSwipeWithPoint(point)
onTouchpadScrollEnded: endSwipe()
onTouchpadScrollMove: (totalDeltaX, totalDeltaY, deltaX, deltaY) => updateOffset(deltaY);
} }

View file

@ -124,6 +124,10 @@ Item {
homeScreenState.swipeMoved(totalDeltaX, totalDeltaY, deltaX, deltaY); 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: { onPressedChanged: {
if (pressed) { if (pressed) {
// ensures that components like the widget settings overlay close when swiping // ensures that components like the widget settings overlay close when swiping

View file

@ -71,16 +71,28 @@ MobileShell.SwipeArea {
} }
} }
onSwipeStarted: cancelAnimations(); function __startSwipe() {
onSwipeEnded: { cancelAnimations();
}
function __endSwipe() {
if (!positionAnim.running) { if (!positionAnim.running) {
updateState(); updateState();
} }
} }
onSwipeMove: (totalDeltaX, totalDeltaY, deltaX, deltaY) => { function __moveSwipe(totalDeltaX, totalDeltaY, deltaX, deltaY) {
position = Math.max(0, Math.min(keypadHeight, position - 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)
} }