Warning, /frameworks/qqc2-desktop-style/org.kde.desktop/ScrollBar.qml is written in an unsupported language. File is not indexed.
0001 /*
0002 SPDX-FileCopyrightText: 2017 Marco Martin <mart@kde.org>
0003 SPDX-FileCopyrightText: 2017 The Qt Company Ltd.
0004 SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk>
0005
0006 SPDX-License-Identifier: LGPL-3.0-only OR GPL-2.0-or-later
0007 */
0008
0009 import QtQuick
0010 import QtQml
0011 import org.kde.qqc2desktopstyle.private as StylePrivate
0012 import QtQuick.Templates as T
0013 import org.kde.kirigami as Kirigami
0014
0015 T.ScrollBar {
0016 id: controlRoot
0017
0018 implicitWidth: background.implicitWidth
0019 implicitHeight: background.implicitHeight
0020
0021 hoverEnabled: true
0022
0023 stepSize: 0.02
0024 interactive: !Kirigami.Settings.hasTransientTouchInput
0025
0026 // Workaround for https://bugreports.qt.io/browse/QTBUG-106118
0027 Binding on visible {
0028 delayed: true
0029 restoreMode: Binding.RestoreBindingOrValue
0030 value: controlRoot.size < 1.0 && controlRoot.size > 0 && controlRoot.policy !== T.ScrollBar.AlwaysOff && controlRoot.parent !== null
0031 }
0032 topPadding: style.topScrollbarPadding
0033 leftPadding: style.leftScrollbarPadding
0034 rightPadding: style.rightScrollbarPadding
0035 bottomPadding: style.bottomScrollbarPadding
0036
0037 onPositionChanged: {
0038 if (handleGraphics.visible) {
0039 disappearTimer.restart();
0040 handleGraphics.handleState = Math.min(1, handleGraphics.handleState + 0.1)
0041 }
0042 }
0043
0044 contentItem: Item {
0045 visible: !controlRoot.interactive
0046
0047 Rectangle {
0048 id: handleGraphics
0049
0050 // Controls auto-hide behavior state, 0 = hidden, 1 = fully visible
0051 property real handleState: 0
0052
0053 x: controlRoot.vertical
0054 ? Math.round(!controlRoot.mirrored
0055 ? (parent.width - width) - (parent.width/2 - width/2) * handleState
0056 : (parent.width/2 - width/2) * handleState)
0057 : 0
0058
0059 y: controlRoot.horizontal
0060 ? Math.round((parent.height - height) - (parent.height/2 - height/2) * handleState)
0061 : 0
0062
0063 NumberAnimation on handleState {
0064 id: resetAnim
0065 from: handleGraphics.handleState
0066 to: 0
0067 duration: Kirigami.Units.longDuration
0068 easing.type: Easing.InOutQuad
0069 // Same trick as in BusyIndicator. Animations using property
0070 // interceptor syntax are running by default. We don't want
0071 // this, as we will only run it with restart() method when needed.
0072 running: false
0073 }
0074
0075 width: Math.round(controlRoot.vertical
0076 ? Math.max(2, Kirigami.Units.smallSpacing * handleState)
0077 : parent.width)
0078 height: Math.round(controlRoot.horizontal
0079 ? Math.max(2, Kirigami.Units.smallSpacing * handleState)
0080 : parent.height)
0081 radius: Math.min(width, height)
0082 color: Kirigami.Theme.textColor
0083 opacity: 0.3
0084 Timer {
0085 id: disappearTimer
0086 interval: 1000
0087 onTriggered: {
0088 resetAnim.restart();
0089 }
0090 }
0091 }
0092 }
0093
0094 background: MouseArea {
0095 id: mouseArea
0096 anchors.fill: parent
0097 visible: controlRoot.size < 1.0 && interactive
0098 hoverEnabled: true
0099 acceptedButtons: Qt.LeftButton | Qt.MiddleButton
0100 onExited: style.activeControl = "groove";
0101 onPressed: mouse => {
0102 const jumpPosition = style.positionFromMouse(mouse);
0103 if (mouse.buttons & Qt.MiddleButton) {
0104 style.activeControl = "handle";
0105 controlRoot.position = jumpPosition;
0106 mouse.accepted = true;
0107 } else if (style.activeControl === "down") {
0108 buttonTimer.increment = 1;
0109 buttonTimer.running = true;
0110 mouse.accepted = true;
0111 } else if (style.activeControl === "up") {
0112 buttonTimer.increment = -1;
0113 buttonTimer.running = true;
0114 mouse.accepted = true;
0115 } else if (style.activeControl === "downPage") {
0116 if (style.scrollToClickPosition(mouse)) {
0117 controlRoot.position = jumpPosition;
0118 } else {
0119 buttonTimer.increment = controlRoot.size;
0120 buttonTimer.running = true;
0121 }
0122 mouse.accepted = true;
0123 } else if (style.activeControl === "upPage") {
0124 if (style.scrollToClickPosition(mouse)) {
0125 controlRoot.position = jumpPosition;
0126 } else {
0127 buttonTimer.increment = -controlRoot.size;
0128 buttonTimer.running = true;
0129 }
0130 mouse.accepted = true;
0131 } else {
0132 mouse.accepted = false;
0133 }
0134 }
0135 onPositionChanged: mouse => {
0136 style.activeControl = style.hitTest(mouse.x, mouse.y);
0137 if (mouse.buttons & Qt.MiddleButton) {
0138 style.activeControl = "handle";
0139 controlRoot.position = style.positionFromMouse(mouse);
0140 mouse.accepted = true;
0141 }
0142 }
0143 onReleased: mouse => {
0144 style.activeControl = style.hitTest(mouse.x, mouse.y);
0145 buttonTimer.running = false;
0146 mouse.accepted = false;
0147 }
0148 onCanceled: buttonTimer.running = false;
0149
0150 implicitWidth: style.implicitWidth
0151 implicitHeight: style.implicitHeight
0152
0153 Timer {
0154 id: buttonTimer
0155 property real increment
0156 repeat: true
0157 interval: 150
0158 triggeredOnStart: true
0159 onTriggered: {
0160 if (increment === 1) {
0161 controlRoot.increase();
0162 } else if (increment === -1) {
0163 controlRoot.decrease();
0164 } else {
0165 controlRoot.position = Math.min(1 - controlRoot.size, Math.max(0, controlRoot.position + increment));
0166 }
0167 }
0168 }
0169 StylePrivate.StyleItem {
0170 id: style
0171
0172 readonly property real length: controlRoot.vertical ? height : width
0173 property rect grooveRect: Qt.rect(0, 0, 0, 0)
0174 readonly property real topScrollbarPadding: grooveRect.top
0175 readonly property real bottomScrollbarPadding: height - grooveRect.bottom
0176 readonly property real leftScrollbarPadding: grooveRect.left
0177 readonly property real rightScrollbarPadding: width - grooveRect.right
0178
0179 onWidthChanged: computeRects()
0180 onHeightChanged: computeRects()
0181 onStyleNameChanged: computeRects()
0182 Component.onCompleted: computeRects()
0183
0184 function computeRects() {
0185 grooveRect = subControlRect("groove");
0186 }
0187
0188 function positionFromMouse(mouse /*MouseEvent*/): real {
0189 return Math.min(1 - controlRoot.size, Math.max(0,
0190 (controlRoot.horizontal
0191 ? mouse.x / width
0192 : mouse.y / height
0193 ) - controlRoot.size / 2
0194 ));
0195 }
0196
0197 // Style hint returns true if it should scroll to click position,
0198 // and false if it should scroll by one page at a time.
0199 // This function inverts the behavior if Alt button is pressed.
0200 function scrollToClickPosition(mouse /*MouseEvent*/): bool {
0201 let behavior = style.styleHint("scrollToClickPosition");
0202 if (mouse.modifiers & Qt.AltModifier) {
0203 behavior = !behavior;
0204 }
0205 return behavior;
0206 }
0207
0208 control: controlRoot
0209 anchors.fill: parent
0210 elementType: "scrollbar"
0211 hover: activeControl !== "none"
0212 activeControl: "none"
0213 sunken: controlRoot.pressed
0214 // Normally, min size should be controlled by
0215 // PM_ScrollBarSliderMin pixel metrics, or ScrollBar.minimumSize
0216 // property. But we are working with visual metrics (0..1) here;
0217 // and despite what documentation says, minimumSize does not
0218 // affect visualSize. So let us at least prevent division by zero.
0219 minimum: 0
0220 maximum: Math.round(length / Math.max(0.001, controlRoot.visualSize) - length)
0221 value: Math.round(length / Math.max(0.001, controlRoot.visualSize) * Math.min(1 - 0.001, controlRoot.visualPosition))
0222
0223 horizontal: controlRoot.horizontal
0224 enabled: controlRoot.enabled
0225
0226 visible: controlRoot.size < 1.0
0227 opacity: 1
0228 }
0229 StylePrivate.StyleItem {
0230 id: inactiveStyle
0231 anchors.fill: parent
0232 control: controlRoot
0233 elementType: "scrollbar"
0234 activeControl: "none"
0235 sunken: false
0236 minimum: 0
0237 maximum: style.maximum
0238 value: style.value
0239 horizontal: style.horizontal
0240 enabled: controlRoot.enabled
0241
0242 visible: controlRoot.size < 1.0
0243 opacity: 1
0244 }
0245 state: "inactive"
0246 states: [
0247 State {
0248 name: "hover"
0249 when: mouseArea.containsMouse
0250 PropertyChanges {
0251 target: style
0252 opacity: 1
0253 }
0254 PropertyChanges {
0255 target: inactiveStyle
0256 opacity: 0
0257 }
0258 },
0259 State {
0260 name: "inactive"
0261 when: !mouseArea.containsMouse
0262 PropertyChanges {
0263 target: style
0264 opacity: 0
0265 }
0266 PropertyChanges {
0267 target: inactiveStyle
0268 opacity: 1
0269 }
0270 }
0271 ]
0272 transitions: Transition {
0273 NumberAnimation {
0274 targets: [style, inactiveStyle]
0275 property: "opacity"
0276 duration: Kirigami.Units.longDuration
0277 easing.type: Easing.InOutQuad
0278 }
0279 }
0280 }
0281 }