ubuntu-touch-coreapps-reviewers team mailing list archive
-
ubuntu-touch-coreapps-reviewers team
-
Mailing list archive
-
Message #06967
[Merge] lp:~mcintire-evan/music-app/songs-fastscroll into lp:music-app
Evan McIntire has proposed merging lp:~mcintire-evan/music-app/songs-fastscroll into lp:music-app.
Requested reviews:
Music App Developers (music-app-dev)
For more details, see:
https://code.launchpad.net/~mcintire-evan/music-app/songs-fastscroll/+merge/281471
Add fastscroll to the song list
--
Your team Music App Developers is requested to review the proposed merge of lp:~mcintire-evan/music-app/songs-fastscroll into lp:music-app.
=== added file 'app/components/FastScroll.qml'
--- app/components/FastScroll.qml 1970-01-01 00:00:00 +0000
+++ app/components/FastScroll.qml 2016-01-02 17:05:37 +0000
@@ -0,0 +1,316 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** Copyright (C) 2014 Canonical Ltda
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@xxxxxxxxx)
+**
+** This file is part of the Qt Components project.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// FastScroll.qml
+import QtQuick 2.4
+import Ubuntu.Components 1.3
+import "../logic/FastScroll.js" as Sections
+
+Item {
+ id: root
+
+ property ListView listView
+ property int pinSize: units.gu(2)
+
+ readonly property var letters: ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "#"]
+ readonly property alias fastScrolling: internal.fastScrolling
+ readonly property bool showing: (rail.opacity !== 0.0)
+ readonly property double minimumHeight: rail.height
+
+ width: units.gu(7)
+ height: rail.height
+ visible: enabled
+
+ onListViewChanged: {
+ if (listView && listView.model) {
+ internal.initDirtyObserver();
+ } else if (listView) {
+ listView.modelChanged.connect(function() {
+ if (listView.model) {
+ internal.initDirtyObserver();
+ }
+ });
+ }
+ }
+
+
+ Rectangle {
+ id: magnified
+
+ color: Theme.palette.normal.foreground
+ radius: height * 0.3
+ height: pinSize * 2
+ width: height
+ opacity: internal.fastScrolling && root.enabled ? 1.0 : 0.0
+ x: -cursor.width - units.gu(3)
+ y: {
+ if (internal.currentItem) {
+ var itemCenterY = rail.y + internal.currentItem.y + (internal.currentItem.height / 2)
+ return (itemCenterY - (magnified.height / 2))
+ } else {
+ return 0
+ }
+ }
+
+ Label {
+ color: Theme.palette.normal.foregroundText
+ anchors.fill: parent
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ text: internal.desireSection
+ fontSize: "small"
+ }
+
+ Behavior on opacity {
+ UbuntuNumberAnimation {}
+ }
+ }
+
+ Rectangle {
+ id: cursor
+
+ property bool showLabel: false
+ property string currentSectionName: ""
+
+ radius: pinSize * 0.3
+ height: pinSize
+ width: height
+ color: Theme.palette.normal.foreground
+ opacity: rail.opacity
+ x: rail.x
+ y: {
+ if (internal.currentItem) {
+ var itemCenterY = rail.y + internal.currentItem.y + (internal.currentItem.height / 2)
+ return (itemCenterY - (cursor.height / 2))
+ } else {
+ return 0
+ }
+ }
+ Behavior on y {
+ enabled: !internal.fastScrolling
+ UbuntuNumberAnimation { }
+ }
+ }
+
+ Column {
+ id: rail
+
+ property bool isVisible: root.enabled &&
+ (listView.flicking || dragArea.pressed)
+ anchors {
+ right: parent.right
+ rightMargin: units.gu(2)
+ left: parent.left
+ leftMargin: units.gu(2)
+ top: parent.top
+ }
+ height: childrenRect.height
+ opacity: 0.0
+ onIsVisibleChanged: {
+ if (isVisible) {
+ rail.opacity = 1.0
+ hideTimer.stop()
+ } else if (!root.enabled) {
+ rail.opacity = 0.0
+ } else {
+ hideTimer.restart()
+ }
+ }
+
+ Behavior on opacity {
+ UbuntuNumberAnimation { }
+ }
+
+ Repeater {
+ id: sectionsRepeater
+
+ model: root.letters
+ Label {
+ id: lbl
+
+ anchors.left: parent.left
+ height: pinSize
+ width: pinSize
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ text: modelData
+ fontSize: "x-small"
+ color: cursor.y === y ? Theme.palette.normal.foregroundText : Theme.palette.selected.backgroundText
+ opacity: !internal.modelDirty && Sections.contains(text) ? 1.0 : 0.5
+ }
+ }
+
+ Timer {
+ id: hideTimer
+
+ running: false
+ interval: 2000
+ onTriggered: rail.opacity = 0.0
+ }
+ }
+
+ MouseArea {
+ id: dragArea
+
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+ y: rail.y
+ height: rail.height
+ visible: rail.opacity == 1.0
+
+ preventStealing: true
+ onPressed: {
+ internal.adjustContentPosition(mouseY)
+ dragginTimer.start()
+ }
+
+ onReleased: {
+ dragginTimer.stop()
+ internal.desireSection = ""
+ internal.fastScrolling = false
+ }
+
+ onPositionChanged: internal.adjustContentPosition(mouseY)
+
+ Timer {
+ id: dragginTimer
+
+ running: false
+ interval: 150
+ onTriggered: {
+ internal.fastScrolling = true
+ }
+ }
+ }
+
+ Timer {
+ id: dirtyTimer
+ interval: 500
+ running: false
+ onTriggered: {
+ Sections.initSectionData(listView);
+ internal.modelDirty = false;
+ }
+ }
+
+ Timer {
+ id: timerScroll
+
+ running: false
+ interval: 10
+ onTriggered: {
+ if (internal.desireSection != internal.currentSection) {
+ var idx = Sections.getIndexFor(internal.desireSection)
+ if (idx !== -1) {
+ listView.cancelFlick()
+ listView.positionViewAtIndex(idx, ListView.Beginning)
+ }
+ }
+ }
+ }
+
+ QtObject {
+ id: internal
+
+ property string currentSection: listView.currentSection
+ property string desireSection: ""
+ property string targetSection: fastScrolling ? desireSection : currentSection
+ property int oldY: 0
+ property bool modelDirty: false
+ property bool down: true
+ property bool fastScrolling: false
+ property var currentItem: null
+
+ onTargetSectionChanged: moveIndicator(targetSection)
+
+ function initDirtyObserver() {
+ Sections.initialize(listView);
+ function dirtyObserver() {
+ if (!internal.modelDirty) {
+ internal.modelDirty = true;
+ dirtyTimer.running = true;
+ }
+ }
+
+ if (listView.model.countChanged)
+ listView.model.countChanged.connect(dirtyObserver);
+
+ if (listView.model.itemsChanged)
+ listView.model.itemsChanged.connect(dirtyObserver);
+
+ if (listView.model.itemsInserted)
+ listView.model.itemsInserted.connect(dirtyObserver);
+
+ if (listView.model.itemsMoved)
+ listView.model.itemsMoved.connect(dirtyObserver);
+
+ if (listView.model.itemsRemoved)
+ listView.model.itemsRemoved.connect(dirtyObserver);
+ }
+
+ function adjustContentPosition(mouseY) {
+ var child = rail.childAt(rail.width / 2, mouseY)
+ if (!child || child.text === "") {
+ return
+ }
+ var section = child.text
+ if (internal.desireSection !== section) {
+ internal.desireSection = section
+ moveIndicator(section)
+ if (dragArea.pressed) {
+ timerScroll.restart()
+ }
+ }
+ }
+
+ function moveIndicator(section)
+ {
+ var index = root.letters.indexOf(section)
+ if (index != -1) {
+ currentItem = sectionsRepeater.itemAt(index)
+ }
+ }
+ }
+}
+
+
=== added file 'app/logic/FastScroll.js'
--- app/logic/FastScroll.js 1970-01-01 00:00:00 +0000
+++ app/logic/FastScroll.js 2016-01-02 17:05:37 +0000
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** Copyright (C) 2014 Canonical Ltda
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@xxxxxxxxx)
+**
+** This file is part of the Qt Components project.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// FastScroll.js - this is just SectionScroller.js with a fix for
+// section.criteria == ViewSection.FirstCharacter
+var sectionData = [];
+var _sections = [];
+
+function initialize(list) {
+ initSectionData(list);
+}
+
+function contains(name) {
+ return (_sections.indexOf(name) > -1)
+}
+
+function initSectionData(list) {
+ if (!list || !list.model) return;
+ sectionData = [];
+ _sections = [];
+ var current = "",
+ prop = list.section.property,
+ sectionText;
+
+ if (list.section.criteria === ViewSection.FullString) {
+ for (var i = 0, count = list.model.count; i < count; i++) {
+ sectionText = list.getSectionText(i)
+ if (sectionText !== current) {
+ current = sectionText;
+ _sections.push(current);
+ sectionData.push({ index: i, header: current });
+ }
+ }
+ } else if (list.section.criteria === ViewSection.FirstCharacter) {
+ for (var i = 0, count = list.model.count; i < count; i++) {
+ sectionText = list.getSectionText(i).substring(0, 1)
+ if (sectionText !== current) {
+ current = sectionText
+ _sections.push(sectionText);
+ sectionData.push({ index: i, header: current });
+ }
+ }
+ }
+}
+
+function getSectionPositionString(name) {
+ var val = _sections.indexOf(name);
+ return val === 0 ? "first" :
+ val === _sections.length - 1 ? "last" : false;
+}
+
+function getAt(pos) {
+ return _sections[pos] ? _sections[pos] : "";
+}
+
+function getRelativeSections(current) {
+ var val = _sections.indexOf(current),
+ sect = [],
+ sl = _sections.length;
+
+ val = val < 1 ? 1 : val >= sl-1 ? sl-2 : val;
+ sect = [getAt(val - 1), getAt(val), getAt(val + 1)];
+
+ return sect;
+}
+
+function getClosestSection(pos, down) {
+ var tmp = (_sections.length) * pos;
+ var val = Math.ceil(tmp) // TODO: better algorithm
+ val = val < 2 ? 1 : val;
+ return _sections[val-1];
+}
+
+function getNextSection(current) {
+ var val = _sections.indexOf(current);
+ return (val > -1 ? _sections[(val < _sections.length - 1 ? val + 1 : val)] : _sections[0]) || "";
+}
+
+function getPreviousSection(current) {
+ var val = _sections.indexOf(current);
+ return (val > -1 ? _sections[(val > 0 ? val - 1 : val)] : _sections[0]) || "";
+}
+
+function getIndexFor(sectionName) {
+ var data = sectionData[_sections.indexOf(sectionName)]
+ if (data) {
+ var val = data.index;
+ return val === 0 || val > 0 ? val : -1;
+ } else {
+ return -1
+ }
+}
+
=== modified file 'app/ui/Songs.qml'
--- app/ui/Songs.qml 2015-10-18 18:16:05 +0000
+++ app/ui/Songs.qml 2016-01-02 17:05:37 +0000
@@ -19,6 +19,7 @@
import QtQuick 2.4
import Ubuntu.Components 1.3
+import Ubuntu.Components.ListItems 0.1 as ListItem
import Ubuntu.MediaScanner 0.1
import Ubuntu.Thumbnailer 0.1
import QtMultimedia 5.0
@@ -30,7 +31,6 @@
import "../components/HeadState"
import "../components/ListItemActions"
-
MusicPage {
id: songsPage
objectName: "songsPage"
@@ -64,7 +64,13 @@
bottomMargin: units.gu(2)
fill: parent
topMargin: units.gu(2)
+ rightMargin: fastScroll.showing ? fastScroll.width - units.gu(1)
+ : 0
}
+
+ clip: true
+ currentIndex: -1
+
objectName: "trackstab-listview"
model: SortFilterModel {
id: songsModelFilter
@@ -90,6 +96,16 @@
}
}
+ section.property: "title"
+ section.criteria: ViewSection.FirstCharacter
+ section.delegate: ListItem.Standard {
+ text: section
+ }
+
+ function getSectionText(index) {
+ return model.get(index).title.substring(0,1)
+ }
+
delegate: MusicListItem {
id: track
objectName: "tracksPageListItem" + index
@@ -126,5 +142,15 @@
}
}
}
+
+ FastScroll {
+ id: fastScroll
+
+ listView: tracklist
+ anchors {
+ right: parent.right
+ verticalCenter: tracklist.verticalCenter
+ }
+ }
}
Follow ups