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 }