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 }