on c08b89c help sign in
unity8/qml/Panel/PanelBar.qml
328 lines 11 kB history raw
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 */
17import QtQuick 2.4
18import Ubuntu.Components 1.3
19import "../Components"
21Item {
22 id: root
23 property alias expanded: row.expanded
24 property alias interactive: flickable.interactive
25 property alias model: row.model
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 property int alignment: Qt.AlignRight
33 property alias hideRow: row.hideRow
34 property alias rowItemDelegate: row.delegate
36 implicitWidth: flickable.contentWidth
38 function selectItemAt(lateralPosition) {
39 if (!expanded) {
40 row.resetCurrentItem();
41 }
42 var mapped = root.mapToItem(row, lateralPosition, 0);
43 row.selectItemAt(mapped.x);
44 }
46 function selectPreviousItem() {
47 if (!expanded) {
48 row.resetCurrentItem();
49 }
50 row.selectPreviousItem();
51 d.alignIndicators();
52 }
54 function selectNextItem() {
55 if (!expanded) {
56 row.resetCurrentItem();
57 }
58 row.selectNextItem();
59 d.alignIndicators();
60 }
62 function setCurrentItemIndex(index) {
63 if (!expanded) {
64 row.resetCurrentItem();
65 }
66 row.setCurrentItemIndex(index);
67 d.alignIndicators();
68 }
70 function addScrollOffset(scrollAmmout) {
71 if (root.alignment == Qt.AlignLeft) {
72 scrollAmmout = -scrollAmmout;
73 }
75 if (scrollAmmout < 0) { // left scroll
76 if (flickable.contentX + flickable.width > row.width) return; // already off the left.
78 if (flickable.contentX + flickable.width - scrollAmmout > row.width) { // going to be off the left
79 scrollAmmout = (flickable.contentX + flickable.width) - row.width;
80 }
81 } else { // right scroll
82 if (flickable.contentX < 0) return; // already off the right.
83 if (flickable.contentX - scrollAmmout < 0) { // going to be off the right
84 scrollAmmout = flickable.contentX;
85 }
86 }
87 d.scrollOffset = d.scrollOffset + scrollAmmout;
88 }
90 QtObject {
91 id: d
92 property var initialItem
93 // the non-expanded distance from alignment edge to center of initial item
94 property real originalDistanceFromEdge: -1
96 // calculate the distance from row alignment edge edge to center of initial item
97 property real distanceFromEdge: {
98 if (originalDistanceFromEdge == -1) return 0;
99 if (!initialItem) return 0;
101 if (root.alignment == Qt.AlignLeft) {
102 return initialItem.x - initialItem.width / 2;
103 } else {
104 return row.width - initialItem.x - initialItem.width / 2;
105 }
106 }
108 // offset to the intially selected expanded item
109 property real rowOffset: 0
110 property real scrollOffset: 0
111 property real alignmentAdjustment: 0
112 property real combinedOffset: 0
114 // when the scroll offset changes, we need to reclaculate the relative lateral position
115 onScrollOffsetChanged: root.lateralPositionChanged()
117 onInitialItemChanged: {
118 if (root.alignment == Qt.AlignLeft) {
119 originalDistanceFromEdge = initialItem ? (initialItem.x - initialItem.width/2) : -1;
120 } else {
121 originalDistanceFromEdge = initialItem ? (row.width - initialItem.x - initialItem.width/2) : -1;
122 }
123 }
125 Behavior on alignmentAdjustment {
126 NumberAnimation { duration: UbuntuAnimation.BriskDuration; easing: UbuntuAnimation.StandardEasing}
127 }
129 function alignIndicators() {
130 flickable.resetContentXComponents();
132 if (expanded && !flickable.moving) {
134 if (root.alignment == Qt.AlignLeft) {
135 // current item overlap on left
136 if (row.currentItem && flickable.contentX > row.currentItem.x) {
137 d.alignmentAdjustment -= (flickable.contentX - row.currentItem.x);
139 // current item overlap on right
140 } else if (row.currentItem && flickable.contentX + flickable.width < row.currentItem.x + row.currentItem.width) {
141 d.alignmentAdjustment += (row.currentItem.x + row.currentItem.width) - (flickable.contentX + flickable.width);
142 }
143 } else {
144 // gap between left and row?
145 if (flickable.contentX + flickable.width > row.width) {
146 // row width is less than flickable
147 if (row.width < flickable.width) {
148 d.alignmentAdjustment -= flickable.contentX;
149 } else {
150 d.alignmentAdjustment -= ((flickable.contentX + flickable.width) - row.width);
151 }
153 // gap between right and row?
154 } else if (flickable.contentX < 0) {
155 d.alignmentAdjustment -= flickable.contentX;
157 // current item overlap on left
158 } else if (row.currentItem && (flickable.contentX + flickable.width) < (row.width - row.currentItem.x)) {
159 d.alignmentAdjustment += ((row.width - row.currentItem.x) - (flickable.contentX + flickable.width));
161 // current item overlap on right
162 } else if (row.currentItem && flickable.contentX > (row.width - row.currentItem.x - row.currentItem.width)) {
163 d.alignmentAdjustment -= flickable.contentX - (row.width - row.currentItem.x - row.currentItem.width);
164 }
165 }
166 }
167 }
168 }
170 Rectangle {
171 id: grayLine
172 height: units.dp(2)
173 width: parent.width
174 anchors.bottom: parent.bottom
176 color: "#888888"
177 opacity: expanded ? 1.0 : 0.0
178 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } }
179 }
181 Item {
182 id: rowContainer
183 anchors.fill: parent
184 clip: expanded || row.width > rowContainer.width
186 Flickable {
187 id: flickable
188 objectName: "flickable"
190 // we rotate it because we want the Flickable to align its content item
191 // on the right instead of on the left
192 rotation: root.alignment != Qt.AlignRight ? 0 : 180
194 anchors.fill: parent
195 contentWidth: row.width
196 contentX: d.combinedOffset
197 interactive: false
199 // contentX can change by user interaction as well as user offset changes
200 // This function re-aligns the offsets so that the offsets match the contentX
201 function resetContentXComponents() {
202 d.scrollOffset += d.combinedOffset - flickable.contentX;
203 }
205 rebound: Transition {
206 NumberAnimation {
207 properties: "x"
208 duration: 600
209 easing.type: Easing.OutCubic
210 }
211 }
213 PanelItemRow {
214 id: row
215 objectName: "panelItemRow"
216 anchors {
217 top: parent.top
218 bottom: parent.bottom
219 }
221 // Compensate for the Flickable rotation (ie, counter-rotate)
222 rotation: root.alignment != Qt.AlignRight ? 0 : 180
224 lateralPosition: {
225 if (root.lateralPosition == -1) return -1;
227 var mapped = root.mapToItem(row, root.lateralPosition, 0);
228 return Math.min(Math.max(mapped.x, 0), row.width);
229 }
231 onCurrentItemChanged: {
232 if (!currentItem) d.initialItem = undefined;
233 else if (!d.initialItem) d.initialItem = currentItem;
234 }
236 MouseArea {
237 anchors.fill: parent
238 enabled: root.expanded
239 onClicked: {
240 row.selectItemAt(mouse.x);
241 d.alignIndicators();
242 }
243 }
244 }
246 }
247 }
249 Timer {
250 id: alignmentTimer
251 interval: UbuntuAnimation.FastDuration // enough for row animation.
252 repeat: false
254 onTriggered: d.alignIndicators();
255 }
257 states: [
258 State {
259 name: "minimized"
260 when: !expanded
261 PropertyChanges {
262 target: d
263 rowOffset: 0
264 scrollOffset: 0
265 alignmentAdjustment: 0
266 combinedOffset: 0
267 restoreEntryValues: false
268 }
269 },
270 State {
271 name: "expanded"
272 when: expanded && !interactive
274 PropertyChanges {
275 target: d
276 combinedOffset: rowOffset + alignmentAdjustment - scrollOffset
277 }
278 PropertyChanges {
279 target: d
280 rowOffset: {
281 if (!initialItem) return 0;
282 if (distanceFromEdge - initialItem.width <= 0) return 0;
284 var rowOffset = distanceFromEdge - originalDistanceFromEdge;
285 return rowOffset;
286 }
287 restoreEntryValues: false
288 }
289 },
290 State {
291 name: "interactive"
292 when: expanded && interactive
294 StateChangeScript {
295 script: {
296 // don't use row offset anymore.
297 d.scrollOffset -= d.rowOffset;
298 d.rowOffset = 0;
299 d.initialItem = undefined;
300 alignmentTimer.start();
301 }
302 }
303 PropertyChanges {
304 target: d
305 combinedOffset: rowOffset + alignmentAdjustment - scrollOffset
306 restoreEntryValues: false
307 }
308 }
309 ]
311 transitions: [
312 Transition {
313 from: "expanded"
314 to: "minimized"
315 PropertyAction {
316 target: d
317 properties: "rowOffset, scrollOffset, alignmentAdjustment"
318 value: 0
319 }
320 PropertyAnimation {
321 target: d
322 properties: "combinedOffset"
323 duration: UbuntuAnimation.SnapDuration
324 easing: UbuntuAnimation.StandardEasing
325 }
326 }
327 ]