Merge lp:~bzoltan/ubuntu-ui-toolkit/new_list_item into lp:ubuntu-ui-toolkit
- new_list_item
- Merge into trunk
Status: | Rejected |
---|---|
Rejected by: | Zoltan Balogh |
Proposed branch: | lp:~bzoltan/ubuntu-ui-toolkit/new_list_item |
Merge into: | lp:ubuntu-ui-toolkit |
Diff against target: |
4506 lines (+3994/-44) (has conflicts) 44 files modified
components.api (+37/-0) debian/changelog (+29/-0) examples/ubuntu-ui-toolkit-gallery/ListItems.qml (+33/-0) export_modules_dir.sh (+3/-0) modules/Ubuntu/Components/ListItemPanel.qml (+143/-0) modules/Ubuntu/Components/ListItemSelectablePanel.qml (+49/-0) modules/Ubuntu/Components/ListItems/Empty.qml (+5/-1) modules/Ubuntu/Components/ListItems/Header.qml (+4/-1) modules/Ubuntu/Components/ListItems/ThinDivider.qml (+0/-2) modules/Ubuntu/Components/Themes/Ambiance/SheetForegroundStyle.qml (+2/-2) modules/Ubuntu/Components/UbuntuListView11.qdoc (+42/-0) modules/Ubuntu/Components/UbuntuListView11.qml (+32/-1) modules/Ubuntu/Components/plugin/plugin.cpp (+8/-0) modules/Ubuntu/Components/plugin/plugin.pro (+10/-2) modules/Ubuntu/Components/plugin/propertychange_p.cpp (+76/-0) modules/Ubuntu/Components/plugin/propertychange_p.h (+39/-0) modules/Ubuntu/Components/plugin/quickutils.cpp (+23/-0) modules/Ubuntu/Components/plugin/quickutils.h (+1/-0) modules/Ubuntu/Components/plugin/uclistitem.cpp (+1116/-0) modules/Ubuntu/Components/plugin/uclistitem.h (+92/-0) modules/Ubuntu/Components/plugin/uclistitem_p.h (+177/-0) modules/Ubuntu/Components/plugin/uclistitemoptions.cpp (+526/-0) modules/Ubuntu/Components/plugin/uclistitemoptions.h (+75/-0) modules/Ubuntu/Components/plugin/uclistitemoptions_p.h (+66/-0) modules/Ubuntu/Components/qmldir (+2/-0) tests/autopilot/ubuntuuitoolkit/environment.py (+19/-15) tests/autopilot/ubuntuuitoolkit/fixture_setup.py (+19/-7) tests/autopilot/ubuntuuitoolkit/tests/__init__.py (+14/-0) tests/autopilot/ubuntuuitoolkit/tests/test_environment.py (+29/-4) tests/autopilot/ubuntuuitoolkit/tests/test_fixture_setup.py (+90/-0) tests/resources/listitems/InUListView.qml (+63/-0) tests/resources/listitems/ListItemTest.qml (+186/-0) tests/unit/tst_performance/ItemList.qml (+30/-0) tests/unit/tst_performance/ListItemList.qml (+30/-0) tests/unit/tst_performance/ListItemWithInlineOptionsList.qml (+39/-0) tests/unit/tst_performance/ListItemWithLeadingOptionsList.qml (+41/-0) tests/unit/tst_performance/ListItemWithOptionsList.qml (+41/-0) tests/unit/tst_performance/ListItemsBaseList.qml (+29/-0) tests/unit/tst_performance/ListItemsEmptyList.qml (+29/-0) tests/unit/tst_performance/tst_performance.cpp (+8/-8) tests/unit/tst_performance/tst_performance.pro (+8/-1) tests/unit_x11/tst_components/tst_listitem.qml (+569/-0) tests/unit_x11/tst_components/tst_listitem_ubuntulistview.qml (+159/-0) tests/unit_x11/tst_components/tst_ubuntulistview11.qml (+1/-0) Text conflict in debian/changelog |
To merge this branch: | bzr merge lp:~bzoltan/ubuntu-ui-toolkit/new_list_item |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Needs Fixing | |
Zsombor Egri | Pending | ||
Review via email: mp+234625@code.launchpad.net |
Commit message
New List Item
Description of the change
New List Item
PS Jenkins bot (ps-jenkins) wrote : | # |
- 1248. By Zoltan Balogh
-
Fix changelog
- 1249. By Zoltan Balogh
- 1250. By Zoltan Balogh
- 1251. By Zoltan Balogh
- 1252. By Zoltan Balogh
- 1253. By Zoltan Balogh
- 1254. By Zoltan Balogh
- 1255. By Zoltan Balogh
- 1256. By Zoltan Balogh
- 1257. By Zoltan Balogh
- 1258. By Zoltan Balogh
- 1259. By Zoltan Balogh
- 1260. By Zoltan Balogh
- 1261. By Zoltan Balogh
- 1262. By Zoltan Balogh
- 1263. By Zoltan Balogh
-
lp:~zsombi/ubuntu-ui-toolkit/86-exposing-status-and-connectedItem
- 1264. By Zoltan Balogh
- 1265. By Zoltan Balogh
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1248
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1247
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1266
http://
Executed test runs:
UNSTABLE: http://
FAILURE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Unmerged revisions
- 1266. By Zoltan Balogh
- 1265. By Zoltan Balogh
- 1264. By Zoltan Balogh
- 1263. By Zoltan Balogh
-
lp:~zsombi/ubuntu-ui-toolkit/86-exposing-status-and-connectedItem
- 1262. By Zoltan Balogh
- 1261. By Zoltan Balogh
- 1260. By Zoltan Balogh
- 1259. By Zoltan Balogh
- 1258. By Zoltan Balogh
- 1257. By Zoltan Balogh
Preview Diff
1 | === modified file 'components.api' |
2 | --- components.api 2014-09-05 05:49:46 +0000 |
3 | +++ components.api 2014-09-15 11:00:03 +0000 |
4 | @@ -673,6 +673,7 @@ |
5 | UbuntuListView 1.1 |
6 | UbuntuListView |
7 | property PullToRefresh pullToRefresh |
8 | + property ListItemOptions leadingOptions |
9 | UbuntuNumberAnimation 0.1 1.0 |
10 | NumberAnimation |
11 | PerformanceOverlay 0.1 1.0 |
12 | @@ -814,6 +815,42 @@ |
13 | name: "UCInverseMouse" |
14 | prototype: "UCMouse" |
15 | exports: ["InverseMouse 0.1", "InverseMouse 1.0"] |
16 | + name: "UCListItem" |
17 | + prototype: "UCStyledItemBase" |
18 | + exports: ["ListItem 1.1"] |
19 | + Property { name: "contentItem"; type: "UCListItemContent"; isReadonly: true; isPointer: true } |
20 | + Property { name: "divider"; type: "UCListItemDivider"; isReadonly: true; isPointer: true } |
21 | + Property { name: "leadingOptions"; type: "UCListItemOptions"; isPointer: true } |
22 | + Property { name: "trailingOptions"; type: "UCListItemOptions"; isPointer: true } |
23 | + Property { name: "pressed"; type: "bool"; isReadonly: true } |
24 | + Property { name: "selectable"; type: "bool" } |
25 | + Property { name: "selected"; type: "bool" } |
26 | + Property { name: "data"; type: "QObject"; isList: true; isReadonly: true } |
27 | + Property { name: "children"; type: "QQuickItem"; isList: true; isReadonly: true } |
28 | + Signal { name: "clicked" } |
29 | + Signal { name: "pressAndHold" } |
30 | + name: "UCListItemContent" |
31 | + prototype: "QQuickItem" |
32 | + Property { name: "color"; type: "QColor" } |
33 | + Property { name: "pressedColor"; type: "QColor" } |
34 | + name: "UCListItemDivider" |
35 | + prototype: "QObject" |
36 | + Property { name: "visible"; type: "bool" } |
37 | + Property { name: "leftMargin"; type: "double" } |
38 | + Property { name: "rightMargin"; type: "double" } |
39 | + Property { name: "colorFrom"; type: "QColor" } |
40 | + Property { name: "colorTo"; type: "QColor" } |
41 | + name: "UCListItemOptions" |
42 | + prototype: "QObject" |
43 | + exports: ["ListItemOptions 1.1"] |
44 | + name: "Status" |
45 | + Property { name: "delegate"; type: "QQmlComponent"; isPointer: true } |
46 | + Property { name: "options"; type: "QObject"; isList: true; isReadonly: true } |
47 | + Property { name: "panelItem"; type: "QQuickItem"; isReadonly: true; isPointer: true } |
48 | + Property { name: "status"; type: "Status"; isReadonly: true } |
49 | + Property { name: "connectedItem"; type: "UCListItem"; isReadonly: true; isPointer: true } |
50 | + Property { name: "panelColor"; type: "QColor" } |
51 | + Property { name: "iconColor"; type: "QColor" } |
52 | name: "UCMouse" |
53 | prototype: "QObject" |
54 | exports: ["Mouse 0.1", "Mouse 1.0"] |
55 | |
56 | === modified file 'debian/changelog' |
57 | --- debian/changelog 2014-09-08 17:53:18 +0000 |
58 | +++ debian/changelog 2014-09-15 11:00:03 +0000 |
59 | @@ -1,3 +1,4 @@ |
60 | +<<<<<<< TREE |
61 | ubuntu-ui-toolkit (1.1.1239+14.10.20140908-0ubuntu1) utopic; urgency=medium |
62 | |
63 | [ Zsombor Egri ] |
64 | @@ -45,6 +46,34 @@ |
65 | |
66 | -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 08 Sep 2014 17:53:18 +0000 |
67 | |
68 | +======= |
69 | +ubuntu-ui-toolkit (1.1.1247+14.10.20140904-0ubuntu2) UNRELEASED; urgency=medium |
70 | + |
71 | + [ Zsombor Egri ] |
72 | + * New ListItem component |
73 | + * Sheet header button space reservation made button presence |
74 | + dependent. Fixes: LP: #1356636 |
75 | + * Tweaks for ListItem dividers. Header margins and font weight |
76 | + updated. |
77 | + * Exclude UbuntuColors dumped erroneously by qmlplugindump as it |
78 | + would be a C++ type. |
79 | + * Organizer audible reminder wasn't updated when Alarm sound |
80 | + changed. |
81 | + |
82 | + [ Zoltán Balogh ] |
83 | + * Updated test plan execution for RTM images and for new phablet |
84 | + tools. |
85 | + |
86 | + [ Christian Dywan ] |
87 | + * Specify /sbin when calling initctl just like autopilot does. |
88 | + |
89 | + [ Leo Arias ] |
90 | + * On autopilot tests launched by upstart, use the local modules |
91 | + if running from a branch. |
92 | + |
93 | + -- Zoltán Balogh <zoltan@bakter.hu> Mon, 15 Sep 2014 10:50:49 +0300 |
94 | + |
95 | +>>>>>>> MERGE-SOURCE |
96 | ubuntu-ui-toolkit (1.1.1227+14.10.20140904-0ubuntu1) utopic; urgency=medium |
97 | |
98 | [ Zsombor Egri ] |
99 | |
100 | === modified file 'examples/ubuntu-ui-toolkit-gallery/ListItems.qml' |
101 | --- examples/ubuntu-ui-toolkit-gallery/ListItems.qml 2014-07-18 12:12:05 +0000 |
102 | +++ examples/ubuntu-ui-toolkit-gallery/ListItems.qml 2014-09-15 11:00:03 +0000 |
103 | @@ -22,6 +22,39 @@ |
104 | objectName: "listItemsTemplate" |
105 | |
106 | ListItemsSection { |
107 | + title: i18n.tr("ListItem - without layout") |
108 | + className: "ListItem" |
109 | + clip: true |
110 | + delegate: Toolkit.ListItem { |
111 | + leadingOptions: Toolkit.ListItemOptions { |
112 | + Toolkit.Action { |
113 | + iconName: "delete" |
114 | + } |
115 | + } |
116 | + trailingOptions: Toolkit.ListItemOptions { |
117 | + Toolkit.Action { |
118 | + iconName: "edit" |
119 | + } |
120 | + Toolkit.Action { |
121 | + iconName: "contact" |
122 | + } |
123 | + Toolkit.Action { |
124 | + iconName: "email" |
125 | + } |
126 | + } |
127 | + |
128 | + Toolkit.Label { |
129 | + anchors { |
130 | + top: parent.top |
131 | + bottom: parent.bottom |
132 | + } |
133 | + verticalAlignment: Text.AlignVCenter |
134 | + text: "Plain ListItem #" + modelData |
135 | + } |
136 | + } |
137 | + } |
138 | + |
139 | + ListItemsSection { |
140 | title: i18n.tr("Standard") |
141 | className: "Standard" |
142 | delegate: ListItem.Standard { |
143 | |
144 | === modified file 'export_modules_dir.sh' |
145 | --- export_modules_dir.sh 2014-07-14 17:34:44 +0000 |
146 | +++ export_modules_dir.sh 2014-09-15 11:00:03 +0000 |
147 | @@ -18,3 +18,6 @@ |
148 | export QML_IMPORT_PATH=$PWD/modules |
149 | export QML2_IMPORT_PATH=$PWD/modules |
150 | export UBUNTU_UI_TOOLKIT_THEMES_PATH=$PWD/modules |
151 | +/sbin/initctl set-env --global QML_IMPORT_PATH=$PWD/modules |
152 | +/sbin/initctl set-env --global QML2_IMPORT_PATH=$PWD/modules |
153 | +/sbin/initctl set-env --global UBUNTU_UI_TOOLKIT_THEMES_PATH=$PWD/modules |
154 | |
155 | === added file 'modules/Ubuntu/Components/ListItemPanel.qml' |
156 | --- modules/Ubuntu/Components/ListItemPanel.qml 1970-01-01 00:00:00 +0000 |
157 | +++ modules/Ubuntu/Components/ListItemPanel.qml 2014-09-15 11:00:03 +0000 |
158 | @@ -0,0 +1,143 @@ |
159 | +/* |
160 | + * Copyright 2014 Canonical Ltd. |
161 | + * |
162 | + * This program is free software; you can redistribute it and/or modify |
163 | + * it under the terms of the GNU Lesser General Public License as published by |
164 | + * the Free Software Foundation; version 3. |
165 | + * |
166 | + * This program is distributed in the hope that it will be useful, |
167 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
168 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
169 | + * GNU Lesser General Public License for more details. |
170 | + * |
171 | + * You should have received a copy of the GNU Lesser General Public License |
172 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
173 | + */ |
174 | + |
175 | +import QtQuick 2.2 |
176 | +import Ubuntu.Components 1.1 |
177 | + |
178 | +/* |
179 | + This component is the holder of the ListItem options. |
180 | + */ |
181 | +Item { |
182 | + id: panel |
183 | + width: optionsRow.childrenRect.width |
184 | + |
185 | + readonly property Item contentItem: parent ? parent.contentItem : null |
186 | + |
187 | + /* |
188 | + Index of the ListItem, if the ListItem is inside a ListView or has been |
189 | + created using a Repeater. |
190 | + */ |
191 | + property int listItemIndex: -1 |
192 | + |
193 | + /* |
194 | + Specifies whether the panel is used to visualize leading or trailing options. |
195 | + */ |
196 | + property bool leadingPanel: false |
197 | + /* |
198 | + The delegate to be used to visualize the options |
199 | + */ |
200 | + property Component delegate |
201 | + |
202 | + /* |
203 | + Options |
204 | + */ |
205 | + property var optionList |
206 | + |
207 | + /* |
208 | + Panel and icon colors |
209 | + */ |
210 | + property color panelColor |
211 | + property color iconColor |
212 | + |
213 | + /* |
214 | + Emitted when action is triggered |
215 | + */ |
216 | + signal selected() |
217 | + |
218 | + // fire selected action when parent is removed |
219 | + onParentChanged: { |
220 | + if (!parent && selectedAction) { |
221 | + selectedAction.triggered(listItemIndex >= 0 ? listItemIndex : null); |
222 | + selectedAction = null; |
223 | + } |
224 | + } |
225 | + property Action selectedAction: null |
226 | + |
227 | + anchors { |
228 | + left: contentItem ? (leadingPanel ? undefined : contentItem.right) : undefined |
229 | + right: contentItem ? (leadingPanel ? contentItem.left : undefined) : undefined |
230 | + top: contentItem ? contentItem.top : undefined |
231 | + bottom: contentItem ? contentItem.bottom : undefined |
232 | + } |
233 | + |
234 | + Rectangle { |
235 | + anchors.fill: parent |
236 | + // FIXME: use Palette colors instead when available |
237 | + color: (panel.panelColor != "#000000") ? panel.panelColor : (leadingPanel ? UbuntuColors.red : "#00000000") |
238 | + } |
239 | + |
240 | + Row { |
241 | + id: optionsRow |
242 | + anchors { |
243 | + left: parent.left |
244 | + top: parent.top |
245 | + bottom: parent.bottom |
246 | + leftMargin: spacing |
247 | + } |
248 | + |
249 | + property real maxItemWidth: panel.parent ? (panel.parent.width / panel.optionList.length) : 0 |
250 | + |
251 | + Repeater { |
252 | + model: panel.optionList |
253 | + AbstractButton { |
254 | + action: modelData |
255 | + width: (!visible || !enabled) ? |
256 | + 0 : MathUtils.clamp(delegateLoader.item ? delegateLoader.item.width : 0, height, optionsRow.maxItemWidth) |
257 | + anchors { |
258 | + top: parent.top |
259 | + bottom: parent.bottom |
260 | + } |
261 | + |
262 | + function trigger() { |
263 | + // save the action as we trigger when the rebound animation is over |
264 | + // to make sure we properly clean up the blockade of teh Flickables |
265 | + panel.selectedAction = action; |
266 | + panel.selected(); |
267 | + } |
268 | + |
269 | + Loader { |
270 | + id: delegateLoader |
271 | + height: parent.height |
272 | + sourceComponent: panel.delegate ? panel.delegate : defaultDelegate |
273 | + property Action option: modelData |
274 | + property int index: index |
275 | + onItemChanged: { |
276 | + // this is needed only for testing purposes |
277 | + if (item && item.objectName === "") { |
278 | + item.objectName = "list_option_" + index |
279 | + } |
280 | + } |
281 | + } |
282 | + } |
283 | + } |
284 | + } |
285 | + |
286 | + Component { |
287 | + id: defaultDelegate |
288 | + Item { |
289 | + width: height |
290 | + Icon { |
291 | + width: units.gu(2.5) |
292 | + height: width |
293 | + name: option.iconName |
294 | + // FIXME: use Palette colors instead when available |
295 | + color: (panel.iconColor != "#000000") ? |
296 | + panel.iconColor : (panel.leadingPanel ? "white" : UbuntuColors.lightGrey) |
297 | + anchors.centerIn: parent |
298 | + } |
299 | + } |
300 | + } |
301 | +} |
302 | |
303 | === added file 'modules/Ubuntu/Components/ListItemSelectablePanel.qml' |
304 | --- modules/Ubuntu/Components/ListItemSelectablePanel.qml 1970-01-01 00:00:00 +0000 |
305 | +++ modules/Ubuntu/Components/ListItemSelectablePanel.qml 2014-09-15 11:00:03 +0000 |
306 | @@ -0,0 +1,49 @@ |
307 | +/* |
308 | + * Copyright 2014 Canonical Ltd. |
309 | + * |
310 | + * This program is free software; you can redistribute it and/or modify |
311 | + * it under the terms of the GNU Lesser General Public License as published by |
312 | + * the Free Software Foundation; version 3. |
313 | + * |
314 | + * This program is distributed in the hope that it will be useful, |
315 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
316 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
317 | + * GNU Lesser General Public License for more details. |
318 | + * |
319 | + * You should have received a copy of the GNU Lesser General Public License |
320 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
321 | + */ |
322 | + |
323 | +import QtQuick 2.2 |
324 | +import Ubuntu.Components 1.1 |
325 | + |
326 | +/* |
327 | + This component is the holder of the ListItem selection checkbox. |
328 | + */ |
329 | +Rectangle { |
330 | + id: panel |
331 | + width: height |
332 | + |
333 | + /* |
334 | + Internally used to link to the list item's content |
335 | + */ |
336 | + readonly property Item contentItem: parent ? parent.contentItem : null |
337 | + |
338 | + /* |
339 | + Exposed checked property linked to the listItem's selected |
340 | + */ |
341 | + property alias checked: checkbox.checked |
342 | + |
343 | + color: checked ? contentItem.pressedColor : contentItem.color |
344 | + |
345 | + anchors { |
346 | + right: contentItem ? contentItem.left : undefined |
347 | + top: contentItem ? contentItem.top : undefined |
348 | + bottom: contentItem ? contentItem.bottom : undefined |
349 | + } |
350 | + |
351 | + CheckBox { |
352 | + id: checkbox |
353 | + anchors.centerIn: parent |
354 | + } |
355 | +} |
356 | |
357 | === modified file 'modules/Ubuntu/Components/ListItems/Empty.qml' |
358 | --- modules/Ubuntu/Components/ListItems/Empty.qml 2014-08-08 14:30:27 +0000 |
359 | +++ modules/Ubuntu/Components/ListItems/Empty.qml 2014-09-15 11:00:03 +0000 |
360 | @@ -307,7 +307,11 @@ |
361 | |
362 | ThinDivider { |
363 | id: bottomDividerLine |
364 | - anchors.bottom: parent.bottom |
365 | + anchors { |
366 | + bottom: parent.bottom |
367 | + leftMargin: units.gu(2) |
368 | + rightMargin: units.gu(2) |
369 | + } |
370 | visible: showDivider && !priv.removed |
371 | } |
372 | |
373 | |
374 | === modified file 'modules/Ubuntu/Components/ListItems/Header.qml' |
375 | --- modules/Ubuntu/Components/ListItems/Header.qml 2014-04-20 19:25:12 +0000 |
376 | +++ modules/Ubuntu/Components/ListItems/Header.qml 2014-09-15 11:00:03 +0000 |
377 | @@ -60,11 +60,12 @@ |
378 | LabelVisual { |
379 | id: label |
380 | fontSize: "medium" |
381 | + font.weight: Text.Normal |
382 | anchors { |
383 | verticalCenter: parent.verticalCenter |
384 | left: parent.left |
385 | right: parent.right |
386 | - margins: units.gu(1) |
387 | + margins: units.gu(2) |
388 | } |
389 | } |
390 | |
391 | @@ -73,6 +74,8 @@ |
392 | left: parent.left |
393 | right: parent.right |
394 | bottom: parent.bottom |
395 | + leftMargin: units.gu(2) |
396 | + rightMargin: units.gu(2) |
397 | } |
398 | } |
399 | } |
400 | |
401 | === modified file 'modules/Ubuntu/Components/ListItems/ThinDivider.qml' |
402 | --- modules/Ubuntu/Components/ListItems/ThinDivider.qml 2014-08-27 09:26:59 +0000 |
403 | +++ modules/Ubuntu/Components/ListItems/ThinDivider.qml 2014-09-15 11:00:03 +0000 |
404 | @@ -49,8 +49,6 @@ |
405 | anchors { |
406 | left: (parent) ? parent.left : null |
407 | right: (parent) ? parent.right : null |
408 | - leftMargin: units.gu(2) |
409 | - rightMargin: units.gu(2) |
410 | } |
411 | height: (visible) ? units.dp(2) : 0 |
412 | // a private property to catch theme background color change |
413 | |
414 | === modified file 'modules/Ubuntu/Components/Themes/Ambiance/SheetForegroundStyle.qml' |
415 | --- modules/Ubuntu/Components/Themes/Ambiance/SheetForegroundStyle.qml 2014-04-23 08:50:20 +0000 |
416 | +++ modules/Ubuntu/Components/Themes/Ambiance/SheetForegroundStyle.qml 2014-09-15 11:00:03 +0000 |
417 | @@ -60,7 +60,7 @@ |
418 | top: parent.top |
419 | bottom: parent.bottom |
420 | } |
421 | - width: visuals.buttonContainerWidth |
422 | + width: styledItem.leftButton ? visuals.buttonContainerWidth : 0 |
423 | Component.onCompleted: header.updateButton(styledItem.leftButton, leftButtonContainer) |
424 | } |
425 | |
426 | @@ -71,7 +71,7 @@ |
427 | top: parent.top |
428 | bottom: parent.bottom |
429 | } |
430 | - width: visuals.buttonContainerWidth |
431 | + width: styledItem.rightButton ? visuals.buttonContainerWidth : 0 |
432 | Component.onCompleted: header.updateButton(styledItem.rightButton, rightButtonContainer) |
433 | } |
434 | |
435 | |
436 | === modified file 'modules/Ubuntu/Components/UbuntuListView11.qdoc' |
437 | --- modules/Ubuntu/Components/UbuntuListView11.qdoc 2014-05-22 07:05:47 +0000 |
438 | +++ modules/Ubuntu/Components/UbuntuListView11.qdoc 2014-09-15 11:00:03 +0000 |
439 | @@ -36,3 +36,45 @@ |
440 | } |
441 | \endqml |
442 | */ |
443 | + |
444 | +/*! |
445 | + * \qmlproperty ListItemOptions UbuntuListView::leadingOptions |
446 | + * The property holds the default leading options defined by the UbuntuListView. |
447 | + * The option list can be overridden either by specifying a different \l ListItemOptions |
448 | + * instance to the property or doing the same in \l ListItem itself. The default |
449 | + * option list contains a single delete action, which removes the model data from |
450 | + * the triggered index. |
451 | + * \qml |
452 | + * UbuntuListView { |
453 | + * width: units.gu(30) |
454 | + * height: units.gu(24) |
455 | + * leadingOptions: ListItemOptions { |
456 | + * Action { |
457 | + * iconName: "edit" |
458 | + * onTriggered: console.log("edit triggered on item index", value) |
459 | + * } |
460 | + * } |
461 | + * model: 10 |
462 | + * delegate: ListItem { |
463 | + * } |
464 | + * } |
465 | + * \endqml |
466 | + * And its equivalent: |
467 | + * \qml |
468 | + * UbuntuListView { |
469 | + * width: units.gu(30) |
470 | + * height: units.gu(24) |
471 | + * model: 10 |
472 | + * delegate: ListItem { |
473 | + * leadingOptions: ListItemOptions { |
474 | + * Action { |
475 | + * iconName: "edit" |
476 | + * onTriggered: console.log("edit triggered on item index", value) |
477 | + * } |
478 | + * } |
479 | + * } |
480 | + * } |
481 | + * \endqml |
482 | + * \note Setting \c leadingOptions on \l ListItem itself has precedence over the |
483 | + * UbuntuListView property. |
484 | + */ |
485 | |
486 | === modified file 'modules/Ubuntu/Components/UbuntuListView11.qml' |
487 | --- modules/Ubuntu/Components/UbuntuListView11.qml 2014-06-04 07:54:35 +0000 |
488 | +++ modules/Ubuntu/Components/UbuntuListView11.qml 2014-09-15 11:00:03 +0000 |
489 | @@ -15,16 +15,47 @@ |
490 | */ |
491 | |
492 | import QtQuick 2.2 |
493 | +import Ubuntu.Components 1.1 |
494 | |
495 | // documentation in UbuntuListView11.qdoc |
496 | UbuntuListView { |
497 | - |
498 | + id: listView |
499 | /*! |
500 | \internal |
501 | \qmlproperty PullToRefresh pullToRefresh |
502 | */ |
503 | property alias pullToRefresh: refreshItem |
504 | |
505 | + /*! |
506 | + \internal |
507 | + \qmlproperty ListItemOptions leadingOptions |
508 | + Use binding so we can set it to each ListItem as binding! |
509 | + */ |
510 | + property ListItemOptions leadingOptions: stockLeadingOption |
511 | + ListItemOptions { |
512 | + id: stockLeadingOption |
513 | + options: Action { |
514 | + iconName: "delete" |
515 | + onTriggered: { |
516 | + // delete the index from model if possible |
517 | + if (Object.prototype.toString.call(listView.model) === "[object Number]") { |
518 | + // the model is a number, decrement it |
519 | + listView.model = listView.count - 1; |
520 | + } else if (Object.prototype.toString.call(listView.model) === "[object Array]") { |
521 | + // the model is an array, remove the item from index |
522 | + var array = listView.model; |
523 | + array.splice(value, 1); |
524 | + listView.model = array; |
525 | + } else { |
526 | + // we can only have an object |
527 | + if (listView.model.hasOwnProperty("remove")) { |
528 | + listView.model.remove(value, 1); |
529 | + } |
530 | + } |
531 | + } |
532 | + } |
533 | + } |
534 | + |
535 | PullToRefresh { |
536 | objectName: "listview_pulltorefresh" |
537 | id: refreshItem |
538 | |
539 | === modified file 'modules/Ubuntu/Components/plugin/plugin.cpp' |
540 | --- modules/Ubuntu/Components/plugin/plugin.cpp 2014-09-05 05:05:54 +0000 |
541 | +++ modules/Ubuntu/Components/plugin/plugin.cpp 2014-09-15 11:00:03 +0000 |
542 | @@ -50,6 +50,9 @@ |
543 | #include "ucinversemouse.h" |
544 | #include "sortfiltermodel.h" |
545 | #include "ucstyleditembase.h" |
546 | +#include "uclistitem.h" |
547 | +#include "uclistitem_p.h" |
548 | +#include "uclistitemoptions.h" |
549 | |
550 | #include <sys/types.h> |
551 | #include <unistd.h> |
552 | @@ -157,6 +160,11 @@ |
553 | qmlRegisterType<QSortFilterProxyModelQML>(uri, 1, 1, "SortFilterModel"); |
554 | qmlRegisterUncreatableType<FilterBehavior>(uri, 1, 1, "FilterBehavior", "Not instantiable"); |
555 | qmlRegisterUncreatableType<SortBehavior>(uri, 1, 1, "SortBehavior", "Not instantiable"); |
556 | + // ListItem and related types |
557 | + qmlRegisterType<UCListItem, 1>(uri, 1, 1, "ListItem"); |
558 | + qmlRegisterType<UCListItemContent>(); |
559 | + qmlRegisterType<UCListItemDivider>(); |
560 | + qmlRegisterType<UCListItemOptions, 1>(uri, 1, 1, "ListItemOptions"); |
561 | } |
562 | |
563 | void UbuntuComponentsPlugin::initializeEngine(QQmlEngine *engine, const char *uri) |
564 | |
565 | === modified file 'modules/Ubuntu/Components/plugin/plugin.pro' |
566 | --- modules/Ubuntu/Components/plugin/plugin.pro 2014-09-02 09:39:09 +0000 |
567 | +++ modules/Ubuntu/Components/plugin/plugin.pro 2014-09-15 11:00:03 +0000 |
568 | @@ -65,7 +65,12 @@ |
569 | ucmouse.h \ |
570 | unixsignalhandler_p.h \ |
571 | ucstyleditembase.h \ |
572 | - ucstyleditembase_p.h |
573 | + ucstyleditembase_p.h \ |
574 | + uclistitem.h \ |
575 | + uclistitem_p.h \ |
576 | + uclistitemoptions.h \ |
577 | + uclistitemoptions_p.h \ |
578 | + propertychange_p.h |
579 | |
580 | SOURCES += plugin.cpp \ |
581 | uctheme.cpp \ |
582 | @@ -99,7 +104,10 @@ |
583 | ucurihandler.cpp \ |
584 | ucmousefilters.cpp \ |
585 | unixsignalhandler_p.cpp \ |
586 | - ucstyleditembase.cpp |
587 | + ucstyleditembase.cpp \ |
588 | + uclistitem.cpp \ |
589 | + uclistitemoptions.cpp \ |
590 | + propertychange_p.cpp |
591 | |
592 | # adapters |
593 | SOURCES += adapters/alarmsadapter_organizer.cpp |
594 | |
595 | === added file 'modules/Ubuntu/Components/plugin/propertychange_p.cpp' |
596 | --- modules/Ubuntu/Components/plugin/propertychange_p.cpp 1970-01-01 00:00:00 +0000 |
597 | +++ modules/Ubuntu/Components/plugin/propertychange_p.cpp 2014-09-15 11:00:03 +0000 |
598 | @@ -0,0 +1,76 @@ |
599 | +/* |
600 | + * Copyright 2014 Canonical Ltd. |
601 | + * |
602 | + * This program is free software; you can redistribute it and/or modify |
603 | + * it under the terms of the GNU Lesser General Public License as published by |
604 | + * the Free Software Foundation; version 3. |
605 | + * |
606 | + * This program is distributed in the hope that it will be useful, |
607 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
608 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
609 | + * GNU Lesser General Public License for more details. |
610 | + * |
611 | + * You should have received a copy of the GNU Lesser General Public License |
612 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
613 | + */ |
614 | + |
615 | +#include "propertychange_p.h" |
616 | + |
617 | +#include <QtQml/private/qqmlabstractbinding_p.h> |
618 | +#define foreach Q_FOREACH //workaround to fix private includes |
619 | +#include <QtQml/private/qqmlbinding_p.h> // for QmlBinding |
620 | +#undef foreach |
621 | + |
622 | +/* |
623 | + * The class is used to save properties and their bindings while the property is |
624 | + * altered temporarily. |
625 | + */ |
626 | +PropertyChange::PropertyChange(QObject *item, const char *property) |
627 | + : m_backedUp(false) |
628 | + , qmlProperty(item, property, qmlContext(item)) |
629 | +{ |
630 | +} |
631 | +PropertyChange::~PropertyChange() |
632 | +{ |
633 | + restore(this); |
634 | +} |
635 | + |
636 | +/* |
637 | + * Sets a value to the property. Will back up the original values if it wasn't yet. |
638 | + * This function can be called many times, it will not destroy the backed up value/binding. |
639 | + */ |
640 | +void PropertyChange::setValue(PropertyChange *change, const QVariant &value) |
641 | +{ |
642 | + if (!change) { |
643 | + return; |
644 | + } |
645 | + if (!change->m_backedUp) { |
646 | + change->backup.first = QQmlPropertyPrivate::setBinding(change->qmlProperty, 0); |
647 | + change->backup.second = change->qmlProperty.read(); |
648 | + change->m_backedUp = true; |
649 | + } |
650 | + change->qmlProperty.write(value); |
651 | +} |
652 | + |
653 | +/* |
654 | + * Restore backed up value or binding. |
655 | + */ |
656 | +void PropertyChange::restore(PropertyChange *change) |
657 | +{ |
658 | + if (!change) { |
659 | + return; |
660 | + } |
661 | + if (change->m_backedUp) { |
662 | + // if there was a binding, restore it |
663 | + if (change->backup.first) { |
664 | + QQmlAbstractBinding *prevBinding = QQmlPropertyPrivate::setBinding(change->qmlProperty, change->backup.first); |
665 | + if (prevBinding && prevBinding != change->backup.first) { |
666 | + prevBinding->destroy(); |
667 | + } |
668 | + } else { |
669 | + // there was no binding, restore previous value |
670 | + change->qmlProperty.write(change->backup.second); |
671 | + } |
672 | + change->m_backedUp = false; |
673 | + } |
674 | +} |
675 | |
676 | === added file 'modules/Ubuntu/Components/plugin/propertychange_p.h' |
677 | --- modules/Ubuntu/Components/plugin/propertychange_p.h 1970-01-01 00:00:00 +0000 |
678 | +++ modules/Ubuntu/Components/plugin/propertychange_p.h 2014-09-15 11:00:03 +0000 |
679 | @@ -0,0 +1,39 @@ |
680 | +/* |
681 | + * Copyright 2014 Canonical Ltd. |
682 | + * |
683 | + * This program is free software; you can redistribute it and/or modify |
684 | + * it under the terms of the GNU Lesser General Public License as published by |
685 | + * the Free Software Foundation; version 3. |
686 | + * |
687 | + * This program is distributed in the hope that it will be useful, |
688 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
689 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
690 | + * GNU Lesser General Public License for more details. |
691 | + * |
692 | + * You should have received a copy of the GNU Lesser General Public License |
693 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
694 | + */ |
695 | + |
696 | +#ifndef PROPERTYCHANGE_P_H |
697 | +#define PROPERTYCHANGE_P_H |
698 | + |
699 | +#include <QtCore/QVariant> |
700 | +#include <QtCore/QObject> |
701 | +#include <QtQml/QQmlProperty> |
702 | + |
703 | +class QQmlAbstractBinding; |
704 | +class PropertyChange |
705 | +{ |
706 | +public: |
707 | + PropertyChange(QObject *item, const char *property); |
708 | + ~PropertyChange(); |
709 | + |
710 | + static void setValue(PropertyChange* change, const QVariant &value); |
711 | + static void restore(PropertyChange* change); |
712 | +private: |
713 | + bool m_backedUp; |
714 | + QQmlProperty qmlProperty; |
715 | + QPair<QQmlAbstractBinding*, QVariant> backup; |
716 | +}; |
717 | + |
718 | +#endif // PROPERTYCHANGE_P_H |
719 | |
720 | === modified file 'modules/Ubuntu/Components/plugin/quickutils.cpp' |
721 | --- modules/Ubuntu/Components/plugin/quickutils.cpp 2014-06-10 11:47:09 +0000 |
722 | +++ modules/Ubuntu/Components/plugin/quickutils.cpp 2014-09-15 11:00:03 +0000 |
723 | @@ -135,6 +135,29 @@ |
724 | return result.left(result.indexOf("_QML")); |
725 | } |
726 | |
727 | +/*! |
728 | + * \internal |
729 | + * The function checks whether an item inherits a given class name. |
730 | + */ |
731 | +bool QuickUtils::inherits(QObject *object, const QString &fromClass) |
732 | +{ |
733 | + if (!object || fromClass.isEmpty()) { |
734 | + return false; |
735 | + } |
736 | + const QMetaObject *mo = object->metaObject(); |
737 | + QString className; |
738 | + while (mo) { |
739 | + className = mo->className(); |
740 | + className = className.left(className.indexOf("_QML")); |
741 | + if (className == fromClass) { |
742 | + return true; |
743 | + } |
744 | + mo = mo->superClass(); |
745 | + } |
746 | + return false; |
747 | +} |
748 | + |
749 | + |
750 | |
751 | /*! |
752 | * \internal |
753 | |
754 | === modified file 'modules/Ubuntu/Components/plugin/quickutils.h' |
755 | --- modules/Ubuntu/Components/plugin/quickutils.h 2014-06-10 11:47:09 +0000 |
756 | +++ modules/Ubuntu/Components/plugin/quickutils.h 2014-09-15 11:00:03 +0000 |
757 | @@ -44,6 +44,7 @@ |
758 | bool touchScreenAvailable() const; |
759 | |
760 | Q_INVOKABLE static QString className(QObject *item); |
761 | + Q_REVISION(1) Q_INVOKABLE static bool inherits(QObject *object, const QString &fromClass); |
762 | QObject* createQmlObject(const QUrl &url, QQmlEngine *engine); |
763 | |
764 | Q_SIGNALS: |
765 | |
766 | === added file 'modules/Ubuntu/Components/plugin/uclistitem.cpp' |
767 | --- modules/Ubuntu/Components/plugin/uclistitem.cpp 1970-01-01 00:00:00 +0000 |
768 | +++ modules/Ubuntu/Components/plugin/uclistitem.cpp 2014-09-15 11:00:03 +0000 |
769 | @@ -0,0 +1,1116 @@ |
770 | +/* |
771 | + * Copyright 2014 Canonical Ltd. |
772 | + * |
773 | + * This program is free software; you can redistribute it and/or modify |
774 | + * it under the terms of the GNU Lesser General Public License as published by |
775 | + * the Free Software Foundation; version 3. |
776 | + * |
777 | + * This program is distributed in the hope that it will be useful, |
778 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
779 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
780 | + * GNU Lesser General Public License for more details. |
781 | + * |
782 | + * You should have received a copy of the GNU Lesser General Public License |
783 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
784 | + */ |
785 | + |
786 | +#include "ucunits.h" |
787 | +#include "uctheme.h" |
788 | +#include "uclistitem.h" |
789 | +#include "uclistitem_p.h" |
790 | +#include "uclistitemoptions.h" |
791 | +#include "uclistitemoptions_p.h" |
792 | +#include "ucubuntuanimation.h" |
793 | +#include "propertychange_p.h" |
794 | +#include "i18n.h" |
795 | +#include "quickutils.h" |
796 | +#include "plugin.h" |
797 | +#include "ucmouse.h" |
798 | +#include <QtQml/QQmlInfo> |
799 | +#include <QtQuick/private/qquickitem_p.h> |
800 | +#include <QtQuick/private/qquickflickable_p.h> |
801 | +#include <QtQuick/private/qquickpositioners_p.h> |
802 | + |
803 | +#include <QtQml/private/qqmlabstractbinding_p.h> |
804 | +#define foreach Q_FOREACH //workaround to fix private includes |
805 | +#include <QtQml/private/qqmlbinding_p.h> // for QmlBinding |
806 | +#undef foreach |
807 | + |
808 | + |
809 | +#define MIN(x, y) ((x < y) ? x : y) |
810 | +#define MAX(x, y) ((x > y) ? x : y) |
811 | +#define CLAMP(v, min, max) (min <= max) ? MAX(min, MIN(v, max)) : MAX(max, MIN(v, min)) |
812 | + |
813 | +QColor getPaletteColor(const char *profile, const char *color) |
814 | +{ |
815 | + QColor result; |
816 | + QObject *palette = UCTheme::instance().palette(); |
817 | + if (palette) { |
818 | + QObject *paletteProfile = palette->property(profile).value<QObject*>(); |
819 | + if (paletteProfile) { |
820 | + result = paletteProfile->property(color).value<QColor>(); |
821 | + } |
822 | + } |
823 | + return result; |
824 | +} |
825 | +/****************************************************************************** |
826 | + * Divider |
827 | + */ |
828 | +UCListItemDivider::UCListItemDivider(QObject *parent) |
829 | + : QObject(parent) |
830 | + , m_visible(true) |
831 | + , m_lastItem(false) |
832 | + , m_leftMarginChanged(false) |
833 | + , m_rightMarginChanged(false) |
834 | + , m_colorFromChanged(false) |
835 | + , m_colorToChanged(false) |
836 | + , m_thickness(0) |
837 | + , m_leftMargin(0) |
838 | + , m_rightMargin(0) |
839 | + , m_listItem(0) |
840 | +{ |
841 | + connect(&UCUnits::instance(), &UCUnits::gridUnitChanged, this, &UCListItemDivider::unitsChanged); |
842 | + connect(&UCTheme::instance(), &UCTheme::paletteChanged, this, &UCListItemDivider::paletteChanged); |
843 | + unitsChanged(); |
844 | + paletteChanged(); |
845 | +} |
846 | +UCListItemDivider::~UCListItemDivider() |
847 | +{ |
848 | +} |
849 | + |
850 | +void UCListItemDivider::init(UCListItem *listItem) |
851 | +{ |
852 | + QQml_setParent_noEvent(this, listItem); |
853 | + m_listItem = UCListItemPrivate::get(listItem); |
854 | +} |
855 | + |
856 | +void UCListItemDivider::unitsChanged() |
857 | +{ |
858 | + m_thickness = UCUnits::instance().dp(2); |
859 | + if (!m_leftMarginChanged) { |
860 | + m_leftMargin = UCUnits::instance().gu(2); |
861 | + } |
862 | + if (!m_rightMarginChanged) { |
863 | + m_rightMargin = UCUnits::instance().gu(2); |
864 | + } |
865 | + if (m_listItem) { |
866 | + m_listItem->update(); |
867 | + } |
868 | +} |
869 | + |
870 | +void UCListItemDivider::paletteChanged() |
871 | +{ |
872 | + QColor background = getPaletteColor("normal", "background"); |
873 | + if (!background.isValid()) { |
874 | + return; |
875 | + } |
876 | + // FIXME: we need a palette value for divider colors, till then base on the background |
877 | + // luminance |
878 | + if (!m_colorFromChanged || !m_colorToChanged) { |
879 | + qreal luminance = (background.red()*212 + background.green()*715 + background.blue()*73)/1000.0/255.0; |
880 | + bool lightBackground = (luminance > 0.85); |
881 | + if (!m_colorFromChanged) { |
882 | + m_colorFrom = lightBackground ? QColor("#26000000") : QColor("#26FFFFFF"); |
883 | + } |
884 | + if (!m_colorToChanged) { |
885 | + m_colorTo = lightBackground ? QColor("#14FFFFFF") : QColor("#14000000"); |
886 | + } |
887 | + updateGradient(); |
888 | + } |
889 | +} |
890 | + |
891 | +void UCListItemDivider::updateGradient() |
892 | +{ |
893 | + m_gradient.clear(); |
894 | + m_gradient.append(QGradientStop(0.0, m_colorFrom)); |
895 | + m_gradient.append(QGradientStop(0.49, m_colorFrom)); |
896 | + m_gradient.append(QGradientStop(0.5, m_colorTo)); |
897 | + m_gradient.append(QGradientStop(1.0, m_colorTo)); |
898 | + if (m_listItem) { |
899 | + m_listItem->update(); |
900 | + } |
901 | +} |
902 | + |
903 | +QSGNode *UCListItemDivider::paint(QSGNode *paintNode, const QRectF &rect) |
904 | +{ |
905 | + if (m_visible && !m_lastItem && (m_gradient.size() > 0)) { |
906 | + QSGRectangleNode *rectNode = static_cast<QSGRectangleNode *>(paintNode); |
907 | + if (!rectNode) { |
908 | + rectNode = m_listItem->sceneGraphContext()->createRectangleNode(); |
909 | + } |
910 | + rectNode->setRect(QRectF(m_leftMargin, rect.height() - m_thickness, |
911 | + rect.width() - m_leftMargin - m_rightMargin, m_thickness)); |
912 | + rectNode->setGradientStops(m_gradient); |
913 | + rectNode->update(); |
914 | + return rectNode; |
915 | + } else { |
916 | + delete paintNode; |
917 | + return 0; |
918 | + } |
919 | +} |
920 | + |
921 | +void UCListItemDivider::setVisible(bool visible) |
922 | +{ |
923 | + if (m_visible == visible) { |
924 | + return; |
925 | + } |
926 | + m_visible = visible; |
927 | + m_listItem->resize(); |
928 | + m_listItem->update(); |
929 | + Q_EMIT visibleChanged(); |
930 | +} |
931 | + |
932 | +void UCListItemDivider::setLeftMargin(qreal leftMargin) |
933 | +{ |
934 | + if (m_leftMargin == leftMargin) { |
935 | + return; |
936 | + } |
937 | + m_leftMargin = leftMargin; |
938 | + m_leftMarginChanged = true; |
939 | + m_listItem->update(); |
940 | + Q_EMIT leftMarginChanged(); |
941 | +} |
942 | + |
943 | +void UCListItemDivider::setRightMargin(qreal rightMargin) |
944 | +{ |
945 | + if (m_rightMargin == rightMargin) { |
946 | + return; |
947 | + } |
948 | + m_rightMargin = rightMargin; |
949 | + m_rightMarginChanged = true; |
950 | + m_listItem->update(); |
951 | + Q_EMIT rightMarginChanged(); |
952 | +} |
953 | + |
954 | +void UCListItemDivider::setColorFrom(const QColor &color) |
955 | +{ |
956 | + if (m_colorFrom == color) { |
957 | + return; |
958 | + } |
959 | + m_colorFrom = color; |
960 | + m_colorFromChanged = true; |
961 | + updateGradient(); |
962 | + Q_EMIT colorFromChanged(); |
963 | +} |
964 | + |
965 | +void UCListItemDivider::setColorTo(const QColor &color) |
966 | +{ |
967 | + if (m_colorTo == color) { |
968 | + return; |
969 | + } |
970 | + m_colorTo = color; |
971 | + m_colorToChanged = true; |
972 | + updateGradient(); |
973 | + Q_EMIT colorToChanged(); |
974 | +} |
975 | + |
976 | +/****************************************************************************** |
977 | + * ListItemContent |
978 | + */ |
979 | +UCListItemContent::UCListItemContent(QQuickItem *parent) |
980 | + : QQuickItem(parent) |
981 | + , m_color(Qt::transparent) |
982 | + , m_pressedColor(Qt::yellow) |
983 | + , m_item(0) |
984 | +{ |
985 | + setFlag(QQuickItem::ItemHasContents); |
986 | + // catch theme palette changes |
987 | + connect(&UCTheme::instance(), &UCTheme::paletteChanged, this, &UCListItemContent::updateColors); |
988 | + updateColors(); |
989 | +} |
990 | + |
991 | +UCListItemContent::~UCListItemContent() |
992 | +{ |
993 | +} |
994 | + |
995 | +void UCListItemContent::setColor(const QColor &color) |
996 | +{ |
997 | + if (m_color == color) { |
998 | + return; |
999 | + } |
1000 | + m_color = color; |
1001 | + update(); |
1002 | + Q_EMIT colorChanged(); |
1003 | +} |
1004 | + |
1005 | +void UCListItemContent::setPressedColor(const QColor &color) |
1006 | +{ |
1007 | + if (m_pressedColor == color) { |
1008 | + return; |
1009 | + } |
1010 | + m_pressedColor = color; |
1011 | + // no more theme change watch |
1012 | + disconnect(&UCTheme::instance(), &UCTheme::paletteChanged, this, &UCListItemContent::updateColors); |
1013 | + update(); |
1014 | + Q_EMIT pressedColorChanged(); |
1015 | +} |
1016 | + |
1017 | +void UCListItemContent::updateColors() |
1018 | +{ |
1019 | + m_pressedColor = getPaletteColor("selected", "background"); |
1020 | + update(); |
1021 | +} |
1022 | + |
1023 | + |
1024 | +void UCListItemContent::itemChange(ItemChange change, const ItemChangeData &data) |
1025 | +{ |
1026 | + if (change == ItemParentHasChanged) { |
1027 | + m_item = qobject_cast<UCListItem*>(data.item); |
1028 | + } |
1029 | + QQuickItem::itemChange(change, data); |
1030 | +} |
1031 | + |
1032 | +QSGNode *UCListItemContent::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) |
1033 | +{ |
1034 | + Q_UNUSED(data); |
1035 | + |
1036 | + UCListItemPrivate *dd = UCListItemPrivate::get(m_item); |
1037 | + bool pressed = (dd && dd->pressed); |
1038 | + bool selected = (dd && dd->selectable && dd->selected); |
1039 | + QColor color = (pressed || selected) ? m_pressedColor : m_color; |
1040 | + |
1041 | + delete oldNode; |
1042 | + if (width() <= 0 || height() <= 0 || (color.alpha() == 0)) { |
1043 | + return 0; |
1044 | + } |
1045 | + |
1046 | + QSGRectangleNode *rectNode = QQuickItemPrivate::get(this)->sceneGraphContext()->createRectangleNode(); |
1047 | + rectNode->setColor(color); |
1048 | + rectNode->setRect(boundingRect()); |
1049 | + rectNode->update(); |
1050 | + return rectNode; |
1051 | +} |
1052 | + |
1053 | + |
1054 | +/****************************************************************************** |
1055 | + * ListItemBasePrivate |
1056 | + */ |
1057 | +UCListItemPrivate::UCListItemPrivate() |
1058 | + : UCStyledItemBasePrivate() |
1059 | + , pressed(false) |
1060 | + , moved(false) |
1061 | + , ready(false) |
1062 | + , selectable(false) |
1063 | + , selected(false) |
1064 | + , index(-1) |
1065 | + , xAxisMoveThresholdGU(1.5) |
1066 | + , reboundAnimation(0) |
1067 | + , flickableInteractive(0) |
1068 | + , disabledOpacity(0) |
1069 | + , contentItem(new UCListItemContent) |
1070 | + , divider(new UCListItemDivider) |
1071 | + , leadingOptions(0) |
1072 | + , trailingOptions(0) |
1073 | + , selectionPanel(0) |
1074 | +{ |
1075 | +} |
1076 | +UCListItemPrivate::~UCListItemPrivate() |
1077 | +{ |
1078 | + delete flickableInteractive; |
1079 | + delete disabledOpacity; |
1080 | +} |
1081 | + |
1082 | +void UCListItemPrivate::init() |
1083 | +{ |
1084 | + Q_Q(UCListItem); |
1085 | + contentItem->setObjectName("ListItemHolder"); |
1086 | + QQml_setParent_noEvent(contentItem, q); |
1087 | + contentItem->setParentItem(q); |
1088 | + divider->init(q); |
1089 | + // content will be redirected to the contentItem, therefore we must report |
1090 | + // children changes as it would come from the main component |
1091 | + QObject::connect(contentItem, &UCListItemContent::childrenChanged, |
1092 | + q, &UCListItem::childrenChanged); |
1093 | + q->setFlag(QQuickItem::ItemHasContents); |
1094 | + // turn activeFocusOnPress on |
1095 | + activeFocusOnPress = true; |
1096 | + setFocusable(); |
1097 | + |
1098 | + // watch size change and set implicit size; |
1099 | + QObject::connect(&UCUnits::instance(), SIGNAL(gridUnitChanged()), q, SLOT(_q_updateSize())); |
1100 | + _q_updateSize(); |
1101 | + |
1102 | + // watch enabledChanged() |
1103 | + QObject::connect(q, SIGNAL(enabledChanged()), q, SLOT(_q_dimmDisabled()), Qt::DirectConnection); |
1104 | + |
1105 | + // create rebound animation |
1106 | + UCUbuntuAnimation animationCodes; |
1107 | + reboundAnimation = new QQuickPropertyAnimation(q); |
1108 | + reboundAnimation->setEasing(animationCodes.StandardEasing()); |
1109 | + reboundAnimation->setDuration(animationCodes.SnapDuration()); |
1110 | + reboundAnimation->setTargetObject(contentItem); |
1111 | + reboundAnimation->setProperty("x"); |
1112 | + reboundAnimation->setAlwaysRunToEnd(true); |
1113 | +} |
1114 | + |
1115 | +void UCListItemPrivate::setFocusable() |
1116 | +{ |
1117 | + // alsways accept mouse events |
1118 | + Q_Q(UCListItem); |
1119 | + q->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton); |
1120 | + q->setFiltersChildMouseEvents(true); |
1121 | +} |
1122 | + |
1123 | +void UCListItemPrivate::_q_dimmDisabled() |
1124 | +{ |
1125 | + Q_Q(UCListItem); |
1126 | + if (q->isEnabled()) { |
1127 | + PropertyChange::restore(disabledOpacity); |
1128 | + } else if (opacity() != 0.5) { |
1129 | + // this is the first time we need to create the property change |
1130 | + if (!disabledOpacity) { |
1131 | + disabledOpacity = new PropertyChange(q, "opacity"); |
1132 | + } |
1133 | + PropertyChange::setValue(disabledOpacity, 0.5); |
1134 | + } |
1135 | +} |
1136 | + |
1137 | +void UCListItemPrivate::_q_rebound() |
1138 | +{ |
1139 | + setPressed(false); |
1140 | + // initiate rebinding only if there were options tugged |
1141 | + Q_Q(UCListItem); |
1142 | + if (!UCListItemOptionsPrivate::isConnectedTo(leadingOptions, q) && !UCListItemOptionsPrivate::isConnectedTo(trailingOptions, q)) { |
1143 | + return; |
1144 | + } |
1145 | + setMoved(false); |
1146 | + //connect rebound completion so we can disconnect |
1147 | + QObject::connect(reboundAnimation, SIGNAL(stopped()), q, SLOT(_q_completeRebinding())); |
1148 | + // then rebound to zero |
1149 | + reboundTo(0); |
1150 | +} |
1151 | +void UCListItemPrivate::_q_completeRebinding() |
1152 | +{ |
1153 | + Q_Q(UCListItem); |
1154 | + // disconnect animation, otherwise snapping will disconnect the panel |
1155 | + QObject::disconnect(reboundAnimation, SIGNAL(stopped()), q, SLOT(_q_completeRebinding())); |
1156 | + // restore flickable's interactive and cleanup |
1157 | + PropertyChange::restore(flickableInteractive); |
1158 | + // disconnect options |
1159 | + grabPanel(leadingOptions, false); |
1160 | + grabPanel(trailingOptions, false); |
1161 | +} |
1162 | + |
1163 | +void UCListItemPrivate::_q_updateIndex(QObject *ownerItem) |
1164 | +{ |
1165 | + Q_Q(UCListItem); |
1166 | + if (!ownerItem) { |
1167 | + ownerItem = q->sender(); |
1168 | + } |
1169 | + Q_ASSERT(ownerItem); |
1170 | + // update the index as well |
1171 | + QQmlContext *context = qmlContext(q); |
1172 | + if (context) { |
1173 | + QVariant indexProperty = context->contextProperty("index"); |
1174 | + index = indexProperty.isValid() ? indexProperty.toInt() : -1; |
1175 | + } |
1176 | + divider->m_lastItem = ready && index == (ownerItem->property("count").toInt() - 1); |
1177 | +} |
1178 | + |
1179 | +void UCListItemPrivate::_q_updateSelected() |
1180 | +{ |
1181 | + Q_Q(UCListItem); |
1182 | + bool checked = selectionPanel->property("checked").toBool(); |
1183 | + q->setSelected(checked); |
1184 | + contentItem->update(); |
1185 | +} |
1186 | + |
1187 | +// the function performs a cleanup on mouse release without any rebound animation |
1188 | +void UCListItemPrivate::cleanup() |
1189 | +{ |
1190 | + setPressed(false); |
1191 | + setMoved(false); |
1192 | + _q_completeRebinding(); |
1193 | +} |
1194 | + |
1195 | +void UCListItemPrivate::reboundTo(qreal x) |
1196 | +{ |
1197 | + reboundAnimation->setFrom(contentItem->x()); |
1198 | + reboundAnimation->setTo(x); |
1199 | + reboundAnimation->restart(); |
1200 | +} |
1201 | + |
1202 | +// set pressed flag and update background |
1203 | +// called when units size changes |
1204 | +void UCListItemPrivate::_q_updateSize() |
1205 | +{ |
1206 | + Q_Q(UCListItem); |
1207 | + QQuickItem *owner = flickable ? flickable : parentItem; |
1208 | + q->setImplicitWidth(owner ? owner->width() : UCUnits::instance().gu(40)); |
1209 | + q->setImplicitHeight(UCUnits::instance().gu(7)); |
1210 | +} |
1211 | + |
1212 | +// set pressed flag and update contentItem |
1213 | +void UCListItemPrivate::setPressed(bool pressed) |
1214 | +{ |
1215 | + if (this->pressed != pressed) { |
1216 | + this->pressed = pressed; |
1217 | + suppressClick = false; |
1218 | + contentItem->update(); |
1219 | + Q_Q(UCListItem); |
1220 | + Q_EMIT q->pressedChanged(); |
1221 | + } |
1222 | +} |
1223 | +// toggles the moved flag and installs/removes event filter |
1224 | +void UCListItemPrivate::setMoved(bool moved) |
1225 | +{ |
1226 | + suppressClick = moved; |
1227 | + if (this->moved == moved) { |
1228 | + return; |
1229 | + } |
1230 | + this->moved = moved; |
1231 | + Q_Q(UCListItem); |
1232 | + QQuickWindow *window = q->window(); |
1233 | + if (!window) { |
1234 | + return; |
1235 | + } |
1236 | + if (moved) { |
1237 | + window->installEventFilter(q); |
1238 | + } else { |
1239 | + window->removeEventFilter(q); |
1240 | + } |
1241 | +} |
1242 | + |
1243 | +// sets the moved flag but also grabs the panels from the leading/trailing options |
1244 | +bool UCListItemPrivate::grabPanel(UCListItemOptions *optionsList, bool isMoved) |
1245 | +{ |
1246 | + Q_Q(UCListItem); |
1247 | + if (isMoved) { |
1248 | + bool grab = UCListItemOptionsPrivate::connectToListItem(optionsList, q, (optionsList == leadingOptions)); |
1249 | + if (grab) { |
1250 | + PropertyChange::setValue(flickableInteractive, false); |
1251 | + } |
1252 | + return grab; |
1253 | + } else { |
1254 | + UCListItemOptionsPrivate::disconnectFromListItem(optionsList); |
1255 | + return false; |
1256 | + } |
1257 | +} |
1258 | + |
1259 | + |
1260 | +// connects/disconnects from the Flickable anchestor to get notified when to do rebound |
1261 | +void UCListItemPrivate::listenToRebind(bool listen) |
1262 | +{ |
1263 | + if (flickable.isNull()) { |
1264 | + return; |
1265 | + } |
1266 | + Q_Q(UCListItem); |
1267 | + if (listen) { |
1268 | + QObject::connect(flickable.data(), SIGNAL(movementStarted()), q, SLOT(_q_rebound())); |
1269 | + } else { |
1270 | + QObject::disconnect(flickable.data(), SIGNAL(movementStarted()), q, SLOT(_q_rebound())); |
1271 | + } |
1272 | +} |
1273 | + |
1274 | +void UCListItemPrivate::resize() |
1275 | +{ |
1276 | + Q_Q(UCListItem); |
1277 | + QRectF rect(q->boundingRect()); |
1278 | + if (divider && divider->m_visible) { |
1279 | + rect.setHeight(rect.height() - divider->m_thickness); |
1280 | + } |
1281 | + contentItem->setSize(rect.size()); |
1282 | +} |
1283 | + |
1284 | +void UCListItemPrivate::update() |
1285 | +{ |
1286 | + if (!ready) { |
1287 | + return; |
1288 | + } |
1289 | + Q_Q(UCListItem); |
1290 | + q->update(); |
1291 | +} |
1292 | + |
1293 | +void UCListItemPrivate::clampX(qreal &x, qreal dx) |
1294 | +{ |
1295 | + UCListItemOptionsPrivate *leading = UCListItemOptionsPrivate::get(leadingOptions); |
1296 | + UCListItemOptionsPrivate *trailing = UCListItemOptionsPrivate::get(trailingOptions); |
1297 | + x += dx; |
1298 | + // min cannot be less than the trailing's panel width |
1299 | + qreal min = (trailing && trailing->panelItem) ? -trailing->panelItem->width() : 0; |
1300 | + // max cannot be bigger than 0 or the leading's width in case we have leading panel |
1301 | + qreal max = (leading && leading->panelItem) ? leading->panelItem->width() : 0; |
1302 | + x = CLAMP(x, min, max); |
1303 | +} |
1304 | + |
1305 | +void UCListItemPrivate::autoLeadingOptions() |
1306 | +{ |
1307 | + Q_Q(UCListItem); |
1308 | + if (flickable && flickable->property("leadingOptions").isValid() && !leadingOptions) { |
1309 | + QQmlProperty prop(flickable, "leadingOptions", qmlContext(flickable)); |
1310 | + QQmlAbstractBinding *binding = QQmlPropertyPrivate::binding(prop); |
1311 | + // set the value first, then bind |
1312 | + q->setLeadingOptions(prop.read().value<UCListItemOptions*>()); |
1313 | + if (binding) { |
1314 | + // set the binding to our leadingOptions property |
1315 | + QQmlProperty leadingOptionsProperty(q, "leadingOptions", qmlContext(q)); |
1316 | + QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::setBinding(leadingOptionsProperty, binding); |
1317 | + if (oldBinding && (oldBinding != binding)) { |
1318 | + oldBinding->destroy(); |
1319 | + } |
1320 | + } |
1321 | + } |
1322 | +} |
1323 | + |
1324 | +QQuickItem *UCListItemPrivate::createSelectionPanel() |
1325 | +{ |
1326 | + Q_Q(UCListItem); |
1327 | + if (!selectionPanel) { |
1328 | + QUrl panelDocument = UbuntuComponentsPlugin::pluginUrl(). |
1329 | + resolved(QUrl::fromLocalFile("ListItemSelectablePanel.qml")); |
1330 | + QQmlComponent component(qmlEngine(q), panelDocument); |
1331 | + if (!component.isError()) { |
1332 | + selectionPanel = qobject_cast<QQuickItem*>(component.beginCreate(qmlContext(q))); |
1333 | + if (selectionPanel) { |
1334 | + QQml_setParent_noEvent(selectionPanel, q); |
1335 | + selectionPanel->setParentItem(q); |
1336 | + selectionPanel->setVisible(false); |
1337 | + selectionPanel->setProperty("checked", selected); |
1338 | + // complete component creation |
1339 | + component.completeCreate(); |
1340 | + } |
1341 | + } else { |
1342 | + qmlInfo(q) << component.errorString(); |
1343 | + } |
1344 | + } |
1345 | + return selectionPanel; |
1346 | +} |
1347 | +void UCListItemPrivate::toggleSelectionMode() |
1348 | +{ |
1349 | + if (!createSelectionPanel()) { |
1350 | + return; |
1351 | + } |
1352 | + Q_Q(UCListItem); |
1353 | + if (selectable) { |
1354 | + // move and dimm content item |
1355 | + selectionPanel->setVisible(true); |
1356 | + reboundTo(selectionPanel->width()); |
1357 | + QObject::connect(selectionPanel, SIGNAL(checkedChanged()), q, SLOT(_q_updateSelected())); |
1358 | + } else { |
1359 | + // remove content item dimming and destroy selection panel as well |
1360 | + reboundTo(0.0); |
1361 | + selectionPanel->setVisible(false); |
1362 | + QObject::disconnect(selectionPanel, SIGNAL(checkedChanged()), q, SLOT(_q_updateSelected())); |
1363 | + } |
1364 | + _q_updateSelected(); |
1365 | +} |
1366 | + |
1367 | +/*! |
1368 | + * \qmltype ListItem |
1369 | + * \instantiates UCListItem |
1370 | + * \inqmlmodule Ubuntu.Components 1.1 |
1371 | + * \ingroup ubuntu |
1372 | + * \brief The ListItem element provides Ubuntu design standards for list or grid |
1373 | + * views. |
1374 | + * |
1375 | + * The component is dedicated to be used in designs with static or dynamic lists |
1376 | + * (i.e. list views where each item's layout differs or in lists where the content |
1377 | + * is determined by a given model, thus each element has the same layout). The |
1378 | + * element does not define any specific layout, components can be placed in any |
1379 | + * ways on it. However, when used in list views, the content must be carefully |
1380 | + * chosen to in order to keep the kinetic behavior and the highest FPS possible. |
1381 | + * |
1382 | + * \c contentItem is an essential part of the component. Beside the fact that it |
1383 | + * holds all components and resources declared as child to ListItem, it can also |
1384 | + * configure the color of the background when in normal mode or when pressed. Being |
1385 | + * an item, all other properties can be accessed or altered, with the exception |
1386 | + * of some: |
1387 | + * \list A |
1388 | + * \li do not alter \c x, \c y, \c width or \c height properties as those are |
1389 | + * controlled by the ListItem itself when leading or trailing options are |
1390 | + * revealed and thus will destroy your logic |
1391 | + * \li never anchor left or right anchor lines as it will block revealing the options. |
1392 | + * \endlist |
1393 | + * |
1394 | + * Each ListItem has a thin divider shown on the bottom of the component. This |
1395 | + * divider can be configured through the \l divider grouped property, which can |
1396 | + * configure its margins from the edges of the ListItem as well as its visibility. |
1397 | + * When used in \c ListView or \l UbuntuListView, the last list item will not |
1398 | + * show the divider no matter of the visible property value set. In other |
1399 | + * circumstances declaring a \c count property in the ListItem's parent item |
1400 | + * or Flickable can help applying the last item detection logic. |
1401 | + * \qml |
1402 | + * Column { |
1403 | + * width: units.gu(40) |
1404 | + * // bring count to Column from Repeater |
1405 | + * property alias count: repeater.count |
1406 | + * Repeater { |
1407 | + * model: 10 |
1408 | + * ListItem { |
1409 | + * Label { |
1410 | + * anchors.fill: parent |
1411 | + * horizontalCenter: Text.AlignHCenter |
1412 | + * verticalCenter: Text.AlignVCenter |
1413 | + * text: "Item #" + modelData |
1414 | + * } |
1415 | + * } |
1416 | + * } |
1417 | + * } |
1418 | + * \endqml |
1419 | + * |
1420 | + * ListItem can handle options that can ge tugged from front ot right of the item. |
1421 | + * These options are Action elements visualized in panels attached to the front |
1422 | + * or to the end of the item, and are revealed by swiping the item horizontally. |
1423 | + * The tug is started only after the mouse/touch move had passed a given threshold. |
1424 | + * These options are configured through the \l leadingOptions as well as \l |
1425 | + * trailingOptions properties. |
1426 | + * \qml |
1427 | + * ListItem { |
1428 | + * id: listItem |
1429 | + * leadingOptions: ListItemOptions { |
1430 | + * Action { |
1431 | + * iconName: "delete" |
1432 | + * onTriggered: listItem.destroy() |
1433 | + * } |
1434 | + * } |
1435 | + * trailingOptions: ListItemOptions { |
1436 | + * Action { |
1437 | + * iconName: "search" |
1438 | + * onTriggered: { |
1439 | + * // do some search |
1440 | + * } |
1441 | + * } |
1442 | + * } |
1443 | + * } |
1444 | + * \endqml |
1445 | + * \note A ListItem cannot use the same ListItemOption instance for both leading or |
1446 | + * trailing options. If it is desired to have the same action present in both leading |
1447 | + * and trailing options, one of the ListItemOption options list can use the other's |
1448 | + * list. In the following example the list item can be deleted through both option |
1449 | + * leading and trailing options: |
1450 | + * \qml |
1451 | + * ListItem { |
1452 | + * id: listItem |
1453 | + * leadingOptions: ListItemOptions { |
1454 | + * Action { |
1455 | + * iconName: "delete" |
1456 | + * onTriggered: listItem.destroy() |
1457 | + * } |
1458 | + * } |
1459 | + * trailingOptions: ListItemOptions { |
1460 | + * options: leadingOptions.options |
1461 | + * } |
1462 | + * } |
1463 | + * \endqml |
1464 | + * \sa ListItemOptions |
1465 | + */ |
1466 | + |
1467 | +/*! |
1468 | + * \qmlsignal ListItem::clicked() |
1469 | + * |
1470 | + * The signal is emitted when the component gets released while the \l pressed property |
1471 | + * is set. The signal is not emitted if the ListItem content is tugged or when used in |
1472 | + * Flickable (or ListView, GridView) and the Flickable gets moved. |
1473 | + */ |
1474 | +UCListItem::UCListItem(QQuickItem *parent) |
1475 | + : UCStyledItemBase(*(new UCListItemPrivate), parent) |
1476 | +{ |
1477 | + Q_D(UCListItem); |
1478 | + d->init(); |
1479 | +} |
1480 | + |
1481 | +UCListItem::~UCListItem() |
1482 | +{ |
1483 | +} |
1484 | + |
1485 | +void UCListItem::componentComplete() |
1486 | +{ |
1487 | + UCStyledItemBase::componentComplete(); |
1488 | + Q_D(UCListItem); |
1489 | + d->ready = true; |
1490 | + /* We only deal with ListView, as for other cases we would need to check the children |
1491 | + * changes, which would have an enormous impact on performance in case of huge amount |
1492 | + * of items. However, if the parent item, or Flickable declares a "count" property, |
1493 | + * the ListItem will take use of it! |
1494 | + */ |
1495 | + QQuickItem *countOwner = (d->flickable && d->flickable->property("count").isValid()) ? |
1496 | + d->flickable : |
1497 | + (d->parentItem && d->parentItem->property("count").isValid()) ? d->parentItem : 0; |
1498 | + if (countOwner) { |
1499 | + QObject::connect(countOwner, SIGNAL(countChanged()), |
1500 | + this, SLOT(_q_updateIndex()), Qt::DirectConnection); |
1501 | + d->_q_updateIndex(countOwner); |
1502 | + } |
1503 | + |
1504 | + // check if the parent ListView has leadingOptions defined, if yes, use it! |
1505 | + d->autoLeadingOptions(); |
1506 | +} |
1507 | + |
1508 | +void UCListItem::itemChange(ItemChange change, const ItemChangeData &data) |
1509 | +{ |
1510 | + UCStyledItemBase::itemChange(change, data); |
1511 | + if (change == ItemParentHasChanged) { |
1512 | + Q_D(UCListItem); |
1513 | + // make sure we are not connected to the previous Flickable |
1514 | + d->listenToRebind(false); |
1515 | + // check if we are in a positioner, and if that positioner is in a Flickable |
1516 | + QQuickBasePositioner *positioner = qobject_cast<QQuickBasePositioner*>(data.item); |
1517 | + if (positioner && positioner->parentItem()) { |
1518 | + d->flickable = qobject_cast<QQuickFlickable*>(positioner->parentItem()->parentItem()); |
1519 | + } else if (data.item && data.item->parentItem()){ |
1520 | + // check if we are in a Flickable then |
1521 | + d->flickable = qobject_cast<QQuickFlickable*>(data.item->parentItem()); |
1522 | + } |
1523 | + |
1524 | + if (d->flickable) { |
1525 | + // create the flickableInteractive property change now |
1526 | + d->flickableInteractive = new PropertyChange(d->flickable, "interactive"); |
1527 | + // connect to flickable to get width changes |
1528 | + QObject::connect(d->flickable, SIGNAL(widthChanged()), this, SLOT(_q_updateSize())); |
1529 | + } else if (data.item) { |
1530 | + QObject::connect(data.item, SIGNAL(widthChanged()), this, SLOT(_q_updateSize())); |
1531 | + } else { |
1532 | + // in case we had a flickableInteractive property change active, destroy it |
1533 | + if (d->flickableInteractive) { |
1534 | + delete d->flickableInteractive; |
1535 | + d->flickableInteractive = 0; |
1536 | + } |
1537 | + // mar as not ready, so no action should be performed which depends on readyness |
1538 | + d->ready = false; |
1539 | + } |
1540 | + |
1541 | + // update size |
1542 | + d->_q_updateSize(); |
1543 | + } |
1544 | +} |
1545 | + |
1546 | +void UCListItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) |
1547 | +{ |
1548 | + UCStyledItemBase::geometryChanged(newGeometry, oldGeometry); |
1549 | + // resize contentItem item |
1550 | + Q_D(UCListItem); |
1551 | + d->resize(); |
1552 | +} |
1553 | + |
1554 | +QSGNode *UCListItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) |
1555 | +{ |
1556 | + Q_UNUSED(data); |
1557 | + Q_D(UCListItem); |
1558 | + if (width() <= 0 || height() <= 0 || !d->divider) { |
1559 | + delete oldNode; |
1560 | + return 0; |
1561 | + } |
1562 | + // paint divider |
1563 | + return d->divider->paint(oldNode, boundingRect()); |
1564 | +} |
1565 | + |
1566 | +void UCListItem::mousePressEvent(QMouseEvent *event) |
1567 | +{ |
1568 | + UCStyledItemBase::mousePressEvent(event); |
1569 | + Q_D(UCListItem); |
1570 | + if (d->selectable || (!d->flickable.isNull() && d->flickable->isMoving())) { |
1571 | + // while moving, we cannot select or tug any items |
1572 | + return; |
1573 | + } |
1574 | + d->setPressed(true); |
1575 | + d->lastPos = d->pressedPos = event->localPos(); |
1576 | + // connect the Flickable to know when to rebound |
1577 | + d->listenToRebind(true); |
1578 | + // start pressandhold timer |
1579 | + d->pressAndHoldTimer.start(DefaultPressAndHoldDelay, this); |
1580 | + // accept the event so we get the rest of the events as well |
1581 | + event->setAccepted(true); |
1582 | +} |
1583 | + |
1584 | +void UCListItem::mouseReleaseEvent(QMouseEvent *event) |
1585 | +{ |
1586 | + UCStyledItemBase::mouseReleaseEvent(event); |
1587 | + Q_D(UCListItem); |
1588 | + if (d->selectable) { |
1589 | + // no move is allowed while selectable mode is on |
1590 | + return; |
1591 | + } |
1592 | + d->pressAndHoldTimer.stop(); |
1593 | + // set released |
1594 | + if (d->pressed) { |
1595 | + // disconnect the flickable |
1596 | + d->listenToRebind(false); |
1597 | + |
1598 | + if (!d->suppressClick) { |
1599 | + Q_EMIT clicked(); |
1600 | + d->_q_rebound(); |
1601 | + } else { |
1602 | + // snap |
1603 | + qreal snapPosition = 0.0; |
1604 | + if (d->contentItem->x() < 0) { |
1605 | + snapPosition = UCListItemOptionsPrivate::snap(d->trailingOptions); |
1606 | + } else if (d->contentItem->x() > 0) { |
1607 | + snapPosition = UCListItemOptionsPrivate::snap(d->leadingOptions); |
1608 | + } |
1609 | + if (d->contentItem->x() == 0.0) { |
1610 | + // do a cleanup, no need to rebound, the item has been dragged back to 0 |
1611 | + d->cleanup(); |
1612 | + } else if (snapPosition == 0.0){ |
1613 | + d->_q_rebound(); |
1614 | + } else { |
1615 | + d->reboundTo(snapPosition); |
1616 | + } |
1617 | + } |
1618 | + } |
1619 | + d->setPressed(false); |
1620 | +} |
1621 | + |
1622 | +void UCListItem::mouseMoveEvent(QMouseEvent *event) |
1623 | +{ |
1624 | + Q_D(UCListItem); |
1625 | + UCStyledItemBase::mouseMoveEvent(event); |
1626 | + |
1627 | + if (d->selectable) { |
1628 | + // no move is allowed while selectable mode is on |
1629 | + return; |
1630 | + } |
1631 | + |
1632 | + // accept the tugging only if the move is within the threshold |
1633 | + bool leadingAttached = UCListItemOptionsPrivate::isConnectedTo(d->leadingOptions, this); |
1634 | + bool trailingAttached = UCListItemOptionsPrivate::isConnectedTo(d->trailingOptions, this); |
1635 | + if (d->pressed && !(leadingAttached || trailingAttached)) { |
1636 | + // check if we can initiate the drag at all |
1637 | + // only X direction matters, if Y-direction leaves the threshold, but X not, the tug is not valid |
1638 | + qreal threshold = UCUnits::instance().gu(d->xAxisMoveThresholdGU); |
1639 | + qreal mouseX = event->localPos().x(); |
1640 | + qreal pressedX = d->pressedPos.x(); |
1641 | + |
1642 | + if ((mouseX < (pressedX - threshold)) || (mouseX > (pressedX + threshold))) { |
1643 | + // the press went out of the threshold area, enable move, if the direction allows it |
1644 | + d->lastPos = event->localPos(); |
1645 | + // connect both panels |
1646 | + leadingAttached = d->grabPanel(d->leadingOptions, true); |
1647 | + trailingAttached = d->grabPanel(d->trailingOptions, true); |
1648 | + // stop pressAndHold timer as we started to drag |
1649 | + d->pressAndHoldTimer.stop(); |
1650 | + } |
1651 | + } |
1652 | + |
1653 | + if (leadingAttached || trailingAttached) { |
1654 | + qreal x = d->contentItem->x(); |
1655 | + qreal dx = event->localPos().x() - d->lastPos.x(); |
1656 | + d->lastPos = event->localPos(); |
1657 | + |
1658 | + if (dx) { |
1659 | + // clamp X into allowed dragging area |
1660 | + d->clampX(x, dx); |
1661 | + // block flickable |
1662 | + d->setMoved(true); |
1663 | + d->contentItem->setX(x); |
1664 | + } |
1665 | + } |
1666 | +} |
1667 | + |
1668 | +bool UCListItem::eventFilter(QObject *target, QEvent *event) |
1669 | +{ |
1670 | + QPointF myPos; |
1671 | + // only filter press events, and rebound when pressed outside |
1672 | + if (event->type() == QEvent::MouseButtonPress) { |
1673 | + QMouseEvent *mouse = static_cast<QMouseEvent*>(event); |
1674 | + QQuickWindow *window = qobject_cast<QQuickWindow*>(target); |
1675 | + if (window) { |
1676 | + myPos = window->contentItem()->mapToItem(this, mouse->localPos()); |
1677 | + } |
1678 | + } else if (event->type() == QEvent::TouchBegin) { |
1679 | + QTouchEvent *touch = static_cast<QTouchEvent*>(event); |
1680 | + QQuickWindow *window = qobject_cast<QQuickWindow*>(target); |
1681 | + if (window) { |
1682 | + myPos = window->contentItem()->mapToItem(this, touch->touchPoints()[0].pos()); |
1683 | + } |
1684 | + } |
1685 | + if (!myPos.isNull() && !contains(myPos)) { |
1686 | + Q_D(UCListItem); |
1687 | + d->_q_rebound(); |
1688 | + // only accept event, but let it be handled by the underlying or surrounding Flickables |
1689 | + event->accept(); |
1690 | + } |
1691 | + return UCStyledItemBase::eventFilter(target, event); |
1692 | +} |
1693 | + |
1694 | +void UCListItem::timerEvent(QTimerEvent *event) |
1695 | +{ |
1696 | + Q_D(UCListItem); |
1697 | + if (isEnabled() && (event->timerId() == d->pressAndHoldTimer.timerId())) { |
1698 | + d->pressAndHoldTimer.stop(); |
1699 | + d->suppressClick = true; |
1700 | + Q_EMIT pressAndHold(); |
1701 | + } else { |
1702 | + QQuickItem::timerEvent(event); |
1703 | + } |
1704 | +} |
1705 | + |
1706 | +/*! |
1707 | + * \qmlproperty ListItemOptions ListItem::leadingOptions |
1708 | + * |
1709 | + * The property holds the options and its configuration to be revealed when swiped |
1710 | + * from left to right. |
1711 | + * |
1712 | + * \sa trailingOptions |
1713 | + */ |
1714 | +UCListItemOptions *UCListItem::leadingOptions() const |
1715 | +{ |
1716 | + Q_D(const UCListItem); |
1717 | + return d->leadingOptions; |
1718 | +} |
1719 | +void UCListItem::setLeadingOptions(UCListItemOptions *options) |
1720 | +{ |
1721 | + Q_D(UCListItem); |
1722 | + if (d->leadingOptions == options) { |
1723 | + return; |
1724 | + } |
1725 | + d->leadingOptions = options; |
1726 | + if (d->leadingOptions == d->trailingOptions && d->leadingOptions) { |
1727 | + qmlInfo(this) << UbuntuI18n::tr("leadingOptions and trailingOptions cannot share the same object!"); |
1728 | + } |
1729 | + Q_EMIT leadingOptionsChanged(); |
1730 | +} |
1731 | + |
1732 | +/*! |
1733 | + * \qmlproperty ListItemOptions ListItem::trailingOptions |
1734 | + * |
1735 | + * The property holds the options and its configuration to be revealed when swiped |
1736 | + * from right to left. |
1737 | + * |
1738 | + * \sa leadingOptions |
1739 | + */ |
1740 | +UCListItemOptions *UCListItem::trailingOptions() const |
1741 | +{ |
1742 | + Q_D(const UCListItem); |
1743 | + return d->trailingOptions; |
1744 | +} |
1745 | +void UCListItem::setTrailingOptions(UCListItemOptions *options) |
1746 | +{ |
1747 | + Q_D(UCListItem); |
1748 | + if (d->trailingOptions == options) { |
1749 | + return; |
1750 | + } |
1751 | + d->trailingOptions = options; |
1752 | + if (d->leadingOptions == d->trailingOptions && d->trailingOptions) { |
1753 | + qmlInfo(this) << UbuntuI18n::tr("leadingOptions and trailingOptions cannot share the same object!"); |
1754 | + } |
1755 | + Q_EMIT trailingOptionsChanged(); |
1756 | +} |
1757 | + |
1758 | +/*! |
1759 | + * \qmlpropertygroup ::ListItem::contentItem |
1760 | + * \qmlproperty color ListItem::contentItem.color |
1761 | + * \qmlproperty color ListItem::contentItem.pressedColor |
1762 | + * |
1763 | + * contentItem holds the components placed on a ListItem. \c color configures |
1764 | + * the color of the normal contentItem, and \c pressedColor configures the color |
1765 | + * when pressed. |
1766 | + */ |
1767 | +UCListItemContent* UCListItem::contentItem() const |
1768 | +{ |
1769 | + Q_D(const UCListItem); |
1770 | + return d->contentItem; |
1771 | +} |
1772 | + |
1773 | +/*! |
1774 | + * \qmlpropertygroup ::ListItem::divider |
1775 | + * \qmlproperty bool ListItem::divider.visible |
1776 | + * \qmlproperty real ListItem::divider.leftMargin |
1777 | + * \qmlproperty real ListItem::divider.rightMargin |
1778 | + * |
1779 | + * This grouped property configures the thin divider shown in the bottom of the |
1780 | + * component. Configures the visibility and the margins from the left and right |
1781 | + * of the ListItem. When tugged (swiped left or right to reveal the options), |
1782 | + * it is not moved together with the content. |
1783 | + * |
1784 | + * When \c visible is true, the ListItem's content size gets thinner with the |
1785 | + * divider's \c thickness. |
1786 | + * |
1787 | + * The default values for the properties are: |
1788 | + * \list |
1789 | + * \li \c visible: true |
1790 | + * \li \c leftMargin: 2 GU |
1791 | + * \li \c rightMargin: 2 GU |
1792 | + * \endlist |
1793 | + */ |
1794 | +UCListItemDivider* UCListItem::divider() const |
1795 | +{ |
1796 | + Q_D(const UCListItem); |
1797 | + return d->divider; |
1798 | +} |
1799 | + |
1800 | +/*! |
1801 | + * \qmlproperty bool ListItem::pressed |
1802 | + * True when the item is pressed. The items stays pressed when the mouse or touch |
1803 | + * is moved horizontally. When in Flickable (or ListView), the item gets un-pressed |
1804 | + * (false) when the mouse or touch is moved towards the vertical direction causing |
1805 | + * the flickable to move. |
1806 | + */ |
1807 | +bool UCListItem::pressed() const |
1808 | +{ |
1809 | + Q_D(const UCListItem); |
1810 | + return d->pressed; |
1811 | +} |
1812 | + |
1813 | +/*! |
1814 | + * \qmlproperty bool ListItem::selectable |
1815 | + * The property drives whether a list item is selectable or not. When set, the item |
1816 | + * will show a check box on the leading side hanving the content item pushed towards |
1817 | + * trailing side and dimmed. The checkbox which will reflect and drive the \l selected |
1818 | + * state. |
1819 | + * Defaults to false. |
1820 | + * \note it is recommended to be used with UbuntuListView which can drive all the |
1821 | + * ListItem's selectable. |
1822 | + */ |
1823 | +bool UCListItem::selectable() const |
1824 | +{ |
1825 | + Q_D(const UCListItem); |
1826 | + return d->selectable; |
1827 | +} |
1828 | +void UCListItem::setSelectable(bool selectable) |
1829 | +{ |
1830 | + Q_D(UCListItem); |
1831 | + if (d->selectable == selectable) { |
1832 | + return; |
1833 | + } |
1834 | + d->selectable = selectable; |
1835 | + d->toggleSelectionMode(); |
1836 | + Q_EMIT selectableChanged(); |
1837 | +} |
1838 | + |
1839 | +/*! |
1840 | + * \qmlproperty bool ListItem::selected |
1841 | + * The property drives whether a list item is selected or not. While selected, the |
1842 | + * ListItem is dimmed and cannot be tugged. The default value is false. |
1843 | + */ |
1844 | +bool UCListItem::selected() const |
1845 | +{ |
1846 | + Q_D(const UCListItem); |
1847 | + return d->selected; |
1848 | +} |
1849 | +void UCListItem::setSelected(bool selected) |
1850 | +{ |
1851 | + Q_D(UCListItem); |
1852 | + if (d->selected == selected) { |
1853 | + return; |
1854 | + } |
1855 | + d->selected = selected; |
1856 | + // update panel as well |
1857 | + if (d->selectionPanel) { |
1858 | + d->selectionPanel->setProperty("checked", d->selected); |
1859 | + } |
1860 | + Q_EMIT selectedChanged(); |
1861 | +} |
1862 | + |
1863 | + |
1864 | +/*! |
1865 | + * \qmlproperty list<Object> ListItem::data |
1866 | + * \default |
1867 | + * Overloaded default property containing all the children and resources. |
1868 | + */ |
1869 | +QQmlListProperty<QObject> UCListItem::data() |
1870 | +{ |
1871 | + Q_D(UCListItem); |
1872 | + return QQuickItemPrivate::get(d->contentItem)->data(); |
1873 | +} |
1874 | + |
1875 | +/*! |
1876 | + * \qmlproperty list<Item> ListItem::children |
1877 | + * Overloaded default property containing all the visible children of the item. |
1878 | + */ |
1879 | +QQmlListProperty<QQuickItem> UCListItem::children() |
1880 | +{ |
1881 | + Q_D(UCListItem); |
1882 | + return QQuickItemPrivate::get(d->contentItem)->children(); |
1883 | +} |
1884 | + |
1885 | +#include "moc_uclistitem.cpp" |
1886 | |
1887 | === added file 'modules/Ubuntu/Components/plugin/uclistitem.h' |
1888 | --- modules/Ubuntu/Components/plugin/uclistitem.h 1970-01-01 00:00:00 +0000 |
1889 | +++ modules/Ubuntu/Components/plugin/uclistitem.h 2014-09-15 11:00:03 +0000 |
1890 | @@ -0,0 +1,92 @@ |
1891 | +/* |
1892 | + * Copyright 2014 Canonical Ltd. |
1893 | + * |
1894 | + * This program is free software; you can redistribute it and/or modify |
1895 | + * it under the terms of the GNU Lesser General Public License as published by |
1896 | + * the Free Software Foundation; version 3. |
1897 | + * |
1898 | + * This program is distributed in the hope that it will be useful, |
1899 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1900 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1901 | + * GNU Lesser General Public License for more details. |
1902 | + * |
1903 | + * You should have received a copy of the GNU Lesser General Public License |
1904 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1905 | + */ |
1906 | + |
1907 | +#ifndef UCLISTITEM_H |
1908 | +#define UCLISTITEM_H |
1909 | + |
1910 | +#include <QtQuick/QQuickItem> |
1911 | +#include "ucstyleditembase.h" |
1912 | + |
1913 | +class UCListItemContent; |
1914 | +class UCListItemDivider; |
1915 | +class UCListItemOptions; |
1916 | +class UCListItemPrivate; |
1917 | +class UCListItem : public UCStyledItemBase |
1918 | +{ |
1919 | + Q_OBJECT |
1920 | + Q_PROPERTY(UCListItemContent *contentItem READ contentItem CONSTANT) |
1921 | + Q_PROPERTY(UCListItemDivider *divider READ divider CONSTANT) |
1922 | + Q_PROPERTY(UCListItemOptions *leadingOptions READ leadingOptions WRITE setLeadingOptions NOTIFY leadingOptionsChanged DESIGNABLE false) |
1923 | + Q_PROPERTY(UCListItemOptions *trailingOptions READ trailingOptions WRITE setTrailingOptions NOTIFY trailingOptionsChanged DESIGNABLE false) |
1924 | + Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged) |
1925 | + Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged) |
1926 | + Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectedChanged) |
1927 | + Q_PROPERTY(QQmlListProperty<QObject> data READ data DESIGNABLE false) |
1928 | + Q_PROPERTY(QQmlListProperty<QQuickItem> children READ children NOTIFY childrenChanged DESIGNABLE false) |
1929 | + Q_CLASSINFO("DefaultProperty", "data") |
1930 | +public: |
1931 | + explicit UCListItem(QQuickItem *parent = 0); |
1932 | + ~UCListItem(); |
1933 | + |
1934 | + UCListItemContent *contentItem() const; |
1935 | + UCListItemDivider *divider() const; |
1936 | + UCListItemOptions *leadingOptions() const; |
1937 | + void setLeadingOptions(UCListItemOptions *options); |
1938 | + UCListItemOptions *trailingOptions() const; |
1939 | + void setTrailingOptions(UCListItemOptions *options); |
1940 | + bool pressed() const; |
1941 | + bool selectable() const; |
1942 | + void setSelectable(bool selectable); |
1943 | + bool selected() const; |
1944 | + void setSelected(bool selected); |
1945 | + |
1946 | +protected: |
1947 | + void componentComplete(); |
1948 | + void itemChange(ItemChange change, const ItemChangeData &data); |
1949 | + QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data); |
1950 | + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); |
1951 | + void mousePressEvent(QMouseEvent *event); |
1952 | + void mouseReleaseEvent(QMouseEvent *event); |
1953 | + void mouseMoveEvent(QMouseEvent *event); |
1954 | + bool eventFilter(QObject *, QEvent *); |
1955 | + void timerEvent(QTimerEvent *event); |
1956 | + |
1957 | +Q_SIGNALS: |
1958 | + void leadingOptionsChanged(); |
1959 | + void trailingOptionsChanged(); |
1960 | + void pressedChanged(); |
1961 | + void selectableChanged(); |
1962 | + void selectedChanged(); |
1963 | + void childrenChanged(); |
1964 | + |
1965 | + void clicked(); |
1966 | + void pressAndHold(); |
1967 | + |
1968 | +public Q_SLOTS: |
1969 | + |
1970 | +private: |
1971 | + Q_DECLARE_PRIVATE(UCListItem) |
1972 | + QQmlListProperty<QObject> data(); |
1973 | + QQmlListProperty<QQuickItem> children(); |
1974 | + Q_PRIVATE_SLOT(d_func(), void _q_dimmDisabled()) |
1975 | + Q_PRIVATE_SLOT(d_func(), void _q_rebound()) |
1976 | + Q_PRIVATE_SLOT(d_func(), void _q_updateSize()) |
1977 | + Q_PRIVATE_SLOT(d_func(), void _q_completeRebinding()) |
1978 | + Q_PRIVATE_SLOT(d_func(), void _q_updateIndex(QObject *ownerItem = 0)) |
1979 | + Q_PRIVATE_SLOT(d_func(), void _q_updateSelected()) |
1980 | +}; |
1981 | + |
1982 | +#endif // UCLISTITEM_H |
1983 | |
1984 | === added file 'modules/Ubuntu/Components/plugin/uclistitem_p.h' |
1985 | --- modules/Ubuntu/Components/plugin/uclistitem_p.h 1970-01-01 00:00:00 +0000 |
1986 | +++ modules/Ubuntu/Components/plugin/uclistitem_p.h 2014-09-15 11:00:03 +0000 |
1987 | @@ -0,0 +1,177 @@ |
1988 | +/* |
1989 | + * Copyright 2014 Canonical Ltd. |
1990 | + * |
1991 | + * This program is free software; you can redistribute it and/or modify |
1992 | + * it under the terms of the GNU Lesser General Public License as published by |
1993 | + * the Free Software Foundation; version 3. |
1994 | + * |
1995 | + * This program is distributed in the hope that it will be useful, |
1996 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1997 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1998 | + * GNU Lesser General Public License for more details. |
1999 | + * |
2000 | + * You should have received a copy of the GNU Lesser General Public License |
2001 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2002 | + */ |
2003 | + |
2004 | +#ifndef UCVIEWITEM_P_H |
2005 | +#define UCVIEWITEM_P_H |
2006 | + |
2007 | +#include "uclistitem.h" |
2008 | +#include "ucstyleditembase_p.h" |
2009 | +#include <QtCore/QPointer> |
2010 | +#include <QtCore/QBasicTimer> |
2011 | +#include <QtQuick/private/qquickrectangle_p.h> |
2012 | + |
2013 | +class QQuickFlickable; |
2014 | +class QQuickPropertyAnimation; |
2015 | +class UCListItemContent; |
2016 | +class UCListItemDivider; |
2017 | +class UCListItemOptions; |
2018 | +class PropertyChange; |
2019 | +class UCListItemPrivate : public UCStyledItemBasePrivate |
2020 | +{ |
2021 | + Q_DECLARE_PUBLIC(UCListItem) |
2022 | +public: |
2023 | + UCListItemPrivate(); |
2024 | + virtual ~UCListItemPrivate(); |
2025 | + void init(); |
2026 | + |
2027 | + static inline UCListItemPrivate *get(UCListItem *that) |
2028 | + { |
2029 | + Q_ASSERT(that); |
2030 | + return that->d_func(); |
2031 | + } |
2032 | + |
2033 | + // override setFocusable() |
2034 | + void setFocusable(); |
2035 | + |
2036 | + void _q_dimmDisabled(); |
2037 | + void _q_rebound(); |
2038 | + void _q_updateSize(); |
2039 | + void _q_completeRebinding(); |
2040 | + void _q_updateIndex(QObject *ownerItem = 0); |
2041 | + void _q_updateSelected(); |
2042 | + void cleanup(); |
2043 | + void reboundTo(qreal x); |
2044 | + void setPressed(bool pressed); |
2045 | + void setMoved(bool moved); |
2046 | + bool grabPanel(UCListItemOptions *optionList, bool isMoved); |
2047 | + void listenToRebind(bool listen); |
2048 | + void resize(); |
2049 | + void update(); |
2050 | + void clampX(qreal &x, qreal dx); |
2051 | + void autoLeadingOptions(); |
2052 | + QQuickItem *createSelectionPanel(); |
2053 | + void toggleSelectionMode(); |
2054 | + |
2055 | + bool pressed:1; |
2056 | + bool moved:1; |
2057 | + bool suppressClick:1; |
2058 | + bool ready:1; |
2059 | + bool selectable:1; |
2060 | + bool selected:1; |
2061 | + int index; |
2062 | + qreal xAxisMoveThresholdGU; |
2063 | + QBasicTimer pressAndHoldTimer; |
2064 | + QPointF lastPos; |
2065 | + QPointF pressedPos; |
2066 | + QPointer<QQuickFlickable> flickable; |
2067 | + QQuickPropertyAnimation *reboundAnimation; |
2068 | + PropertyChange *flickableInteractive; |
2069 | + PropertyChange *disabledOpacity; |
2070 | + UCListItemContent *contentItem; |
2071 | + UCListItemDivider *divider; |
2072 | + UCListItemOptions *leadingOptions; |
2073 | + UCListItemOptions *trailingOptions; |
2074 | + QQuickItem *selectionPanel; |
2075 | +}; |
2076 | + |
2077 | +class UCListItemContent : public QQuickItem |
2078 | +{ |
2079 | + Q_OBJECT |
2080 | + Q_PROPERTY(QColor color MEMBER m_color WRITE setColor NOTIFY colorChanged) |
2081 | + Q_PROPERTY(QColor pressedColor MEMBER m_pressedColor WRITE setPressedColor NOTIFY pressedColorChanged) |
2082 | +public: |
2083 | + explicit UCListItemContent(QQuickItem *parent = 0); |
2084 | + ~UCListItemContent(); |
2085 | + |
2086 | + void setColor(const QColor &color); |
2087 | + void setPressedColor(const QColor &color); |
2088 | + |
2089 | +Q_SIGNALS: |
2090 | + void colorChanged(); |
2091 | + void pressedColorChanged(); |
2092 | + |
2093 | +protected: |
2094 | + void itemChange(ItemChange change, const ItemChangeData &data); |
2095 | + QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data); |
2096 | + |
2097 | +private Q_SLOTS: |
2098 | + void updateColors(); |
2099 | + |
2100 | +private: |
2101 | + QColor m_color; |
2102 | + QColor m_pressedColor; |
2103 | + UCListItem *m_item; |
2104 | + bool m_pressedColorChanged:1; |
2105 | +}; |
2106 | + |
2107 | +class UCListItemDivider : public QObject |
2108 | +{ |
2109 | + Q_OBJECT |
2110 | + Q_PROPERTY(bool visible MEMBER m_visible WRITE setVisible NOTIFY visibleChanged) |
2111 | + Q_PROPERTY(qreal leftMargin MEMBER m_leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged) |
2112 | + Q_PROPERTY(qreal rightMargin MEMBER m_rightMargin WRITE setRightMargin NOTIFY rightMarginChanged) |
2113 | + Q_PROPERTY(QColor colorFrom MEMBER m_colorFrom WRITE setColorFrom NOTIFY colorFromChanged) |
2114 | + Q_PROPERTY(QColor colorTo MEMBER m_colorTo WRITE setColorTo NOTIFY colorToChanged) |
2115 | +public: |
2116 | + explicit UCListItemDivider(QObject *parent = 0); |
2117 | + ~UCListItemDivider(); |
2118 | + void init(UCListItem *listItem); |
2119 | + |
2120 | +Q_SIGNALS: |
2121 | + void visibleChanged(); |
2122 | + void leftMarginChanged(); |
2123 | + void rightMarginChanged(); |
2124 | + void colorFromChanged(); |
2125 | + void colorToChanged(); |
2126 | + |
2127 | +protected: |
2128 | + QSGNode *paint(QSGNode *paintNode, const QRectF &rect); |
2129 | + |
2130 | +private Q_SLOTS: |
2131 | + void unitsChanged(); |
2132 | + void paletteChanged(); |
2133 | + |
2134 | +private: |
2135 | + void updateGradient(); |
2136 | + void setVisible(bool visible); |
2137 | + void setLeftMargin(qreal leftMargin); |
2138 | + void setRightMargin(qreal rightMargin); |
2139 | + void setColorFrom(const QColor &color); |
2140 | + void setColorTo(const QColor &color); |
2141 | + |
2142 | + bool m_visible:1; |
2143 | + bool m_lastItem:1; |
2144 | + bool m_leftMarginChanged:1; |
2145 | + bool m_rightMarginChanged:1; |
2146 | + bool m_colorFromChanged:1; |
2147 | + bool m_colorToChanged:1; |
2148 | + qreal m_thickness; |
2149 | + qreal m_leftMargin; |
2150 | + qreal m_rightMargin; |
2151 | + QColor m_colorFrom; |
2152 | + QColor m_colorTo; |
2153 | + QGradientStops m_gradient; |
2154 | + UCListItemPrivate *m_listItem; |
2155 | + friend class UCListItem; |
2156 | + friend class UCListItemPrivate; |
2157 | +}; |
2158 | + |
2159 | +QColor getPaletteColor(const char *profile, const char *color); |
2160 | + |
2161 | +QML_DECLARE_TYPE(UCListItemContent) |
2162 | +QML_DECLARE_TYPE(UCListItemDivider) |
2163 | + |
2164 | +#endif // UCVIEWITEM_P_H |
2165 | |
2166 | === added file 'modules/Ubuntu/Components/plugin/uclistitemoptions.cpp' |
2167 | --- modules/Ubuntu/Components/plugin/uclistitemoptions.cpp 1970-01-01 00:00:00 +0000 |
2168 | +++ modules/Ubuntu/Components/plugin/uclistitemoptions.cpp 2014-09-15 11:00:03 +0000 |
2169 | @@ -0,0 +1,526 @@ |
2170 | +/* |
2171 | + * Copyright 2014 Canonical Ltd. |
2172 | + * |
2173 | + * This program is free software; you can redistribute it and/or modify |
2174 | + * it under the terms of the GNU Lesser General Public License as published by |
2175 | + * the Free Software Foundation; version 3. |
2176 | + * |
2177 | + * This program is distributed in the hope that it will be useful, |
2178 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2179 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2180 | + * GNU Lesser General Public License for more details. |
2181 | + * |
2182 | + * You should have received a copy of the GNU Lesser General Public License |
2183 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2184 | + */ |
2185 | + |
2186 | +#include "uclistitemoptions.h" |
2187 | +#include "uclistitemoptions_p.h" |
2188 | +#include "uclistitem_p.h" |
2189 | +#include "quickutils.h" |
2190 | +#include "i18n.h" |
2191 | +#include "plugin.h" |
2192 | +#include <QtQml/QQmlInfo> |
2193 | +#include <QtQuick/private/qquickitem_p.h> |
2194 | +#include <QtQml/private/qqmlabstractbinding_p.h> |
2195 | +#define foreach Q_FOREACH //workaround to fix private includes |
2196 | +#include <QtQml/private/qqmlbinding_p.h> // for QmlBinding |
2197 | +#undef foreach |
2198 | + |
2199 | + |
2200 | +UCListItemOptionsPrivate::UCListItemOptionsPrivate() |
2201 | + : QObjectPrivate() |
2202 | + , optionsFailure(false) |
2203 | + , panelColorChanged(false) |
2204 | + , iconColorChanged(false) |
2205 | + , status(UCListItemOptions::Disconnected) |
2206 | + , delegate(0) |
2207 | + , panelItem(0) |
2208 | + , panelColor(Qt::transparent) |
2209 | + , iconColor(Qt::transparent) |
2210 | + , optionSlotWidth(0.0) |
2211 | + , offsetDragged(0.0) |
2212 | + , optionsVisible(0) |
2213 | +{ |
2214 | +} |
2215 | +UCListItemOptionsPrivate::~UCListItemOptionsPrivate() |
2216 | +{ |
2217 | +} |
2218 | + |
2219 | +void UCListItemOptionsPrivate::_q_handlePanelDrag() |
2220 | +{ |
2221 | + UCListItem *listItem = qobject_cast<UCListItem*>(panelItem->parentItem()); |
2222 | + if (!listItem) { |
2223 | + return; |
2224 | + } |
2225 | + |
2226 | + Q_Q(UCListItemOptions); |
2227 | + offsetDragged = (status == UCListItemOptions::LeadingOptions) ? panelItem->width() + panelItem->x() : |
2228 | + listItem->width() - panelItem->x(); |
2229 | + if (offsetDragged < 0.0) { |
2230 | + offsetDragged = 0.0; |
2231 | + } |
2232 | + if (optionSlotWidth > 0.0) { |
2233 | + optionsVisible = (int)trunc(offsetDragged / optionSlotWidth); |
2234 | + } |
2235 | +} |
2236 | + |
2237 | +void UCListItemOptionsPrivate::_q_handlePanelWidth() |
2238 | +{ |
2239 | + optionSlotWidth = panelItem->width() / options.count(); |
2240 | + _q_handlePanelDrag(); |
2241 | +} |
2242 | + |
2243 | +void UCListItemOptionsPrivate::funcAppend(QQmlListProperty<QObject> *list, QObject *option) |
2244 | +{ |
2245 | + UCListItemOptions *_this = static_cast<UCListItemOptions*>(list->object); |
2246 | + UCListItemOptionsPrivate *plist = UCListItemOptionsPrivate::get(_this); |
2247 | + if (!QuickUtils::inherits(option, "Action")) { |
2248 | + qmlInfo(_this) << UbuntuI18n::instance().tr(QString("Option at index %1 is not an Action or a derivate of it.").arg(plist->options.size())); |
2249 | + plist->optionsFailure = true; |
2250 | + plist->options.clear(); |
2251 | + return; |
2252 | + } |
2253 | + if (!plist->optionsFailure) { |
2254 | + plist->options.append(option); |
2255 | + } |
2256 | +} |
2257 | +int UCListItemOptionsPrivate::funcCount(QQmlListProperty<QObject> *list) |
2258 | +{ |
2259 | + UCListItemOptions *_this = static_cast<UCListItemOptions*>(list->object); |
2260 | + UCListItemOptionsPrivate *plist = UCListItemOptionsPrivate::get(_this); |
2261 | + return plist->options.size(); |
2262 | +} |
2263 | +QObject *UCListItemOptionsPrivate::funcAt(QQmlListProperty<QObject> *list, int index) |
2264 | +{ |
2265 | + UCListItemOptions *_this = static_cast<UCListItemOptions*>(list->object); |
2266 | + UCListItemOptionsPrivate *plist = UCListItemOptionsPrivate::get(_this); |
2267 | + return plist->options.at(index); |
2268 | +} |
2269 | +void UCListItemOptionsPrivate::funcClear(QQmlListProperty<QObject> *list) |
2270 | +{ |
2271 | + UCListItemOptions *_this = static_cast<UCListItemOptions*>(list->object); |
2272 | + UCListItemOptionsPrivate *plist = UCListItemOptionsPrivate::get(_this); |
2273 | + plist->optionsFailure = false; |
2274 | + return plist->options.clear(); |
2275 | +} |
2276 | + |
2277 | +bool UCListItemOptionsPrivate::connectToListItem(UCListItemOptions *options, UCListItem *listItem, bool leading) |
2278 | +{ |
2279 | + UCListItemOptionsPrivate *_this = get(options); |
2280 | + if (!_this || !_this->createPanelItem() || isConnectedTo(options, listItem)) { |
2281 | + return isConnectedTo(options, listItem); |
2282 | + } |
2283 | + // check if the panel is still connected to a ListItem |
2284 | + // this may happen if there is a swipe over an other item while the previous |
2285 | + // one is rebounding |
2286 | + if (_this->panelItem->parentItem()) { |
2287 | + // set the requesting listItem as queuedItem |
2288 | + _this->queuedItem = listItem; |
2289 | + return false; |
2290 | + } |
2291 | + _this->panelItem->setProperty("listItemIndex", UCListItemPrivate::get(listItem)->index); |
2292 | + _this->panelItem->setProperty("leadingPanel", leading); |
2293 | + _this->panelItem->setParentItem(listItem); |
2294 | + _this->offsetDragged = 0.0; |
2295 | + QObject::connect(_this->panelItem, SIGNAL(selected()), _this->panelItem->parentItem(), SLOT(_q_rebound())); |
2296 | + _this->status = (leading) ? UCListItemOptions::LeadingOptions : UCListItemOptions::TrailingOptions; |
2297 | + Q_EMIT options->statusChanged(); |
2298 | + Q_EMIT options->connectedItemChanged(); |
2299 | + return true; |
2300 | +} |
2301 | + |
2302 | +void UCListItemOptionsPrivate::disconnectFromListItem(UCListItemOptions *options) |
2303 | +{ |
2304 | + UCListItemOptionsPrivate *_this = get(options); |
2305 | + if (!_this || !_this->panelItem || !_this->panelItem->parentItem()) { |
2306 | + return; |
2307 | + } |
2308 | + |
2309 | + QObject::disconnect(_this->panelItem, SIGNAL(selected()), _this->panelItem->parentItem(), SLOT(_q_rebound())); |
2310 | + _this->panelItem->setParentItem(0); |
2311 | + _this->status = UCListItemOptions::Disconnected; |
2312 | + Q_EMIT options->statusChanged(); |
2313 | + Q_EMIT options->connectedItemChanged(); |
2314 | + // if there was a queuedItem, make it grab the options list |
2315 | + if (_this->queuedItem) { |
2316 | + UCListItemPrivate::get(_this->queuedItem.data())->grabPanel(options, true); |
2317 | + // remove item from queue |
2318 | + _this->queuedItem.clear(); |
2319 | + } |
2320 | +} |
2321 | + |
2322 | +bool UCListItemOptionsPrivate::isConnectedTo(UCListItemOptions *options, UCListItem *listItem) |
2323 | +{ |
2324 | + UCListItemOptionsPrivate *_this = get(options); |
2325 | + return _this && _this->panelItem && |
2326 | + (_this->status != UCListItemOptions::Disconnected) && |
2327 | + (_this->panelItem->parentItem() == listItem); |
2328 | +} |
2329 | + |
2330 | +qreal UCListItemOptionsPrivate::snap(UCListItemOptions *options) |
2331 | +{ |
2332 | + UCListItemOptionsPrivate *_this = get(options); |
2333 | + if (!_this || !_this->panelItem) { |
2334 | + return 0.0; |
2335 | + } |
2336 | + qreal ratio = _this->offsetDragged / _this->optionSlotWidth; |
2337 | + int visible = _this->optionsVisible; |
2338 | + if (ratio > 0.0 && (ratio - trunc(ratio)) > 0.5) { |
2339 | + visible++; |
2340 | + } |
2341 | + return visible * _this->optionSlotWidth * (_this->status == UCListItemOptions::LeadingOptions ? 1 : -1); |
2342 | +} |
2343 | + |
2344 | + |
2345 | +QQuickItem *UCListItemOptionsPrivate::createPanelItem() |
2346 | +{ |
2347 | + if (panelItem) { |
2348 | + return panelItem; |
2349 | + } |
2350 | + Q_Q(UCListItemOptions); |
2351 | + QUrl panelDocument = UbuntuComponentsPlugin::pluginUrl(). |
2352 | + resolved(QUrl::fromLocalFile("ListItemPanel.qml")); |
2353 | + QQmlComponent component(qmlEngine(q), panelDocument); |
2354 | + if (!component.isError()) { |
2355 | + panelItem = qobject_cast<QQuickItem*>(component.beginCreate(qmlContext(q))); |
2356 | + if (panelItem) { |
2357 | + QQml_setParent_noEvent(panelItem, q); |
2358 | + if (delegate) { |
2359 | + panelItem->setProperty("delegate", QVariant::fromValue(delegate)); |
2360 | + } |
2361 | + panelItem->setProperty("optionList", QVariant::fromValue(options)); |
2362 | + component.completeCreate(); |
2363 | + if (panelColorChanged) { |
2364 | + updateColor("panelColor", panelColor); |
2365 | + } |
2366 | + if (iconColorChanged) { |
2367 | + updateColor("iconColor", iconColor); |
2368 | + } |
2369 | + Q_EMIT q->panelItemChanged(); |
2370 | + |
2371 | + // calculate option's slot size |
2372 | + optionSlotWidth = panelItem->width() / options.count(); |
2373 | + offsetDragged = 0.0; |
2374 | + optionsVisible = 0; |
2375 | + // connect to panel to catch dragging |
2376 | + QObject::connect(panelItem, SIGNAL(widthChanged()), q, SLOT(_q_handlePanelWidth())); |
2377 | + QObject::connect(panelItem, SIGNAL(xChanged()), q, SLOT(_q_handlePanelDrag())); |
2378 | + } |
2379 | + } else { |
2380 | + qmlInfo(q) << component.errorString(); |
2381 | + } |
2382 | + return panelItem; |
2383 | +} |
2384 | + |
2385 | +void UCListItemOptionsPrivate::updateColor(const char *property, const QColor &color) |
2386 | +{ |
2387 | + if (!panelItem) { |
2388 | + return; |
2389 | + } |
2390 | + Q_Q(UCListItemOptions); |
2391 | + // get this property binding |
2392 | + QQmlProperty myColor(q, property, qmlContext(q)); |
2393 | + QQmlBinding *binding = static_cast<QQmlBinding*>(QQmlPropertyPrivate::binding(myColor)); |
2394 | + if (!binding) { |
2395 | + // this is a simple value assignment, so only override panel's color |
2396 | + panelItem->setProperty(property, QVariant::fromValue(color)); |
2397 | + } else { |
2398 | + // transfer the binding to panelItem |
2399 | + // get panel item's color binding |
2400 | + QQmlProperty colorProperty(panelItem, property, qmlContext(panelItem)); |
2401 | + QQmlAbstractBinding *prevBinding = QQmlPropertyPrivate::binding(colorProperty); |
2402 | + if (prevBinding != binding) { |
2403 | + // clear the previous binding |
2404 | + prevBinding = QQmlPropertyPrivate::setBinding(colorProperty, 0); |
2405 | + if (prevBinding) { |
2406 | + prevBinding->destroy(); |
2407 | + } |
2408 | + // do the "alias" |
2409 | + binding->retargetBinding(panelItem, colorProperty.index()); |
2410 | + } |
2411 | + } |
2412 | +} |
2413 | + |
2414 | +/*! |
2415 | + * \qmltype ListItemOptions |
2416 | + * \instantiates UCListItemOptions |
2417 | + * \inherits QtQObject |
2418 | + * \inqmlmodule Ubuntu.Components 1.1 |
2419 | + * \ingroup ubuntu |
2420 | + * \brief Provides configuration for options to be added to a ListItem. |
2421 | + * |
2422 | + * ListItem accepts options that can be configured to appear when tugged to left |
2423 | + * or right. There is no limitation on how many options can be displayed on both |
2424 | + * sides, this depends on the space available. However design guides say that it |
2425 | + * can be a maximum of one option on the left (leadng) and a maximum of 3 options |
2426 | + * on the right (trailing) side of a ListItem. |
2427 | + * |
2428 | + * The options are Action instances or elements derived from Action. The default |
2429 | + * visualization of the options can be overridden using the \l delegate property, |
2430 | + * and the default implementation uses the \c name property of the Action. |
2431 | + * |
2432 | + * The leading and trailing options are placed on \l panelItem, which is created |
2433 | + * the first time the options are accessed. The colors of the panel is taken from |
2434 | + * the theme's palette. |
2435 | + * |
2436 | + * When tugged, panels reveal the options one by one. In case an option is revealed |
2437 | + * more than 50%, the option will be snapped and revealed completely. This is also |
2438 | + * valid for the case when the option is visible less than 50%, in which case the |
2439 | + * option is hidden. Options can be triggered by tapping. |
2440 | + * |
2441 | + * \note You cannot use the same ListItemOptions for leading and for trailing options |
2442 | + * the same time as when the item content is tugged, both options' panels will be |
2443 | + * bound to the list item, and teh same item cannot be bount to both edges. However |
2444 | + * the same set of actions can be used for both options either by using a shared |
2445 | + * set or by using the others' list. Example: |
2446 | + * \qml |
2447 | + * ListItem { |
2448 | + * leadingOptions: ListItemOptions { |
2449 | + * Action { |
2450 | + * iconName: "edit" |
2451 | + * } |
2452 | + * Action { |
2453 | + * iconName: "delete" |
2454 | + * } |
2455 | + * } |
2456 | + * trailingOptions: ListItemOptions { |
2457 | + * options: leadingOptions.options |
2458 | + * } |
2459 | + * } |
2460 | + * \endqml |
2461 | + * |
2462 | + * \section3 Notes on performance |
2463 | + * When used with views, or when the amount of items of same kind to be created |
2464 | + * is huge, it is recommended to use cached actions as well as cached ListItemOption |
2465 | + * instances. In this way we can reduce the creation time of the items: |
2466 | + * \qml |
2467 | + * import QtQuick 2.2 |
2468 | + * import Ubuntu.Components 1.1 |
2469 | + * |
2470 | + * MainView { |
2471 | + * width: units.gu(40) |
2472 | + * height: units.gu(71) |
2473 | + * |
2474 | + * UbuntuListView { |
2475 | + * anchors.fill: parent |
2476 | + * model: 10000 |
2477 | + * ListItemOptions { |
2478 | + * id: commonOptions |
2479 | + * Action { |
2480 | + * iconName: "search" |
2481 | + * } |
2482 | + * Action { |
2483 | + * iconName: "edit" |
2484 | + * } |
2485 | + * Action { |
2486 | + * iconName: "copy" |
2487 | + * } |
2488 | + * } |
2489 | + * delegate: ListItem { |
2490 | + * trailingOptions: commonOptions |
2491 | + * } |
2492 | + * } |
2493 | + * } |
2494 | + * \endqml |
2495 | + */ |
2496 | + |
2497 | +UCListItemOptions::UCListItemOptions(QObject *parent) |
2498 | + : QObject(*(new UCListItemOptionsPrivate), parent) |
2499 | +{ |
2500 | +} |
2501 | +UCListItemOptions::~UCListItemOptions() |
2502 | +{ |
2503 | +} |
2504 | + |
2505 | + |
2506 | +/*! |
2507 | + * \qmlproperty Component ListItemOptions::delegate |
2508 | + * Custom delegate which overrides the default one used by the ListItem. If the |
2509 | + * value is null, the default delegate will be used. |
2510 | + * |
2511 | + * ListItemOptions provides the \c option context property which contains the |
2512 | + * Action instance currently visualized. Using this property delegates can access |
2513 | + * the information to be visualized. The trigger is handled by the \l panelItem |
2514 | + * therefore only visualization is needed by the custom delegates. The other |
2515 | + * context property exposed to delegates is the \c index, which specifies the |
2516 | + * index of the option visualized. |
2517 | + * |
2518 | + * The delegate height is set automatically by the panelItem, and the width value |
2519 | + * is clamped between height and the maximum width of the list item divided by the |
2520 | + * number of options in the list. |
2521 | + * \qml |
2522 | + * import QtQuick 2.2 |
2523 | + * import Ubuntu.Components 1.1 |
2524 | + * |
2525 | + * MainView { |
2526 | + * width: units.gu(40) |
2527 | + * height: units.gu(71) |
2528 | + * |
2529 | + * UbuntuListView { |
2530 | + * anchors.fill: parent |
2531 | + * model: 50 |
2532 | + * delegate: ListItem { |
2533 | + * trailingOptions: optionsList |
2534 | + * } |
2535 | + * ListItemOptions { |
2536 | + * id: optionsList |
2537 | + * delegate: Column { |
2538 | + * width: height + units.gu(2) |
2539 | + * Icon { |
2540 | + * name: option.iconName |
2541 | + * width: units.gu(3) |
2542 | + * height: width |
2543 | + * color: "blue" |
2544 | + * anchors.horizontalCenter: parent.horizontalCenter |
2545 | + * } |
2546 | + * Label { |
2547 | + * text: option.text + "#" + index |
2548 | + * width: parent.width |
2549 | + * horizontalAlignment: Text.AlignHCenter |
2550 | + * } |
2551 | + * } |
2552 | + * Action { |
2553 | + * iconName: "starred" |
2554 | + * text: "Star" |
2555 | + * } |
2556 | + * } |
2557 | + * } |
2558 | + * } |
2559 | + * \endqml |
2560 | + * \note Putting a Rectangle in the delegate can be used to override the color |
2561 | + * of the panel. |
2562 | + * |
2563 | + * Defaults to null. |
2564 | + */ |
2565 | +QQmlComponent *UCListItemOptions::delegate() const |
2566 | +{ |
2567 | + Q_D(const UCListItemOptions); |
2568 | + return d->delegate; |
2569 | +} |
2570 | +void UCListItemOptions::setDelegate(QQmlComponent *delegate) |
2571 | +{ |
2572 | + Q_D(UCListItemOptions); |
2573 | + if (d->delegate == delegate) { |
2574 | + return; |
2575 | + } |
2576 | + d->delegate = delegate; |
2577 | + if (d->panelItem) { |
2578 | + // update panel's delegate as well |
2579 | + d->panelItem->setProperty("delegate", QVariant::fromValue(delegate)); |
2580 | + } |
2581 | + Q_EMIT delegateChanged(); |
2582 | +} |
2583 | + |
2584 | +/*! |
2585 | + * \qmlproperty list<Action> ListItemOptions::options |
2586 | + * \default |
2587 | + * The property holds the options to be displayed. It can hold instances cached or |
2588 | + * declared in place. An example of cached options: |
2589 | + * \qml |
2590 | + * ListItemOptions { |
2591 | + * id: cacedOptions |
2592 | + * options: [ |
2593 | + * copyAction, searchAction, cutAction |
2594 | + * ] |
2595 | + * } |
2596 | + * \endqml |
2597 | + */ |
2598 | +QQmlListProperty<QObject> UCListItemOptions::options() |
2599 | +{ |
2600 | + Q_D(UCListItemOptions); |
2601 | + return QQmlListProperty<QObject>(this, &(d->options), |
2602 | + &UCListItemOptionsPrivate::funcAppend, |
2603 | + &UCListItemOptionsPrivate::funcCount, |
2604 | + &UCListItemOptionsPrivate::funcAt, |
2605 | + &UCListItemOptionsPrivate::funcClear); |
2606 | +} |
2607 | + |
2608 | +/*! |
2609 | + * \qmlproperty Item ListItemOptions::panelItem |
2610 | + * The property presents the Item holding the visualized options. The panel is |
2611 | + * created when used the first time. |
2612 | + */ |
2613 | +QQuickItem *UCListItemOptions::panelItem() const |
2614 | +{ |
2615 | + Q_D(const UCListItemOptions); |
2616 | + return d->panelItem; |
2617 | +} |
2618 | + |
2619 | +/*! |
2620 | + * \qmlproperty enum ListItemOptions::status |
2621 | + * \readonly |
2622 | + * The property holds the status of the ListItemOptions, whether is connected |
2623 | + * as leading or as trailing option list to a \l ListItem. Possible valueas are: |
2624 | + * \list A |
2625 | + * \li \b \c Disconnected - default, the options list is not connected to any \l ListItem |
2626 | + * \li \b \c LeadingOptions - the options list is connected as leading list |
2627 | + * \li \b \c TrailingOptions - the options list is connected as trailing list |
2628 | + * \endlist |
2629 | + * \sa connectedItem |
2630 | + */ |
2631 | +UCListItemOptions::Status UCListItemOptions::status() const |
2632 | +{ |
2633 | + Q_D(const UCListItemOptions); |
2634 | + return d->status; |
2635 | +} |
2636 | + |
2637 | +/*! |
2638 | + * \qmlproperty ListItem ListItemOptions::connectedItem |
2639 | + * \readonly |
2640 | + * The property holds the \l ListItem the options list is connected to. It is |
2641 | + * null by default and when the status is \c Disconnected. |
2642 | + * \sa status |
2643 | + */ |
2644 | +UCListItem *UCListItemOptions::connectedItem() const |
2645 | +{ |
2646 | + Q_D(const UCListItemOptions); |
2647 | + return d->panelItem ? qobject_cast<UCListItem*>(d->panelItem->parentItem()) : 0; |
2648 | +} |
2649 | + |
2650 | +/*! |
2651 | + * \qmlproperty color ListItemOptions::panelColor |
2652 | + * The property overrides the default colouring of the \l panelItem. |
2653 | + */ |
2654 | +QColor UCListItemOptions::panelColor() const |
2655 | +{ |
2656 | + Q_D(const UCListItemOptions); |
2657 | + return d->panelColor; |
2658 | +} |
2659 | +void UCListItemOptions::setPanelColor(const QColor &color) |
2660 | +{ |
2661 | + Q_D(UCListItemOptions); |
2662 | + if (d->panelColor == color) { |
2663 | + return; |
2664 | + } |
2665 | + d->panelColor = color; |
2666 | + d->panelColorChanged = true; |
2667 | + // update panelItem's color |
2668 | + d->updateColor("panelColor", d->panelColor); |
2669 | + Q_EMIT panelColorChanged(); |
2670 | +} |
2671 | + |
2672 | +/*! |
2673 | + * \qmlproperty color ListItemOptions::iconColor |
2674 | + * The property overrides the default colouring of the icons in the default |
2675 | + * options visualization. |
2676 | + */ |
2677 | +QColor UCListItemOptions::iconColor() const |
2678 | +{ |
2679 | + Q_D(const UCListItemOptions); |
2680 | + return d->iconColor; |
2681 | +} |
2682 | +void UCListItemOptions::setIconColor(const QColor &color) |
2683 | +{ |
2684 | + Q_D(UCListItemOptions); |
2685 | + if (d->iconColor == color) { |
2686 | + return; |
2687 | + } |
2688 | + d->iconColor = color; |
2689 | + d->iconColorChanged = true; |
2690 | + // update panelItem's color |
2691 | + d->updateColor("iconColor", d->iconColor); |
2692 | + Q_EMIT iconColorChanged(); |
2693 | +} |
2694 | + |
2695 | +#include "moc_uclistitemoptions.cpp" |
2696 | |
2697 | === added file 'modules/Ubuntu/Components/plugin/uclistitemoptions.h' |
2698 | --- modules/Ubuntu/Components/plugin/uclistitemoptions.h 1970-01-01 00:00:00 +0000 |
2699 | +++ modules/Ubuntu/Components/plugin/uclistitemoptions.h 2014-09-15 11:00:03 +0000 |
2700 | @@ -0,0 +1,75 @@ |
2701 | +/* |
2702 | + * Copyright 2014 Canonical Ltd. |
2703 | + * |
2704 | + * This program is free software; you can redistribute it and/or modify |
2705 | + * it under the terms of the GNU Lesser General Public License as published by |
2706 | + * the Free Software Foundation; version 3. |
2707 | + * |
2708 | + * This program is distributed in the hope that it will be useful, |
2709 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2710 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2711 | + * GNU Lesser General Public License for more details. |
2712 | + * |
2713 | + * You should have received a copy of the GNU Lesser General Public License |
2714 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2715 | + */ |
2716 | + |
2717 | +#ifndef UCLISTITEMOPTIONS_H |
2718 | +#define UCLISTITEMOPTIONS_H |
2719 | + |
2720 | +#include <QtCore/QObject> |
2721 | +#include "uclistitem_p.h" |
2722 | + |
2723 | +class QQmlComponent; |
2724 | +class UCListItemOptionsPrivate; |
2725 | +class QQmlBinding; |
2726 | +class UCListItemOptions : public QObject |
2727 | +{ |
2728 | + Q_OBJECT |
2729 | + Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) |
2730 | + Q_PROPERTY(QQmlListProperty<QObject> options READ options CONSTANT) |
2731 | + Q_PROPERTY(QQuickItem *panelItem READ panelItem NOTIFY panelItemChanged) |
2732 | + Q_PROPERTY(Status status READ status NOTIFY statusChanged) |
2733 | + Q_PROPERTY(UCListItem *connectedItem READ connectedItem NOTIFY connectedItemChanged) |
2734 | + Q_PROPERTY(QColor panelColor READ panelColor WRITE setPanelColor NOTIFY panelColorChanged) |
2735 | + Q_PROPERTY(QColor iconColor READ iconColor WRITE setIconColor NOTIFY iconColorChanged) |
2736 | + Q_CLASSINFO("DefaultProperty", "options") |
2737 | + Q_ENUMS(Status) |
2738 | +public: |
2739 | + enum Status { |
2740 | + Disconnected = 0, |
2741 | + LeadingOptions, |
2742 | + TrailingOptions |
2743 | + }; |
2744 | + |
2745 | + explicit UCListItemOptions(QObject *parent = 0); |
2746 | + ~UCListItemOptions(); |
2747 | + |
2748 | + QQmlComponent *delegate() const; |
2749 | + void setDelegate(QQmlComponent *delegate); |
2750 | + QQmlListProperty<QObject> options(); |
2751 | + QQuickItem *panelItem() const; |
2752 | + Status status() const; |
2753 | + UCListItem *connectedItem() const; |
2754 | + QColor panelColor() const; |
2755 | + void setPanelColor(const QColor &color); |
2756 | + QColor iconColor() const; |
2757 | + void setIconColor(const QColor &color); |
2758 | + |
2759 | +Q_SIGNALS: |
2760 | + void delegateChanged(); |
2761 | + void panelItemChanged(); |
2762 | + void statusChanged(); |
2763 | + void connectedItemChanged(); |
2764 | + void panelColorChanged(); |
2765 | + void iconColorChanged(); |
2766 | + |
2767 | +public Q_SLOTS: |
2768 | + |
2769 | +private: |
2770 | + Q_DECLARE_PRIVATE(UCListItemOptions) |
2771 | + Q_PRIVATE_SLOT(d_func(), void _q_handlePanelDrag()) |
2772 | + Q_PRIVATE_SLOT(d_func(), void _q_handlePanelWidth()) |
2773 | +}; |
2774 | + |
2775 | +#endif // UCLISTITEMOPTIONS_H |
2776 | |
2777 | === added file 'modules/Ubuntu/Components/plugin/uclistitemoptions_p.h' |
2778 | --- modules/Ubuntu/Components/plugin/uclistitemoptions_p.h 1970-01-01 00:00:00 +0000 |
2779 | +++ modules/Ubuntu/Components/plugin/uclistitemoptions_p.h 2014-09-15 11:00:03 +0000 |
2780 | @@ -0,0 +1,66 @@ |
2781 | +/* |
2782 | + * Copyright 2014 Canonical Ltd. |
2783 | + * |
2784 | + * This program is free software; you can redistribute it and/or modify |
2785 | + * it under the terms of the GNU Lesser General Public License as published by |
2786 | + * the Free Software Foundation; version 3. |
2787 | + * |
2788 | + * This program is distributed in the hope that it will be useful, |
2789 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2790 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2791 | + * GNU Lesser General Public License for more details. |
2792 | + * |
2793 | + * You should have received a copy of the GNU Lesser General Public License |
2794 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2795 | + */ |
2796 | + |
2797 | +#ifndef UCLISTITEMOPTIONS_P_H |
2798 | +#define UCLISTITEMOPTIONS_P_H |
2799 | + |
2800 | +#include "uclistitemoptions.h" |
2801 | +#include "QtCore/private/qobject_p.h" |
2802 | + |
2803 | +class UCListItem; |
2804 | +class UCListItemOptionsPrivate : public QObjectPrivate { |
2805 | + Q_DECLARE_PUBLIC(UCListItemOptions) |
2806 | +public: |
2807 | + UCListItemOptionsPrivate(); |
2808 | + ~UCListItemOptionsPrivate(); |
2809 | + static UCListItemOptionsPrivate* get(UCListItemOptions *options) |
2810 | + { |
2811 | + return options ? options->d_func() : 0; |
2812 | + } |
2813 | + |
2814 | + bool optionsFailure:1; |
2815 | + bool panelColorChanged:1; |
2816 | + bool iconColorChanged:1; |
2817 | + UCListItemOptions::Status status; |
2818 | + QQmlComponent *delegate; |
2819 | + QQuickItem *panelItem; |
2820 | + QColor panelColor; |
2821 | + QColor iconColor; |
2822 | + QList<QObject*> options; |
2823 | + QPointer<UCListItem> queuedItem; |
2824 | + qreal optionSlotWidth; |
2825 | + qreal offsetDragged; |
2826 | + int optionsVisible; |
2827 | + |
2828 | + void _q_handlePanelDrag(); |
2829 | + void _q_handlePanelWidth(); |
2830 | + |
2831 | + // options list property functions |
2832 | + static void funcAppend(QQmlListProperty<QObject>*, QObject*); |
2833 | + static int funcCount(QQmlListProperty<QObject>*); |
2834 | + static QObject *funcAt(QQmlListProperty<QObject>*, int); |
2835 | + static void funcClear(QQmlListProperty<QObject>*); |
2836 | + |
2837 | + static bool connectToListItem(UCListItemOptions *options, UCListItem *listItem, bool leading); |
2838 | + static void disconnectFromListItem(UCListItemOptions *options); |
2839 | + static bool isConnectedTo(UCListItemOptions *options, UCListItem *listItem); |
2840 | + static qreal snap(UCListItemOptions *options); |
2841 | + |
2842 | + QQuickItem *createPanelItem(); |
2843 | + void updateColor(const char *property, const QColor &color); |
2844 | +}; |
2845 | + |
2846 | +#endif // UCLISTITEMOPTIONS_P_H |
2847 | |
2848 | === modified file 'modules/Ubuntu/Components/qmldir' |
2849 | --- modules/Ubuntu/Components/qmldir 2014-09-03 08:17:24 +0000 |
2850 | +++ modules/Ubuntu/Components/qmldir 2014-09-15 11:00:03 +0000 |
2851 | @@ -106,3 +106,5 @@ |
2852 | Icon 1.1 Icon11.qml |
2853 | StyledItem 1.1 StyledItem.qml |
2854 | singleton UbuntuColors 1.1 11/UbuntuColors.qml |
2855 | +internal ListItemPanel ListItemPanel.qml |
2856 | +internal ListItemSelectablePanel ListItemSelectablePanel.qml |
2857 | |
2858 | === modified file 'tests/autopilot/ubuntuuitoolkit/environment.py' |
2859 | --- tests/autopilot/ubuntuuitoolkit/environment.py 2014-03-10 18:53:37 +0000 |
2860 | +++ tests/autopilot/ubuntuuitoolkit/environment.py 2014-09-15 11:00:03 +0000 |
2861 | @@ -23,42 +23,46 @@ |
2862 | logger = logging.getLogger(__name__) |
2863 | |
2864 | |
2865 | -def is_initctl_env_var_set(variable): |
2866 | +def is_initctl_env_var_set(variable, global_=False): |
2867 | """Check True if an initctl environment variable is set. |
2868 | |
2869 | :param variable: The name of the variable to check. |
2870 | + :param global: if True, the method will operate on the global environment |
2871 | + table. Default is False. |
2872 | :return: True if the variable is set. False otherwise. |
2873 | |
2874 | """ |
2875 | try: |
2876 | - get_initctl_env_var(variable) |
2877 | + get_initctl_env_var(variable, global_) |
2878 | return True |
2879 | except subprocess.CalledProcessError: |
2880 | return False |
2881 | |
2882 | |
2883 | -def get_initctl_env_var(variable): |
2884 | +def get_initctl_env_var(variable, global_=False): |
2885 | """Return the value of an initctl environment variable.""" |
2886 | + command = ['/sbin/initctl', 'get-env', variable] |
2887 | + if global_: |
2888 | + command += ['--global'] |
2889 | output = subprocess.check_output( |
2890 | - ['/sbin/initctl', 'get-env', variable], |
2891 | - stderr=subprocess.STDOUT, |
2892 | - universal_newlines=True) |
2893 | + command, stderr=subprocess.STDOUT, universal_newlines=True) |
2894 | return output.rstrip() |
2895 | |
2896 | |
2897 | @autopilot_logging.log_action(logger.info) |
2898 | -def set_initctl_env_var(variable, value): |
2899 | +def set_initctl_env_var(variable, value, global_=False): |
2900 | """Set the value of an initctl environment variable.""" |
2901 | - subprocess.call( |
2902 | - ['/sbin/initctl', 'set-env', '%s=%s' % (variable, value)], |
2903 | - stderr=subprocess.STDOUT, |
2904 | - universal_newlines=True) |
2905 | + command = ['/sbin/initctl', 'set-env', '%s=%s' % (variable, value)] |
2906 | + if global_: |
2907 | + command += ['--global'] |
2908 | + subprocess.call(command, stderr=subprocess.STDOUT, universal_newlines=True) |
2909 | |
2910 | |
2911 | @autopilot_logging.log_action(logger.info) |
2912 | -def unset_initctl_env_var(variable): |
2913 | +def unset_initctl_env_var(variable, global_=False): |
2914 | """Remove an initctl environment variable.""" |
2915 | + command = ['/sbin/initctl', 'unset-env', variable] |
2916 | + if global_: |
2917 | + command += ['--global'] |
2918 | subprocess.call( |
2919 | - ['/sbin/initctl', 'unset-env', variable], |
2920 | - stderr=subprocess.STDOUT, |
2921 | - universal_newlines=True) |
2922 | + command, stderr=subprocess.STDOUT, universal_newlines=True) |
2923 | |
2924 | === modified file 'tests/autopilot/ubuntuuitoolkit/fixture_setup.py' |
2925 | --- tests/autopilot/ubuntuuitoolkit/fixture_setup.py 2014-07-22 07:44:40 +0000 |
2926 | +++ tests/autopilot/ubuntuuitoolkit/fixture_setup.py 2014-09-15 11:00:03 +0000 |
2927 | @@ -115,24 +115,36 @@ |
2928 | class InitctlEnvironmentVariable(fixtures.Fixture): |
2929 | """Set the value of initctl environment variables.""" |
2930 | |
2931 | - def __init__(self, **kwargs): |
2932 | + def __init__(self, global_=False, **kwargs): |
2933 | super(InitctlEnvironmentVariable, self).__init__() |
2934 | + # Added one level of indirection to be able to spy the calls to |
2935 | + # environment during tests. |
2936 | + self.environment = environment |
2937 | self.variables = kwargs |
2938 | + self.global_ = global_ |
2939 | |
2940 | def setUp(self): |
2941 | super(InitctlEnvironmentVariable, self).setUp() |
2942 | for variable, value in self.variables.items(): |
2943 | self._add_variable_cleanup(variable) |
2944 | - environment.set_initctl_env_var(variable, value) |
2945 | + self.environment.set_initctl_env_var( |
2946 | + variable, value, global_=self.global_) |
2947 | |
2948 | def _add_variable_cleanup(self, variable): |
2949 | - if environment.is_initctl_env_var_set(variable): |
2950 | - original_value = environment.get_initctl_env_var(variable) |
2951 | + if self.environment.is_initctl_env_var_set( |
2952 | + variable, global_=self.global_): |
2953 | + original_value = self.environment.get_initctl_env_var( |
2954 | + variable, global_=self.global_) |
2955 | self.addCleanup( |
2956 | - environment.set_initctl_env_var, variable, |
2957 | - original_value) |
2958 | + self.environment.set_initctl_env_var, |
2959 | + variable, |
2960 | + original_value, |
2961 | + global_=self.global_) |
2962 | else: |
2963 | - self.addCleanup(environment.unset_initctl_env_var, variable) |
2964 | + self.addCleanup( |
2965 | + self.environment.unset_initctl_env_var, |
2966 | + variable, |
2967 | + global_=self.global_) |
2968 | |
2969 | |
2970 | class FakeHome(fixtures.Fixture): |
2971 | |
2972 | === modified file 'tests/autopilot/ubuntuuitoolkit/tests/__init__.py' |
2973 | --- tests/autopilot/ubuntuuitoolkit/tests/__init__.py 2014-07-24 21:28:05 +0000 |
2974 | +++ tests/autopilot/ubuntuuitoolkit/tests/__init__.py 2014-09-15 11:00:03 +0000 |
2975 | @@ -87,6 +87,9 @@ |
2976 | qml_file_contents=self.test_qml) |
2977 | self.useFixture(fake_application) |
2978 | |
2979 | + local_modules_path = _get_module_include_path() |
2980 | + if os.path.exists(local_modules_path): |
2981 | + self.use_local_modules(local_modules_path) |
2982 | desktop_file_name = os.path.basename( |
2983 | fake_application.desktop_file_path) |
2984 | application_name, _ = os.path.splitext(desktop_file_name) |
2985 | @@ -94,6 +97,17 @@ |
2986 | application_name, |
2987 | emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase) |
2988 | |
2989 | + def use_local_modules(self, local_modules_path): |
2990 | + env_vars = [ |
2991 | + 'QML_IMPORT_PATH', |
2992 | + 'QML2_IMPORT_PATH', |
2993 | + 'UBUNTU_UI_TOOLKIT_THEMES_PATH' |
2994 | + ] |
2995 | + kwargs = {'global_': True} |
2996 | + for env in env_vars: |
2997 | + kwargs[env] = local_modules_path |
2998 | + self.useFixture(fixture_setup.InitctlEnvironmentVariable(**kwargs)) |
2999 | + |
3000 | |
3001 | class QMLStringAppTestCase(UbuntuUIToolkitWithFakeAppRunningTestCase): |
3002 | """Base test case for self tests that define the QML on an string.""" |
3003 | |
3004 | === modified file 'tests/autopilot/ubuntuuitoolkit/tests/test_environment.py' |
3005 | --- tests/autopilot/ubuntuuitoolkit/tests/test_environment.py 2014-03-13 17:54:38 +0000 |
3006 | +++ tests/autopilot/ubuntuuitoolkit/tests/test_environment.py 2014-09-15 11:00:03 +0000 |
3007 | @@ -30,7 +30,7 @@ |
3008 | |
3009 | def test_is_environment_variable_set_with_set_variable(self): |
3010 | """Test that is_initctl_env_var_set returns True for existing vars.""" |
3011 | - variable = 'Testvariabletoset' |
3012 | + variable = 'Test variable to set {}'.format(uuid.uuid1()) |
3013 | self.addCleanup(environment.unset_initctl_env_var, variable) |
3014 | |
3015 | environment.set_initctl_env_var(variable, 'dummy') |
3016 | @@ -39,7 +39,7 @@ |
3017 | |
3018 | def test_get_environment_variable(self): |
3019 | """Test that get_initctl_env_var returns the right value.""" |
3020 | - variable = 'Testvariabletoget' |
3021 | + variable = 'Test variable to get {}'.format(uuid.uuid1()) |
3022 | self.addCleanup(environment.unset_initctl_env_var, variable) |
3023 | environment.set_initctl_env_var(variable, 'test value') |
3024 | |
3025 | @@ -48,7 +48,7 @@ |
3026 | |
3027 | def test_unset_environment_variable(self): |
3028 | """Test that unset_initctl_env_var removes the variable.""" |
3029 | - variable = 'Testvariabletoget' |
3030 | + variable = 'Test variable to unset {}'.format(uuid.uuid1()) |
3031 | environment.set_initctl_env_var(variable, 'dummy') |
3032 | |
3033 | environment.unset_initctl_env_var(variable) |
3034 | @@ -57,8 +57,33 @@ |
3035 | |
3036 | def test_unset_environment_variable_with_unset_variable(self): |
3037 | """Test that unset_initctl_env_var does nothing with unset var.""" |
3038 | - variable = 'I do not exist' |
3039 | + variable = 'I do not exist {}'.format(uuid.uuid1()) |
3040 | |
3041 | environment.unset_initctl_env_var(variable) |
3042 | |
3043 | self.assertFalse(environment.is_initctl_env_var_set(variable)) |
3044 | + |
3045 | + def test_is_global_environment_variable_set_with_unset_variable(self): |
3046 | + """Test is_initctl_env_var_set returns False for unset global vars.""" |
3047 | + variable = 'I do not exist global {}'.format(uuid.uuid1()) |
3048 | + |
3049 | + self.assertFalse(environment.is_initctl_env_var_set( |
3050 | + variable, global_=True)) |
3051 | + |
3052 | + def test_get_global_environment_variable(self): |
3053 | + """Test that get_initctl_env_var returns the right global value.""" |
3054 | + variable = 'Test variable to get {}'.format(uuid.uuid1()) |
3055 | + self.addCleanup( |
3056 | + environment.unset_initctl_env_var, variable, global_=True) |
3057 | + environment.set_initctl_env_var(variable, 'test value', global_=True) |
3058 | + |
3059 | + self.assertEqual( |
3060 | + 'test value', |
3061 | + environment.get_initctl_env_var(variable, global_=True)) |
3062 | + |
3063 | + def test_unset_global_environment_variable(self): |
3064 | + """Test that unset_initctl_env_var removes the global variable.""" |
3065 | + variable = 'Test variable to unset {}'.format(uuid.uuid1()) |
3066 | + |
3067 | + environment.set_initctl_env_var(variable, 'dummy', global_=True) |
3068 | + environment.unset_initctl_env_var(variable, global_=True) |
3069 | |
3070 | === modified file 'tests/autopilot/ubuntuuitoolkit/tests/test_fixture_setup.py' |
3071 | --- tests/autopilot/ubuntuuitoolkit/tests/test_fixture_setup.py 2014-06-26 04:28:38 +0000 |
3072 | +++ tests/autopilot/ubuntuuitoolkit/tests/test_fixture_setup.py 2014-09-15 11:00:03 +0000 |
3073 | @@ -23,6 +23,7 @@ |
3074 | except ImportError: |
3075 | # Python 2 add-on: python-mock. |
3076 | import mock |
3077 | +import testscenarios |
3078 | import testtools |
3079 | from autopilot import ( |
3080 | display, |
3081 | @@ -241,6 +242,95 @@ |
3082 | environment.get_initctl_env_var('testenvvarforfixture')) |
3083 | |
3084 | |
3085 | +class InitctlGlobalEnvironmentVariableTestCase( |
3086 | + testscenarios.TestWithScenarios): |
3087 | + |
3088 | + scenarios = [ |
3089 | + ('global unset variable', { |
3090 | + 'is_variable_set': False, |
3091 | + 'variable_value': 'dummy', |
3092 | + 'global_value': 'value', |
3093 | + 'expected_calls': [ |
3094 | + mock.call.is_initctl_env_var_set( |
3095 | + 'testenvvarforfixture', global_='value'), |
3096 | + mock.call.set_initctl_env_var( |
3097 | + 'testenvvarforfixture', 'new test value', global_='value'), |
3098 | + mock.call.unset_initctl_env_var( |
3099 | + 'testenvvarforfixture', global_='value') |
3100 | + ] |
3101 | + }), |
3102 | + ('global set variable', { |
3103 | + 'is_variable_set': True, |
3104 | + 'variable_value': 'original_value', |
3105 | + 'global_value': 'value', |
3106 | + 'expected_calls': [ |
3107 | + mock.call.is_initctl_env_var_set( |
3108 | + 'testenvvarforfixture', global_='value'), |
3109 | + mock.call.get_initctl_env_var( |
3110 | + 'testenvvarforfixture', global_='value'), |
3111 | + mock.call.set_initctl_env_var( |
3112 | + 'testenvvarforfixture', 'new test value', global_='value'), |
3113 | + mock.call.set_initctl_env_var( |
3114 | + 'testenvvarforfixture', 'original_value', global_='value') |
3115 | + ] |
3116 | + }), |
3117 | + ('default unset variable', { |
3118 | + 'is_variable_set': False, |
3119 | + 'variable_value': 'dummy', |
3120 | + 'global_value': 'default', |
3121 | + 'expected_calls': [ |
3122 | + mock.call.is_initctl_env_var_set( |
3123 | + 'testenvvarforfixture', global_=False), |
3124 | + mock.call.set_initctl_env_var( |
3125 | + 'testenvvarforfixture', 'new test value', global_=False), |
3126 | + mock.call.unset_initctl_env_var( |
3127 | + 'testenvvarforfixture', global_=False) |
3128 | + ] |
3129 | + }), |
3130 | + ('global set variable', { |
3131 | + 'is_variable_set': True, |
3132 | + 'variable_value': 'original_value', |
3133 | + 'global_value': 'default', |
3134 | + 'expected_calls': [ |
3135 | + mock.call.is_initctl_env_var_set( |
3136 | + 'testenvvarforfixture', global_=False), |
3137 | + mock.call.get_initctl_env_var( |
3138 | + 'testenvvarforfixture', global_=False), |
3139 | + mock.call.set_initctl_env_var( |
3140 | + 'testenvvarforfixture', 'new test value', global_=False), |
3141 | + mock.call.set_initctl_env_var( |
3142 | + 'testenvvarforfixture', 'original_value', global_=False) |
3143 | + ] |
3144 | + }), |
3145 | + ] |
3146 | + |
3147 | + def test_use_initctl_environment_variable_with_global_unset_variable(self): |
3148 | + if self.global_value == 'default': |
3149 | + initctl_env_var = fixture_setup.InitctlEnvironmentVariable( |
3150 | + testenvvarforfixture='new test value') |
3151 | + else: |
3152 | + initctl_env_var = fixture_setup.InitctlEnvironmentVariable( |
3153 | + testenvvarforfixture='new test value', |
3154 | + global_=self.global_value) |
3155 | + |
3156 | + mock_env = mock.Mock() |
3157 | + initctl_env_var.environment = mock_env |
3158 | + mock_env.is_initctl_env_var_set.return_value = self.is_variable_set |
3159 | + mock_env.get_initctl_env_var.return_value = self.variable_value |
3160 | + |
3161 | + def inner_test(): |
3162 | + class TestWithInitctlEnvVar(testtools.TestCase): |
3163 | + def test_it(self): |
3164 | + self.useFixture(initctl_env_var) |
3165 | + |
3166 | + return TestWithInitctlEnvVar('test_it') |
3167 | + |
3168 | + inner_test().run() |
3169 | + |
3170 | + self.assertEquals( |
3171 | + self.expected_calls, mock_env.mock_calls) |
3172 | + |
3173 | + |
3174 | class FakeHomeTestCase(testtools.TestCase): |
3175 | |
3176 | def test_fake_home_fixture_patches_initctl_env_var(self): |
3177 | |
3178 | === added directory 'tests/resources/listitems' |
3179 | === added file 'tests/resources/listitems/InUListView.qml' |
3180 | --- tests/resources/listitems/InUListView.qml 1970-01-01 00:00:00 +0000 |
3181 | +++ tests/resources/listitems/InUListView.qml 2014-09-15 11:00:03 +0000 |
3182 | @@ -0,0 +1,63 @@ |
3183 | +/* |
3184 | + * Copyright 2014 Canonical Ltd. |
3185 | + * |
3186 | + * This program is free software; you can redistribute it and/or modify |
3187 | + * it under the terms of the GNU Lesser General Public License as published by |
3188 | + * the Free Software Foundation; version 3. |
3189 | + * |
3190 | + * This program is distributed in the hope that it will be useful, |
3191 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3192 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3193 | + * GNU Lesser General Public License for more details. |
3194 | + * |
3195 | + * You should have received a copy of the GNU Lesser General Public License |
3196 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3197 | + */ |
3198 | + |
3199 | +import QtQuick 2.2 |
3200 | +import Ubuntu.Components 1.1 |
3201 | + |
3202 | +MainView { |
3203 | + id: main |
3204 | + width: units.gu(50) |
3205 | + height: units.gu(100) |
3206 | + |
3207 | + Page { |
3208 | + title: "UbuntuListView" |
3209 | + |
3210 | + UbuntuListView { |
3211 | + anchors.fill: parent |
3212 | + |
3213 | + ListItemOptions { |
3214 | + id: stockOptions |
3215 | + Action { |
3216 | + iconName: "edit" |
3217 | + } |
3218 | + Action { |
3219 | + iconName: "share" |
3220 | + } |
3221 | + Action { |
3222 | + iconName: "attachment" |
3223 | + } |
3224 | + } |
3225 | + |
3226 | + model: ListModel { |
3227 | + Component.onCompleted: { |
3228 | + for (var i = 0; i < 20; i++) |
3229 | + append({data: "List item mode #" + i}) |
3230 | + } |
3231 | + } |
3232 | + |
3233 | + delegate: ListItem { |
3234 | + trailingOptions: stockOptions |
3235 | + |
3236 | + Label { |
3237 | + anchors.fill: parent |
3238 | + horizontalAlignment: Text.AlignHCenter |
3239 | + verticalAlignment: Text.AlignVCenter |
3240 | + text: modelData + " @index " + index |
3241 | + } |
3242 | + } |
3243 | + } |
3244 | + } |
3245 | +} |
3246 | |
3247 | === added file 'tests/resources/listitems/ListItemTest.qml' |
3248 | --- tests/resources/listitems/ListItemTest.qml 1970-01-01 00:00:00 +0000 |
3249 | +++ tests/resources/listitems/ListItemTest.qml 2014-09-15 11:00:03 +0000 |
3250 | @@ -0,0 +1,186 @@ |
3251 | +/* |
3252 | + * Copyright 2014 Canonical Ltd. |
3253 | + * |
3254 | + * This program is free software; you can redistribute it and/or modify |
3255 | + * it under the terms of the GNU Lesser General Public License as published by |
3256 | + * the Free Software Foundation; version 3. |
3257 | + * |
3258 | + * This program is distributed in the hope that it will be useful, |
3259 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3260 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3261 | + * GNU Lesser General Public License for more details. |
3262 | + * |
3263 | + * You should have received a copy of the GNU Lesser General Public License |
3264 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3265 | + */ |
3266 | + |
3267 | +import QtQuick 2.2 |
3268 | +import Ubuntu.Components 1.1 |
3269 | + |
3270 | +MainView { |
3271 | + id: main |
3272 | + width: units.gu(50) |
3273 | + height: units.gu(100) |
3274 | + |
3275 | + property bool override: false |
3276 | + |
3277 | + Action { |
3278 | + objectName: "stock" |
3279 | + id: stock |
3280 | + iconName: "starred" |
3281 | + text: "Staaaar" |
3282 | + onTriggered: print(iconName, "triggered", value) |
3283 | + } |
3284 | + |
3285 | + ListItemOptions { |
3286 | + id: leading |
3287 | + objectName: "StockLeading" |
3288 | + Action { |
3289 | + iconName: "delete" |
3290 | + onTriggered: { print(iconName, "triggered", value) |
3291 | + leading.panelColor = Qt.binding(function() { |
3292 | + return (leading.status == ListItemOptions.LeadingOptions) ? UbuntuColors.blue : UbuntuColors.lightGrey; |
3293 | + }) |
3294 | + leading.iconColor = Qt.binding(function() { |
3295 | + return (leading.status == ListItemOptions.LeadingOptions) ? "white" : UbuntuColors.red; |
3296 | + }) |
3297 | + } |
3298 | + } |
3299 | + Action { |
3300 | + iconName: "alarm-clock" |
3301 | + enabled: false |
3302 | + onTriggered: print(iconName, "triggered") |
3303 | + } |
3304 | + Action { |
3305 | + iconName: "camcorder" |
3306 | + onTriggered: print(iconName, "triggered", value) |
3307 | + } |
3308 | + Action { |
3309 | + iconName: "stock_website" |
3310 | + onTriggered: print(iconName, "triggered", value) |
3311 | + } |
3312 | + } |
3313 | + |
3314 | + property bool selectable: false |
3315 | + Column { |
3316 | + anchors { |
3317 | + left: parent.left |
3318 | + right: parent.right |
3319 | + } |
3320 | + |
3321 | + Button { |
3322 | + text: "Selectable " + (selectable ? "OFF" : "ON") |
3323 | + onClicked: selectable = !selectable |
3324 | + } |
3325 | + |
3326 | + ListItem { |
3327 | + id: testItem |
3328 | + objectName: "single" |
3329 | + selectable: main.selectable |
3330 | + onClicked: { |
3331 | + print("click") |
3332 | + main.override = !main.override |
3333 | + } |
3334 | + onPressAndHold: print("pressAndHold", objectName) |
3335 | + Label { |
3336 | + anchors.fill: parent |
3337 | + text: units.gridUnit + "PX/unit" |
3338 | + } |
3339 | + leadingOptions: ListItemOptions { |
3340 | + objectName: "InlineLeading" |
3341 | + options: [stock] |
3342 | + delegate: Column { |
3343 | + width: height + units.gu(2) |
3344 | + Icon { |
3345 | + width: units.gu(3) |
3346 | + height: width |
3347 | + name: option.iconName |
3348 | + color: "blue" |
3349 | + anchors.horizontalCenter: parent.horizontalCenter |
3350 | + } |
3351 | + Label { |
3352 | + text: option.text + index |
3353 | + width: parent.width |
3354 | + horizontalAlignment: Text.AlignHCenter |
3355 | + } |
3356 | + } |
3357 | + } |
3358 | + trailingOptions: leading |
3359 | + } |
3360 | + |
3361 | + ListView { |
3362 | + id: view |
3363 | + clip: true |
3364 | + width: parent.width |
3365 | + height: units.gu(36) |
3366 | + model: 10 |
3367 | + pressDelay: 0 |
3368 | + delegate: ListItem { |
3369 | + objectName: "ListItem" + index |
3370 | + id: listItem |
3371 | + selectable: main.selectable |
3372 | + selected: true |
3373 | + onClicked: print(" clicked") |
3374 | + leadingOptions: leading |
3375 | + Label { |
3376 | + text: modelData + " item" |
3377 | + } |
3378 | + states: State { |
3379 | + name: "override" |
3380 | + when: main.override |
3381 | + PropertyChanges { |
3382 | + target: listItem.contentItem |
3383 | + pressedColor: "brown" |
3384 | + } |
3385 | + } |
3386 | + } |
3387 | + } |
3388 | + Flickable { |
3389 | + id: flicker |
3390 | + width: parent.width |
3391 | + height: units.gu(36) |
3392 | + clip: true |
3393 | + contentHeight: column.childrenRect.height |
3394 | + Column { |
3395 | + id: column |
3396 | + width: view.width |
3397 | + property alias count: repeater.count |
3398 | + Repeater { |
3399 | + id: repeater |
3400 | + model: 10 |
3401 | + ListItem { |
3402 | + objectName: "InFlickable"+index |
3403 | + selectable: main.selectable |
3404 | + leadingOptions: ListItemOptions { |
3405 | + id: optionData |
3406 | + panelColor: "pink" |
3407 | + Action { |
3408 | + iconName: "edit" |
3409 | + onTriggered: print(iconName, "triggered", value) |
3410 | + } |
3411 | + Action { |
3412 | + iconName: "delete" |
3413 | + onTriggered: print(iconName, "triggered", value) |
3414 | + } |
3415 | + } |
3416 | + trailingOptions: ListItemOptions { |
3417 | + panelColor: leadingOptions.panelColor |
3418 | + options: leadingOptions.options |
3419 | + } |
3420 | + |
3421 | + contentItem { |
3422 | + color: UbuntuColors.red |
3423 | + pressedColor: "lime" |
3424 | + } |
3425 | + divider.colorFrom: UbuntuColors.green |
3426 | + |
3427 | + Label { |
3428 | + text: modelData + " Flickable item" |
3429 | + } |
3430 | + onClicked: divider.visible = !divider.visible |
3431 | + } |
3432 | + } |
3433 | + } |
3434 | + } |
3435 | + } |
3436 | +} |
3437 | |
3438 | === added file 'tests/unit/tst_performance/ItemList.qml' |
3439 | --- tests/unit/tst_performance/ItemList.qml 1970-01-01 00:00:00 +0000 |
3440 | +++ tests/unit/tst_performance/ItemList.qml 2014-09-15 11:00:03 +0000 |
3441 | @@ -0,0 +1,30 @@ |
3442 | +/* |
3443 | + * Copyright 2014 Canonical Ltd. |
3444 | + * |
3445 | + * This program is free software; you can redistribute it and/or modify |
3446 | + * it under the terms of the GNU Lesser General Public License as published by |
3447 | + * the Free Software Foundation; version 3. |
3448 | + * |
3449 | + * This program is distributed in the hope that it will be useful, |
3450 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3451 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3452 | + * GNU Lesser General Public License for more details. |
3453 | + * |
3454 | + * You should have received a copy of the GNU Lesser General Public License |
3455 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3456 | + */ |
3457 | + |
3458 | +import QtQuick 2.0 |
3459 | +import Ubuntu.Components 1.1 |
3460 | + |
3461 | +Column { |
3462 | + width: 800 |
3463 | + height: 600 |
3464 | + Repeater { |
3465 | + model: 5000 |
3466 | + Item { |
3467 | + width: parent.width |
3468 | + height: units.gu(6) |
3469 | + } |
3470 | + } |
3471 | +} |
3472 | |
3473 | === added file 'tests/unit/tst_performance/ListItemList.qml' |
3474 | --- tests/unit/tst_performance/ListItemList.qml 1970-01-01 00:00:00 +0000 |
3475 | +++ tests/unit/tst_performance/ListItemList.qml 2014-09-15 11:00:03 +0000 |
3476 | @@ -0,0 +1,30 @@ |
3477 | +/* |
3478 | + * Copyright 2014 Canonical Ltd. |
3479 | + * |
3480 | + * This program is free software; you can redistribute it and/or modify |
3481 | + * it under the terms of the GNU Lesser General Public License as published by |
3482 | + * the Free Software Foundation; version 3. |
3483 | + * |
3484 | + * This program is distributed in the hope that it will be useful, |
3485 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3486 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3487 | + * GNU Lesser General Public License for more details. |
3488 | + * |
3489 | + * You should have received a copy of the GNU Lesser General Public License |
3490 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3491 | + */ |
3492 | + |
3493 | +import QtQuick 2.0 |
3494 | +import Ubuntu.Components 1.1 |
3495 | + |
3496 | +Column { |
3497 | + width: 800 |
3498 | + height: 600 |
3499 | + property alias count: repeater.count |
3500 | + Repeater { |
3501 | + id: repeater |
3502 | + model: 5000 |
3503 | + ListItem { |
3504 | + } |
3505 | + } |
3506 | +} |
3507 | |
3508 | === added file 'tests/unit/tst_performance/ListItemWithInlineOptionsList.qml' |
3509 | --- tests/unit/tst_performance/ListItemWithInlineOptionsList.qml 1970-01-01 00:00:00 +0000 |
3510 | +++ tests/unit/tst_performance/ListItemWithInlineOptionsList.qml 2014-09-15 11:00:03 +0000 |
3511 | @@ -0,0 +1,39 @@ |
3512 | +/* |
3513 | + * Copyright 2014 Canonical Ltd. |
3514 | + * |
3515 | + * This program is free software; you can redistribute it and/or modify |
3516 | + * it under the terms of the GNU Lesser General Public License as published by |
3517 | + * the Free Software Foundation; version 3. |
3518 | + * |
3519 | + * This program is distributed in the hope that it will be useful, |
3520 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3521 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3522 | + * GNU Lesser General Public License for more details. |
3523 | + * |
3524 | + * You should have received a copy of the GNU Lesser General Public License |
3525 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3526 | + */ |
3527 | + |
3528 | +import QtQuick 2.0 |
3529 | +import Ubuntu.Components 1.1 |
3530 | + |
3531 | +Column { |
3532 | + width: 800 |
3533 | + height: 600 |
3534 | + |
3535 | + Repeater { |
3536 | + model: 5000 |
3537 | + ListItem { |
3538 | + trailingOptions: ListItemOptions { |
3539 | + Action {} |
3540 | + Action {} |
3541 | + Action {} |
3542 | + } |
3543 | + leadingOptions: ListItemOptions { |
3544 | + Action {} |
3545 | + Action {} |
3546 | + Action {} |
3547 | + } |
3548 | + } |
3549 | + } |
3550 | +} |
3551 | |
3552 | === added file 'tests/unit/tst_performance/ListItemWithLeadingOptionsList.qml' |
3553 | --- tests/unit/tst_performance/ListItemWithLeadingOptionsList.qml 1970-01-01 00:00:00 +0000 |
3554 | +++ tests/unit/tst_performance/ListItemWithLeadingOptionsList.qml 2014-09-15 11:00:03 +0000 |
3555 | @@ -0,0 +1,41 @@ |
3556 | +/* |
3557 | + * Copyright 2014 Canonical Ltd. |
3558 | + * |
3559 | + * This program is free software; you can redistribute it and/or modify |
3560 | + * it under the terms of the GNU Lesser General Public License as published by |
3561 | + * the Free Software Foundation; version 3. |
3562 | + * |
3563 | + * This program is distributed in the hope that it will be useful, |
3564 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3565 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3566 | + * GNU Lesser General Public License for more details. |
3567 | + * |
3568 | + * You should have received a copy of the GNU Lesser General Public License |
3569 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3570 | + */ |
3571 | + |
3572 | +import QtQuick 2.0 |
3573 | +import Ubuntu.Components 1.1 |
3574 | + |
3575 | +Column { |
3576 | + width: 800 |
3577 | + height: 600 |
3578 | + property ListItemOptions leadingOptions: options1 |
3579 | + ListItemOptions { |
3580 | + id: options1 |
3581 | + Action {} |
3582 | + } |
3583 | + ListItemOptions { |
3584 | + id: options2 |
3585 | + Action {} |
3586 | + Action {} |
3587 | + Action {} |
3588 | + } |
3589 | + |
3590 | + Repeater { |
3591 | + model: 5000 |
3592 | + ListItem { |
3593 | + trailingOptions: options1 |
3594 | + } |
3595 | + } |
3596 | +} |
3597 | |
3598 | === added file 'tests/unit/tst_performance/ListItemWithOptionsList.qml' |
3599 | --- tests/unit/tst_performance/ListItemWithOptionsList.qml 1970-01-01 00:00:00 +0000 |
3600 | +++ tests/unit/tst_performance/ListItemWithOptionsList.qml 2014-09-15 11:00:03 +0000 |
3601 | @@ -0,0 +1,41 @@ |
3602 | +/* |
3603 | + * Copyright 2014 Canonical Ltd. |
3604 | + * |
3605 | + * This program is free software; you can redistribute it and/or modify |
3606 | + * it under the terms of the GNU Lesser General Public License as published by |
3607 | + * the Free Software Foundation; version 3. |
3608 | + * |
3609 | + * This program is distributed in the hope that it will be useful, |
3610 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3611 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3612 | + * GNU Lesser General Public License for more details. |
3613 | + * |
3614 | + * You should have received a copy of the GNU Lesser General Public License |
3615 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3616 | + */ |
3617 | + |
3618 | +import QtQuick 2.0 |
3619 | +import Ubuntu.Components 1.1 |
3620 | + |
3621 | +Column { |
3622 | + width: 800 |
3623 | + height: 600 |
3624 | + ListItemOptions { |
3625 | + id: options1 |
3626 | + Action {} |
3627 | + } |
3628 | + ListItemOptions { |
3629 | + id: options2 |
3630 | + Action {} |
3631 | + Action {} |
3632 | + Action {} |
3633 | + } |
3634 | + |
3635 | + Repeater { |
3636 | + model: 5000 |
3637 | + ListItem { |
3638 | + trailingOptions: options1 |
3639 | + leadingOptions: options2 |
3640 | + } |
3641 | + } |
3642 | +} |
3643 | |
3644 | === added file 'tests/unit/tst_performance/ListItemsBaseList.qml' |
3645 | --- tests/unit/tst_performance/ListItemsBaseList.qml 1970-01-01 00:00:00 +0000 |
3646 | +++ tests/unit/tst_performance/ListItemsBaseList.qml 2014-09-15 11:00:03 +0000 |
3647 | @@ -0,0 +1,29 @@ |
3648 | +/* |
3649 | + * Copyright 2014 Canonical Ltd. |
3650 | + * |
3651 | + * This program is free software; you can redistribute it and/or modify |
3652 | + * it under the terms of the GNU Lesser General Public License as published by |
3653 | + * the Free Software Foundation; version 3. |
3654 | + * |
3655 | + * This program is distributed in the hope that it will be useful, |
3656 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3657 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3658 | + * GNU Lesser General Public License for more details. |
3659 | + * |
3660 | + * You should have received a copy of the GNU Lesser General Public License |
3661 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3662 | + */ |
3663 | + |
3664 | +import QtQuick 2.0 |
3665 | +import Ubuntu.Components 1.1 |
3666 | +import Ubuntu.Components.ListItems 1.0 as ListItems |
3667 | + |
3668 | +Column { |
3669 | + width: 800 |
3670 | + height: 600 |
3671 | + Repeater { |
3672 | + model: 5000 |
3673 | + ListItems.Base { |
3674 | + } |
3675 | + } |
3676 | +} |
3677 | |
3678 | === added file 'tests/unit/tst_performance/ListItemsEmptyList.qml' |
3679 | --- tests/unit/tst_performance/ListItemsEmptyList.qml 1970-01-01 00:00:00 +0000 |
3680 | +++ tests/unit/tst_performance/ListItemsEmptyList.qml 2014-09-15 11:00:03 +0000 |
3681 | @@ -0,0 +1,29 @@ |
3682 | +/* |
3683 | + * Copyright 2014 Canonical Ltd. |
3684 | + * |
3685 | + * This program is free software; you can redistribute it and/or modify |
3686 | + * it under the terms of the GNU Lesser General Public License as published by |
3687 | + * the Free Software Foundation; version 3. |
3688 | + * |
3689 | + * This program is distributed in the hope that it will be useful, |
3690 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3691 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3692 | + * GNU Lesser General Public License for more details. |
3693 | + * |
3694 | + * You should have received a copy of the GNU Lesser General Public License |
3695 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3696 | + */ |
3697 | + |
3698 | +import QtQuick 2.0 |
3699 | +import Ubuntu.Components 1.1 |
3700 | +import Ubuntu.Components.ListItems 1.0 as ListItems |
3701 | + |
3702 | +Column { |
3703 | + width: 800 |
3704 | + height: 600 |
3705 | + Repeater { |
3706 | + model: 5000 |
3707 | + ListItems.Empty { |
3708 | + } |
3709 | + } |
3710 | +} |
3711 | |
3712 | === modified file 'tests/unit/tst_performance/tst_performance.cpp' |
3713 | --- tests/unit/tst_performance/tst_performance.cpp 2013-06-27 15:35:23 +0000 |
3714 | +++ tests/unit/tst_performance/tst_performance.cpp 2014-09-15 11:00:03 +0000 |
3715 | @@ -74,16 +74,16 @@ |
3716 | QTest::newRow("grid with Label") << "LabelGrid.qml" << QUrl(); |
3717 | QTest::newRow("grid with UbuntuShape") << "UbuntuShapeGrid.qml" << QUrl(); |
3718 | QTest::newRow("grid with UbuntuShapePair") << "PairOfUbuntuShapeGrid.qml" << QUrl(); |
3719 | - QTest::newRow("grid with ButtonStyle") << "ButtonStyleGrid.qml" << QUrl(); |
3720 | QTest::newRow("grid with Button") << "ButtonGrid.qml" << QUrl(); |
3721 | -// QTest::newRow("grid with CheckBoxStyle") << "CheckBoxStyleGrid.qml" << QUrl(); |
3722 | -// QTest::newRow("grid with CheckBox") << "CheckBoxGrid.qml" << QUrl(); |
3723 | -// QTest::newRow("grid with SwitchStyle") << "SwitchStyleGrid.qml" << QUrl(); |
3724 | -// QTest::newRow("grid with Switch") << "SwitchGrid.qml" << QUrl(); |
3725 | -// QTest::newRow("grid with SwitchStyle") << "SwitchStyleGrid.qml" << QUrl(); |
3726 | -// QTest::newRow("grid with Switch") << "SwitchGrid.qml" << QUrl(); |
3727 | - QTest::newRow("grid with SliderStyle") << "SliderStyleGrid.qml" << QUrl(); |
3728 | QTest::newRow("grid with Slider") << "SliderGrid.qml" << QUrl(); |
3729 | + QTest::newRow("list with QtQuick Item") << "ItemList.qml" << QUrl(); |
3730 | + QTest::newRow("list with new ListItem") << "ListItemList.qml" << QUrl(); |
3731 | + QTest::newRow("list with new ListItem with options") << "ListItemWithOptionsList.qml" << QUrl(); |
3732 | + QTest::newRow("list with new ListItem using default leadingOptions") << "ListItemWithLeadingOptionsList.qml" << QUrl(); |
3733 | + QTest::newRow("list with new ListItem with inline options") << "ListItemWithInlineOptionsList.qml" << QUrl(); |
3734 | + QTest::newRow("list with ListItems.Empty (equivalent to the new ListItem") << "ListItemsEmptyList.qml" << QUrl(); |
3735 | + // disable this test as it takes >20 seconds. Kept still for measurements to be done during development |
3736 | +// QTest::newRow("list with ListItems.Base (one icon, one label and one chevron)") << "ListItemsBaseList.qml" << QUrl(); |
3737 | } |
3738 | |
3739 | void benchmark_GridOfComponents() |
3740 | |
3741 | === modified file 'tests/unit/tst_performance/tst_performance.pro' |
3742 | --- tests/unit/tst_performance/tst_performance.pro 2013-06-27 15:20:12 +0000 |
3743 | +++ tests/unit/tst_performance/tst_performance.pro 2014-09-15 11:00:03 +0000 |
3744 | @@ -19,4 +19,11 @@ |
3745 | TextWithImport.qml \ |
3746 | TextWithImportGrid.qml \ |
3747 | TextWithImportPopupsGrid.qml \ |
3748 | - TextWithImportPopups.qml |
3749 | + TextWithImportPopups.qml \ |
3750 | + ItemList.qml \ |
3751 | + ListItemList.qml \ |
3752 | + ListItemWithOptionsList.qml \ |
3753 | + ListItemWithInlineOptionsList.qml \ |
3754 | + ListItemsEmptyList.qml \ |
3755 | + ListItemsBaseList.qml \ |
3756 | + ListItemWithLeadingOptionsList.qml |
3757 | |
3758 | === added file 'tests/unit_x11/tst_components/tst_listitem.qml' |
3759 | --- tests/unit_x11/tst_components/tst_listitem.qml 1970-01-01 00:00:00 +0000 |
3760 | +++ tests/unit_x11/tst_components/tst_listitem.qml 2014-09-15 11:00:03 +0000 |
3761 | @@ -0,0 +1,569 @@ |
3762 | +/* |
3763 | + * Copyright 2014 Canonical Ltd. |
3764 | + * |
3765 | + * This program is free software; you can redistribute it and/or modify |
3766 | + * it under the terms of the GNU Lesser General Public License as published by |
3767 | + * the Free Software Foundation; version 3. |
3768 | + * |
3769 | + * This program is distributed in the hope that it will be useful, |
3770 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3771 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3772 | + * GNU Lesser General Public License for more details. |
3773 | + * |
3774 | + * You should have received a copy of the GNU Lesser General Public License |
3775 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3776 | + */ |
3777 | + |
3778 | +import QtQuick 2.0 |
3779 | +import QtTest 1.0 |
3780 | +import Ubuntu.Test 1.0 |
3781 | +import Ubuntu.Components 1.1 |
3782 | + |
3783 | +Item { |
3784 | + id: main |
3785 | + width: units.gu(40) |
3786 | + height: units.gu(71) |
3787 | + |
3788 | + Action { |
3789 | + id: stockAction |
3790 | + iconName: "starred" |
3791 | + property var param |
3792 | + onTriggered: param = value |
3793 | + } |
3794 | + ListItemOptions { |
3795 | + id: leading |
3796 | + Action { |
3797 | + iconName: "delete" |
3798 | + property var param |
3799 | + onTriggered: param = value |
3800 | + } |
3801 | + Action { |
3802 | + iconName: "edit" |
3803 | + property var param |
3804 | + onTriggered: param = value |
3805 | + } |
3806 | + Action { |
3807 | + iconName: "camcorder" |
3808 | + property var param |
3809 | + onTriggered: param = value |
3810 | + } |
3811 | + } |
3812 | + ListItemOptions { |
3813 | + id: trailing |
3814 | + panelColor: leading.panelColor |
3815 | + iconColor: leading.iconColor |
3816 | + options: [ |
3817 | + stockAction, |
3818 | + ] |
3819 | + delegate: Item { |
3820 | + objectName: "custom_delegate" |
3821 | + } |
3822 | + } |
3823 | + ListItemOptions { |
3824 | + id: wrongOption |
3825 | + Action { |
3826 | + id: goodAction |
3827 | + iconName: "starred" |
3828 | + } |
3829 | + QtObject { |
3830 | + id: badAction |
3831 | + } |
3832 | + Action { |
3833 | + iconName: "starred" |
3834 | + } |
3835 | + } |
3836 | + ListItemOptions { |
3837 | + id: optionsDefault |
3838 | + property int optionCount: options.length |
3839 | + } |
3840 | + |
3841 | + Column { |
3842 | + width: parent.width |
3843 | + ListItem { |
3844 | + id: defaults |
3845 | + width: parent.width |
3846 | + } |
3847 | + ListItem { |
3848 | + id: testItem |
3849 | + width: parent.width |
3850 | + contentItem.color: "blue" |
3851 | + leadingOptions: leading |
3852 | + trailingOptions: ListItemOptions { |
3853 | + options: leading.options |
3854 | + } |
3855 | + |
3856 | + Item { |
3857 | + id: bodyItem |
3858 | + anchors.fill: parent |
3859 | + } |
3860 | + } |
3861 | + ListView { |
3862 | + id: listView |
3863 | + width: parent.width |
3864 | + height: units.gu(24) |
3865 | + clip: true |
3866 | + model: 10 |
3867 | + delegate: ListItem { |
3868 | + objectName: "listItem" + index |
3869 | + width: parent.width |
3870 | + leadingOptions: leading |
3871 | + trailingOptions: trailing |
3872 | + } |
3873 | + } |
3874 | + } |
3875 | + |
3876 | + UbuntuTestCase { |
3877 | + name: "ListItemAPI" |
3878 | + when: windowShown |
3879 | + |
3880 | + SignalSpy { |
3881 | + id: pressedSpy |
3882 | + signalName: "pressedChanged" |
3883 | + target: testItem |
3884 | + } |
3885 | + |
3886 | + SignalSpy { |
3887 | + id: clickSpy |
3888 | + signalName: "clicked" |
3889 | + target: testItem; |
3890 | + } |
3891 | + |
3892 | + SignalSpy { |
3893 | + id: actionSpy |
3894 | + signalName: "triggered" |
3895 | + } |
3896 | + |
3897 | + SignalSpy { |
3898 | + id: xChangeSpy |
3899 | + signalName: "xChanged" |
3900 | + } |
3901 | + |
3902 | + function waitReboundCompletion(item) { |
3903 | + var prevX; |
3904 | + tryCompareFunction(function() { var b = prevX == item.contentItem.x; prevX = item.contentItem.x; return b; }, true, 1000); |
3905 | + } |
3906 | + |
3907 | + function initTestCase() { |
3908 | + TestExtras.registerTouchDevice(); |
3909 | + waitForRendering(main); |
3910 | + } |
3911 | + |
3912 | + function cleanup() { |
3913 | + testItem.selected = false; |
3914 | + testItem.selectable = false; |
3915 | + waitForRendering(testItem, 200); |
3916 | + pressedSpy.clear(); |
3917 | + clickSpy.clear(); |
3918 | + actionSpy.clear(); |
3919 | + xChangeSpy.clear(); |
3920 | + listView.interactive = true; |
3921 | + // tap on the first item to make sure we are rebounding all |
3922 | + mouseClick(defaults, 0, 0); |
3923 | + // make sure all events are processed |
3924 | + wait(200); |
3925 | + } |
3926 | + |
3927 | + function test_0_defaults() { |
3928 | + verify(defaults.contentItem !== null, "Defaults is null"); |
3929 | + compare(defaults.contentItem.color, "#000000", "Transparent by default"); |
3930 | + compare(defaults.contentItem.pressedColor, Theme.palette.selected.background, "Theme.palette.selected.background color by default") |
3931 | + compare(defaults.pressed, false, "Not pressed buy default"); |
3932 | + compare(defaults.divider.visible, true, "divider is visible by default"); |
3933 | + compare(defaults.divider.leftMargin, units.gu(2), "divider's left margin is 2GU"); |
3934 | + compare(defaults.divider.rightMargin, units.gu(2), "divider's right margin is 2GU"); |
3935 | + compare(defaults.divider.colorFrom, "#000000", "colorFrom differs."); |
3936 | + fuzzyCompare(defaults.divider.colorFrom.a, 0.14, 0.01, "colorFrom alpha differs"); |
3937 | + compare(defaults.divider.colorTo, "#ffffff", "colorTo differs."); |
3938 | + fuzzyCompare(defaults.divider.colorTo.a, 0.07, 0.01, "colorTo alpha differs"); |
3939 | + |
3940 | + compare(optionsDefault.delegate, null, "ListItemOptions has no delegate set by default."); |
3941 | + compare(optionsDefault.options.length, 0, "ListItemOptions has no options set."); |
3942 | + compare(optionsDefault.panelItem, null, "There is no panelItem created by default."); |
3943 | + compare(optionsDefault.status, ListItemOptions.Disconnected, "optiosn list is disconnected by default"); |
3944 | + compare(optionsDefault.connectedItem, null, "No item is connected by default"); |
3945 | + compare(optionsDefault.panelColor, "#000000", "default panelColor must be black"); |
3946 | + } |
3947 | + |
3948 | + function test_children_in_content_item() { |
3949 | + compare(bodyItem.parent, testItem.contentItem, "Content is not in the right holder!"); |
3950 | + } |
3951 | + |
3952 | + function test_pressedChanged_on_click() { |
3953 | + mousePress(testItem, testItem.width / 2, testItem.height / 2); |
3954 | + pressedSpy.wait(); |
3955 | + mouseRelease(testItem, testItem.width / 2, testItem.height / 2); |
3956 | + } |
3957 | + function test_pressedChanged_on_tap() { |
3958 | + TestExtras.touchPress(0, testItem, centerOf(testItem)); |
3959 | + pressedSpy.wait(); |
3960 | + TestExtras.touchRelease(0, testItem, centerOf(testItem)); |
3961 | + // local cleanup, wait few msecs to suppress double tap |
3962 | + wait(400); |
3963 | + } |
3964 | + |
3965 | + function test_clicked_on_mouse() { |
3966 | + mouseClick(testItem, testItem.width / 2, testItem.height / 2); |
3967 | + clickSpy.wait(); |
3968 | + } |
3969 | + function test_clicked_on_tap() { |
3970 | + TestExtras.touchClick(0, testItem, centerOf(testItem)); |
3971 | + clickSpy.wait(); |
3972 | + } |
3973 | + |
3974 | + function test_mouse_click_on_listitem() { |
3975 | + var listItem = findChild(listView, "listItem0"); |
3976 | + verify(listItem, "Cannot find listItem0"); |
3977 | + |
3978 | + mousePress(listItem, listItem.width / 2, 0); |
3979 | + compare(listItem.pressed, true, "Item is not pressed?"); |
3980 | + // do 5 moves to be able to sense it |
3981 | + var dy = 0; |
3982 | + for (var i = 1; i <= 5; i++) { |
3983 | + dy += i * 10; |
3984 | + mouseMove(listItem, listItem.width / 2, dy); |
3985 | + } |
3986 | + compare(listItem.pressed, false, "Item is pressed still!"); |
3987 | + mouseRelease(listItem, listItem.width / 2, dy); |
3988 | + } |
3989 | + function test_touch_click_on_listitem() { |
3990 | + var listItem = findChild(listView, "listItem0"); |
3991 | + verify(listItem, "Cannot find listItem0"); |
3992 | + |
3993 | + TestExtras.touchPress(0, listItem, Qt.point(listItem.width / 2, 5)); |
3994 | + compare(listItem.pressed, true, "Item is not pressed?"); |
3995 | + // do 5 moves to be able to sense it |
3996 | + var dy = 0; |
3997 | + for (var i = 1; i <= 5; i++) { |
3998 | + dy += i * 10; |
3999 | + TestExtras.touchMove(0, listItem, Qt.point(listItem.width / 2, dy)); |
4000 | + } |
4001 | + compare(listItem.pressed, false, "Item is pressed still!"); |
4002 | + // cleanup, wait few milliseconds to avoid dbl-click collision |
4003 | + TestExtras.touchRelease(0, listItem, Qt.point(listItem.width / 2, dy)); |
4004 | + } |
4005 | + |
4006 | + function test_background_height_change_on_divider_visible() { |
4007 | + // make sure the testItem's divider is shown |
4008 | + testItem.divider.visible = true; |
4009 | + verify(testItem.contentItem.height < testItem.height, "ListItem's background height must be less than the item itself."); |
4010 | + testItem.divider.visible = false; |
4011 | + compare(testItem.contentItem.height, testItem.height, "ListItem's background height must be the same as the item itself."); |
4012 | + testItem.divider.visible = true; |
4013 | + } |
4014 | + |
4015 | + // ListItemOptions tests |
4016 | + function test_valid_options_data() { |
4017 | + return [ |
4018 | + {tag: "Inline Actions", object: leading, expected: 3, xfail: false}, |
4019 | + {tag: "Stock Actions", object: trailing, expected: 1, xfail: false}, |
4020 | + {tag: "Wrong Actions", object: wrongOption, expected: 3, xfail: true}, |
4021 | + {tag: "Wrong Actions", object: wrongOption, expected: 0, xfail: false}, |
4022 | + ]; |
4023 | + } |
4024 | + function test_valid_options(data) { |
4025 | + if (data.xfail) { |
4026 | + expectFailContinue(data.tag, "expected to fail"); |
4027 | + } |
4028 | + compare(data.object.options.length, data.expected, data.tag + ": expected options differ."); |
4029 | + } |
4030 | + |
4031 | + function test_touch_tug_options_data() { |
4032 | + var item = findChild(listView, "listItem0"); |
4033 | + return [ |
4034 | + {tag: "Trailing, mouse", item: item, pos: centerOf(item), dx: -units.gu(20), positiveDirection: false, mouse: true}, |
4035 | + {tag: "Leading, mouse", item: item, pos: centerOf(item), dx: units.gu(20), positiveDirection: true, mouse: true}, |
4036 | + {tag: "Trailing, touch", item: item, pos: centerOf(item), dx: -units.gu(20), positiveDirection: false, mouse: false}, |
4037 | + {tag: "Leading, touch", item: item, pos: centerOf(item), dx: units.gu(20), positiveDirection: true, mouse: false}, |
4038 | + ]; |
4039 | + } |
4040 | + function test_touch_tug_options(data) { |
4041 | + listView.positionViewAtBeginning(); |
4042 | + if (data.mouse) { |
4043 | + flick(data.item, data.pos.x, data.pos.y, data.dx, 0); |
4044 | + } else { |
4045 | + TestExtras.touchDrag(0, data.item, data.pos, Qt.point(data.dx, 0)); |
4046 | + } |
4047 | + waitForRendering(data.item, 400); |
4048 | + if (data.positiveDirection) { |
4049 | + verify(data.item.contentItem.x > 0, data.tag + " options did not show up"); |
4050 | + } else { |
4051 | + verify(data.item.contentItem.x < 0, data.tag + " options did not show up"); |
4052 | + } |
4053 | + |
4054 | + // dismiss |
4055 | + if (data.mouse) { |
4056 | + mouseClick(main, 1, 1); |
4057 | + } else { |
4058 | + TestExtras.touchClick(0, main, Qt.point(1, 1)); |
4059 | + } |
4060 | + waitForRendering(data.item, 400); |
4061 | + } |
4062 | + |
4063 | + function test_rebound_when_pressed_outside_or_clicked_data() { |
4064 | + var item0 = findChild(listView, "listItem0"); |
4065 | + var item1 = findChild(listView, "listItem1"); |
4066 | + return [ |
4067 | + {tag: "Click on an other Item", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item1, mouse: true}, |
4068 | + {tag: "Click on the same Item", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item0.contentItem, mouse: true}, |
4069 | + {tag: "Tap on an other Item", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item1, mouse: false}, |
4070 | + {tag: "Tap on the same Item", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item0.contentItem, mouse: false}, |
4071 | + ]; |
4072 | + } |
4073 | + function test_rebound_when_pressed_outside_or_clicked(data) { |
4074 | + listView.positionViewAtBeginning(); |
4075 | + if (data.mouse) { |
4076 | + flick(data.item, data.pos.x, data.pos.y, data.dx, 0); |
4077 | + } else { |
4078 | + TestExtras.touchDrag(0, data.item, data.pos, Qt.point(data.dx, 0)); |
4079 | + } |
4080 | + waitForRendering(data.item, 400); |
4081 | + verify(data.item.contentItem.x != 0, "The component wasn't tugged!"); |
4082 | + // dismiss |
4083 | + if (data.mouse) { |
4084 | + mouseClick(data.clickOn, centerOf(data.clickOn).x, centerOf(data.clickOn).y); |
4085 | + } else { |
4086 | + TestExtras.touchClick(0, data.clickOn, centerOf(data.clickOn)); |
4087 | + } |
4088 | + waitForRendering(data.item, 400); |
4089 | + tryCompareFunction(function(){ return data.item.contentItem.x; }, 0, 1000); |
4090 | + } |
4091 | + |
4092 | + function test_listview_not_interactive_while_tugged_data() { |
4093 | + var item0 = findChild(listView, "listItem0"); |
4094 | + var item1 = findChild(listView, "listItem1"); |
4095 | + return [ |
4096 | + {tag: "Trailing", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item1, mouse: true}, |
4097 | + {tag: "Leading", item: item0, pos: centerOf(item0), dx: units.gu(20), clickOn: item0.contentItem, mouse: true}, |
4098 | + {tag: "Trailing", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item1, mouse: false}, |
4099 | + {tag: "Leading", item: item0, pos: centerOf(item0), dx: units.gu(20), clickOn: item0.contentItem, mouse: false}, |
4100 | + ]; |
4101 | + } |
4102 | + function test_listview_not_interactive_while_tugged(data) { |
4103 | + listView.positionViewAtBeginning(); |
4104 | + if (data.mouse) { |
4105 | + flick(data.item, data.pos.x, data.pos.y, data.dx, 0); |
4106 | + } else { |
4107 | + TestExtras.touchDrag(0, data.item, data.pos, Qt.point(data.dx, 0)); |
4108 | + } |
4109 | + waitForRendering(data.item, 800); |
4110 | + compare(listView.interactive, false, "The ListView is still interactive!"); |
4111 | + // dismiss |
4112 | + if (data.mouse) { |
4113 | + mouseClick(data.clickOn, centerOf(data.clickOn).x, centerOf(data.clickOn).y); |
4114 | + } else { |
4115 | + TestExtras.touchClick(0, data.clickOn, centerOf(data.clickOn)); |
4116 | + } |
4117 | + waitForRendering(data.item, 400); |
4118 | + tryCompareFunction(function(){ return listView.interactive; }, true, 1000); |
4119 | + } |
4120 | + |
4121 | + function test_selecting_option_rebounds_data() { |
4122 | + var item0 = findChild(listView, "listItem0"); |
4123 | + return [ |
4124 | + {tag: "With mouse", item: item0, pos: centerOf(item0), dx: units.gu(20), options: item0.leadingOptions, select: "list_option_0", mouse: true}, |
4125 | + {tag: "With touch", item: item0, pos: centerOf(item0), dx: units.gu(20), options: item0.leadingOptions, select: "list_option_0", mouse: false}, |
4126 | + ] |
4127 | + } |
4128 | + function test_selecting_option_rebounds(data) { |
4129 | + listView.positionViewAtBeginning(); |
4130 | + if (data.mouse) { |
4131 | + flick(data.item, data.pos.x, data.pos.y, data.dx, 0); |
4132 | + } else { |
4133 | + TestExtras.touchDrag(0, data.item, data.pos, Qt.point(data.dx, 0)); |
4134 | + } |
4135 | + waitForRendering(data.item, 800); |
4136 | + var selectedOption = findChild(data.options.panelItem, data.select); |
4137 | + verify(selectedOption, "Cannot select option " + data.select); |
4138 | + // dismiss |
4139 | + if (data.mouse) { |
4140 | + mouseClick(selectedOption, centerOf(selectedOption).x, centerOf(selectedOption).y); |
4141 | + } else { |
4142 | + TestExtras.touchClick(0, selectedOption, centerOf(selectedOption)); |
4143 | + } |
4144 | + waitForRendering(data.item, 400); |
4145 | + tryCompareFunction(function(){ return data.item.contentItem.x; }, 0, 1000); |
4146 | + } |
4147 | + |
4148 | + function test_custom_trailing_delegate() { |
4149 | + listView.positionViewAtBeginning(); |
4150 | + var item = findChild(listView, "listItem0"); |
4151 | + flick(item, centerOf(item).x, centerOf(item).y, -units.gu(20), 0); |
4152 | + verify(trailing.panelItem, "Panel is not visible"); |
4153 | + var custom = findChild(trailing.panelItem, "custom_delegate"); |
4154 | + verify(custom, "Custom delegate not in use"); |
4155 | + // cleanup |
4156 | + mouseClick(main, 0, 0); |
4157 | + } |
4158 | + |
4159 | + // execute as last so we make sure we have the panel created |
4160 | + function test_snap_data() { |
4161 | + verify(testItem.leadingOptions.panelItem, "Panel had not been created!"); |
4162 | + var option = findChild(testItem.leadingOptions.panelItem, "list_option_0"); |
4163 | + verify(option, "Options not accessible"); |
4164 | + var optionSize = option.width; |
4165 | + return [ |
4166 | + {tag: "Snap back leading, mouse", item: testItem.contentItem, dx: optionSize / 2 - 10, list: testItem.leadingOptions, snap: false, mouse: true}, |
4167 | + {tag: "Snap back leading, touch", item: testItem.contentItem, dx: optionSize / 2 - 10, list: testItem.leadingOptions, snap: false, mouse: false}, |
4168 | + {tag: "Snap in leading, mouse", item: testItem.contentItem, dx: optionSize / 2 + 10, list: testItem.leadingOptions, snap: true, mouse: true}, |
4169 | + {tag: "Snap in leading, touch", item: testItem.contentItem, dx: optionSize / 2 + 10, list: testItem.leadingOptions, snap: true, mouse: false}, |
4170 | + |
4171 | + {tag: "Snap back trailing, mouse", item: testItem.contentItem, dx: -(optionSize / 2 - 10), list: testItem.trailingOptions, snap: false, mouse: true}, |
4172 | + {tag: "Snap back trailing, touch", item: testItem.contentItem, dx: -(optionSize / 2 - 10), list: testItem.trailingOptions, snap: false, mouse: false}, |
4173 | + {tag: "Snap in trailing, mouse", item: testItem.contentItem, dx: -(optionSize / 2 + 10), list: testItem.trailingOptions, snap: true, mouse: true}, |
4174 | + {tag: "Snap in trailing, touch", item: testItem.contentItem, dx: -(optionSize / 2 + 10), list: testItem.trailingOptions, snap: true, mouse: false}, |
4175 | + ]; |
4176 | + } |
4177 | + function test_snap(data) { |
4178 | + if (data.mouse) { |
4179 | + flick(data.item, centerOf(data.item).x, centerOf(data.item).y, data.dx, 0); |
4180 | + } else { |
4181 | + TestExtras.touchDrag(0, data.item, centerOf(data.item), Qt.point(data.dx, 0)); |
4182 | + } |
4183 | + |
4184 | + waitForRendering(data.item, 800); |
4185 | + if (data.snap) { |
4186 | + verify(data.item.x != 0, "Not snapped to be visible"); |
4187 | + } else { |
4188 | + verify(data.item.x == 0, "Not snapped back"); |
4189 | + } |
4190 | + |
4191 | + // cleanup |
4192 | + if (data.mouse) { |
4193 | + mouseClick(data.item, centerOf(data.item).x, centerOf(data.item).y); |
4194 | + } else { |
4195 | + TestExtras.touchClick(0, data.item, centerOf(data.item)); |
4196 | + } |
4197 | + waitForRendering(data.item, 800); |
4198 | + } |
4199 | + |
4200 | + function test_verify_action_value_data() { |
4201 | + return [ |
4202 | + {tag: "Undefined", item: testItem, result: undefined}, |
4203 | + {tag: "Index 0", item: findChild(listView, "listItem0"), result: 0}, |
4204 | + {tag: "Index 1", item: findChild(listView, "listItem1"), result: 1}, |
4205 | + {tag: "Index 2", item: findChild(listView, "listItem2"), result: 2}, |
4206 | + {tag: "Index 3", item: findChild(listView, "listItem3"), result: 3}, |
4207 | + ]; |
4208 | + } |
4209 | + function test_verify_action_value(data) { |
4210 | + var option = findChild(data.item.leadingOptions.panelItem, "list_option_0"); |
4211 | + verify(option, "Options panel cannot be reached"); |
4212 | + // we test the last action, as we tug the first action on leading, which means teh alst will be accessible |
4213 | + var len = data.item.leadingOptions.options.length; |
4214 | + var action = data.item.leadingOptions.options[len - 1]; |
4215 | + actionSpy.target = action; |
4216 | + actionSpy.clear(); |
4217 | + // tug options in |
4218 | + flick(data.item.contentItem, centerOf(data.item.contentItem).x, centerOf(data.item.contentItem).y, option.width, 0); |
4219 | + waitForRendering(data.item.contentItem, 800); |
4220 | + |
4221 | + // select the option |
4222 | + mouseClick(data.item, centerOf(option).x, centerOf(option).y); |
4223 | + waitForRendering(data.item.contentItem, 800); |
4224 | + |
4225 | + // check the action param |
4226 | + actionSpy.wait(); |
4227 | + compare(action.param, data.result, "Action parameter differs"); |
4228 | + } |
4229 | + |
4230 | + SignalSpy { |
4231 | + id: panelItemSpy |
4232 | + signalName: "onXChanged" |
4233 | + } |
4234 | + |
4235 | + function test_disabled_item_locked_data() { |
4236 | + var item0 = findChild(listView, "listItem0"); |
4237 | + return [ |
4238 | + // drag same amount as height is |
4239 | + {tag: "Simple item, leading", item: testItem, enabled: false, dx: testItem.height}, |
4240 | + {tag: "Simple item, trailing", item: testItem, enabled: false, dx: -testItem.height}, |
4241 | + {tag: "ListView item, leading", item: item0, enabled: false, dx: item0.height}, |
4242 | + {tag: "ListView item, trailing", item: item0, enabled: false, dx: -item0.height}, |
4243 | + ]; |
4244 | + } |
4245 | + function test_disabled_item_locked(data) { |
4246 | + var oldEnabled = data.item.enabled; |
4247 | + panelItemSpy.clear(); |
4248 | + panelItemSpy.target = data.item; |
4249 | + data.item.enabled = false; |
4250 | + // tug |
4251 | + flick(data.item.contentItem, centerOf(data.item.contentItem).x, centerOf(data.item.contentItem).y, data.dx, 0); |
4252 | + compare(panelItemSpy.count, 0, "Item had been tugged despite being disabled!"); |
4253 | + // check opacity |
4254 | + fuzzyCompare(data.item.opacity, 0.5, 0.1, "Disabled item must be 50% transparent"); |
4255 | + //cleanup |
4256 | + data.item.enabled = oldEnabled; |
4257 | + } |
4258 | + |
4259 | + function test_toggle_selectable_data() { |
4260 | + return [ |
4261 | + {tag: "When not selected", selected: false}, |
4262 | + {tag: "When selected", selected: true}, |
4263 | + ] |
4264 | + } |
4265 | + function test_toggle_selectable(data) { |
4266 | + xChangeSpy.target = testItem.contentItem; |
4267 | + testItem.selectable = true; |
4268 | + waitForRendering(testItem.contentItem, 800); |
4269 | + testItem.selected = data.selected; |
4270 | + xChangeSpy.wait(); |
4271 | + } |
4272 | + |
4273 | + function test_no_tug_when_selectable() { |
4274 | + xChangeSpy.target = null; |
4275 | + testItem.selectable = true; |
4276 | + // wait till animation to selection mode ends |
4277 | + waitReboundCompletion(testItem); |
4278 | + |
4279 | + // try to tug leading |
4280 | + xChangeSpy.target = testItem.contentItem; |
4281 | + xChangeSpy.clear(); |
4282 | + compare(xChangeSpy.count, 0, "Wrong signal count!"); |
4283 | + flick(testItem.contentItem, centerOf(testItem.contentItem).x, centerOf(testItem.contentItem).y, testItem.contentItem.width / 2, 0); |
4284 | + compare(xChangeSpy.count, 0, "No tug allowed when in selection mode"); |
4285 | + } |
4286 | + |
4287 | + SignalSpy { |
4288 | + id: pressAndHoldSpy |
4289 | + signalName: "pressAndHold" |
4290 | + } |
4291 | + function test_pressandhold_suppress_click() { |
4292 | + var center = centerOf(testItem); |
4293 | + pressAndHoldSpy.target = testItem; |
4294 | + clickSpy.target = testItem; |
4295 | + clickSpy.clear(); |
4296 | + mouseLongPress(testItem, center.x, center.y); |
4297 | + mouseRelease(testItem, center.x, center.y); |
4298 | + pressAndHoldSpy.wait(); |
4299 | + compare(clickSpy.count, 0, "Click must be suppressed when long pressed"); |
4300 | + } |
4301 | + |
4302 | + function test_listitemoptions_status_data() { |
4303 | + var drag = testItem.contentItem.width / 2; |
4304 | + return [ |
4305 | + {tag:"Leading", item: testItem, dx: drag, list: testItem.leadingOptions, expectedStatus: ListItemOptions.LeadingOptions}, |
4306 | + {tag:"Trailing", item: testItem, dx: -drag, list: testItem.trailingOptions, expectedStatus: ListItemOptions.TrailingOptions}, |
4307 | + ]; |
4308 | + } |
4309 | + function test_listitemoptions_status(data) { |
4310 | + var testItem = data.item.contentItem; |
4311 | + flick(testItem, centerOf(testItem).x, centerOf(testItem).y, data.dx, 0); |
4312 | + waitForRendering(testItem, 800); |
4313 | + compare(data.list.status, data.expectedStatus, "Status on the option list differs."); |
4314 | + compare(data.list.connectedItem, data.item, "connectedItem is not the tugged item."); |
4315 | + } |
4316 | + |
4317 | + function test_panelColor_change() { |
4318 | + // change panel color for the leading and observe the trailing panelItem color change |
4319 | + leading.panelColor = UbuntuColors.blue; |
4320 | + compare(leading.panelItem.panelColor, UbuntuColors.blue, "leading panelItem color differs"); |
4321 | + compare(trailing.panelItem.panelColor, UbuntuColors.blue, "trailing panelItem color has not been set"); |
4322 | + } |
4323 | + function test_iconColor_change() { |
4324 | + // change panel color for the leading and observe the trailing panelItem color change |
4325 | + leading.iconColor = UbuntuColors.green; |
4326 | + compare(leading.panelItem.iconColor, UbuntuColors.green, "leading panelItem color differs"); |
4327 | + compare(trailing.panelItem.iconColor, UbuntuColors.green, "trailing panelItem color has not been set"); |
4328 | + } |
4329 | + } |
4330 | +} |
4331 | |
4332 | === added file 'tests/unit_x11/tst_components/tst_listitem_ubuntulistview.qml' |
4333 | --- tests/unit_x11/tst_components/tst_listitem_ubuntulistview.qml 1970-01-01 00:00:00 +0000 |
4334 | +++ tests/unit_x11/tst_components/tst_listitem_ubuntulistview.qml 2014-09-15 11:00:03 +0000 |
4335 | @@ -0,0 +1,159 @@ |
4336 | +/* |
4337 | + * Copyright 2014 Canonical Ltd. |
4338 | + * |
4339 | + * This program is free software; you can redistribute it and/or modify |
4340 | + * it under the terms of the GNU Lesser General Public License as published by |
4341 | + * the Free Software Foundation; version 3. |
4342 | + * |
4343 | + * This program is distributed in the hope that it will be useful, |
4344 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4345 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4346 | + * GNU Lesser General Public License for more details. |
4347 | + * |
4348 | + * You should have received a copy of the GNU Lesser General Public License |
4349 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4350 | + */ |
4351 | + |
4352 | +import QtQuick 2.0 |
4353 | +import QtTest 1.0 |
4354 | +import Ubuntu.Test 1.0 |
4355 | +import Ubuntu.Components 1.1 |
4356 | + |
4357 | +Item { |
4358 | + id: main |
4359 | + width: units.gu(40) |
4360 | + height: units.gu(71) |
4361 | + |
4362 | + Column { |
4363 | + anchors.fill: parent |
4364 | + UbuntuListView { |
4365 | + id: listView |
4366 | + clip: true |
4367 | + width: parent.width |
4368 | + height: parent.height / 3 |
4369 | + delegate: ListItem { |
4370 | + objectName: "listItem" + index |
4371 | + } |
4372 | + } |
4373 | + // override default leading |
4374 | + UbuntuListView { |
4375 | + id: customListView1 |
4376 | + clip: true |
4377 | + width: parent.width |
4378 | + height: parent.height / 3 |
4379 | + model: numberModel |
4380 | + leadingOptions: ListItemOptions { |
4381 | + Action { |
4382 | + iconName: "edit" |
4383 | + } |
4384 | + } |
4385 | + delegate: ListItem { |
4386 | + objectName: "listItem" + index |
4387 | + } |
4388 | + } |
4389 | + // inline option list takes precedence (overrides UbuntuListView's default) |
4390 | + UbuntuListView { |
4391 | + id: customListView2 |
4392 | + clip: true |
4393 | + width: parent.width |
4394 | + height: parent.height / 3 |
4395 | + model: numberModel |
4396 | + delegate: ListItem { |
4397 | + objectName: "listItem" + index |
4398 | + leadingOptions: ListItemOptions { |
4399 | + Action { |
4400 | + iconName: "edit" |
4401 | + } |
4402 | + } |
4403 | + } |
4404 | + } |
4405 | + } |
4406 | + |
4407 | + property int numberModel: 10 |
4408 | + property var arrayModel: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |
4409 | + property ListModel objectModel: ListModel { |
4410 | + function fill() { |
4411 | + clear(); |
4412 | + for (var i = 0; i < 10; i++) { |
4413 | + append({data: "ListElement #" + i}) |
4414 | + } |
4415 | + } |
4416 | + } |
4417 | + |
4418 | + UbuntuTestCase { |
4419 | + name: "ListItem with UbuntuListView" |
4420 | + when: windowShown |
4421 | + |
4422 | + SignalSpy { |
4423 | + id: actionSpy |
4424 | + signalName: "triggered"; |
4425 | + } |
4426 | + |
4427 | + function init() { |
4428 | + numberModel = 10; |
4429 | + arrayModel = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; |
4430 | + objectModel.fill(); |
4431 | + } |
4432 | + function cleanup() { |
4433 | + listView.model = undefined; |
4434 | + actionSpy.target = null; |
4435 | + actionSpy.clear(); |
4436 | + } |
4437 | + |
4438 | + function test_leading_option_is_delete_alone() { |
4439 | + // set a model to the listView |
4440 | + listView.model = numberModel; |
4441 | + waitForRendering(listView, 800); |
4442 | + var listItem = findChild(listView, "listItem0"); |
4443 | + verify(listItem, "Cannot get list item at index 0"); |
4444 | + // tug the leadingOptions in |
4445 | + flick(listItem.contentItem, centerOf(listItem.contentItem).x, centerOf(listItem.contentItem).y, listItem.contentItem.width / 2, 0); |
4446 | + // leading options panel must be created |
4447 | + verify(listItem.leadingOptions.panelItem, "Panel of LeadingOptions cannot be reached!"); |
4448 | + compare(listItem.leadingOptions.options.length, 1, "More options in the default leadingOptions than it should be!"); |
4449 | + compare(listItem.leadingOptions.options[0].iconName, "delete", "The default leading option is not 'delete'"); |
4450 | + } |
4451 | + |
4452 | + function test_delete_item_data() { |
4453 | + return [ |
4454 | + {tag: "Number model", model: numberModel, index: 3, resultModelSize: 9 }, |
4455 | + {tag: "Array model", model: arrayModel, index: 4, resultModelSize: 9 }, |
4456 | + {tag: "Object model", model: objectModel, index: 2, resultModelSize: 9 }, |
4457 | + ]; |
4458 | + } |
4459 | + function test_delete_item(data) { |
4460 | + listView.model = data.model; |
4461 | + waitForRendering(listView, 800); |
4462 | + // navigate to teh list item |
4463 | + listView.positionViewAtIndex(data.index, ListView.Contain); |
4464 | + // perform delete by tugging the leading panel in |
4465 | + var listItem = findChild(listView, "listItem" + data.index); |
4466 | + verify(listItem, "Cannot get list item at index " + data.index); |
4467 | + flick(listItem.contentItem, centerOf(listItem.contentItem).x, centerOf(listItem.contentItem).y, listItem.contentItem.width / 2, 0); |
4468 | + |
4469 | + // watch action triggers |
4470 | + actionSpy.target = listItem.leadingOptions.options[0]; |
4471 | + // select the option on the leadingOptions |
4472 | + var option = findChild(listItem.leadingOptions.panelItem, "list_option_0"); |
4473 | + verify(option, "Option cannot be reached"); |
4474 | + mouseClick(listItem, centerOf(option).x, centerOf(option).y); |
4475 | + actionSpy.wait(); |
4476 | + |
4477 | + // check model size by comparing the listview count |
4478 | + compare(listView.count, data.resultModelSize, "Item had not been deleted!"); |
4479 | + } |
4480 | + |
4481 | + function test_default_optionlist_override_data() { |
4482 | + return [ |
4483 | + {tag: "Default leadingOption overridden", view: customListView1}, |
4484 | + {tag: "Inline leadingOption has precedence over default", view: customListView2}, |
4485 | + ]; |
4486 | + } |
4487 | + function test_default_optionlist_override(data) { |
4488 | + var listItem = findChild(data.view, "listItem0"); |
4489 | + verify(listItem, "Cannot get list item at index 0"); |
4490 | + compare(listItem.leadingOptions.options.length, 1, "More than 1 option in the leadingOptions list"); |
4491 | + compare(listItem.leadingOptions.options[0].iconName, "edit", "Default UbuntuListItem option not overridden!"); |
4492 | + } |
4493 | + } |
4494 | +} |
4495 | |
4496 | === modified file 'tests/unit_x11/tst_components/tst_ubuntulistview11.qml' |
4497 | --- tests/unit_x11/tst_components/tst_ubuntulistview11.qml 2014-06-26 15:59:10 +0000 |
4498 | +++ tests/unit_x11/tst_components/tst_ubuntulistview11.qml 2014-09-15 11:00:03 +0000 |
4499 | @@ -91,6 +91,7 @@ |
4500 | function test_0_defaults() { |
4501 | verify(ubuntuListView.hasOwnProperty("pullToRefresh"), "PullToRefresh is missing"); |
4502 | compare(ubuntuListView.pullToRefresh.enabled, false, "PullToRefresh functionality is disabled by default"); |
4503 | + verify(ubuntuListView.leadingOptions, "LeadingOptions is not defined!"); |
4504 | } |
4505 | |
4506 | function test_pullToRefresh_manual_refresh() { |
FAILED: Continuous integration, rev:1247 jenkins. qa.ubuntu. com/job/ ubuntu- ui-toolkit- ci/2148/ jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- utopic- touch/4811/ console jenkins. qa.ubuntu. com/job/ generic- mediumtests- utopic/ 3520/console jenkins. qa.ubuntu. com/job/ ubuntu- ui-toolkit- utopic- amd64-ci/ 94/console jenkins. qa.ubuntu. com/job/ ubuntu- ui-toolkit- utopic- armhf-ci/ 94/console jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- armhf/6063/ console jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- amd64/3826/ console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/ubuntu- ui-toolkit- ci/2148/ rebuild
http://