on e1659ca help sign in
unity8/qml/Panel/PanelItemRow.qml
262 lines 9.1 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 Ubuntu.Components 1.3
19import "../Components"
21Item {
22 id: root
23 implicitWidth: showRowTitle && !expanded ? rowTitle != "" ? rowLabel.width : 0 : row.width
24 implicitHeight: units.gu(3)
26 property bool showRowTitle: false
27 property alias rowTitle: rowLabel.text
28 property QtObject model: null
29 property real overFlowWidth: width
30 property bool expanded: false
31 readonly property alias currentItem: row.currentItem
32 readonly property alias currentItemIndex: row.currentIndex
34 property real unitProgress: 0.0
35 property real selectionChangeBuffer: units.gu(2)
36 property bool enableLateralChanges: false
37 property color hightlightColor: "#ffffff"
39 property alias delegate: row.delegate
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.itemAt(x, y);
90 return item && item.hasOwnProperty("ownIndex") ? item : null;
91 }
93 function resetCurrentItem() {
94 d.firstItemSwitch = true;
95 d.previousItem = null;
96 row.currentIndex = -1;
97 }
99 function setCurrentItemIndex(index) {
100 for (var i = 0; i < row.contentItem.children.length; i++) {
101 var item = row.contentItem.children[i];
102 if (item.hasOwnProperty("ownIndex") && item.ownIndex === index) {
103 if (currentItem !== item) row.currentIndex = index;
104 break;
105 }
106 }
107 }
109 function selectItemAt(lateralPosition) {
110 var item = indicatorAt(lateralPosition, 0);
111 if (item && item.opacity > 0) {
112 row.currentIndex = item.ownIndex;
113 } else {
114 // Select default item.
115 var searchIndex = lateralPosition >= width ? row.count - 1 : 0;
117 for (var i = 0; i < row.contentItem.children.length; i++) {
118 if (row.contentItem.children[i].hasOwnProperty("ownIndex") && row.contentItem.children[i].ownIndex === searchIndex) {
119 item = row.contentItem.children[i];
120 break;
121 }
122 }
123 if (currentItem !== item) row.currentIndex = item ? item.ownIndex : -1;
124 }
125 }
127 QtObject {
128 id: d
129 property bool firstItemSwitch: true
130 property var previousItem
131 property bool forceAlignmentAnimationDisabled: false
132 }
134 onCurrentItemChanged: {
135 if (d.previousItem) {
136 d.firstItemSwitch = false;
137 }
138 d.previousItem = currentItem;
139 }
141 Label {
142 id: rowLabel
143 objectName: "panelTitle"
144 anchors {
145 left: parent.left
146 leftMargin: units.gu(1)
147 verticalCenter: parent.verticalCenter
148 }
149 width: implicitWidth + units.gu(2)
150 elide: Text.ElideRight
151 maximumLineCount: 1
152 fontSize: "medium"
153 font.weight: Font.Medium
154 color: Theme.palette.selected.backgroundText
155 opacity: showRowTitle ? 1 : 0
156 visible: opacity != 0
157 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } }
158 }
160 ListView {
161 id: row
162 objectName: "panelRow"
163 orientation: ListView.Horizontal
164 model: root.model
165 opacity: showRowTitle ? 0 : 1
166 // dont set visible on basis of opacity; otherwise width will not be calculated correctly
167 anchors {
168 top: parent.top
169 bottom: parent.bottom
170 }
172 // TODO: make this better
173 // when the width changes, the highlight will lag behind due to animation, so we need to disable the animation
174 // and adjust the highlight X immediately.
175 width: childrenRect.width
176 Behavior on width {
177 SequentialAnimation {
178 ScriptAction {
179 script: {
180 d.forceAlignmentAnimationDisabled = true;
181 highlight.currentItemX = Qt.binding(function() { return currentItem ? currentItem.x : 0 });
182 d.forceAlignmentAnimationDisabled = false;
183 }
184 }
185 }
186 }
188 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } }
189 }
191 Rectangle {
192 id: highlight
193 objectName: "highlight"
195 anchors.bottom: row.bottom
196 height: units.dp(2)
197 color: root.hightlightColor
198 visible: currentItem !== null
199 opacity: 0.0
201 width: currentItem ? currentItem.width : 0
202 Behavior on width {
203 enabled: !d.firstItemSwitch && expanded
204 UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
205 }
207 // micromovements of the highlight line when user moves the finger across the items while pulling
208 // the handle downwards.
209 property real highlightCenterOffset: {
210 if (!currentItem || lateralPosition == -1 || !enableLateralChanges) return 0;
212 var itemMapped = root.mapToItem(currentItem, lateralPosition, 0);
214 var distanceFromCenter = itemMapped.x - currentItem.width / 2;
215 if (distanceFromCenter > 0) {
216 distanceFromCenter = Math.max(0, distanceFromCenter - currentItem.width / 8);
217 } else {
218 distanceFromCenter = Math.min(0, distanceFromCenter + currentItem.width / 8);
219 }
221 if (currentItem && currentItem.ownIndex === 0 && distanceFromCenter < 0) {
222 return 0;
223 } else if (currentItem && currentItem.ownIndex === row.count-1 & distanceFromCenter > 0) {
224 return 0;
225 }
226 return (distanceFromCenter / (currentItem.width / 4)) * units.gu(1);
227 }
228 Behavior on highlightCenterOffset {
229 NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
230 }
232 property real currentItemX: currentItem ? currentItem.x : 0
233 Behavior on currentItemX {
234 id: currentItemXBehavior
235 enabled: !d.firstItemSwitch && expanded && !d.forceAlignmentAnimationDisabled
236 NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
237 }
238 x: currentItemX + highlightCenterOffset
239 }
241 states: [
242 State {
243 name: "minimised"
244 when: !expanded
245 },
246 State {
247 name: "expanded"
248 when: expanded
249 PropertyChanges { target: highlight; opacity: 0.9 }
250 }
251 ]
253 transitions: [
254 Transition {
255 PropertyAnimation {
256 properties: "opacity";
257 duration: UbuntuAnimation.SnapDuration
258 easing: UbuntuAnimation.StandardEasing
259 }
260 }
261 ]