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 }