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