Unity 8
IndicatorsBar.qml
1 /*
2  * Copyright (C) 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  */
16 
17 import QtQuick 2.4
18 import Ubuntu.Components 1.3
19 import "../Components"
20 
21 Item {
22  id: root
23  property alias expanded: row.expanded
24  property alias interactive: flickable.interactive
25  property alias indicatorsModel: row.indicatorsModel
26  property alias unitProgress: row.unitProgress
27  property alias enableLateralChanges: row.enableLateralChanges
28  property alias overFlowWidth: row.overFlowWidth
29  readonly property alias currentItemIndex: row.currentItemIndex
30  property real lateralPosition: -1
31  readonly property string currentIndicator: row.currentItem ? row.currentItem.identifier : ""
32 
33  function selectItemAt(lateralPosition) {
34  if (!expanded) {
35  row.resetCurrentItem();
36  }
37  var mapped = root.mapToItem(row, lateralPosition, 0);
38  row.selectItemAt(mapped.x);
39  }
40 
41  function setCurrentItemIndex(index) {
42  if (!expanded) {
43  row.resetCurrentItem();
44  }
45  row.setCurrentItemIndex(index);
46  }
47 
48  function addScrollOffset(scrollAmmout) {
49  if (scrollAmmout < 0) { // left scroll
50  if (flickable.contentX + flickable.width > row.width) return; // already off the left.
51 
52  if (flickable.contentX + flickable.width - scrollAmmout > row.width) { // going to be off the right
53  scrollAmmout = (flickable.contentX + flickable.width) - row.width;
54  }
55  } else { // right scroll
56  if (flickable.contentX < 0) return; // already off the right.
57  if (flickable.contentX - scrollAmmout < 0) scrollAmmout = flickable.contentX; // going to be off the right
58  }
59  d.scrollOffset = d.scrollOffset + scrollAmmout;
60  }
61 
62  QtObject {
63  id: d
64  property var initialItem
65  // the non-expanded distance from row offset to center of initial item
66  property real originalDistanceFromRight: -1
67 
68  // calculate the distance from row offset to center of initial item
69  property real distanceFromRight: {
70  if (originalDistanceFromRight == -1) return 0;
71  if (!initialItem) return 0;
72  return row.width - initialItem.x - initialItem.width /2;
73  }
74 
75  // offset to the intially selected expanded item
76  property real rowOffset: 0
77  property real scrollOffset: 0
78  property real alignmentAdjustment: 0
79  property real combinedOffset: 0
80 
81  // when the scroll offset changes, we need to reclaculate the relative lateral position
82  onScrollOffsetChanged: root.lateralPositionChanged()
83 
84  onInitialItemChanged: {
85  originalDistanceFromRight = initialItem ? (row.width - initialItem.x - initialItem.width/2) : -1;
86  }
87 
88  Behavior on alignmentAdjustment {
89  NumberAnimation { duration: UbuntuAnimation.BriskDuration; easing: UbuntuAnimation.StandardEasing}
90  }
91 
92  function alignIndicators() {
93  flickable.resetContentXComponents();
94 
95  if (expanded && !flickable.moving) {
96  // gap between left and row?
97  if (flickable.contentX + flickable.width > row.width) {
98  // row width is less than flickable
99  if (row.width < flickable.width) {
100  d.alignmentAdjustment -= flickable.contentX;
101  } else {
102  d.alignmentAdjustment -= ((flickable.contentX + flickable.width) - row.width);
103  }
104 
105  // gap between right and row?
106  } else if (flickable.contentX < 0) {
107  d.alignmentAdjustment -= flickable.contentX;
108 
109  // current item overlap on left
110  } else if (row.currentItem && (flickable.contentX + flickable.width) < (row.width - row.currentItem.x)) {
111  d.alignmentAdjustment += ((row.width - row.currentItem.x) - (flickable.contentX + flickable.width));
112 
113  // current item overlap on right
114  } else if (row.currentItem && flickable.contentX > (row.width - row.currentItem.x - row.currentItem.width)) {
115  d.alignmentAdjustment -= flickable.contentX - (row.width - row.currentItem.x - row.currentItem.width);
116  }
117  }
118  }
119  }
120 
121  Rectangle {
122  id: grayLine
123  height: units.dp(2)
124  width: parent.width
125  anchors.bottom: parent.bottom
126 
127  color: "#888888"
128  opacity: expanded ? 1.0 : 0.0
129  Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } }
130  }
131 
132  Item {
133  id: rowContainer
134  anchors.fill: parent
135  clip: expanded || row.width > rowContainer.width
136 
137  Flickable {
138  id: flickable
139  objectName: "flickable"
140 
141  // we rotate it because we want the Flickable to align its content item
142  // on the right instead of on the left
143  rotation: 180
144 
145  anchors.fill: parent
146  contentWidth: row.width
147  contentX: d.combinedOffset
148  interactive: false
149 
150  // contentX can change by user interaction as well as user offset changes
151  // This function re-aligns the offsets so that the offsets match the contentX
152  function resetContentXComponents() {
153  d.scrollOffset += d.combinedOffset - flickable.contentX;
154  }
155 
156  rebound: Transition {
157  NumberAnimation {
158  properties: "x"
159  duration: 600
160  easing.type: Easing.OutCubic
161  }
162  }
163 
164  IndicatorItemRow {
165  id: row
166  objectName: "indicatorItemRow"
167  anchors {
168  top: parent.top
169  bottom: parent.bottom
170  }
171 
172  // Compensate for the Flickable rotation (ie, counter-rotate)
173  rotation: 180
174 
175  lateralPosition: {
176  if (root.lateralPosition == -1) return -1;
177 
178  var mapped = root.mapToItem(row, root.lateralPosition, 0);
179  return Math.min(Math.max(mapped.x, 0), row.width);
180  }
181 
182  onCurrentItemChanged: {
183  if (!currentItem) d.initialItem = undefined;
184  else if (!d.initialItem) d.initialItem = currentItem;
185  }
186 
187  MouseArea {
188  anchors.fill: parent
189  enabled: root.expanded
190  onClicked: {
191  row.selectItemAt(mouse.x);
192  d.alignIndicators();
193  }
194  }
195  }
196 
197  }
198  }
199 
200  Timer {
201  id: alignmentTimer
202  interval: UbuntuAnimation.FastDuration // enough for row animation.
203  repeat: false
204 
205  onTriggered: d.alignIndicators();
206  }
207 
208  states: [
209  State {
210  name: "minimized"
211  when: !expanded
212  PropertyChanges {
213  target: d
214  rowOffset: 0
215  scrollOffset: 0
216  alignmentAdjustment: 0
217  combinedOffset: 0
218  restoreEntryValues: false
219  }
220  },
221  State {
222  name: "expanded"
223  when: expanded && !interactive
224 
225  PropertyChanges {
226  target: d
227  combinedOffset: rowOffset + alignmentAdjustment - scrollOffset
228  }
229  PropertyChanges {
230  target: d
231  rowOffset: {
232  if (!initialItem) return 0;
233  if (distanceFromRight - initialItem.width <= 0) return 0;
234 
235  var rowOffset = distanceFromRight - originalDistanceFromRight;
236  return rowOffset;
237  }
238  restoreEntryValues: false
239  }
240  }
241  , State {
242  name: "interactive"
243  when: expanded && interactive
244 
245  StateChangeScript {
246  script: {
247  // don't use row offset anymore.
248  d.scrollOffset -= d.rowOffset;
249  d.rowOffset = 0;
250  d.initialItem = undefined;
251  alignmentTimer.start();
252  }
253  }
254  PropertyChanges {
255  target: d
256  combinedOffset: rowOffset + alignmentAdjustment - scrollOffset
257  restoreEntryValues: false
258  }
259  }
260  ]
261 
262  transitions: [
263  Transition {
264  from: "expanded"
265  to: "minimized"
266  PropertyAction {
267  target: d
268  properties: "rowOffset, scrollOffset, alignmentAdjustment"
269  value: 0
270  }
271  PropertyAnimation {
272  target: d
273  properties: "combinedOffset"
274  duration: UbuntuAnimation.SnapDuration
275  easing: UbuntuAnimation.StandardEasing
276  }
277  }
278  ]
279 }