Merge lp:~zsombi/ubuntu-ui-toolkit/35-options-panel-swipe into lp:ubuntu-ui-toolkit/staging

Proposed by Zsombor Egri
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
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.

To post a comment you must log in.
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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 }

Subscribers

People subscribed via source and target branches