2 * Copyright (C) 2013-2014 Canonical, Ltd.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 import Ubuntu.Components 1.3
19 import "../Components"
23 implicitWidth: row.width
24 implicitHeight: units.gu(3)
26 property bool hideRow: false
27 property QtObject model: null
28 property real overFlowWidth: width
29 property bool expanded: false
30 readonly property alias currentItem: row.currentItem
31 readonly property alias currentItemIndex: row.currentIndex
33 property real unitProgress: 0.0
34 property real selectionChangeBuffer: units.gu(2)
35 property bool enableLateralChanges: false
36 property color hightlightColor: "#ffffff"
38 property alias delegate: row.delegate
40 property real lateralPosition: -1
41 onLateralPositionChanged: {
42 updateItemFromLateralPosition();
45 onEnableLateralChangesChanged: {
46 updateItemFromLateralPosition();
49 function updateItemFromLateralPosition() {
50 if (currentItem && !enableLateralChanges) return;
51 if (lateralPosition === -1) return;
54 selectItemAt(lateralPosition);
58 var maximumBufferOffset = selectionChangeBuffer * unitProgress;
59 var proposedItem = indicatorAt(lateralPosition, 0);
61 var bufferExceeded = false;
63 if (proposedItem !== currentItem) {
64 // Proposed item is not directly adjacent to current?
65 if (Math.abs(proposedItem.ownIndex - currentItem.ownIndex) > 1) {
66 bufferExceeded = true;
68 var currentItemLateralPosition = root.mapToItem(proposedItem, lateralPosition, 0).x;
70 // Is the distance into proposed item greater than max buffer?
71 // Proposed item is before current item
72 if (proposedItem.x < currentItem.x) {
73 bufferExceeded = (proposedItem.width - currentItemLateralPosition) > maximumBufferOffset;
75 bufferExceeded = currentItemLateralPosition > maximumBufferOffset;
79 selectItemAt(lateralPosition);
83 selectItemAt(lateralPosition);
87 function indicatorAt(x, y) {
88 var item = row.itemAt(x, y);
89 return item && item.hasOwnProperty("ownIndex") ? item : null;
92 function resetCurrentItem() {
93 d.firstItemSwitch = true;
94 d.previousItem = null;
95 row.currentIndex = -1;
98 function selectPreviousItem() {
99 var indexToSelect = currentItemIndex - 1;
100 while (indexToSelect >= 0) {
101 if (setCurrentItemIndex(indexToSelect))
103 indexToSelect = indexToSelect - 1;
107 function selectNextItem() {
108 var indexToSelect = currentItemIndex + 1;
109 while (indexToSelect < row.contentItem.children.length) {
110 if (setCurrentItemIndex(indexToSelect))
112 indexToSelect = indexToSelect + 1;
116 function setCurrentItemIndex(index) {
117 for (var i = 0; i < row.contentItem.children.length; i++) {
118 var item = row.contentItem.children[i];
119 if (item.hasOwnProperty("ownIndex") && item.ownIndex === index && item.enabled) {
120 if (currentItem !== item) {
121 row.currentIndex = index;
129 function selectItemAt(lateralPosition) {
130 var item = indicatorAt(lateralPosition, 0);
131 if (item && item.opacity > 0 && item.enabled) {
132 row.currentIndex = item.ownIndex;
134 // Select default item.
135 var searchIndex = lateralPosition >= width ? row.count - 1 : 0;
137 for (var i = 0; i < row.contentItem.children.length; i++) {
138 if (row.contentItem.children[i].hasOwnProperty("ownIndex") &&
139 row.contentItem.children[i].ownIndex === searchIndex &&
140 row.contentItem.children[i].enabled)
142 item = row.contentItem.children[i];
146 if (item && currentItem !== item) {
147 row.currentIndex = item.ownIndex;
154 property bool firstItemSwitch: true
155 property var previousItem
156 property bool forceAlignmentAnimationDisabled: false
159 onCurrentItemChanged: {
160 if (d.previousItem) {
161 d.firstItemSwitch = false;
163 d.previousItem = currentItem;
168 objectName: "panelRow"
169 orientation: ListView.Horizontal
171 opacity: hideRow ? 0 : 1
172 // dont set visible on basis of opacity; otherwise width will not be calculated correctly
175 bottom: parent.bottom
178 // TODO: make this better
179 // when the width changes, the highlight will lag behind due to animation, so we need to disable the animation
180 // and adjust the highlight X immediately.
181 width: childrenRect.width
183 SequentialAnimation {
186 d.forceAlignmentAnimationDisabled = true;
187 highlight.currentItemX = Qt.binding(function() { return currentItem ? currentItem.x : 0 });
188 d.forceAlignmentAnimationDisabled = false;
194 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } }
199 objectName: "highlight"
201 anchors.bottom: row.bottom
203 color: root.hightlightColor
204 visible: currentItem !== null
207 width: currentItem ? currentItem.width : 0
209 enabled: !d.firstItemSwitch && expanded
210 UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
213 // micromovements of the highlight line when user moves the finger across the items while pulling
214 // the handle downwards.
215 property real highlightCenterOffset: {
216 if (!currentItem || lateralPosition == -1 || !enableLateralChanges) return 0;
218 var itemMapped = root.mapToItem(currentItem, lateralPosition, 0);
220 var distanceFromCenter = itemMapped.x - currentItem.width / 2;
221 if (distanceFromCenter > 0) {
222 distanceFromCenter = Math.max(0, distanceFromCenter - currentItem.width / 8);
224 distanceFromCenter = Math.min(0, distanceFromCenter + currentItem.width / 8);
227 if (currentItem && currentItem.ownIndex === 0 && distanceFromCenter < 0) {
229 } else if (currentItem && currentItem.ownIndex === row.count-1 & distanceFromCenter > 0) {
232 return (distanceFromCenter / (currentItem.width / 4)) * units.gu(1);
234 Behavior on highlightCenterOffset {
235 NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
238 property real currentItemX: currentItem ? currentItem.x : 0
239 Behavior on currentItemX {
240 id: currentItemXBehavior
241 enabled: !d.firstItemSwitch && expanded && !d.forceAlignmentAnimationDisabled
242 NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
244 x: currentItemX + highlightCenterOffset
255 PropertyChanges { target: highlight; opacity: 0.9 }
262 properties: "opacity";
263 duration: UbuntuAnimation.SnapDuration
264 easing: UbuntuAnimation.StandardEasing