[plasma-mobile/qmlkonsole/release/26.08] src: Implement inertial scrolling for touch in terminal view
Devin Lin <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 1de032fed2e5b145205c30b2ef16b9d528c6bc4d by Devin Lin.
Committed on 19/07/2026 at 16:19.
Pushed by devinlin into branch 'release/26.08'.
Implement inertial scrolling for touch in terminal view
M +3 -12 src/contents/ui/TerminalPage.qml
A +125 -0 src/contents/ui/TerminalTouchScrollHandler.qml [License: GPL(v2.0+)]
M +1 -0 src/resources.qrc
https://invent.kde.org/plasma-mobile/qmlkonsole/-/commit/1de032fed2e5b145205c30b2ef16b9d528c6bc4d
diff --git a/src/contents/ui/TerminalPage.qml b/src/contents/ui/TerminalPage.qml
index 42f81fe..502b171 100644
--- a/src/contents/ui/TerminalPage.qml
+++ b/src/contents/ui/TerminalPage.qml
@@ -546,19 +546,10 @@ Kirigami.Page {
onLongPressed: terminal.touchSelectionMode = true
}
- // simulate scrolling for touch (TODO velocity)
- DragHandler {
- acceptedDevices: PointerDevice.TouchScreen
+ TerminalTouchScrollHandler {
+ anchors.fill: parent
enabled: !terminal.touchSelectionMode
-
- property real previousY
- onActiveChanged: {
- previousY = 0;
- }
- onTranslationChanged: {
- terminal.simulateWheel(0, 0, 0, 0, Qt.point(0, (translation.y - previousY) * 2));
- previousY = translation.y;
- }
+ terminalItem: terminal
}
}
}
diff --git a/src/contents/ui/TerminalTouchScrollHandler.qml b/src/contents/ui/TerminalTouchScrollHandler.qml
new file mode 100644
index 0000000..3bc16b4
--- /dev/null
+++ b/src/contents/ui/TerminalTouchScrollHandler.qml
@@ -0,0 +1,125 @@
+// SPDX-FileCopyrightText: 2019-2020 Jonah Brüchert <[email protected]>
+// SPDX-FileCopyrightText: 2021-2022 Devin Lin <[email protected]>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+import QtQuick
+
+import org.kde.konsoleqml
+
+Item {
+ id: root
+
+ required property TerminalEmulator terminalItem
+
+ onEnabledChanged: {
+ if (!enabled) {
+ touchScrollHandler.stopInertia();
+ }
+ }
+
+ TapHandler {
+ acceptedDevices: PointerDevice.TouchScreen
+ onPressedChanged: {
+ if (pressed) {
+ touchScrollHandler.pauseInertia();
+ }
+ }
+ onTapped: touchScrollHandler.stopInertia()
+ }
+
+ DragHandler {
+ id: touchScrollHandler
+
+ acceptedDevices: PointerDevice.TouchScreen
+ target: null
+ xAxis.enabled: false
+
+ property real inertialY
+ property real previousInertialY
+ property real wheelRemainder
+ property real carriedVelocity
+ property real initialInertialVelocity
+ property double inertiaStartedAt
+
+ readonly property real maximumVelocity: 10000
+ readonly property real minimumVelocity: 50
+ readonly property real deceleration: 2500
+
+ function scrollBy(pixelDelta) {
+ wheelRemainder += pixelDelta * 2;
+
+ const wheelDelta = wheelRemainder < 0 ? Math.ceil(wheelRemainder) : Math.floor(wheelRemainder);
+ if (wheelDelta === 0) {
+ return;
+ }
+
+ root.terminalItem.simulateWheel(0, 0, 0, 0, Qt.point(0, wheelDelta));
+ wheelRemainder -= wheelDelta;
+ }
+
+ function pauseInertia() {
+ if (!touchScrollAnimation.running) {
+ return;
+ }
+
+ const elapsed = (Date.now() - inertiaStartedAt) / 1000;
+ carriedVelocity = Math.sign(initialInertialVelocity)
+ * Math.max(0, Math.abs(initialInertialVelocity) - deceleration * elapsed);
+ touchScrollAnimation.stop();
+ }
+
+ function stopInertia() {
+ touchScrollAnimation.stop();
+ carriedVelocity = 0;
+ initialInertialVelocity = 0;
+ }
+
+ onActiveChanged: {
+ if (active) {
+ pauseInertia();
+ return;
+ }
+
+ let velocity = centroid.velocity.y;
+ if (Math.abs(velocity) < minimumVelocity) {
+ stopInertia();
+ return;
+ }
+
+ if (velocity * carriedVelocity > 0) {
+ velocity += carriedVelocity;
+ }
+ velocity = Math.max(-maximumVelocity, Math.min(maximumVelocity, velocity));
+
+ carriedVelocity = 0;
+ initialInertialVelocity = velocity;
+ inertiaStartedAt = Date.now();
+ previousInertialY = 0;
+ inertialY = 0;
+ touchScrollAnimation.to = velocity * Math.abs(velocity) / (2 * deceleration);
+ touchScrollAnimation.duration = Math.round(Math.abs(velocity) / deceleration * 1000);
+ touchScrollAnimation.restart();
+ }
+ yAxis.onActiveValueChanged: delta => {
+ if (active) {
+ scrollBy(delta);
+ }
+ }
+ onInertialYChanged: {
+ scrollBy(inertialY - previousInertialY);
+ previousInertialY = inertialY;
+ }
+ }
+
+ NumberAnimation {
+ id: touchScrollAnimation
+ target: touchScrollHandler
+ property: "inertialY"
+ easing.type: Easing.OutQuad
+ onFinished: {
+ touchScrollHandler.carriedVelocity = 0;
+ touchScrollHandler.initialInertialVelocity = 0;
+ }
+ }
+}
diff --git a/src/resources.qrc b/src/resources.qrc
index d8e5825..30d5e63 100644
--- a/src/resources.qrc
+++ b/src/resources.qrc
@@ -10,6 +10,7 @@
<file alias="SettingsPage.qml">contents/ui/SettingsPage.qml</file>
<file alias="SettingsDialog.qml">contents/ui/SettingsDialog.qml</file>
<file alias="TerminalPage.qml">contents/ui/TerminalPage.qml</file>
+ <file alias="TerminalTouchScrollHandler.qml">contents/ui/TerminalTouchScrollHandler.qml</file>
<file alias="TerminalKeyButton.qml">contents/ui/TerminalKeyButton.qml</file>
<file alias="TerminalKeyToolBar.qml">contents/ui/TerminalKeyToolBar.qml</file>
<file alias="SavedCommandsSettings.qml">contents/ui/SavedCommandsSettings.qml</file>