diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -299,6 +299,7 @@ qml/ListBrowserView.qml qml/FileBrowserDelegate.qml qml/FileBrowserView.qml + qml/ScrollHelper.qml ) qt5_add_resources(elisa_SOURCES resources.qrc) diff --git a/src/qml/FileBrowserView.qml b/src/qml/FileBrowserView.qml --- a/src/qml/FileBrowserView.qml +++ b/src/qml/FileBrowserView.qml @@ -106,6 +106,12 @@ } boundsBehavior: Flickable.StopAtBounds + ScrollHelper { + id: scrollHelper + flickable: contentDirectoryView + anchors.fill: contentDirectoryView + } + add: Transition { PropertyAnimation { property: "opacity" diff --git a/src/qml/GridBrowserView.qml b/src/qml/GridBrowserView.qml --- a/src/qml/GridBrowserView.qml +++ b/src/qml/GridBrowserView.qml @@ -122,6 +122,12 @@ text: 'example' } + ScrollHelper { + id: scrollHelper + flickable: contentDirectoryView + anchors.fill: contentDirectoryView + } + cellWidth: elisaTheme.gridDelegateWidth cellHeight: (delegateDisplaySecondaryText ? elisaTheme.gridDelegateHeight : elisaTheme.gridDelegateHeight - secondaryLabelSize.height) diff --git a/src/qml/ListBrowserView.qml b/src/qml/ListBrowserView.qml --- a/src/qml/ListBrowserView.qml +++ b/src/qml/ListBrowserView.qml @@ -110,6 +110,12 @@ } boundsBehavior: Flickable.StopAtBounds clip: true + + ScrollHelper { + id: scrollHelper + flickable: contentDirectoryView + anchors.fill: contentDirectoryView + } } } } diff --git a/src/qml/MediaPlayListView.qml b/src/qml/MediaPlayListView.qml --- a/src/qml/MediaPlayListView.qml +++ b/src/qml/MediaPlayListView.qml @@ -173,6 +173,12 @@ boundsBehavior: Flickable.StopAtBounds clip: true + ScrollHelper { + id: scrollHelper + flickable: playListView + anchors.fill: playListView + } + TextEdit { readOnly: true visible: playListModelDelegate.count === 0 diff --git a/src/qml/ScrollHelper.qml b/src/qml/ScrollHelper.qml new file mode 100644 --- /dev/null +++ b/src/qml/ScrollHelper.qml @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2016 Michael Bohlender, + * Copyright (C) 2017 Christian Mollekopf, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.7 +import QtQuick.Controls 2 + +/* +* The MouseArea + interactive: false + maximumFlickVelocity are required +* to fix scrolling for desktop systems where we don't want flicking behaviour. +* +* See also: +* ScrollView.qml in qtquickcontrols +* qquickwheelarea.cpp in qtquickcontrols +*/ +MouseArea { + id: root + propagateComposedEvents: true + + property Flickable flickable + + //Place the mouse area under the flickable + z: -1 + onFlickableChanged: { + flickable.interactive = false + flickable.maximumFlickVelocity = 100000 + flickable.boundsBehavior = Flickable.StopAtBounds + root.parent = flickable + } + + function scrollByPixelDelta(flickableItem, pixelDelta) { + if (!pixelDelta) { + return flickableItem.contentY; + } + + var minYExtent = flickableItem.originY + flickableItem.topMargin; + var maxYExtent = (flickableItem.contentHeight + flickableItem.bottomMargin + flickableItem.originY) - flickableItem.height; + + if (typeof(flickableItem.headerItem) !== "undefined" && flickableItem.headerItem) { + minYExtent += flickableItem.headerItem.height + } + + //Avoid overscrolling + return Math.max(minYExtent, Math.min(maxYExtent, flickableItem.contentY - pixelDelta)); + } + + function calculateNewPosition(flickableItem, wheel) { + //Nothing to scroll + if (flickableItem.contentHeight < flickableItem.height) { + return flickableItem.contentY; + } + //Ignore 0 events (happens at least with Christians trackpad) + if (wheel.pixelDelta.y == 0 && wheel.angleDelta.y == 0) { + return flickableItem.contentY; + } + //pixelDelta seems to be the same as angleDelta/8 + var pixelDelta = 0 + //The pixelDelta is a smaller number if both are provided, so pixelDelta can be 0 while angleDelta is still something. So we check the angleDelta + if (wheel.angleDelta.y) { + var wheelScrollLines = 3 //Default value of QApplication wheelScrollLines property + var pixelPerLine = 20 //Default value in Qt, originally comes from QTextEdit + var ticks = (wheel.angleDelta.y / 8) / 15.0 //Divide by 8 gives us pixels typically come in 15pixel steps. + pixelDelta = ticks * pixelPerLine * wheelScrollLines + } else { + pixelDelta = wheel.pixelDelta.y + } + + return scrollByPixelDelta(flickableItem, pixelDelta); + } + + function scrollDown() { + flickable.flick(0, 0); + flickable.contentY = scrollByPixelDelta(flickable, -60); //3 lines * 20 pixels + } + + function scrollUp() { + flickable.flick(0, 0); + flickable.contentY = scrollByPixelDelta(flickable, 60); //3 lines * 20 pixels + } + + onWheel: { + var newPos = calculateNewPosition(flickable, wheel); + // console.warn("Delta: ", wheel.pixelDelta.y); + // console.warn("Old position: ", flickable.contentY); + // console.warn("New position: ", newPos); + + // Show the scrollbars + flickable.flick(0, 0); + flickable.contentY = newPos; + cancelFlickStateTimer.start() + } + + + Timer { + id: cancelFlickStateTimer + //How long the scrollbar will remain visible + interval: 500 + // Hide the scrollbars + onTriggered: flickable.cancelFlick(); + } +} + diff --git a/src/resources.qrc b/src/resources.qrc --- a/src/resources.qrc +++ b/src/resources.qrc @@ -32,6 +32,7 @@ background.png qml/ViewManager.qml qml/BaseTheme.qml + qml/ScrollHelper.qml windows/WindowsTheme.qml