on 02fa86a help sign in
unity8/qml/Panel/IndicatorItemRow.qml
297 lines 10 kB history raw
1/*
2 * Copyright (C) 2013-2014 Canonical, Ltd.
3 *
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.
7 *
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.
12 *
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/>.
15 */
17import QtQuick 2.2
18import Ubuntu.Components 1.1
20Item {
21 id: root
22 width: row.width
23 height: units.gu(3)
25 property QtObject indicatorsModel: null
26 property real overFlowWidth: width
27 property bool expanded: false
28 property var currentItem
29 readonly property int currentItemIndex: currentItem ? currentItem.ownIndex : -1
31 property real unitProgress: 0.0
32 property real selectionChangeBuffer: units.gu(2)
33 property bool enableLateralChanges: false
34 property color hightlightColor: "#ededed"
36 property real lateralPosition: -1
37 onLateralPositionChanged: {
38 updateItemFromLateralPosition();
39 }
41 onEnableLateralChangesChanged: {
42 updateItemFromLateralPosition();
43 }
45 function updateItemFromLateralPosition() {
46 if (currentItem && !enableLateralChanges) return;
47 if (lateralPosition === -1) return;
49 if (!currentItem) {
50 selectItemAt(lateralPosition);
51 return;
52 }
54 var maximumBufferOffset = selectionChangeBuffer * unitProgress;
55 var proposedItem = indicatorAt(lateralPosition, 0);
56 if (proposedItem) {
57 var bufferExceeded = false;
59 if (proposedItem !== currentItem) {
60 // Proposed item is not directly adjacent to current?
61 if (Math.abs(proposedItem.ownIndex - currentItem.ownIndex) > 1) {
62 bufferExceeded = true;
63 } else { // no
64 var currentItemLateralPosition = root.mapToItem(proposedItem, lateralPosition, 0).x;
66 // Is the distance into proposed item greater than max buffer?
67 // Proposed item is before current item
68 if (proposedItem.x < currentItem.x) {
69 bufferExceeded = (proposedItem.width - currentItemLateralPosition) > maximumBufferOffset;
70 } else { // After
71 bufferExceeded = currentItemLateralPosition > maximumBufferOffset;
72 }
73 }
74 if (bufferExceeded) {
75 selectItemAt(lateralPosition);
76 }
77 }
78 } else {
79 selectItemAt(lateralPosition);
80 }
81 }
83 function indicatorAt(x, y) {
84 var item = row.childAt(x, y);
85 return item && item.hasOwnProperty("ownIndex") ? item : null;
86 }
88 function resetCurrentItem() {
89 d.firstItemSwitch = true;
90 d.previousItem = undefined;
91 currentItem = undefined;
92 }
94 function setCurrentItemIndex(index) {
95 for (var i = 0; i < row.children.length; i++) {
96 var item = row.children[i];
97 if (item.hasOwnProperty("ownIndex") && item.ownIndex === index) {
98 if (currentItem !== item) currentItem = item;
99 break;
100 }
101 }
102 }
104 function selectItemAt(lateralPosition) {
105 var item = indicatorAt(lateralPosition, 0);
106 if (item && item.opacity > 0) {
107 currentItem = item;
108 } else {
109 // Select default item.
110 var searchIndex = lateralPosition > width ? repeater.count - 1 : 0;
112 for (var i = 0; i < row.children.length; i++) {
113 if (row.children[i].hasOwnProperty("ownIndex") && row.children[i].ownIndex === searchIndex) {
114 item = row.children[i];
115 break;
116 }
117 }
118 if (currentItem !== item) currentItem = item;
119 }
120 }
122 QtObject {
123 id: d
124 property bool firstItemSwitch: true
125 property var previousItem
126 property bool forceAlignmentAnimationDisabled: false
127 }
129 onCurrentItemChanged: {
130 if (d.previousItem) {
131 d.firstItemSwitch = false;
132 }
133 d.previousItem = currentItem;
134 }
136 Row {
137 id: row
138 anchors {
139 top: parent.top
140 bottom: parent.bottom
141 }
143 // TODO: make this better
144 // when the width changes, the highlight will lag behind due to animation, so we need to disable the animation
145 // and adjust the highlight X immediately.
146 width: implicitWidth
147 Behavior on width {
148 SequentialAnimation {
149 ScriptAction {
150 script: {
151 d.forceAlignmentAnimationDisabled = true;
152 highlight.currentItemX = Qt.binding(function() { return currentItem ? currentItem.x : 0 });
153 d.forceAlignmentAnimationDisabled = false;
154 }
155 }
156 }
157 }
159 Repeater {
160 id: repeater
161 model: indicatorsModel
162 visible: false
164 onItemRemoved: {
165 // current item removed.
166 if (currentItem === item) {
167 var i = 0;
168 while (i < row.children.length) {
169 var childItem = row.children[i];
170 if (childItem !== item) {
171 setCurrentItemIndex(i);
172 break;
173 }
174 i++;
175 }
176 }
177 }
180 delegate: IndicatorItem {
181 id: indicatorItem
182 objectName: identifier+"-panelItem"
184 property int ownIndex: index
185 property bool overflow: row.width - x > overFlowWidth
186 property bool hidden: !expanded && (overflow || !indicatorVisible)
188 height: row.height
189 expanded: root.expanded
190 selected: currentItem === this
192 identifier: model.identifier
193 busName: indicatorProperties.busName
194 actionsObjectPath: indicatorProperties.actionsObjectPath
195 menuObjectPath: indicatorProperties.menuObjectPath
197 opacity: hidden ? 0.0 : 1.0
198 Behavior on opacity {
199 NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing }
200 }
202 width: (expanded || indicatorVisible) ? implicitWidth : 0
204 Behavior on width {
205 NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing }
206 }
208 Component.onDestruction: {
209 // current item removed.
210 if (currentItem === this) {
211 var i = 0;
212 while (i < row.children.length) {
213 var childItem = row.children[i];
214 if (childItem !== this) {
215 setCurrentItemIndex(i);
216 break;
217 }
218 i++;
219 }
220 }
221 }
222 }
223 }
224 }
226 Rectangle {
227 id: highlight
228 objectName: "highlight"
230 anchors.bottom: row.bottom
231 height: units.dp(2)
232 color: root.hightlightColor
233 visible: currentItem !== undefined
234 opacity: 0.0
236 width: currentItem ? currentItem.width : 0
237 Behavior on width {
238 enabled: !d.firstItemSwitch && expanded
239 UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
240 }
242 // micromovements of the highlight line when user moves the finger across the items while pulling
243 // the handle downwards.
244 property real highlightCenterOffset: {
245 if (!currentItem || lateralPosition == -1 || !enableLateralChanges) return 0;
247 var itemMapped = root.mapToItem(currentItem, lateralPosition, 0);
249 var distanceFromCenter = itemMapped.x - currentItem.width / 2;
250 if (distanceFromCenter > 0) {
251 distanceFromCenter = Math.max(0, distanceFromCenter - currentItem.width / 8);
252 } else {
253 distanceFromCenter = Math.min(0, distanceFromCenter + currentItem.width / 8);
254 }
256 if (currentItem && currentItem.ownIndex === 0 && distanceFromCenter < 0) {
257 return 0;
258 } else if (currentItem && currentItem.ownIndex === repeater.count-1 & distanceFromCenter > 0) {
259 return 0;
260 }
261 return (distanceFromCenter / (currentItem.width / 4)) * units.gu(1);
262 }
263 Behavior on highlightCenterOffset {
264 NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
265 }
267 property real currentItemX: currentItem ? currentItem.x : 0
268 Behavior on currentItemX {
269 id: currentItemXBehavior
270 enabled: !d.firstItemSwitch && expanded && !d.forceAlignmentAnimationDisabled
271 NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
272 }
273 x: currentItemX + highlightCenterOffset
274 }
276 states: [
277 State {
278 name: "minimised"
279 when: !expanded
280 },
281 State {
282 name: "expanded"
283 when: expanded
284 PropertyChanges { target: highlight; opacity: 0.9 }
285 }
286 ]
288 transitions: [
289 Transition {
290 PropertyAnimation {
291 properties: "opacity";
292 duration: UbuntuAnimation.SnapDuration
293 easing: UbuntuAnimation.StandardEasing
294 }
295 }
296 ]