Merge lp:~zsombi/ubuntu-ui-toolkit/35-options-panel-swipe into lp:ubuntu-ui-toolkit/staging
- 35-options-panel-swipe
- Merge into staging
Status: | Superseded |
---|---|
Proposed branch: | lp:~zsombi/ubuntu-ui-toolkit/35-options-panel-swipe |
Merge into: | lp:ubuntu-ui-toolkit/staging |
Prerequisite: | lp:~zsombi/ubuntu-ui-toolkit/30-options |
Diff against target: |
1081 lines (+671/-16) 16 files modified
modules/Ubuntu/Components/ListItemPanel.qml (+53/-0) modules/Ubuntu/Components/plugin/plugin.pro (+4/-2) modules/Ubuntu/Components/plugin/propertychange_p.cpp (+76/-0) modules/Ubuntu/Components/plugin/propertychange_p.h (+39/-0) modules/Ubuntu/Components/plugin/uclistitem.cpp (+235/-10) modules/Ubuntu/Components/plugin/uclistitem.h (+3/-0) modules/Ubuntu/Components/plugin/uclistitem_p.h (+15/-0) modules/Ubuntu/Components/plugin/uclistitemoptions.cpp (+80/-0) modules/Ubuntu/Components/plugin/uclistitemoptions_p.h (+11/-2) modules/Ubuntu/Components/qmldir (+1/-0) tests/resources/listitems/ListItemTest.qml (+9/-0) tests/unit/tst_performance/ListItemWithInlineOptionsList.qml (+39/-0) tests/unit/tst_performance/ListItemWithOptionsList.qml (+7/-1) tests/unit/tst_performance/tst_performance.cpp (+1/-0) tests/unit/tst_performance/tst_performance.pro (+2/-1) tests/unit_x11/tst_components/tst_listitem.qml (+96/-0) |
To merge this branch: | bzr merge lp:~zsombi/ubuntu-ui-toolkit/35-options-panel-swipe |
Related bugs: | |
Related blueprints: |
SDK: Design a new ListItem and layouts
(Undefined)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu SDK team | Pending | ||
Review via email: mp+232083@code.launchpad.net |
Commit message
ListOptions swipe implementation. Uses an empty panel with a fix size to get teh measures working.
Description of the change
- 1217. By Zsombor Egri
-
Flickable control
- 1218. By Zsombor Egri
-
tapping outside or when clicked rebinds ListItem
- 1219. By Zsombor Egri
-
test cases added, touch interaction fixed
- 1220. By Zsombor Egri
-
test app reverted
- 1221. By Zsombor Egri
-
reverting unneeded changes
- 1222. By Zsombor Egri
-
prereq merge
- 1223. By Zsombor Egri
-
performance tests extended
- 1224. By Zsombor Egri
-
some fixtures
- 1225. By Zsombor Egri
-
prereq merge
- 1226. By Zsombor Egri
-
minor adjustments
- 1227. By Zsombor Egri
-
disconnect when panel goes off
- 1228. By Zsombor Egri
-
prereq merge
- 1229. By Zsombor Egri
-
binding loop fixes
- 1230. By Zsombor Egri
-
simplification
- 1231. By Zsombor Egri
-
minor update on event filter
- 1232. By Zsombor Egri
-
prereq merge
- 1233. By Zsombor Egri
-
small fixes due to merge
- 1234. By Zsombor Egri
-
prereq merge
- 1235. By Zsombor Egri
-
accidental rename of palette color
- 1236. By Zsombor Egri
-
code reorganized: ListItemOptions connects only when released from the previous item.
- 1237. By Zsombor Egri
-
tug acceptance condition fixed
- 1238. By Zsombor Egri
-
documentation update
- 1239. By Zsombor Egri
-
asynchronous panel detach API added; documentation updated
- 1240. By Zsombor Egri
-
prereq merge
- 1241. By Zsombor Egri
-
test fixed
- 1242. By Zsombor Egri
-
fix for the leading flag
- 1243. By Zsombor Egri
-
tugging fix when only one option list is assigned
- 1244. By Zsombor Egri
-
undesired changes reverted, tug threshold fixed
- 1245. By Zsombor Egri
-
prereq
- 1246. By Zsombor Egri
-
merge fix
- 1247. By Zsombor Egri
-
performance test amount adjusted to match the others
- 1248. By Zsombor Egri
-
signal removed, queuing is done with a single pointer member
- 1249. By Zsombor Egri
-
prereq merge
- 1250. By Zsombor Egri
-
prereq merge
- 1251. By Zsombor Egri
-
prereq
- 1252. By Zsombor Egri
-
trailing panel color fix
- 1253. By Zsombor Egri
-
prereq merge
- 1254. By Zsombor Egri
-
prereq
- 1255. By Zsombor Egri
-
version fix
- 1256. By Zsombor Egri
-
prereq
- 1257. By Zsombor Egri
-
prereq
- 1258. By Zsombor Egri
-
comments applied, ListItemOptions
.options list is no longer the default property - 1259. By Zsombor Egri
-
defautl property data added.
- 1260. By Zsombor Egri
-
doc fix
- 1261. By Zsombor Egri
-
small API changes
- 1262. By Zsombor Egri
-
performance test fixed
- 1263. By Zsombor Egri
-
ListItemOptions turned to ListItemActions
- 1264. By Zsombor Egri
-
prereq
- 1265. By Zsombor Egri
-
prereq merge & build fixed
- 1266. By Zsombor Egri
-
prereq sync
- 1267. By Zsombor Egri
-
prereq sync
- 1268. By Zsombor Egri
-
prereq sync
- 1269. By Zsombor Egri
-
code fixed due to panelItem removal
- 1270. By Zsombor Egri
-
prereq sync
- 1271. By Zsombor Egri
-
prereq sync
- 1272. By Zsombor Egri
-
prereq sync
- 1273. By Zsombor Egri
-
prereq sync
- 1274. By Zsombor Egri
-
rebound animation easing changed to elastic
- 1275. By Zsombor Egri
-
painting optimized, do not delete painter node every time the list item/divider is updated
- 1276. By Zsombor Egri
-
animation easing curve and duration fixed
- 1277. By Zsombor Egri
-
easing period and duration fixed
- 1278. By Zsombor Egri
-
rogue logs removed
- 1279. By Zsombor Egri
-
prereq sync
- 1280. By Zsombor Egri
-
adding moving API
- 1281. By Zsombor Egri
-
movingStarted and movingEnded signals added, being in sync with Flickable's approach
- 1282. By Zsombor Egri
-
prereq sync
- 1283. By Zsombor Egri
-
change step
- 1284. By Zsombor Egri
-
prereq sync
- 1285. By Zsombor Egri
-
adjusting code to support ListItemActions attached properties
- 1286. By Zsombor Egri
-
adjusting new prerequisite
- 1287. By Zsombor Egri
-
small fixes
- 1288. By Zsombor Egri
-
prereq sync
- 1289. By Zsombor Egri
-
adjustments to prerequisite changes; documentation fix
- 1290. By Zsombor Egri
-
accessing private property fix
- 1291. By Zsombor Egri
-
test cases fixed
- 1292. By Zsombor Egri
-
prereq sync
- 1293. By Zsombor Egri
-
fixing eronous trailingActions assignment
- 1294. By Zsombor Egri
-
introduce styling; move ListItemPanel into Ambiance theme, expose ListItemStyle C++ style component from the main plugin
- 1295. By Zsombor Egri
-
animation cleanup
- 1296. By Zsombor Egri
-
make leadingPanel property readonly
- 1297. By Zsombor Egri
-
prereq sync
- 1298. By Zsombor Egri
-
adjusting code to prereq changes
- 1299. By Zsombor Egri
-
prereq sync
- 1300. By Zsombor Egri
-
staging in test fixing
- 1301. By Zsombor Egri
-
prereq sync
- 1302. By Zsombor Egri
-
prereq sync
- 1303. By Zsombor Egri
-
test fix
- 1304. By Zsombor Egri
-
fixing tests to comply with prereq changes
- 1305. By Zsombor Egri
-
prereq sync
- 1306. By Zsombor Egri
-
prereq sync
- 1307. By Zsombor Egri
-
method name fixed
- 1308. By Zsombor Egri
-
prereq sync
- 1309. By Zsombor Egri
-
prereq sync
- 1310. By Zsombor Egri
-
prereq sync
- 1311. By Zsombor Egri
-
review comments applied
Unmerged revisions
Preview Diff
1 | === added file 'modules/Ubuntu/Components/ListItemPanel.qml' |
2 | --- modules/Ubuntu/Components/ListItemPanel.qml 1970-01-01 00:00:00 +0000 |
3 | +++ modules/Ubuntu/Components/ListItemPanel.qml 2014-09-11 06:51:26 +0000 |
4 | @@ -0,0 +1,53 @@ |
5 | +/* |
6 | + * Copyright 2014 Canonical Ltd. |
7 | + * |
8 | + * This program is free software; you can redistribute it and/or modify |
9 | + * it under the terms of the GNU Lesser General Public License as published by |
10 | + * the Free Software Foundation; version 3. |
11 | + * |
12 | + * This program is distributed in the hope that it will be useful, |
13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | + * GNU Lesser General Public License for more details. |
16 | + * |
17 | + * You should have received a copy of the GNU Lesser General Public License |
18 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | + */ |
20 | + |
21 | +import QtQuick 2.2 |
22 | +import Ubuntu.Components 1.1 |
23 | + |
24 | +/* |
25 | + This component is the holder of the ListItem options. |
26 | + */ |
27 | +Item { |
28 | + id: panel |
29 | + width: units.gu(20) |
30 | + |
31 | + readonly property Item contentItem: parent ? parent.contentItem : null |
32 | + /* |
33 | + Specifies whether the panel is used to visualize leading or trailing options. |
34 | + */ |
35 | + property bool leadingPanel: false |
36 | + /* |
37 | + The delegate to be used to visualize the options |
38 | + */ |
39 | + property Component delegate |
40 | + |
41 | + /* |
42 | + Options |
43 | + */ |
44 | + property var optionList |
45 | + |
46 | + anchors { |
47 | + left: contentItem ? (leadingPanel ? undefined : contentItem.right) : undefined |
48 | + right: contentItem ? (leadingPanel ? contentItem.left : undefined) : undefined |
49 | + top: contentItem ? contentItem.top : undefined |
50 | + bottom: contentItem ? contentItem.bottom : undefined |
51 | + } |
52 | + |
53 | + Rectangle { |
54 | + anchors.fill: parent |
55 | + color: leadingPanel ? UbuntuColors.red : UbuntuColors.green |
56 | + } |
57 | +} |
58 | |
59 | === modified file 'modules/Ubuntu/Components/plugin/plugin.pro' |
60 | --- modules/Ubuntu/Components/plugin/plugin.pro 2014-09-11 06:51:25 +0000 |
61 | +++ modules/Ubuntu/Components/plugin/plugin.pro 2014-09-11 06:51:26 +0000 |
62 | @@ -69,7 +69,8 @@ |
63 | uclistitem.h \ |
64 | uclistitem_p.h \ |
65 | uclistitemoptions.h \ |
66 | - uclistitemoptions_p.h |
67 | + uclistitemoptions_p.h \ |
68 | + propertychange_p.h |
69 | |
70 | SOURCES += plugin.cpp \ |
71 | uctheme.cpp \ |
72 | @@ -105,7 +106,8 @@ |
73 | unixsignalhandler_p.cpp \ |
74 | ucstyleditembase.cpp \ |
75 | uclistitem.cpp \ |
76 | - uclistitemoptions.cpp |
77 | + uclistitemoptions.cpp \ |
78 | + propertychange_p.cpp |
79 | |
80 | # adapters |
81 | SOURCES += adapters/alarmsadapter_organizer.cpp |
82 | |
83 | === added file 'modules/Ubuntu/Components/plugin/propertychange_p.cpp' |
84 | --- modules/Ubuntu/Components/plugin/propertychange_p.cpp 1970-01-01 00:00:00 +0000 |
85 | +++ modules/Ubuntu/Components/plugin/propertychange_p.cpp 2014-09-11 06:51:26 +0000 |
86 | @@ -0,0 +1,76 @@ |
87 | +/* |
88 | + * Copyright 2014 Canonical Ltd. |
89 | + * |
90 | + * This program is free software; you can redistribute it and/or modify |
91 | + * it under the terms of the GNU Lesser General Public License as published by |
92 | + * the Free Software Foundation; version 3. |
93 | + * |
94 | + * This program is distributed in the hope that it will be useful, |
95 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
96 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
97 | + * GNU Lesser General Public License for more details. |
98 | + * |
99 | + * You should have received a copy of the GNU Lesser General Public License |
100 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
101 | + */ |
102 | + |
103 | +#include "propertychange_p.h" |
104 | + |
105 | +#include <QtQml/private/qqmlabstractbinding_p.h> |
106 | +#define foreach Q_FOREACH //workaround to fix private includes |
107 | +#include <QtQml/private/qqmlbinding_p.h> // for QmlBinding |
108 | +#undef foreach |
109 | + |
110 | +/* |
111 | + * The class is used to save properties and their bindings while the property is |
112 | + * altered temporarily. |
113 | + */ |
114 | +PropertyChange::PropertyChange(QObject *item, const char *property) |
115 | + : m_backedUp(false) |
116 | + , qmlProperty(item, property, qmlContext(item)) |
117 | +{ |
118 | +} |
119 | +PropertyChange::~PropertyChange() |
120 | +{ |
121 | + restore(this); |
122 | +} |
123 | + |
124 | +/* |
125 | + * Sets a value to the property. Will back up the original values if it wasn't yet. |
126 | + * This function can be called many times, it will not destroy the backed up value/binding. |
127 | + */ |
128 | +void PropertyChange::setValue(PropertyChange *change, const QVariant &value) |
129 | +{ |
130 | + if (!change) { |
131 | + return; |
132 | + } |
133 | + if (!change->m_backedUp) { |
134 | + change->backup.first = QQmlPropertyPrivate::setBinding(change->qmlProperty, 0); |
135 | + change->backup.second = change->qmlProperty.read(); |
136 | + change->m_backedUp = true; |
137 | + } |
138 | + change->qmlProperty.write(value); |
139 | +} |
140 | + |
141 | +/* |
142 | + * Restore backed up value or binding. |
143 | + */ |
144 | +void PropertyChange::restore(PropertyChange *change) |
145 | +{ |
146 | + if (!change) { |
147 | + return; |
148 | + } |
149 | + if (change->m_backedUp) { |
150 | + // if there was a binding, restore it |
151 | + if (change->backup.first) { |
152 | + QQmlAbstractBinding *prevBinding = QQmlPropertyPrivate::setBinding(change->qmlProperty, change->backup.first); |
153 | + if (prevBinding && prevBinding != change->backup.first) { |
154 | + prevBinding->destroy(); |
155 | + } |
156 | + } else { |
157 | + // there was no binding, restore previous value |
158 | + change->qmlProperty.write(change->backup.second); |
159 | + } |
160 | + change->m_backedUp = false; |
161 | + } |
162 | +} |
163 | |
164 | === added file 'modules/Ubuntu/Components/plugin/propertychange_p.h' |
165 | --- modules/Ubuntu/Components/plugin/propertychange_p.h 1970-01-01 00:00:00 +0000 |
166 | +++ modules/Ubuntu/Components/plugin/propertychange_p.h 2014-09-11 06:51:26 +0000 |
167 | @@ -0,0 +1,39 @@ |
168 | +/* |
169 | + * Copyright 2014 Canonical Ltd. |
170 | + * |
171 | + * This program is free software; you can redistribute it and/or modify |
172 | + * it under the terms of the GNU Lesser General Public License as published by |
173 | + * the Free Software Foundation; version 3. |
174 | + * |
175 | + * This program is distributed in the hope that it will be useful, |
176 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
177 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
178 | + * GNU Lesser General Public License for more details. |
179 | + * |
180 | + * You should have received a copy of the GNU Lesser General Public License |
181 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
182 | + */ |
183 | + |
184 | +#ifndef PROPERTYCHANGE_P_H |
185 | +#define PROPERTYCHANGE_P_H |
186 | + |
187 | +#include <QtCore/QVariant> |
188 | +#include <QtCore/QObject> |
189 | +#include <QtQml/QQmlProperty> |
190 | + |
191 | +class QQmlAbstractBinding; |
192 | +class PropertyChange |
193 | +{ |
194 | +public: |
195 | + PropertyChange(QObject *item, const char *property); |
196 | + ~PropertyChange(); |
197 | + |
198 | + static void setValue(PropertyChange* change, const QVariant &value); |
199 | + static void restore(PropertyChange* change); |
200 | +private: |
201 | + bool m_backedUp; |
202 | + QQmlProperty qmlProperty; |
203 | + QPair<QQmlAbstractBinding*, QVariant> backup; |
204 | +}; |
205 | + |
206 | +#endif // PROPERTYCHANGE_P_H |
207 | |
208 | === modified file 'modules/Ubuntu/Components/plugin/uclistitem.cpp' |
209 | --- modules/Ubuntu/Components/plugin/uclistitem.cpp 2014-09-11 06:51:25 +0000 |
210 | +++ modules/Ubuntu/Components/plugin/uclistitem.cpp 2014-09-11 06:51:26 +0000 |
211 | @@ -18,11 +18,19 @@ |
212 | #include "uctheme.h" |
213 | #include "uclistitem.h" |
214 | #include "uclistitem_p.h" |
215 | +#include "uclistitemoptions.h" |
216 | +#include "uclistitemoptions_p.h" |
217 | +#include "ucubuntuanimation.h" |
218 | +#include "propertychange_p.h" |
219 | #include <QtQml/QQmlInfo> |
220 | #include <QtQuick/private/qquickitem_p.h> |
221 | #include <QtQuick/private/qquickflickable_p.h> |
222 | #include <QtQuick/private/qquickpositioners_p.h> |
223 | |
224 | +#define MIN(x, y) ((x < y) ? x : y) |
225 | +#define MAX(x, y) ((x > y) ? x : y) |
226 | +#define CLAMP(v, min, max) (min <= max) ? MAX(min, MIN(v, max)) : MAX(max, MIN(v, min)) |
227 | + |
228 | QColor getPaletteColor(const char *profile, const char *color) |
229 | { |
230 | QColor result; |
231 | @@ -234,6 +242,11 @@ |
232 | UCListItemPrivate::UCListItemPrivate() |
233 | : UCStyledItemBasePrivate() |
234 | , pressed(false) |
235 | + , moved(false) |
236 | + , ready(false) |
237 | + , xAxisMoveThresholdGU(1.5) |
238 | + , reboundAnimation(0) |
239 | + , flickableInteractive(0) |
240 | , contentItem(new UCListItemContent) |
241 | , divider(new UCListItemDivider) |
242 | , leadingOptions(0) |
243 | @@ -242,6 +255,7 @@ |
244 | } |
245 | UCListItemPrivate::~UCListItemPrivate() |
246 | { |
247 | + delete flickableInteractive; |
248 | } |
249 | |
250 | void UCListItemPrivate::init() |
251 | @@ -263,6 +277,16 @@ |
252 | // watch size change and set implicit size; |
253 | QObject::connect(&UCUnits::instance(), SIGNAL(gridUnitChanged()), q, SLOT(_q_updateSize())); |
254 | _q_updateSize(); |
255 | + |
256 | + // create rebound animation |
257 | + UCUbuntuAnimation animationCodes; |
258 | + reboundAnimation = new QQuickPropertyAnimation(q); |
259 | + reboundAnimation->setEasing(animationCodes.StandardEasing()); |
260 | + reboundAnimation->setDuration(animationCodes.SnapDuration()); |
261 | + reboundAnimation->setTargetObject(contentItem); |
262 | + reboundAnimation->setProperty("x"); |
263 | + reboundAnimation->setAlwaysRunToEnd(true); |
264 | + QObject::connect(reboundAnimation, SIGNAL(stopped()), q, SLOT(_q_completeRebinding())); |
265 | } |
266 | |
267 | void UCListItemPrivate::setFocusable() |
268 | @@ -276,8 +300,37 @@ |
269 | void UCListItemPrivate::_q_rebound() |
270 | { |
271 | setPressed(false); |
272 | - // disconnect the flickable |
273 | - listenToRebind(false); |
274 | + // initiate rebinding only if there were options tugged |
275 | + Q_Q(UCListItem); |
276 | + if (!UCListItemOptionsPrivate::isConnectedTo(leadingOptions, q) && !UCListItemOptionsPrivate::isConnectedTo(trailingOptions, q)) { |
277 | + return; |
278 | + } |
279 | + setMoved(false); |
280 | + // rebound to zero |
281 | + reboundTo(0); |
282 | +} |
283 | +void UCListItemPrivate::_q_completeRebinding() |
284 | +{ |
285 | + // restore flickable's interactive and cleanup |
286 | + PropertyChange::restore(flickableInteractive); |
287 | + // disconnect options |
288 | + grabPanel(leadingOptions, false); |
289 | + grabPanel(trailingOptions, false); |
290 | +} |
291 | + |
292 | +// the function performs a cleanup on mouse release without any rebound animation |
293 | +void UCListItemPrivate::cleanup() |
294 | +{ |
295 | + setPressed(false); |
296 | + setMoved(false); |
297 | + _q_completeRebinding(); |
298 | +} |
299 | + |
300 | +void UCListItemPrivate::reboundTo(qreal x) |
301 | +{ |
302 | + reboundAnimation->setFrom(contentItem->x()); |
303 | + reboundAnimation->setTo(x); |
304 | + reboundAnimation->restart(); |
305 | } |
306 | |
307 | // called when units size changes |
308 | @@ -294,11 +347,45 @@ |
309 | { |
310 | if (this->pressed != pressed) { |
311 | this->pressed = pressed; |
312 | + suppressClick = false; |
313 | contentItem->update(); |
314 | Q_Q(UCListItem); |
315 | Q_EMIT q->pressedChanged(); |
316 | } |
317 | } |
318 | +// toggles the moved flag and installs/removes event filter |
319 | +void UCListItemPrivate::setMoved(bool moved) |
320 | +{ |
321 | + suppressClick = moved; |
322 | + if (this->moved == moved) { |
323 | + return; |
324 | + } |
325 | + this->moved = moved; |
326 | + Q_Q(UCListItem); |
327 | + QQuickWindow *window = q->window(); |
328 | + if (moved) { |
329 | + window->installEventFilter(q); |
330 | + } else { |
331 | + window->removeEventFilter(q); |
332 | + } |
333 | +} |
334 | + |
335 | +// sets the moved flag but also grabs the panels from the leading/trailing options |
336 | +bool UCListItemPrivate::grabPanel(UCListItemOptions *optionsList, bool isMoved) |
337 | +{ |
338 | + Q_Q(UCListItem); |
339 | + if (isMoved) { |
340 | + bool grab = UCListItemOptionsPrivate::connectToListItem(optionsList, q, (optionsList == leadingOptions)); |
341 | + if (grab) { |
342 | + PropertyChange::setValue(flickableInteractive, false); |
343 | + } |
344 | + return grab; |
345 | + } else { |
346 | + UCListItemOptionsPrivate::disconnectFromListItem(optionsList); |
347 | + return false; |
348 | + } |
349 | +} |
350 | + |
351 | |
352 | // connects/disconnects from the Flickable anchestor to get notified when to do rebound |
353 | void UCListItemPrivate::listenToRebind(bool listen) |
354 | @@ -333,6 +420,18 @@ |
355 | q->update(); |
356 | } |
357 | |
358 | +void UCListItemPrivate::clampX(qreal &x, qreal dx) |
359 | +{ |
360 | + UCListItemOptionsPrivate *leading = UCListItemOptionsPrivate::get(leadingOptions); |
361 | + UCListItemOptionsPrivate *trailing = UCListItemOptionsPrivate::get(trailingOptions); |
362 | + x += dx; |
363 | + // min cannot be less than the trailing's panel width |
364 | + qreal min = (trailing && trailing->panelItem) ? -trailing->panelItem->width() : 0; |
365 | + // max cannot be bigger than 0 or the leading's width in case we have leading panel |
366 | + qreal max = (leading && leading->panelItem) ? leading->panelItem->width() : 0; |
367 | + x = CLAMP(x, min, max); |
368 | +} |
369 | + |
370 | /*! |
371 | * \qmltype ListItem |
372 | * \instantiates UCListItem |
373 | @@ -363,6 +462,52 @@ |
374 | * Each ListItem has a thin divider shown on the bottom of the component. This |
375 | * divider can be configured through the \l divider grouped property, which can |
376 | * configure its margins from the edges of the ListItem as well as its visibility. |
377 | + * |
378 | + * ListItem can handle options that can ge tugged from front ot right of the item. |
379 | + * These options are Action elements visualized in panels attached to the front |
380 | + * or to the end of the item, and are revealed by swiping the item horizontally. |
381 | + * The tug is started only after the mouse/touch move had passed a given threshold. |
382 | + * These options are configured through the \l leadingOptions as well as \l |
383 | + * trailingOptions properties. |
384 | + * \qml |
385 | + * ListItem { |
386 | + * id: listItem |
387 | + * leadingOptions: ListItemOptions { |
388 | + * Action { |
389 | + * iconName: "delete" |
390 | + * onTriggered: listItem.destroy() |
391 | + * } |
392 | + * } |
393 | + * trailingOptions: ListItemOptions { |
394 | + * Action { |
395 | + * iconName: "search" |
396 | + * onTriggered: { |
397 | + * // do some search |
398 | + * } |
399 | + * } |
400 | + * } |
401 | + * } |
402 | + * \endqml |
403 | + * \note A ListItem cannot use the same ListItemOption instance for both leading or |
404 | + * trailing options. If it is desired to have the same action present in both leading |
405 | + * and trailing options, one of the ListItemOption options list can use the other's |
406 | + * list. In the following example the list item can be deleted through both option |
407 | + * leading and trailing options: |
408 | + * \qml |
409 | + * ListItem { |
410 | + * id: listItem |
411 | + * leadingOptions: ListItemOptions { |
412 | + * Action { |
413 | + * iconName: "delete" |
414 | + * onTriggered: listItem.destroy() |
415 | + * } |
416 | + * } |
417 | + * trailingOptions: ListItemOptions { |
418 | + * options: leadingOptions.options |
419 | + * } |
420 | + * } |
421 | + * \endqml |
422 | + * \sa ListItemOptions |
423 | */ |
424 | |
425 | /*! |
426 | @@ -406,10 +551,18 @@ |
427 | } |
428 | |
429 | if (d->flickable) { |
430 | + // create the flickableInteractive property change now |
431 | + d->flickableInteractive = new PropertyChange(d->flickable, "interactive"); |
432 | // connect to flickable to get width changes |
433 | QObject::connect(d->flickable, SIGNAL(widthChanged()), this, SLOT(_q_updateSize())); |
434 | } else if (data.item) { |
435 | QObject::connect(data.item, SIGNAL(widthChanged()), this, SLOT(_q_updateSize())); |
436 | + } else { |
437 | + // in case we had a flickableInteractive property change active, destroy it |
438 | + if (d->flickableInteractive) { |
439 | + delete d->flickableInteractive; |
440 | + d->flickableInteractive = 0; |
441 | + } |
442 | } |
443 | |
444 | // update size |
445 | @@ -420,7 +573,7 @@ |
446 | void UCListItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) |
447 | { |
448 | UCStyledItemBase::geometryChanged(newGeometry, oldGeometry); |
449 | - // resize background item |
450 | + // resize contentItem item |
451 | Q_D(UCListItem); |
452 | d->resize(); |
453 | } |
454 | @@ -442,10 +595,11 @@ |
455 | UCStyledItemBase::mousePressEvent(event); |
456 | Q_D(UCListItem); |
457 | if (!d->flickable.isNull() && d->flickable->isMoving()) { |
458 | - // while moving, we cannot select any items |
459 | + // while moving, we cannot select or tug any items |
460 | return; |
461 | } |
462 | d->setPressed(true); |
463 | + d->lastPos = d->pressedPos = event->localPos(); |
464 | // connect the Flickable to know when to rebound |
465 | d->listenToRebind(true); |
466 | // accept the event so we get the rest of the events as well |
467 | @@ -454,18 +608,89 @@ |
468 | |
469 | void UCListItem::mouseReleaseEvent(QMouseEvent *event) |
470 | { |
471 | + UCStyledItemBase::mouseReleaseEvent(event); |
472 | Q_D(UCListItem); |
473 | // set released |
474 | if (d->pressed) { |
475 | - Q_EMIT clicked(); |
476 | + // disconnect the flickable |
477 | + d->listenToRebind(false); |
478 | + |
479 | + if (!d->suppressClick) { |
480 | + Q_EMIT clicked(); |
481 | + d->_q_rebound(); |
482 | + } else if (d->contentItem->x() == 0.0) { |
483 | + // if no options list is connected, release flickable blockade |
484 | + d->cleanup(); |
485 | + } |
486 | } |
487 | - // save pressed state as UCFocusScope resets it seemlessly |
488 | - bool wasPressed = d->pressed; |
489 | - UCStyledItemBase::mouseReleaseEvent(event); |
490 | - d->pressed = wasPressed; |
491 | d->setPressed(false); |
492 | } |
493 | |
494 | +void UCListItem::mouseMoveEvent(QMouseEvent *event) |
495 | +{ |
496 | + Q_D(UCListItem); |
497 | + UCStyledItemBase::mouseMoveEvent(event); |
498 | + |
499 | + // accept the tugging only if the move is within the threshold |
500 | + bool leadingAttached = UCListItemOptionsPrivate::isConnectedTo(d->leadingOptions, this); |
501 | + bool trailingAttached = UCListItemOptionsPrivate::isConnectedTo(d->trailingOptions, this); |
502 | + if (d->pressed && !(leadingAttached || trailingAttached)) { |
503 | + // check if we can initiate the drag at all |
504 | + // only X direction matters, if Y-direction leaves the threshold, but X not, the tug is not valid |
505 | + qreal threshold = UCUnits::instance().gu(d->xAxisMoveThresholdGU); |
506 | + qreal mouseX = event->localPos().x(); |
507 | + qreal pressedX = d->pressedPos.x(); |
508 | + |
509 | + if ((mouseX < (pressedX - threshold)) || (mouseX > (pressedX + threshold))) { |
510 | + // the press went out of the threshold area, enable move, if the direction allows it |
511 | + d->lastPos = event->localPos(); |
512 | + // connect both panels |
513 | + leadingAttached = d->grabPanel(d->leadingOptions, true); |
514 | + trailingAttached = d->grabPanel(d->trailingOptions, true); |
515 | + } |
516 | + } |
517 | + |
518 | + if (leadingAttached || trailingAttached) { |
519 | + qreal x = d->contentItem->x(); |
520 | + qreal dx = event->localPos().x() - d->lastPos.x(); |
521 | + d->lastPos = event->localPos(); |
522 | + |
523 | + if (dx) { |
524 | + // clamp X into allowed dragging area |
525 | + d->clampX(x, dx); |
526 | + // block flickable |
527 | + d->setMoved(true); |
528 | + d->contentItem->setX(x); |
529 | + } |
530 | + } |
531 | +} |
532 | + |
533 | +bool UCListItem::eventFilter(QObject *target, QEvent *event) |
534 | +{ |
535 | + QPointF myPos; |
536 | + // only filter press events, and rebound when pressed outside |
537 | + if (event->type() == QEvent::MouseButtonPress) { |
538 | + QMouseEvent *mouse = static_cast<QMouseEvent*>(event); |
539 | + QQuickWindow *window = qobject_cast<QQuickWindow*>(target); |
540 | + if (window) { |
541 | + myPos = window->contentItem()->mapToItem(this, mouse->localPos()); |
542 | + } |
543 | + } else if (event->type() == QEvent::TouchBegin) { |
544 | + QTouchEvent *touch = static_cast<QTouchEvent*>(event); |
545 | + QQuickWindow *window = qobject_cast<QQuickWindow*>(target); |
546 | + if (window) { |
547 | + myPos = window->contentItem()->mapToItem(this, touch->touchPoints()[0].pos()); |
548 | + } |
549 | + } |
550 | + if (!myPos.isNull() && !contains(myPos)) { |
551 | + Q_D(UCListItem); |
552 | + d->_q_rebound(); |
553 | + // only accept event, but let it be handled by the underlying or surrounding Flickables |
554 | + event->accept(); |
555 | + } |
556 | + return UCStyledItemBase::eventFilter(target, event); |
557 | +} |
558 | + |
559 | /*! |
560 | * \qmlproperty ListItemOptions ListItem::leadingOptions |
561 | * |
562 | @@ -514,7 +739,7 @@ |
563 | * \qmlproperty color ListItem::contentItem.pressedColor |
564 | * |
565 | * contentItem holds the components placed on a ListItem. \c color configures |
566 | - * the color of the normal background, and \c pressedColor configures the color |
567 | + * the color of the normal contentItem, and \c pressedColor configures the color |
568 | * when pressed. |
569 | */ |
570 | UCListItemContent* UCListItem::contentItem() const |
571 | |
572 | === modified file 'modules/Ubuntu/Components/plugin/uclistitem.h' |
573 | --- modules/Ubuntu/Components/plugin/uclistitem.h 2014-09-11 06:51:25 +0000 |
574 | +++ modules/Ubuntu/Components/plugin/uclistitem.h 2014-09-11 06:51:26 +0000 |
575 | @@ -54,6 +54,8 @@ |
576 | void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); |
577 | void mousePressEvent(QMouseEvent *event); |
578 | void mouseReleaseEvent(QMouseEvent *event); |
579 | + void mouseMoveEvent(QMouseEvent *event); |
580 | + bool eventFilter(QObject *, QEvent *); |
581 | |
582 | Q_SIGNALS: |
583 | void leadingOptionsChanged(); |
584 | @@ -71,6 +73,7 @@ |
585 | QQmlListProperty<QQuickItem> children(); |
586 | Q_PRIVATE_SLOT(d_func(), void _q_rebound()) |
587 | Q_PRIVATE_SLOT(d_func(), void _q_updateSize()) |
588 | + Q_PRIVATE_SLOT(d_func(), void _q_completeRebinding()) |
589 | }; |
590 | |
591 | #endif // UCLISTITEM_H |
592 | |
593 | === modified file 'modules/Ubuntu/Components/plugin/uclistitem_p.h' |
594 | --- modules/Ubuntu/Components/plugin/uclistitem_p.h 2014-09-11 06:51:25 +0000 |
595 | +++ modules/Ubuntu/Components/plugin/uclistitem_p.h 2014-09-11 06:51:26 +0000 |
596 | @@ -23,9 +23,11 @@ |
597 | #include <QtQuick/private/qquickrectangle_p.h> |
598 | |
599 | class QQuickFlickable; |
600 | +class QQuickPropertyAnimation; |
601 | class UCListItemContent; |
602 | class UCListItemDivider; |
603 | class UCListItemOptions; |
604 | +class PropertyChange; |
605 | class UCListItemPrivate : public UCStyledItemBasePrivate |
606 | { |
607 | Q_DECLARE_PUBLIC(UCListItem) |
608 | @@ -45,14 +47,27 @@ |
609 | |
610 | void _q_rebound(); |
611 | void _q_updateSize(); |
612 | + void _q_completeRebinding(); |
613 | + void cleanup(); |
614 | + void reboundTo(qreal x); |
615 | void setPressed(bool pressed); |
616 | + void setMoved(bool moved); |
617 | + bool grabPanel(UCListItemOptions *optionList, bool isMoved); |
618 | void listenToRebind(bool listen); |
619 | void resize(); |
620 | void update(); |
621 | + void clampX(qreal &x, qreal dx); |
622 | |
623 | bool pressed:1; |
624 | + bool moved:1; |
625 | + bool suppressClick:1; |
626 | bool ready:1; |
627 | + qreal xAxisMoveThresholdGU; |
628 | + QPointF lastPos; |
629 | + QPointF pressedPos; |
630 | QPointer<QQuickFlickable> flickable; |
631 | + QQuickPropertyAnimation *reboundAnimation; |
632 | + PropertyChange *flickableInteractive; |
633 | UCListItemContent *contentItem; |
634 | UCListItemDivider *divider; |
635 | UCListItemOptions *leadingOptions; |
636 | |
637 | === modified file 'modules/Ubuntu/Components/plugin/uclistitemoptions.cpp' |
638 | --- modules/Ubuntu/Components/plugin/uclistitemoptions.cpp 2014-09-11 06:51:25 +0000 |
639 | +++ modules/Ubuntu/Components/plugin/uclistitemoptions.cpp 2014-09-11 06:51:26 +0000 |
640 | @@ -19,11 +19,14 @@ |
641 | #include "uclistitem_p.h" |
642 | #include "quickutils.h" |
643 | #include "i18n.h" |
644 | +#include "plugin.h" |
645 | #include <QtQml/QQmlInfo> |
646 | |
647 | UCListItemOptionsPrivate::UCListItemOptionsPrivate() |
648 | : QObjectPrivate() |
649 | , optionsFailure(false) |
650 | + , connected(false) |
651 | + , leading(false) |
652 | , delegate(0) |
653 | , panelItem(0) |
654 | { |
655 | @@ -66,6 +69,76 @@ |
656 | return plist->options.clear(); |
657 | } |
658 | |
659 | +bool UCListItemOptionsPrivate::connectToListItem(UCListItemOptions *options, UCListItem *listItem, bool leading) |
660 | +{ |
661 | + UCListItemOptionsPrivate *_this = get(options); |
662 | + if (!_this || !_this->createPanelItem() || isConnectedTo(options, listItem)) { |
663 | + return isConnectedTo(options, listItem); |
664 | + } |
665 | + // check if the panel is still connected to a ListItem |
666 | + // this may happen if there is a swipe over an other item while the previous |
667 | + // one is rebounding |
668 | + if (_this->panelItem->parentItem()) { |
669 | + // set the requesting listItem as queuedItem |
670 | + _this->queuedItem = listItem; |
671 | + return false; |
672 | + } |
673 | + _this->leading = leading; |
674 | + _this->panelItem->setProperty("leadingPanel", leading); |
675 | + _this->panelItem->setParentItem(listItem); |
676 | + _this->connected = true; |
677 | + return true; |
678 | +} |
679 | + |
680 | +void UCListItemOptionsPrivate::disconnectFromListItem(UCListItemOptions *options) |
681 | +{ |
682 | + UCListItemOptionsPrivate *_this = get(options); |
683 | + if (!_this || !_this->panelItem || !_this->panelItem->parentItem()) { |
684 | + return; |
685 | + } |
686 | + _this->panelItem->setParentItem(0); |
687 | + _this->connected = false; |
688 | + _this->leading = false; |
689 | + // if there was a queuedItem, make it grab the options list |
690 | + if (_this->queuedItem) { |
691 | + UCListItemPrivate::get(_this->queuedItem.data())->grabPanel(options, true); |
692 | + // remove item from queue |
693 | + _this->queuedItem.clear(); |
694 | + } |
695 | +} |
696 | + |
697 | +bool UCListItemOptionsPrivate::isConnectedTo(UCListItemOptions *options, UCListItem *listItem) |
698 | +{ |
699 | + UCListItemOptionsPrivate *_this = get(options); |
700 | + return _this && _this->panelItem && _this->connected && (_this->panelItem->parentItem() == listItem); |
701 | +} |
702 | + |
703 | +QQuickItem *UCListItemOptionsPrivate::createPanelItem() |
704 | +{ |
705 | + if (panelItem) { |
706 | + return panelItem; |
707 | + } |
708 | + Q_Q(UCListItemOptions); |
709 | + QUrl panelDocument = UbuntuComponentsPlugin::pluginUrl(). |
710 | + resolved(QUrl::fromLocalFile("ListItemPanel.qml")); |
711 | + QQmlComponent component(qmlEngine(q), panelDocument); |
712 | + if (!component.isError()) { |
713 | + panelItem = qobject_cast<QQuickItem*>(component.beginCreate(qmlContext(q))); |
714 | + if (panelItem) { |
715 | + QQml_setParent_noEvent(panelItem, q); |
716 | + if (delegate) { |
717 | + panelItem->setProperty("delegate", QVariant::fromValue(delegate)); |
718 | + } |
719 | + panelItem->setProperty("optionList", QVariant::fromValue(options)); |
720 | + component.completeCreate(); |
721 | + Q_EMIT q->panelItemChanged(); |
722 | + } |
723 | + } else { |
724 | + qmlInfo(q) << component.errorString(); |
725 | + } |
726 | + return panelItem; |
727 | +} |
728 | + |
729 | /*! |
730 | * \qmltype ListItemOptions |
731 | * \instantiates UCListItemOptions |
732 | @@ -93,6 +166,11 @@ |
733 | * valid for the case when the option is visibl eless than 50%, in which case the |
734 | * option is hidden. Options can be triggered by tapping. |
735 | * |
736 | + * \note You can use the same ListItemOptions for leading and for trailing options |
737 | + * the same time only if the options are used in a ListView or in a list where the |
738 | + * list items are scrolled by the same Flickable. In any other circumstances use |
739 | + * separate ListItemOptions for leading and trailing options. |
740 | + * |
741 | * \section3 Notes on performance |
742 | * When used with views, or when the amount of items of same kind to be created |
743 | * is huge, it is recommended to use cached actions as well as cached ListItemOption |
744 | @@ -193,3 +271,5 @@ |
745 | Q_D(const UCListItemOptions); |
746 | return d->panelItem; |
747 | } |
748 | + |
749 | +#include "moc_uclistitemoptions.cpp" |
750 | |
751 | === modified file 'modules/Ubuntu/Components/plugin/uclistitemoptions_p.h' |
752 | --- modules/Ubuntu/Components/plugin/uclistitemoptions_p.h 2014-09-11 06:51:25 +0000 |
753 | +++ modules/Ubuntu/Components/plugin/uclistitemoptions_p.h 2014-09-11 06:51:26 +0000 |
754 | @@ -20,6 +20,7 @@ |
755 | #include "uclistitemoptions.h" |
756 | #include "QtCore/private/qobject_p.h" |
757 | |
758 | +class UCListItem; |
759 | class UCListItemOptionsPrivate : public QObjectPrivate { |
760 | Q_DECLARE_PUBLIC(UCListItemOptions) |
761 | public: |
762 | @@ -27,20 +28,28 @@ |
763 | ~UCListItemOptionsPrivate(); |
764 | static UCListItemOptionsPrivate* get(UCListItemOptions *options) |
765 | { |
766 | - Q_ASSERT(options); |
767 | - return options->d_func(); |
768 | + return options ? options->d_func() : 0; |
769 | } |
770 | |
771 | bool optionsFailure:1; |
772 | + bool connected:1; |
773 | + bool leading:1; |
774 | QQmlComponent *delegate; |
775 | QQuickItem *panelItem; |
776 | QList<QObject*> options; |
777 | + QPointer<UCListItem> queuedItem; |
778 | |
779 | // options list property functions |
780 | static void funcAppend(QQmlListProperty<QObject>*, QObject*); |
781 | static int funcCount(QQmlListProperty<QObject>*); |
782 | static QObject *funcAt(QQmlListProperty<QObject>*, int); |
783 | static void funcClear(QQmlListProperty<QObject>*); |
784 | + |
785 | + static bool connectToListItem(UCListItemOptions *options, UCListItem *listItem, bool leading); |
786 | + static void disconnectFromListItem(UCListItemOptions *options); |
787 | + static bool isConnectedTo(UCListItemOptions *options, UCListItem *listItem); |
788 | + |
789 | + QQuickItem *createPanelItem(); |
790 | }; |
791 | |
792 | #endif // UCLISTITEMOPTIONS_P_H |
793 | |
794 | === modified file 'modules/Ubuntu/Components/qmldir' |
795 | --- modules/Ubuntu/Components/qmldir 2014-09-03 08:17:24 +0000 |
796 | +++ modules/Ubuntu/Components/qmldir 2014-09-11 06:51:26 +0000 |
797 | @@ -106,3 +106,4 @@ |
798 | Icon 1.1 Icon11.qml |
799 | StyledItem 1.1 StyledItem.qml |
800 | singleton UbuntuColors 1.1 11/UbuntuColors.qml |
801 | +internal ListItemPanel ListItemPanel.qml |
802 | |
803 | === modified file 'tests/resources/listitems/ListItemTest.qml' |
804 | --- tests/resources/listitems/ListItemTest.qml 2014-09-11 06:51:25 +0000 |
805 | +++ tests/resources/listitems/ListItemTest.qml 2014-09-11 06:51:26 +0000 |
806 | @@ -31,6 +31,7 @@ |
807 | |
808 | ListItemOptions { |
809 | id: leading |
810 | + objectName: "StockLeading" |
811 | Action { |
812 | } |
813 | Action { |
814 | @@ -47,6 +48,7 @@ |
815 | |
816 | ListItem { |
817 | id: testItem |
818 | + objectName: "single" |
819 | contentItem.color: "lime" |
820 | onClicked: { |
821 | print("click") |
822 | @@ -57,6 +59,7 @@ |
823 | text: units.gridUnit + "PX/unit" |
824 | } |
825 | leadingOptions: ListItemOptions { |
826 | + objectName: "InlineLeading" |
827 | options: [stock] |
828 | } |
829 | trailingOptions: leading |
830 | @@ -70,6 +73,7 @@ |
831 | model: 100 |
832 | pressDelay: 0 |
833 | delegate: ListItem { |
834 | + objectName: "ListItem" + index |
835 | id: listItem |
836 | onClicked: print(" clicked") |
837 | leadingOptions: leading |
838 | @@ -98,10 +102,15 @@ |
839 | Repeater { |
840 | model: 100 |
841 | ListItem { |
842 | + objectName: "InFlickable"+index |
843 | contentItem { |
844 | color: "red" |
845 | pressedColor: "lime" |
846 | } |
847 | + trailingOptions: ListItemOptions { |
848 | + options: leading.options |
849 | + } |
850 | + |
851 | Label { |
852 | text: modelData + " Flickable item" |
853 | } |
854 | |
855 | === added file 'tests/unit/tst_performance/ListItemWithInlineOptionsList.qml' |
856 | --- tests/unit/tst_performance/ListItemWithInlineOptionsList.qml 1970-01-01 00:00:00 +0000 |
857 | +++ tests/unit/tst_performance/ListItemWithInlineOptionsList.qml 2014-09-11 06:51:26 +0000 |
858 | @@ -0,0 +1,39 @@ |
859 | +/* |
860 | + * Copyright 2014 Canonical Ltd. |
861 | + * |
862 | + * This program is free software; you can redistribute it and/or modify |
863 | + * it under the terms of the GNU Lesser General Public License as published by |
864 | + * the Free Software Foundation; version 3. |
865 | + * |
866 | + * This program is distributed in the hope that it will be useful, |
867 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
868 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
869 | + * GNU Lesser General Public License for more details. |
870 | + * |
871 | + * You should have received a copy of the GNU Lesser General Public License |
872 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
873 | + */ |
874 | + |
875 | +import QtQuick 2.0 |
876 | +import Ubuntu.Components 1.1 |
877 | + |
878 | +Column { |
879 | + width: 800 |
880 | + height: 600 |
881 | + |
882 | + Repeater { |
883 | + model: 5000 |
884 | + ListItem { |
885 | + trailingOptions: ListItemOptions { |
886 | + Action {} |
887 | + Action {} |
888 | + Action {} |
889 | + } |
890 | + leadingOptions: ListItemOptions { |
891 | + Action {} |
892 | + Action {} |
893 | + Action {} |
894 | + } |
895 | + } |
896 | + } |
897 | +} |
898 | |
899 | === modified file 'tests/unit/tst_performance/ListItemWithOptionsList.qml' |
900 | --- tests/unit/tst_performance/ListItemWithOptionsList.qml 2014-09-11 06:51:25 +0000 |
901 | +++ tests/unit/tst_performance/ListItemWithOptionsList.qml 2014-09-11 06:51:26 +0000 |
902 | @@ -22,13 +22,19 @@ |
903 | height: 600 |
904 | ListItemOptions { |
905 | id: options1 |
906 | + Action {} |
907 | + Action {} |
908 | + Action {} |
909 | } |
910 | ListItemOptions { |
911 | id: options2 |
912 | + Action {} |
913 | + Action {} |
914 | + Action {} |
915 | } |
916 | |
917 | Repeater { |
918 | - model: 10000 |
919 | + model: 5000 |
920 | ListItem { |
921 | trailingOptions: options1 |
922 | leadingOptions: options2 |
923 | |
924 | === modified file 'tests/unit/tst_performance/tst_performance.cpp' |
925 | --- tests/unit/tst_performance/tst_performance.cpp 2014-09-11 06:51:25 +0000 |
926 | +++ tests/unit/tst_performance/tst_performance.cpp 2014-09-11 06:51:26 +0000 |
927 | @@ -79,6 +79,7 @@ |
928 | QTest::newRow("list with QtQuick Item") << "ItemList.qml" << QUrl(); |
929 | QTest::newRow("list with new ListItem") << "ListItemList.qml" << QUrl(); |
930 | QTest::newRow("list with new ListItem with options") << "ListItemWithOptionsList.qml" << QUrl(); |
931 | + QTest::newRow("list with new ListItem with inline options") << "ListItemWithInlineOptionsList.qml" << QUrl(); |
932 | QTest::newRow("list with ListItems.Empty (equivalent to the new ListItem") << "ListItemsEmptyList.qml" << QUrl(); |
933 | QTest::newRow("list with ListItems.Base (one icon, one label and one chevron)") << "ListItemsBaseList.qml" << QUrl(); |
934 | } |
935 | |
936 | === modified file 'tests/unit/tst_performance/tst_performance.pro' |
937 | --- tests/unit/tst_performance/tst_performance.pro 2014-09-11 06:51:25 +0000 |
938 | +++ tests/unit/tst_performance/tst_performance.pro 2014-09-11 06:51:26 +0000 |
939 | @@ -22,6 +22,7 @@ |
940 | TextWithImportPopups.qml \ |
941 | ItemList.qml \ |
942 | ListItemList.qml \ |
943 | - ListItemWithOptionsList.qml |
944 | + ListItemWithOptionsList.qml \ |
945 | + ListItemWithInlineOptionsList.qml \ |
946 | ListItemsEmptyList.qml \ |
947 | ListItemsBaseList.qml |
948 | |
949 | === modified file 'tests/unit_x11/tst_components/tst_listitem.qml' |
950 | --- tests/unit_x11/tst_components/tst_listitem.qml 2014-09-11 06:51:25 +0000 |
951 | +++ tests/unit_x11/tst_components/tst_listitem.qml 2014-09-11 06:51:26 +0000 |
952 | @@ -68,6 +68,8 @@ |
953 | id: testItem |
954 | width: parent.width |
955 | contentItem.color: "blue" |
956 | + leadingOptions: leading |
957 | + trailingOptions: leading |
958 | Item { |
959 | id: bodyItem |
960 | anchors.fill: parent |
961 | @@ -82,6 +84,8 @@ |
962 | delegate: ListItem { |
963 | objectName: "listItem" + index |
964 | width: parent.width |
965 | + leadingOptions: leading |
966 | + trailingOptions: trailing |
967 | } |
968 | } |
969 | } |
970 | @@ -114,6 +118,7 @@ |
971 | function cleanup() { |
972 | pressedSpy.clear(); |
973 | clickSpy.clear(); |
974 | + listView.interactive = true; |
975 | // make sure all events are processed |
976 | wait(200); |
977 | } |
978 | @@ -186,6 +191,7 @@ |
979 | TestExtras.touchMove(0, listItem, Qt.point(listItem.width / 2, dy)); |
980 | } |
981 | compare(listItem.pressed, false, "Item is pressed still!"); |
982 | + // cleanup, wait few milliseconds to avoid dbl-click collision |
983 | TestExtras.touchRelease(0, listItem, Qt.point(listItem.width / 2, dy)); |
984 | } |
985 | |
986 | @@ -213,5 +219,95 @@ |
987 | } |
988 | compare(data.object.options.length, data.expected, data.tag + ": expected options differ."); |
989 | } |
990 | + |
991 | + function test_touch_tug_options_data() { |
992 | + var item = findChild(listView, "listItem0"); |
993 | + return [ |
994 | + {tag: "Trailing, mouse", item: item, pos: centerOf(item), dx: -units.gu(20), positiveDirection: false, mouse: true}, |
995 | + {tag: "Leading, mouse", item: item, pos: centerOf(item), dx: units.gu(20), positiveDirection: true, mouse: true}, |
996 | + {tag: "Trailing, touch", item: item, pos: centerOf(item), dx: -units.gu(20), positiveDirection: false, mouse: false}, |
997 | + {tag: "Leading, touch", item: item, pos: centerOf(item), dx: units.gu(20), positiveDirection: true, mouse: false}, |
998 | + ]; |
999 | + } |
1000 | + function test_touch_tug_options(data) { |
1001 | + listView.positionViewAtBeginning(); |
1002 | + if (data.mouse) { |
1003 | + flick(data.item, data.pos.x, data.pos.y, data.dx, 0); |
1004 | + } else { |
1005 | + TestExtras.touchDrag(0, data.item, data.pos, Qt.point(data.dx, 0)); |
1006 | + } |
1007 | + waitForRendering(data.item, 400); |
1008 | + if (data.positiveDirection) { |
1009 | + verify(data.item.contentItem.x > 0, data.tag + " options did not show up"); |
1010 | + } else { |
1011 | + verify(data.item.contentItem.x < 0, data.tag + " options did not show up"); |
1012 | + } |
1013 | + |
1014 | + // dismiss |
1015 | + if (data.mouse) { |
1016 | + mouseClick(main, 1, 1); |
1017 | + } else { |
1018 | + TestExtras.touchClick(0, main, Qt.point(1, 1)); |
1019 | + } |
1020 | + waitForRendering(data.item, 400); |
1021 | + } |
1022 | + |
1023 | + function test_rebound_when_pressed_outside_or_clicked_data() { |
1024 | + var item0 = findChild(listView, "listItem0"); |
1025 | + var item1 = findChild(listView, "listItem1"); |
1026 | + return [ |
1027 | + {tag: "Click on an other Item", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item1, mouse: true}, |
1028 | + {tag: "Click on the same Item", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item0.contentItem, mouse: true}, |
1029 | + {tag: "Tap on an other Item", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item1, mouse: false}, |
1030 | + {tag: "Tap on the same Item", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item0.contentItem, mouse: false}, |
1031 | + ]; |
1032 | + } |
1033 | + function test_rebound_when_pressed_outside_or_clicked(data) { |
1034 | + listView.positionViewAtBeginning(); |
1035 | + if (data.mouse) { |
1036 | + flick(data.item, data.pos.x, data.pos.y, data.dx, 0); |
1037 | + } else { |
1038 | + TestExtras.touchDrag(0, data.item, data.pos, Qt.point(data.dx, 0)); |
1039 | + } |
1040 | + waitForRendering(data.item, 400); |
1041 | + verify(data.item.contentItem.x != 0, "The component wasn't tugged!"); |
1042 | + // dismiss |
1043 | + if (data.mouse) { |
1044 | + mouseClick(data.clickOn, centerOf(data.clickOn).x, centerOf(data.clickOn).y); |
1045 | + } else { |
1046 | + TestExtras.touchClick(0, data.clickOn, centerOf(data.clickOn)); |
1047 | + } |
1048 | + waitForRendering(data.item, 400); |
1049 | + tryCompareFunction(function(){ return data.item.contentItem.x; }, 0, 1000); |
1050 | + } |
1051 | + |
1052 | + function test_listview_not_interactive_while_tugged_data() { |
1053 | + var item0 = findChild(listView, "listItem0"); |
1054 | + var item1 = findChild(listView, "listItem1"); |
1055 | + return [ |
1056 | + {tag: "Trailing", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item1, mouse: true}, |
1057 | + {tag: "Leading", item: item0, pos: centerOf(item0), dx: units.gu(20), clickOn: item0.contentItem, mouse: true}, |
1058 | + {tag: "Trailing", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item1, mouse: false}, |
1059 | + {tag: "Leading", item: item0, pos: centerOf(item0), dx: units.gu(20), clickOn: item0.contentItem, mouse: false}, |
1060 | + ]; |
1061 | + } |
1062 | + function test_listview_not_interactive_while_tugged(data) { |
1063 | + listView.positionViewAtBeginning(); |
1064 | + if (data.mouse) { |
1065 | + flick(data.item, data.pos.x, data.pos.y, data.dx, 0); |
1066 | + } else { |
1067 | + TestExtras.touchDrag(0, data.item, data.pos, Qt.point(data.dx, 0)); |
1068 | + } |
1069 | + waitForRendering(data.item, 800); |
1070 | + compare(listView.interactive, false, "The ListView is still interactive!"); |
1071 | + // dismiss |
1072 | + if (data.mouse) { |
1073 | + mouseClick(data.clickOn, centerOf(data.clickOn).x, centerOf(data.clickOn).y); |
1074 | + } else { |
1075 | + TestExtras.touchClick(0, data.clickOn, centerOf(data.clickOn)); |
1076 | + } |
1077 | + waitForRendering(data.item, 400); |
1078 | + tryCompareFunction(function(){ return listView.interactive; }, true, 1000); |
1079 | + } |
1080 | } |
1081 | } |