on e1659ca help sign in
unity8/qml/Panel/PanelBar.qml
312 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 showRowTitle: row.showRowTitle
34 property alias rowTitle: row.rowTitle
35 property alias rowItemDelegate: row.delegate
37 implicitWidth: flickable.contentWidth
39 function selectItemAt(lateralPosition) {
40 if (!expanded) {
41 row.resetCurrentItem();
42 }
43 var mapped = root.mapToItem(row, lateralPosition, 0);
44 row.selectItemAt(mapped.x);
45 }
47 function setCurrentItemIndex(index) {
48 if (!expanded) {
49 row.resetCurrentItem();
50 }
51 row.setCurrentItemIndex(index);
52 }
54 function addScrollOffset(scrollAmmout) {
55 if (root.alignment == Qt.AlignLeft) {
56 scrollAmmout = -scrollAmmout;
57 }
59 if (scrollAmmout < 0) { // left scroll
60 if (flickable.contentX + flickable.width > row.width) return; // already off the left.
62 if (flickable.contentX + flickable.width - scrollAmmout > row.width) { // going to be off the left
63 scrollAmmout = (flickable.contentX + flickable.width) - row.width;
64 }
65 } else { // right scroll
66 if (flickable.contentX < 0) return; // already off the right.
67 if (flickable.contentX - scrollAmmout < 0) { // going to be off the right
68 scrollAmmout = flickable.contentX;
69 }
70 }
71 d.scrollOffset = d.scrollOffset + scrollAmmout;
72 }
74 QtObject {
75 id: d
76 property var initialItem
77 // the non-expanded distance from alignment edge to center of initial item
78 property real originalDistanceFromEdge: -1
80 // calculate the distance from row alignment edge edge to center of initial item
81 property real distanceFromEdge: {
82 if (originalDistanceFromEdge == -1) return 0;
83 if (!initialItem) return 0;
85 if (root.alignment == Qt.AlignLeft) {
86 return initialItem.x - initialItem.width / 2;
87 } else {
88 return row.width - initialItem.x - initialItem.width / 2;
89 }
90 }
92 // offset to the intially selected expanded item
93 property real rowOffset: 0
94 property real scrollOffset: 0
95 property real alignmentAdjustment: 0
96 property real combinedOffset: 0
98 // when the scroll offset changes, we need to reclaculate the relative lateral position
99 onScrollOffsetChanged: root.lateralPositionChanged()
101 onInitialItemChanged: {
102 if (root.alignment == Qt.AlignLeft) {
103 originalDistanceFromEdge = initialItem ? (initialItem.x - initialItem.width/2) : -1;
104 } else {
105 originalDistanceFromEdge = initialItem ? (row.width - initialItem.x - initialItem.width/2) : -1;
106 }
107 }
109 Behavior on alignmentAdjustment {
110 NumberAnimation { duration: UbuntuAnimation.BriskDuration; easing: UbuntuAnimation.StandardEasing}
111 }
113 function alignIndicators() {
114 flickable.resetContentXComponents();
116 if (expanded && !flickable.moving) {
118 if (root.alignment == Qt.AlignLeft) {
119 // current item overlap on left
120 if (row.currentItem && flickable.contentX > row.currentItem.x) {
121 d.alignmentAdjustment -= (flickable.contentX - row.currentItem.x);
123 // current item overlap on right
124 } else if (row.currentItem && flickable.contentX + flickable.width < row.currentItem.x + row.currentItem.width) {
125 d.alignmentAdjustment += (row.currentItem.x + row.currentItem.width) - (flickable.contentX + flickable.width);
126 }
127 } else {
128 // gap between left and row?
129 if (flickable.contentX + flickable.width > row.width) {
130 // row width is less than flickable
131 if (row.width < flickable.width) {
132 d.alignmentAdjustment -= flickable.contentX;
133 } else {
134 d.alignmentAdjustment -= ((flickable.contentX + flickable.width) - row.width);
135 }
137 // gap between right and row?
138 } else if (flickable.contentX < 0) {
139 d.alignmentAdjustment -= flickable.contentX;
141 // current item overlap on left
142 } else if (row.currentItem && (flickable.contentX + flickable.width) < (row.width - row.currentItem.x)) {
143 d.alignmentAdjustment += ((row.width - row.currentItem.x) - (flickable.contentX + flickable.width));
145 // current item overlap on right
146 } else if (row.currentItem && flickable.contentX > (row.width - row.currentItem.x - row.currentItem.width)) {
147 d.alignmentAdjustment -= flickable.contentX - (row.width - row.currentItem.x - row.currentItem.width);
148 }
149 }
150 }
151 }
152 }
154 Rectangle {
155 id: grayLine
156 height: units.dp(2)
157 width: parent.width
158 anchors.bottom: parent.bottom
160 color: "#888888"
161 opacity: expanded ? 1.0 : 0.0
162 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } }
163 }
165 Item {
166 id: rowContainer
167 anchors.fill: parent
168 clip: expanded || row.width > rowContainer.width
170 Flickable {
171 id: flickable
172 objectName: "flickable"
174 // we rotate it because we want the Flickable to align its content item
175 // on the right instead of on the left
176 rotation: root.alignment != Qt.AlignRight ? 0 : 180
178 anchors.fill: parent
179 contentWidth: row.width
180 contentX: d.combinedOffset
181 interactive: false
183 // contentX can change by user interaction as well as user offset changes
184 // This function re-aligns the offsets so that the offsets match the contentX
185 function resetContentXComponents() {
186 d.scrollOffset += d.combinedOffset - flickable.contentX;
187 }
189 rebound: Transition {
190 NumberAnimation {
191 properties: "x"
192 duration: 600
193 easing.type: Easing.OutCubic
194 }
195 }
197 PanelItemRow {
198 id: row
199 objectName: "panelItemRow"
200 anchors {
201 top: parent.top
202 bottom: parent.bottom
203 }
205 // Compensate for the Flickable rotation (ie, counter-rotate)
206 rotation: root.alignment != Qt.AlignRight ? 0 : 180
208 lateralPosition: {
209 if (root.lateralPosition == -1) return -1;
211 var mapped = root.mapToItem(row, root.lateralPosition, 0);
212 return Math.min(Math.max(mapped.x, 0), row.width);
213 }
215 onCurrentItemChanged: {
216 if (!currentItem) d.initialItem = undefined;
217 else if (!d.initialItem) d.initialItem = currentItem;
218 }
220 MouseArea {
221 anchors.fill: parent
222 enabled: root.expanded
223 onClicked: {
224 row.selectItemAt(mouse.x);
225 d.alignIndicators();
226 }
227 }
228 }
230 }
231 }
233 Timer {
234 id: alignmentTimer
235 interval: UbuntuAnimation.FastDuration // enough for row animation.
236 repeat: false
238 onTriggered: d.alignIndicators();
239 }
241 states: [
242 State {
243 name: "minimized"
244 when: !expanded
245 PropertyChanges {
246 target: d
247 rowOffset: 0
248 scrollOffset: 0
249 alignmentAdjustment: 0
250 combinedOffset: 0
251 restoreEntryValues: false
252 }
253 },
254 State {
255 name: "expanded"
256 when: expanded && !interactive
258 PropertyChanges {
259 target: d
260 combinedOffset: rowOffset + alignmentAdjustment - scrollOffset
261 }
262 PropertyChanges {
263 target: d
264 rowOffset: {
265 if (!initialItem) return 0;
266 if (distanceFromEdge - initialItem.width <= 0) return 0;
268 var rowOffset = distanceFromEdge - originalDistanceFromEdge;
269 return rowOffset;
270 }
271 restoreEntryValues: false
272 }
273 },
274 State {
275 name: "interactive"
276 when: expanded && interactive
278 StateChangeScript {
279 script: {
280 // don't use row offset anymore.
281 d.scrollOffset -= d.rowOffset;
282 d.rowOffset = 0;
283 d.initialItem = undefined;
284 alignmentTimer.start();
285 }
286 }
287 PropertyChanges {
288 target: d
289 combinedOffset: rowOffset + alignmentAdjustment - scrollOffset
290 restoreEntryValues: false
291 }
292 }
293 ]
295 transitions: [
296 Transition {
297 from: "expanded"
298 to: "minimized"
299 PropertyAction {
300 target: d
301 properties: "rowOffset, scrollOffset, alignmentAdjustment"
302 value: 0
303 }
304 PropertyAnimation {
305 target: d
306 properties: "combinedOffset"
307 duration: UbuntuAnimation.SnapDuration
308 easing: UbuntuAnimation.StandardEasing
309 }
310 }
311 ]