| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt Quick Controls module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| import QtQuick 2.2 |
| import QtQuick.Controls 1.2 |
| import QtQuick.Controls.Private 1.0 |
| |
| /*! |
| \qmltype ScrollViewStyle |
| \inqmlmodule QtQuick.Controls.Styles |
| \since 5.1 |
| \ingroup viewsstyling |
| \ingroup controlsstyling |
| \brief Provides custom styling for ScrollView. |
| */ |
| Style { |
| id: root |
| |
| /*! The \l ScrollView this style is attached to. */ |
| readonly property ScrollView control: __control |
| |
| /*! This property controls the frame border padding of the scrollView. */ |
| padding {left: 1; top: 1; right: 1; bottom: 1} |
| |
| /*! This Component paints the corner area between scroll bars */ |
| property Component corner: Rectangle { color: "#ccc" } |
| |
| /*! This component determines if the flickable should reposition itself at the |
| mouse location when clicked. */ |
| property bool scrollToClickedPosition: true |
| |
| /*! This property holds whether the scroll bars are transient. Transient scroll bars |
| appear when the content is scrolled and disappear when they are no longer needed. |
| |
| The default value is platform dependent. */ |
| property bool transientScrollBars: Settings.isMobile && Settings.hasTouchScreen |
| |
| /*! This Component paints the frame around scroll bars. */ |
| property Component frame: Rectangle { |
| color: control["backgroundVisible"] ? "white": "transparent" |
| border.color: "#999" |
| border.width: 1 |
| radius: 1 |
| visible: control.frameVisible |
| } |
| |
| /*! This is the minimum extent of the scroll bar handle. |
| |
| The default value is \c 30. |
| */ |
| |
| property int minimumHandleLength: 30 |
| |
| /*! This property controls the edge overlap |
| between the handle and the increment/decrement buttons. |
| |
| The default value is \c 30. |
| */ |
| |
| property int handleOverlap: 1 |
| |
| /*! This component controls the appearance of the |
| scroll bar background. |
| |
| You can access the following state properties: |
| |
| \table |
| \row \li property bool \b styleData.hovered |
| \row \li property bool \b styleData.horizontal |
| \endtable |
| */ |
| |
| property Component scrollBarBackground: Item { |
| property bool sticky: false |
| property bool hovered: styleData.hovered |
| implicitWidth: Math.round(TextSingleton.implicitHeight) |
| implicitHeight: Math.round(TextSingleton.implicitHeight) |
| clip: true |
| opacity: transientScrollBars ? 0.5 : 1.0 |
| visible: !Settings.hasTouchScreen && (!transientScrollBars || sticky) |
| Rectangle { |
| anchors.fill: parent |
| color: "#ddd" |
| border.color: "#aaa" |
| anchors.rightMargin: styleData.horizontal ? -2 : -1 |
| anchors.leftMargin: styleData.horizontal ? -2 : 0 |
| anchors.topMargin: styleData.horizontal ? 0 : -2 |
| anchors.bottomMargin: styleData.horizontal ? -1 : -2 |
| } |
| onHoveredChanged: if (hovered) sticky = true |
| onVisibleChanged: if (!visible) sticky = false |
| } |
| |
| /*! This component controls the appearance of the |
| scroll bar handle. |
| |
| You can access the following state properties: |
| |
| \table |
| \row \li property bool \b styleData.hovered |
| \row \li property bool \b styleData.pressed |
| \row \li property bool \b styleData.horizontal |
| \endtable |
| */ |
| |
| property Component handle: Item { |
| property bool sticky: false |
| property bool hovered: __activeControl !== "none" |
| implicitWidth: Math.round(TextSingleton.implicitHeight) + 1 |
| implicitHeight: Math.round(TextSingleton.implicitHeight) + 1 |
| BorderImage { |
| id: img |
| opacity: styleData.pressed && !transientScrollBars ? 0.5 : styleData.hovered ? 1 : 0.8 |
| source: "images/scrollbar-handle-" + (transientScrollBars ? "transient" : styleData.horizontal ? "horizontal" : "vertical") + ".png" |
| border.left: transientScrollBars ? 5 : 2 |
| border.top: transientScrollBars ? 5 : 2 |
| border.right: transientScrollBars ? 5 : 2 |
| border.bottom: transientScrollBars ? 5 : 2 |
| anchors.top: !styleData.horizontal ? parent.top : undefined |
| anchors.margins: transientScrollBars ? 2 : 0 |
| anchors.bottom: parent.bottom |
| anchors.right: parent.right |
| anchors.left: styleData.horizontal ? parent.left : undefined |
| width: !styleData.horizontal && transientScrollBars ? sticky ? 13 : 10 : parent.width |
| height: styleData.horizontal && transientScrollBars ? sticky ? 13 : 10 : parent.height |
| Behavior on width { enabled: !styleData.horizontal && transientScrollBars; NumberAnimation { duration: 100 } } |
| Behavior on height { enabled: styleData.horizontal && transientScrollBars; NumberAnimation { duration: 100 } } |
| } |
| onHoveredChanged: if (hovered) sticky = true |
| onVisibleChanged: if (!visible) sticky = false |
| } |
| |
| /*! This component controls the appearance of the |
| scroll bar increment button. |
| |
| You can access the following state properties: |
| |
| \table |
| \row \li property bool \b styleData.hovered |
| \row \li property bool \b styleData.pressed |
| \row \li property bool \b styleData.horizontal |
| \endtable |
| */ |
| property Component incrementControl: Rectangle { |
| visible: !transientScrollBars |
| implicitWidth: transientScrollBars ? 0 : Math.round(TextSingleton.implicitHeight) |
| implicitHeight: transientScrollBars ? 0 : Math.round(TextSingleton.implicitHeight) |
| Rectangle { |
| anchors.fill: parent |
| anchors.bottomMargin: -1 |
| anchors.rightMargin: -1 |
| border.color: "#aaa" |
| Rectangle { |
| anchors.fill: parent |
| anchors.margins: 1 |
| color: "transparent" |
| border.color: "#44ffffff" |
| } |
| Image { |
| source: styleData.horizontal ? "images/arrow-right.png" : "images/arrow-down.png" |
| anchors.centerIn: parent |
| opacity: control.enabled ? 0.6 : 0.5 |
| } |
| gradient: Gradient { |
| GradientStop {color: styleData.pressed ? "lightgray" : "white" ; position: 0} |
| GradientStop {color: styleData.pressed ? "lightgray" : "lightgray" ; position: 1} |
| } |
| } |
| } |
| |
| /*! This component controls the appearance of the |
| scroll bar decrement button. |
| |
| You can access the following state properties: |
| |
| \table |
| \row \li property bool \b styleData.hovered |
| \row \li property bool \b styleData.pressed |
| \row \li property bool \b styleData.horizontal |
| \endtable |
| */ |
| property Component decrementControl: Rectangle { |
| visible: !transientScrollBars |
| implicitWidth: transientScrollBars ? 0 : Math.round(TextSingleton.implicitHeight) |
| implicitHeight: transientScrollBars ? 0 : Math.round(TextSingleton.implicitHeight) |
| Rectangle { |
| anchors.fill: parent |
| anchors.topMargin: styleData.horizontal ? 0 : -1 |
| anchors.leftMargin: styleData.horizontal ? -1 : 0 |
| anchors.bottomMargin: styleData.horizontal ? -1 : 0 |
| anchors.rightMargin: styleData.horizontal ? 0 : -1 |
| color: "lightgray" |
| Rectangle { |
| anchors.fill: parent |
| anchors.margins: 1 |
| color: "transparent" |
| border.color: "#44ffffff" |
| } |
| Image { |
| source: styleData.horizontal ? "images/arrow-left.png" : "images/arrow-up.png" |
| anchors.centerIn: parent |
| anchors.verticalCenterOffset: styleData.horizontal ? 0 : -1 |
| anchors.horizontalCenterOffset: styleData.horizontal ? -1 : 0 |
| opacity: control.enabled ? 0.6 : 0.5 |
| } |
| gradient: Gradient { |
| GradientStop {color: styleData.pressed ? "lightgray" : "white" ; position: 0} |
| GradientStop {color: styleData.pressed ? "lightgray" : "lightgray" ; position: 1} |
| } |
| border.color: "#aaa" |
| } |
| } |
| |
| /*! \internal */ |
| property Component __scrollbar: Item { |
| id: panel |
| property string activeControl: "none" |
| property bool scrollToClickPosition: true |
| property bool isTransient: transientScrollBars |
| |
| property bool on: false |
| property bool raised: false |
| property bool sunken: __styleData.upPressed | __styleData.downPressed | __styleData.handlePressed |
| |
| states: State { |
| name: "out" |
| when: isTransient |
| && (!__stickyScrollbars || !flickableItem.moving) |
| && panel.activeControl === "none" |
| && !panel.on |
| && !panel.raised |
| PropertyChanges { target: panel; opacity: 0 } |
| } |
| |
| transitions: Transition { |
| to: "out" |
| SequentialAnimation { |
| PauseAnimation { duration: root.__scrollBarFadeDelay } |
| NumberAnimation { properties: "opacity"; duration: root.__scrollBarFadeDuration } |
| PropertyAction { target: panel; property: "visible"; value: false } |
| } |
| } |
| |
| implicitWidth: __styleData.horizontal ? 200 : bg.implicitWidth |
| implicitHeight: __styleData.horizontal ? bg.implicitHeight : 200 |
| |
| function pixelMetric(arg) { |
| if (arg === "scrollbarExtent") |
| return (__styleData.horizontal ? bg.height : bg.width); |
| return 0; |
| } |
| |
| function styleHint(arg) { |
| return false; |
| } |
| |
| function hitTest(argX, argY) { |
| if (itemIsHit(handleControl, argX, argY)) |
| return "handle" |
| else if (itemIsHit(incrementLoader, argX, argY)) |
| return "up"; |
| else if (itemIsHit(decrementLoader, argX, argY)) |
| return "down"; |
| else if (itemIsHit(bg, argX, argY)) { |
| if (__styleData.horizontal && argX < handleControl.x || !__styleData.horizontal && argY < handleControl.y) |
| return "upPage" |
| else |
| return "downPage" |
| } |
| |
| return "none"; |
| } |
| |
| function subControlRect(arg) { |
| if (arg === "handle") { |
| return Qt.rect(handleControl.x, handleControl.y, handleControl.width, handleControl.height); |
| } else if (arg === "groove") { |
| if (__styleData.horizontal) { |
| return Qt.rect(incrementLoader.width - handleOverlap, |
| 0, |
| __control.width - (incrementLoader.width + decrementLoader.width - handleOverlap * 2), |
| __control.height); |
| } else { |
| return Qt.rect(0, |
| incrementLoader.height - handleOverlap, |
| __control.width, |
| __control.height - (incrementLoader.height + decrementLoader.height - handleOverlap * 2)); |
| } |
| } |
| return Qt.rect(0,0,0,0); |
| } |
| |
| function itemIsHit(argItem, argX, argY) { |
| var pos = argItem.mapFromItem(__control, argX, argY); |
| return (pos.x >= 0 && pos.x <= argItem.width && pos.y >= 0 && pos.y <= argItem.height); |
| } |
| |
| Loader { |
| id: incrementLoader |
| anchors.top: parent.top |
| anchors.left: parent.left |
| sourceComponent: decrementControl |
| property QtObject styleData: QtObject { |
| readonly property bool hovered: activeControl === "up" |
| readonly property bool pressed: __styleData.upPressed |
| readonly property bool horizontal: __styleData.horizontal |
| } |
| } |
| |
| Loader { |
| id: bg |
| anchors.top: __styleData.horizontal ? undefined : incrementLoader.bottom |
| anchors.bottom: __styleData.horizontal ? undefined : decrementLoader.top |
| anchors.left: __styleData.horizontal ? incrementLoader.right : undefined |
| anchors.right: __styleData.horizontal ? decrementLoader.left : undefined |
| sourceComponent: scrollBarBackground |
| property QtObject styleData: QtObject { |
| readonly property bool horizontal: __styleData.horizontal |
| readonly property bool hovered: activeControl !== "none" |
| } |
| } |
| |
| Loader { |
| id: decrementLoader |
| anchors.bottom: __styleData.horizontal ? undefined : parent.bottom |
| anchors.right: __styleData.horizontal ? parent.right : undefined |
| sourceComponent: incrementControl |
| property QtObject styleData: QtObject { |
| readonly property bool hovered: activeControl === "down" |
| readonly property bool pressed: __styleData.downPressed |
| readonly property bool horizontal: __styleData.horizontal |
| } |
| } |
| |
| property var flickableItem: control.flickableItem |
| property int extent: Math.max(minimumHandleLength, __styleData.horizontal ? |
| Math.min(1, ((flickableItem && flickableItem.contentWidth > 0.0) ? flickableItem.width/flickableItem.contentWidth : 1)) * bg.width : |
| Math.min(1, ((flickableItem && flickableItem.contentHeight > 0.0) ? flickableItem.height/flickableItem.contentHeight : 1)) * bg.height) |
| readonly property real range: __control.maximumValue - __control.minimumValue |
| readonly property real begin: __control.value - __control.minimumValue |
| |
| Loader { |
| id: handleControl |
| height: __styleData.horizontal ? implicitHeight : extent |
| width: __styleData.horizontal ? extent : implicitWidth |
| anchors.top: bg.top |
| anchors.left: bg.left |
| anchors.topMargin: __styleData.horizontal || range === 0 ? 0 : -handleOverlap + (2 * begin * (bg.height + (2 * handleOverlap) - extent) + range) / (2 * range) |
| anchors.leftMargin: __styleData.horizontal && range !== 0 ? -handleOverlap + (2 * begin * (bg.width + (2 * handleOverlap) - extent) + range) / (2 * range) : 0 |
| sourceComponent: handle |
| property QtObject styleData: QtObject { |
| readonly property bool hovered: activeControl === "handle" |
| readonly property bool pressed: __styleData.handlePressed |
| readonly property bool horizontal: __styleData.horizontal |
| } |
| readonly property alias __activeControl: panel.activeControl |
| } |
| } |
| |
| /*! \internal */ |
| property bool __externalScrollBars: false |
| /*! \internal */ |
| property int __scrollBarSpacing: 4 |
| /*! \internal */ |
| property int __scrollBarFadeDelay: 450 |
| /*! \internal */ |
| property int __scrollBarFadeDuration: 200 |
| /*! \internal */ |
| property bool __stickyScrollbars: false |
| } |