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