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