Merge lp:~zsombi/ubuntu-ui-toolkit/listitem-master into lp:ubuntu-ui-toolkit/staging

Proposed by Zsombor Egri
Status: Merged
Approved by: Tim Peeters
Approved revision: 1281
Merged at revision: 1359
Proposed branch: lp:~zsombi/ubuntu-ui-toolkit/listitem-master
Merge into: lp:ubuntu-ui-toolkit/staging
Diff against target: 4615 lines (+4323/-22)
35 files modified
components.api (+57/-0)
documentation/overview.qdoc (+5/-0)
modules/Ubuntu/Components/Themes/Ambiance/ListItemPanel.qml (+180/-0)
modules/Ubuntu/Components/Themes/Ambiance/ListItemStyle.qml (+36/-0)
modules/Ubuntu/Components/Themes/Ambiance/qmldir (+4/-0)
modules/Ubuntu/Components/plugin/plugin.cpp (+13/-0)
modules/Ubuntu/Components/plugin/plugin.pro (+14/-2)
modules/Ubuntu/Components/plugin/propertychange_p.cpp (+76/-0)
modules/Ubuntu/Components/plugin/propertychange_p.h (+39/-0)
modules/Ubuntu/Components/plugin/quickutils.cpp (+23/-0)
modules/Ubuntu/Components/plugin/quickutils.h (+1/-0)
modules/Ubuntu/Components/plugin/ucaction.h (+2/-0)
modules/Ubuntu/Components/plugin/uclistitem.cpp (+1260/-0)
modules/Ubuntu/Components/plugin/uclistitem.h (+126/-0)
modules/Ubuntu/Components/plugin/uclistitem_p.h (+202/-0)
modules/Ubuntu/Components/plugin/uclistitemactions.cpp (+541/-0)
modules/Ubuntu/Components/plugin/uclistitemactions.h (+113/-0)
modules/Ubuntu/Components/plugin/uclistitemactions_p.h (+61/-0)
modules/Ubuntu/Components/plugin/uclistitemactionsattached.cpp (+247/-0)
modules/Ubuntu/Components/plugin/uclistitemattached.cpp (+207/-0)
modules/Ubuntu/Components/plugin/uclistitemstyle.cpp (+70/-0)
modules/Ubuntu/Components/plugin/uclistitemstyle.h (+53/-0)
modules/Ubuntu/Components/plugin/ucstyleditembase.cpp (+2/-4)
modules/Ubuntu/Test/UbuntuTestCase.qml (+3/-6)
tests/qmlapicheck.sh (+1/-1)
tests/resources/listitems/ListItemTest.qml (+201/-0)
tests/unit/tst_performance/ItemList.qml (+30/-0)
tests/unit/tst_performance/ListItemList.qml (+30/-0)
tests/unit/tst_performance/ListItemWithActionsList.qml (+43/-0)
tests/unit/tst_performance/ListItemWithInlineActionsList.qml (+41/-0)
tests/unit/tst_performance/ListItemsBaseList.qml (+29/-0)
tests/unit/tst_performance/ListItemsEmptyList.qml (+29/-0)
tests/unit/tst_performance/tst_performance.cpp (+7/-8)
tests/unit/tst_performance/tst_performance.pro (+7/-1)
tests/unit_x11/tst_components/tst_listitem.qml (+570/-0)
To merge this branch: bzr merge lp:~zsombi/ubuntu-ui-toolkit/listitem-master
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Tim Peeters Approve
Review via email: mp+243408@code.launchpad.net

Commit message

New ListItem component base. Support for leading/trailing actions. Exported in Ubuntu.Components 1.2 UNSTABLE release.

To post a comment you must log in.
Revision history for this message
Tim Peeters (tpeeters) wrote :

67- branch (already reviewed) + staging sync.

Let's get it landed :)

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

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

Subscribers

People subscribed via source and target branches