Details
-
Bug
-
Resolution: Unresolved
-
P3: Somewhat important
-
None
-
6.7, 6.8
Description
QtQuick.Controls counterpart of ScrollBar component needs more API to provide feature-parity with its QtWidgets QScrollBar twin. Namely, it should be able to:
- Scroll in page steps or "single" steps. Currently QQC2.ScrollBar has only one property for pageStep, which is a relative value (not absolute/pixel size) and defaults to 0.1 (which is 10%). QQC2.ScrollBar is unable to handle mouse clicks outside of a scroll handle to perform a page step.
- QQC2.ScrollBar doesn't support arrow indicators on either side, let alone double arrows like QtWidgets do. Well, in fact it does, but via some questionable __privateApi invented for a sole reason to implement "window" NativeStyle.
- QQC2.ScrollBar does not support Middle Mouse Button press and drag to drag the handle around regardless of the "pageStep" setting.
Here is how we work around these limitations in KDE qqc2-desktop-style framework (which is a QQC2 style implementation) using a MouseArea as a background component:
import QtQuick import QtQuick.Templates as T import org.kde.qqc2desktopstyle.private as StylePrivate T.ScrollBar { id: controlRoot background: MouseArea { anchors.fill: parent hoverEnabled: true acceptedButtons: Qt.LeftButton | Qt.MiddleButton onExited: style.activeControl = "groove"; onPressed: mouse => { const jumpPosition = style.positionFromMouse(mouse); if (mouse.buttons & Qt.MiddleButton) { style.activeControl = "handle"; controlRoot.position = jumpPosition; mouse.accepted = true; } else if (style.activeControl === "down") { buttonTimer.increment = 1; buttonTimer.running = true; mouse.accepted = true; } else if (style.activeControl === "up") { buttonTimer.increment = -1; buttonTimer.running = true; mouse.accepted = true; } else if (style.activeControl === "downPage") { if (style.scrollToClickPosition(mouse)) { controlRoot.position = jumpPosition; } else { buttonTimer.increment = controlRoot.size; buttonTimer.running = true; } mouse.accepted = true; } else if (style.activeControl === "upPage") { if (style.scrollToClickPosition(mouse)) { controlRoot.position = jumpPosition; } else { buttonTimer.increment = -controlRoot.size; buttonTimer.running = true; } mouse.accepted = true; } else { mouse.accepted = false; } } onPositionChanged: mouse => { style.activeControl = style.hitTest(mouse.x, mouse.y); if (mouse.buttons & Qt.MiddleButton) { style.activeControl = "handle"; controlRoot.position = style.positionFromMouse(mouse); mouse.accepted = true; } } onReleased: mouse => { style.activeControl = style.hitTest(mouse.x, mouse.y); buttonTimer.running = false; mouse.accepted = false; } onCanceled: buttonTimer.running = false; Timer { id: buttonTimer property real increment repeat: true interval: 150 triggeredOnStart: true onTriggered: { if (increment === 1) { controlRoot.increase(); } else if (increment === -1) { controlRoot.decrease(); } else { controlRoot.position = Math.min(1 - controlRoot.size, Math.max(0, controlRoot.position + increment)); } } } StylePrivate.StyleItem { id: style function positionFromMouse(mouse/*: MouseEvent*/): real { return Math.min(1 - controlRoot.size, Math.max(0, (controlRoot.horizontal ? mouse.x / width : mouse.y / height ) - controlRoot.size / 2 )); } } } }
The source code is here: https://invent.kde.org/frameworks/qqc2-desktop-style/-/blob/79c592b449f44a8a0a5df6ac31568656091a28a7/org.kde.desktop/ScrollBar.qml
Somewhat related: our MouseArea implementation broke with the transition to Qt 6, but I found a workaround to set its z-index to something bigger than zero, so a ScrollBar won't intercept the mouse events itself. I'd wish we didn't have to do that, but we lack QQC API for that.
- Downstream bug report: https://bugs.kde.org/show_bug.cgi?id=488092
- and an MR with the workaround: https://invent.kde.org/frameworks/qqc2-desktop-style/-/merge_requests/421