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