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
=== modified file 'components.api'
--- components.api 2014-12-03 05:54:13 +0000
+++ components.api 2014-12-03 06:10:30 +0000
@@ -864,6 +864,63 @@
864 name: "UCInverseMouse"864 name: "UCInverseMouse"
865 prototype: "UCMouse"865 prototype: "UCMouse"
866 exports: ["InverseMouse 0.1", "InverseMouse 1.0"]866 exports: ["InverseMouse 0.1", "InverseMouse 1.0"]
867 name: "UCListItem"
868 prototype: "UCStyledItemBase"
869 exports: ["ListItem 1.2"]
870 Property { name: "contentItem"; type: "QQuickItem"; isReadonly: true; isPointer: true }
871 Property { name: "divider"; type: "UCListItemDivider"; isReadonly: true; isPointer: true }
872 Property { name: "leadingActions"; type: "UCListItemActions"; isPointer: true }
873 Property { name: "trailingActions"; type: "UCListItemActions"; isPointer: true }
874 Property { name: "pressed"; type: "bool"; isReadonly: true }
875 Property { name: "swipeOvershoot"; type: "double" }
876 Property { name: "contentMoving"; type: "bool"; isReadonly: true }
877 Property { name: "color"; type: "QColor" }
878 Property { name: "highlightColor"; type: "QColor" }
879 Property { name: "listItemData"; type: "QObject"; isList: true; isReadonly: true }
880 Property { name: "listItemChildren"; type: "QQuickItem"; isList: true; isReadonly: true }
881 Property { name: "style"; type: "QQmlComponent"; isPointer: true }
882 Property { name: "__styleInstance"; type: "QQuickItem"; isReadonly: true; isPointer: true }
883 Signal { name: "clicked" }
884 Signal { name: "contentMovementStarted" }
885 Signal { name: "contentMovementEnded" }
886 name: "UCListItemActions"
887 prototype: "QObject"
888 exports: ["ListItemActions 1.2"]
889 name: "Status"
890 Property { name: "delegate"; type: "QQmlComponent"; isPointer: true }
891 Property { name: "actions"; type: "UCAction"; isList: true; isReadonly: true }
892 Property { name: "data"; type: "QObject"; isList: true; isReadonly: true }
893 Signal {
894 name: "statusChanged"
895 Parameter { name: "status"; type: "Status" }
896 name: "UCListItemActionsAttached"
897 prototype: "QObject"
898 Property { name: "container"; type: "UCListItemActions"; isReadonly: true; isPointer: true }
899 Property { name: "visibleActions"; type: "UCAction"; isList: true; isReadonly: true }
900 Property { name: "listItem"; type: "UCListItem"; isReadonly: true; isPointer: true }
901 Property { name: "listItemIndex"; type: "int"; isReadonly: true }
902 Property { name: "offset"; type: "double"; isReadonly: true }
903 Property { name: "status"; type: "UCListItemActions::Status"; isReadonly: true }
904 Property { name: "swiping"; type: "bool"; isReadonly: true }
905 Method {
906 name: "snapToPosition"
907 Parameter { name: "position"; type: "double" }
908 Component { name: "UCListItemAttached"; prototype: "QObject" }
909 name: "UCListItemDivider"
910 prototype: "QObject"
911 Property { name: "visible"; type: "bool" }
912 Property { name: "leftMargin"; type: "double" }
913 Property { name: "rightMargin"; type: "double" }
914 Property { name: "colorFrom"; type: "QColor" }
915 Property { name: "colorTo"; type: "QColor" }
916 name: "UCListItemStyle"
917 prototype: "QQuickItem"
918 exports: ["Ubuntu.Components.Styles/ListItemStyle 1.2"]
919 Property { name: "actionsDelegate"; type: "QQmlComponent"; isPointer: true }
920 Property { name: "selectionDelegate"; type: "QQmlComponent"; isPointer: true }
921 Property { name: "dragHandlerDelegate"; type: "QQmlComponent"; isPointer: true }
922 Property { name: "snapAnimation"; type: "QQuickPropertyAnimation"; isPointer: true }
923 Property { name: "swipeOvershoot"; type: "double" }
867 name: "UCMouse"924 name: "UCMouse"
868 prototype: "QObject"925 prototype: "QObject"
869 exports: ["Mouse 0.1", "Mouse 1.0"]926 exports: ["Mouse 0.1", "Mouse 1.0"]
870927
=== modified file 'documentation/overview.qdoc'
--- documentation/overview.qdoc 2014-06-18 06:36:00 +0000
+++ documentation/overview.qdoc 2014-12-03 06:10:30 +0000
@@ -113,4 +113,9 @@
113 import Ubuntu Test 1.0113 import Ubuntu Test 1.0
114 \endcode114 \endcode
115 \annotatedlist ubuntu-test115 \annotatedlist ubuntu-test
116
117 \part Unstable QML Types
118 The following section lists components which will be part of future releases.
119 Their interface is unstable and thus should not be used in production applications.
120 \annotatedlist unstable-ubuntu-listitems
116 */121 */
117122
=== added file 'modules/Ubuntu/Components/Themes/Ambiance/ListItemPanel.qml'
--- modules/Ubuntu/Components/Themes/Ambiance/ListItemPanel.qml 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Themes/Ambiance/ListItemPanel.qml 2014-12-03 06:10:30 +0000
@@ -0,0 +1,180 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.2
18import Ubuntu.Components 1.2
19
20/*
21 This component is the holder of the ListItem actions.
22 */
23Item {
24
25 // styling properties
26 /*
27 Color of the background.
28 */
29 // FIXME: use Palette colors instead when available
30 property color backgroundColor: (leading ? UbuntuColors.red : "white")
31
32 /*
33 Color used in coloring the icons.
34 */
35 // FIXME: use Palette colors instead when available
36 property color foregroundColor: leading ? "white" : UbuntuColors.darkGrey
37
38 /*
39 Specifies the width of the component visualizing the action.
40 */
41 property real visualizedActionWidth: units.gu(2.5)
42
43 // panel implementation
44 id: panel
45 width: Math.max(
46 actionsRow.childrenRect.width,
47 ListItemActions.visibleActions.length * MathUtils.clamp(visualizedActionWidth, height, actionsRow.maxItemWidth))
48
49 // used for module/autopilot testing
50 objectName: "ListItemPanel" + (leading ? "Leading" : "Trailing")
51
52 /*
53 Property holding the ListItem's contentItem instance
54 */
55 readonly property Item contentItem: parent ? parent.contentItem : null
56
57 /*
58 Specifies whether the panel is used to visualize leading or trailing actions.
59 */
60 readonly property bool leading: panel.ListItemActions.status == panel.ListItemActions.Leading
61
62 anchors {
63 left: contentItem ? (leading ? undefined : contentItem.right) : undefined
64 right: contentItem ? (leading ? contentItem.left : undefined) : undefined
65 top: contentItem ? contentItem.top : undefined
66 bottom: contentItem ? contentItem.bottom : undefined
67 }
68
69 Rectangle {
70 objectName: "panel_background"
71 anchors {
72 fill: parent
73 // add 4 times the overshoot margins to cover the background when tugged
74 leftMargin: (leading && panel.ListItemActions.listItem) ? -units.gu(4 * panel.ListItemActions.listItem.swipeOvershoot) : 0
75 rightMargin: (!leading && panel.ListItemActions.listItem) ? -units.gu(4 * panel.ListItemActions.listItem.swipeOvershoot) : 0
76 }
77 color: panel.backgroundColor
78 }
79
80 // handle action triggering
81 ListItemActions.onStatusChanged: {
82 if (ListItemActions.status === ListItemActions.Disconnected && actionsRow.selectedAction) {
83 actionsRow.selectedAction.trigger(actionsRow.listItemIndex >= 0 ? actionsRow.listItemIndex : null);
84 actionsRow.selectedAction = null;
85 }
86 }
87
88 // track drag direction, so we know in which direction we should snap
89 property real prevX: 0.0
90 property bool leftToRight: false
91 onXChanged: {
92 leftToRight = prevX < x;
93 prevX = x;
94 }
95 // default snapping!
96 ListItemActions.onSwipingChanged: {
97 if (ListItemActions.swiping) {
98 // the dragging got started, set prevX
99 prevX = panel.x;
100 return;
101 }
102 if (!visible) {
103 return;
104 }
105 // snap in if the offset is bigger than the overshoot and the direction of the drag is to reveal the panel
106 var snapPos = (ListItemActions.offset > ListItemActions.listItem.swipeOvershoot &&
107 (leftToRight && leading || !leftToRight && !leading)) ? panel.width : 0.0;
108 ListItemActions.snapToPosition(snapPos);
109 }
110
111 Row {
112 id: actionsRow
113 anchors {
114 left: parent.left
115 top: parent.top
116 bottom: parent.bottom
117 leftMargin: spacing
118 }
119
120 property real maxItemWidth: panel.parent ? (panel.parent.width / panel.ListItemActions.visibleActions.length) : 0
121
122 property Action selectedAction
123 property int listItemIndex
124
125 Repeater {
126 model: panel.ListItemActions.visibleActions
127 AbstractButton {
128 id: actionButton
129 action: modelData
130 enabled: action.enabled
131 opacity: action.enabled ? 1.0 : 0.5
132 width: MathUtils.clamp(delegateLoader.item ? delegateLoader.item.width : 0, height, actionsRow.maxItemWidth)
133 anchors {
134 top: parent.top
135 bottom: parent.bottom
136 }
137 function trigger() {
138 actionsRow.selectedAction = modelData;
139 actionsRow.listItemIndex = panel.ListItemActions.listItemIndex;
140 panel.ListItemActions.snapToPosition(0.0);
141 }
142
143 Rectangle {
144 anchors.fill: parent
145 color: Theme.palette.selected.background
146 visible: pressed
147 }
148
149 Loader {
150 id: delegateLoader
151 height: parent.height
152 sourceComponent: panel.ListItemActions.container.delegate ? panel.ListItemActions.container.delegate : defaultDelegate
153 property Action action: modelData
154 property int index: index
155 property bool pressed: actionButton.pressed
156 onItemChanged: {
157 // use action's objectName to identify the visualized action
158 if (item && item.objectName === "") {
159 item.objectName = modelData.objectName;
160 }
161 }
162 }
163 }
164 }
165 }
166
167 Component {
168 id: defaultDelegate
169 Item {
170 width: height
171 Icon {
172 width: panel.visualizedActionWidth
173 height: width
174 name: action.iconName
175 color: panel.foregroundColor
176 anchors.centerIn: parent
177 }
178 }
179 }
180}
0181
=== added file 'modules/Ubuntu/Components/Themes/Ambiance/ListItemStyle.qml'
--- modules/Ubuntu/Components/Themes/Ambiance/ListItemStyle.qml 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Themes/Ambiance/ListItemStyle.qml 2014-12-03 06:10:30 +0000
@@ -0,0 +1,36 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.3
18import Ubuntu.Components.Styles 1.2 as Styles
19import Ubuntu.Components 1.2
20
21Styles.ListItemStyle {
22
23 swipeOvershoot: units.gu(2)
24 actionsDelegate: ListItemPanel{}
25
26 snapAnimation: PropertyAnimation {
27 property: "x"
28 easing {
29 type: Easing.OutElastic
30 period: 0.5
31 }
32 duration: UbuntuAnimation.BriskDuration
33 alwaysRunToEnd: true
34 }
35
36}
037
=== modified file 'modules/Ubuntu/Components/Themes/Ambiance/qmldir'
--- modules/Ubuntu/Components/Themes/Ambiance/qmldir 2014-11-25 15:20:11 +0000
+++ modules/Ubuntu/Components/Themes/Ambiance/qmldir 2014-12-03 06:10:30 +0000
@@ -54,3 +54,7 @@
54internal TextSelectionEndCursorStyle TextSelectionEndCursorStyle.qml54internal TextSelectionEndCursorStyle TextSelectionEndCursorStyle.qml
55internal OverflowPanel OverflowPanel.qml55internal OverflowPanel OverflowPanel.qml
56internal HeadDividerStyle HeadDividerStyle.qml56internal HeadDividerStyle HeadDividerStyle.qml
57
58#version 1.2
59ListItemPanel 1.2 ListItemPanel.qml
60ListItemStyle 1.2 ListItemStyle.qml
5761
=== modified file 'modules/Ubuntu/Components/plugin/plugin.cpp'
--- modules/Ubuntu/Components/plugin/plugin.cpp 2014-12-03 05:54:13 +0000
+++ modules/Ubuntu/Components/plugin/plugin.cpp 2014-12-03 06:10:30 +0000
@@ -52,6 +52,10 @@
52#include "ucaction.h"52#include "ucaction.h"
53#include "ucactioncontext.h"53#include "ucactioncontext.h"
54#include "ucactionmanager.h"54#include "ucactionmanager.h"
55#include "uclistitem.h"
56#include "uclistitem_p.h"
57#include "uclistitemactions.h"
58#include "uclistitemstyle.h"
5559
56#include <sys/types.h>60#include <sys/types.h>
57#include <unistd.h>61#include <unistd.h>
@@ -162,6 +166,11 @@
162 qmlRegisterType<QSortFilterProxyModelQML>(uri, 1, 1, "SortFilterModel");166 qmlRegisterType<QSortFilterProxyModelQML>(uri, 1, 1, "SortFilterModel");
163 qmlRegisterUncreatableType<FilterBehavior>(uri, 1, 1, "FilterBehavior", "Not instantiable");167 qmlRegisterUncreatableType<FilterBehavior>(uri, 1, 1, "FilterBehavior", "Not instantiable");
164 qmlRegisterUncreatableType<SortBehavior>(uri, 1, 1, "SortBehavior", "Not instantiable");168 qmlRegisterUncreatableType<SortBehavior>(uri, 1, 1, "SortBehavior", "Not instantiable");
169
170 // ListItem and related types, released to 1.2
171 qmlRegisterType<UCListItem, 2>(uri, 1, 2, "ListItem");
172 qmlRegisterType<UCListItemDivider>();
173 qmlRegisterType<UCListItemActions, 2>(uri, 1, 2, "ListItemActions");
165}174}
166175
167void UbuntuComponentsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)176void UbuntuComponentsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
@@ -169,6 +178,10 @@
169 // initialize baseURL178 // initialize baseURL
170 m_baseUrl = QUrl(baseUrl().toString() + '/');179 m_baseUrl = QUrl(baseUrl().toString() + '/');
171180
181 // register internal styles
182 const char *styleUri = "Ubuntu.Components.Styles";
183 qmlRegisterType<UCListItemStyle, 2>(styleUri, 1, 2, "ListItemStyle");
184
172 QQmlExtensionPlugin::initializeEngine(engine, uri);185 QQmlExtensionPlugin::initializeEngine(engine, uri);
173 QQmlContext* context = engine->rootContext();186 QQmlContext* context = engine->rootContext();
174187
175188
=== modified file 'modules/Ubuntu/Components/plugin/plugin.pro'
--- modules/Ubuntu/Components/plugin/plugin.pro 2014-12-03 05:54:13 +0000
+++ modules/Ubuntu/Components/plugin/plugin.pro 2014-12-03 06:10:30 +0000
@@ -66,7 +66,13 @@
66 ucaction.h \66 ucaction.h \
67 ucactioncontext.h \67 ucactioncontext.h \
68 ucactionmanager.h \68 ucactionmanager.h \
69 adapters/actionsproxy_p.h69 adapters/actionsproxy_p.h \
70 uclistitem.h \
71 uclistitem_p.h \
72 uclistitemactions.h \
73 uclistitemactions_p.h \
74 propertychange_p.h \
75 uclistitemstyle.h
7076
71SOURCES += plugin.cpp \77SOURCES += plugin.cpp \
72 uctheme.cpp \78 uctheme.cpp \
@@ -102,7 +108,13 @@
102 ucaction.cpp \108 ucaction.cpp \
103 ucactioncontext.cpp \109 ucactioncontext.cpp \
104 ucactionmanager.cpp \110 ucactionmanager.cpp \
105 adapters/actionsproxy_p.cpp111 adapters/actionsproxy_p.cpp \
112 uclistitem.cpp \
113 uclistitemactions.cpp \
114 uclistitemactionsattached.cpp \
115 uclistitemattached.cpp \
116 propertychange_p.cpp \
117 uclistitemstyle.cpp
106118
107# adapters119# adapters
108SOURCES += adapters/alarmsadapter_organizer.cpp120SOURCES += adapters/alarmsadapter_organizer.cpp
109121
=== added file 'modules/Ubuntu/Components/plugin/propertychange_p.cpp'
--- modules/Ubuntu/Components/plugin/propertychange_p.cpp 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/plugin/propertychange_p.cpp 2014-12-03 06:10:30 +0000
@@ -0,0 +1,76 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "propertychange_p.h"
18
19#include <QtQml/private/qqmlabstractbinding_p.h>
20#define foreach Q_FOREACH //workaround to fix private includes
21#include <QtQml/private/qqmlbinding_p.h> // for QmlBinding
22#undef foreach
23
24/*
25 * The class is used to save properties and their bindings while the property is
26 * altered temporarily.
27 */
28PropertyChange::PropertyChange(QObject *item, const char *property)
29 : m_backedUp(false)
30 , qmlProperty(item, property, qmlContext(item))
31{
32}
33PropertyChange::~PropertyChange()
34{
35 restore(this);
36}
37
38/*
39 * Sets a value to the property. Will back up the original values if it wasn't yet.
40 * This function can be called many times, it will not destroy the backed up value/binding.
41 */
42void PropertyChange::setValue(PropertyChange *change, const QVariant &value)
43{
44 if (!change) {
45 return;
46 }
47 if (!change->m_backedUp) {
48 change->backup.first = QQmlPropertyPrivate::setBinding(change->qmlProperty, 0);
49 change->backup.second = change->qmlProperty.read();
50 change->m_backedUp = true;
51 }
52 change->qmlProperty.write(value);
53}
54
55/*
56 * Restore backed up value or binding.
57 */
58void PropertyChange::restore(PropertyChange *change)
59{
60 if (!change) {
61 return;
62 }
63 if (change->m_backedUp) {
64 // if there was a binding, restore it
65 if (change->backup.first) {
66 QQmlAbstractBinding *prevBinding = QQmlPropertyPrivate::setBinding(change->qmlProperty, change->backup.first);
67 if (prevBinding && prevBinding != change->backup.first) {
68 prevBinding->destroy();
69 }
70 } else {
71 // there was no binding, restore previous value
72 change->qmlProperty.write(change->backup.second);
73 }
74 change->m_backedUp = false;
75 }
76}
077
=== added file 'modules/Ubuntu/Components/plugin/propertychange_p.h'
--- modules/Ubuntu/Components/plugin/propertychange_p.h 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/plugin/propertychange_p.h 2014-12-03 06:10:30 +0000
@@ -0,0 +1,39 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef PROPERTYCHANGE_P_H
18#define PROPERTYCHANGE_P_H
19
20#include <QtCore/QVariant>
21#include <QtCore/QObject>
22#include <QtQml/QQmlProperty>
23
24class QQmlAbstractBinding;
25class PropertyChange
26{
27public:
28 PropertyChange(QObject *item, const char *property);
29 ~PropertyChange();
30
31 static void setValue(PropertyChange* change, const QVariant &value);
32 static void restore(PropertyChange* change);
33private:
34 bool m_backedUp;
35 QQmlProperty qmlProperty;
36 QPair<QQmlAbstractBinding*, QVariant> backup;
37};
38
39#endif // PROPERTYCHANGE_P_H
040
=== modified file 'modules/Ubuntu/Components/plugin/quickutils.cpp'
--- modules/Ubuntu/Components/plugin/quickutils.cpp 2014-11-04 16:14:07 +0000
+++ modules/Ubuntu/Components/plugin/quickutils.cpp 2014-12-03 06:10:30 +0000
@@ -138,6 +138,29 @@
138 return result.left(result.indexOf("_QML"));138 return result.left(result.indexOf("_QML"));
139}139}
140140
141/*!
142 * \internal
143 * The function checks whether an item inherits a given class name.
144 */
145bool QuickUtils::inherits(QObject *object, const QString &fromClass)
146{
147 if (!object || fromClass.isEmpty()) {
148 return false;
149 }
150 const QMetaObject *mo = object->metaObject();
151 QString className;
152 while (mo) {
153 className = mo->className();
154 className = className.left(className.indexOf("_QML"));
155 if (className == fromClass) {
156 return true;
157 }
158 mo = mo->superClass();
159 }
160 return false;
161}
162
163
141164
142/*!165/*!
143 * \internal166 * \internal
144167
=== modified file 'modules/Ubuntu/Components/plugin/quickutils.h'
--- modules/Ubuntu/Components/plugin/quickutils.h 2014-11-04 16:14:07 +0000
+++ modules/Ubuntu/Components/plugin/quickutils.h 2014-12-03 06:10:30 +0000
@@ -44,6 +44,7 @@
44 bool touchScreenAvailable() const;44 bool touchScreenAvailable() const;
4545
46 Q_INVOKABLE static QString className(QObject *item);46 Q_INVOKABLE static QString className(QObject *item);
47 Q_REVISION(1) Q_INVOKABLE static bool inherits(QObject *object, const QString &fromClass);
47 QObject* createQmlObject(const QUrl &url, QQmlEngine *engine);48 QObject* createQmlObject(const QUrl &url, QQmlEngine *engine);
4849
49Q_SIGNALS:50Q_SIGNALS:
5051
=== modified file 'modules/Ubuntu/Components/plugin/ucaction.h'
--- modules/Ubuntu/Components/plugin/ucaction.h 2014-09-01 06:56:39 +0000
+++ modules/Ubuntu/Components/plugin/ucaction.h 2014-12-03 06:10:30 +0000
@@ -87,6 +87,8 @@
87 Type m_parameterType;87 Type m_parameterType;
8888
89 friend class UCActionContext;89 friend class UCActionContext;
90 friend class UCListItemActionsAttached;
91 friend class UCListItemActionsPrivate;
9092
91 bool isValidType(QVariant::Type valueType);93 bool isValidType(QVariant::Type valueType);
92 void generateName();94 void generateName();
9395
=== added file 'modules/Ubuntu/Components/plugin/uclistitem.cpp'
--- modules/Ubuntu/Components/plugin/uclistitem.cpp 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/plugin/uclistitem.cpp 2014-12-03 06:10:30 +0000
@@ -0,0 +1,1260 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "ucunits.h"
18#include "uctheme.h"
19#include "uclistitem.h"
20#include "uclistitem_p.h"
21#include "uclistitemactions.h"
22#include "uclistitemactions_p.h"
23#include "ucubuntuanimation.h"
24#include "propertychange_p.h"
25#include "i18n.h"
26#include "quickutils.h"
27#include <QtQml/QQmlInfo>
28#include <QtQuick/private/qquickitem_p.h>
29#include <QtQuick/private/qquickflickable_p.h>
30#include <QtQuick/private/qquickpositioners_p.h>
31#include <QtQuick/private/qquickanimation_p.h>
32#include <QtQuick/private/qquickmousearea_p.h>
33#include "uclistitemstyle.h"
34
35#define MIN(x, y) ((x < y) ? x : y)
36#define MAX(x, y) ((x > y) ? x : y)
37#define CLAMP(v, min, max) (min <= max) ? MAX(min, MIN(v, max)) : MAX(max, MIN(v, min))
38
39QColor getPaletteColor(const char *profile, const char *color)
40{
41 QColor result;
42 QObject *palette = UCTheme::instance().palette();
43 if (palette) {
44 QObject *paletteProfile = palette->property(profile).value<QObject*>();
45 if (paletteProfile) {
46 result = paletteProfile->property(color).value<QColor>();
47 }
48 }
49 return result;
50}
51/******************************************************************************
52 * SnapAnimator
53 *
54 * The class handles the animation executed when the ListItemAction panel is
55 * swiped. The animation is executed from the swipe position the mouse/touch is
56 * released to the desired position given in snap(). The action panel is assumed
57 * to be anchored to the ListItem.contentItem left or right, depending on which
58 * action list is swiped in. Therefore the animator only changes the ListItem.contentItem
59 * x coordinate.
60 * The animation is defined by the style.
61 */
62UCListItemSnapAnimator::UCListItemSnapAnimator(UCListItem *item)
63 : QObject(item)
64 , item(item)
65{
66}
67UCListItemSnapAnimator::~UCListItemSnapAnimator()
68{
69 // make sure we cannot animate anymore, for safety
70 item = 0;
71}
72
73/*
74 * Snap the ListItem.contentItem in or out, depending on the position specified
75 * in "to" parameter. If the position is 0, a snap out will be executed - see
76 * snapOut(). In any other cases a snap in action will be performed - see snapIn().
77 */
78bool UCListItemSnapAnimator::snap(qreal to)
79{
80 if (!item) {
81 return false;
82 }
83 UCListItemPrivate *listItem = UCListItemPrivate::get(item);
84 QQuickPropertyAnimation *snap = getDefaultAnimation();
85 if (!snap) {
86 // no animation, so we simply position the component
87 listItem->contentItem->setX(to);
88 return false;
89 }
90 snap->setTargetObject(listItem->contentItem);
91 if (to == 0.0) {
92 QObject::connect(snap, &QQuickAbstractAnimation::stopped,
93 this, &UCListItemSnapAnimator::snapOut);
94 } else {
95 QObject::connect(snap, &QQuickAbstractAnimation::stopped,
96 this, &UCListItemSnapAnimator::snapIn);
97 }
98 if (snap->properties().isEmpty() && snap->property().isEmpty()) {
99 snap->setProperty("x");
100 }
101 snap->setFrom(listItem->contentItem->property(snap->property().toLocal8Bit().constData()));
102 snap->setTo(to);
103 snap->setAlwaysRunToEnd(true);
104 listItem->setContentMoving(true);
105 snap->start();
106 return true;
107}
108
109/*
110 * The function completes a running snap animation.
111 */
112void UCListItemSnapAnimator::complete()
113{
114 QQuickPropertyAnimation *snap = getDefaultAnimation();
115 if (snap && snap->isRunning()) {
116 snap->complete();
117 }
118}
119
120/*
121 * Snap out is performed when the ListItem.contentItem returns back to its original
122 * X coordinates (0). At this point both leading and trailing action panels will
123 * be disconnected, ascending Flickables will get unlocked (interactive value restored
124 * to the state before they were locked) and ListItem.contentMoving will be reset.
125 */
126void UCListItemSnapAnimator::snapOut()
127{
128 if (senderSignalIndex() >= 0) {
129 // disconnect animation, otherwise snapping will disconnect the panel
130 QQuickAbstractAnimation *snap = getDefaultAnimation();
131 QObject::disconnect(snap, 0, 0, 0);
132 }
133 UCListItemPrivate *listItem = UCListItemPrivate::get(item);
134 if (listItem->attachedProperties) {
135 // restore flickable's interactive and cleanup
136 listItem->attachedProperties->disableInteractive(item, false);
137 // no need to listen flickables any longer
138 listItem->listenToRebind(false);
139 }
140 // disconnect actions
141 listItem->grabPanel(listItem->leadingActions, false);
142 listItem->grabPanel(listItem->trailingActions, false);
143 // set contentMoved to false
144 listItem->setContentMoving(false);
145}
146
147/*
148 * Snap in only resets the ListItem.contentMoving property, but will keep leading/trailing
149 * actions connected as well as all ascendant Flickables locked (interactive = false).
150 */
151void UCListItemSnapAnimator::snapIn()
152{
153 if (senderSignalIndex() >= 0) {
154 // disconnect animation
155 QQuickAbstractAnimation *snap = getDefaultAnimation();
156 QObject::disconnect(snap, 0, 0, 0);
157 }
158 // turn content moving off
159 UCListItemPrivate *listItem = UCListItemPrivate::get(item);
160 listItem->setContentMoving(false);
161}
162
163/*
164 * Returns the animation specified by the style.
165 */
166QQuickPropertyAnimation *UCListItemSnapAnimator::getDefaultAnimation()
167{
168 UCListItemPrivate *listItem = UCListItemPrivate::get(item);
169 listItem->initStyleItem();
170 return listItem->styleItem ? listItem->styleItem->m_snapAnimation : 0;
171}
172
173/******************************************************************************
174 * Divider
175 */
176UCListItemDivider::UCListItemDivider(QObject *parent)
177 : QObject(parent)
178 , m_visible(true)
179 , m_leftMarginChanged(false)
180 , m_rightMarginChanged(false)
181 , m_colorFromChanged(false)
182 , m_colorToChanged(false)
183 , m_thickness(0)
184 , m_leftMargin(0)
185 , m_rightMargin(0)
186 , m_listItem(0)
187{
188 connect(&UCUnits::instance(), &UCUnits::gridUnitChanged, this, &UCListItemDivider::unitsChanged);
189 connect(&UCTheme::instance(), &UCTheme::paletteChanged, this, &UCListItemDivider::paletteChanged);
190 unitsChanged();
191 paletteChanged();
192}
193UCListItemDivider::~UCListItemDivider()
194{
195}
196
197void UCListItemDivider::init(UCListItem *listItem)
198{
199 QQml_setParent_noEvent(this, listItem);
200 m_listItem = UCListItemPrivate::get(listItem);
201}
202
203void UCListItemDivider::unitsChanged()
204{
205 m_thickness = UCUnits::instance().dp(2);
206 if (!m_leftMarginChanged) {
207 m_leftMargin = UCUnits::instance().dp(2);
208 }
209 if (!m_rightMarginChanged) {
210 m_rightMargin = UCUnits::instance().dp(2);
211 }
212 if (m_listItem) {
213 m_listItem->update();
214 }
215}
216
217void UCListItemDivider::paletteChanged()
218{
219 QColor background = getPaletteColor("normal", "background");
220 if (!background.isValid()) {
221 return;
222 }
223 // FIXME: we need a palette value for divider colors, till then base on the background
224 // luminance
225 if (!m_colorFromChanged || !m_colorToChanged) {
226 qreal luminance = (background.red()*212 + background.green()*715 + background.blue()*73)/1000.0/255.0;
227 bool lightBackground = (luminance > 0.85);
228 if (!m_colorFromChanged) {
229 m_colorFrom = lightBackground ? QColor("#26000000") : QColor("#26FFFFFF");
230 }
231 if (!m_colorToChanged) {
232 m_colorTo = lightBackground ? QColor("#14FFFFFF") : QColor("#14000000");
233 }
234 updateGradient();
235 }
236}
237
238void UCListItemDivider::updateGradient()
239{
240 m_gradient.clear();
241 m_gradient.append(QGradientStop(0.0, m_colorFrom));
242 m_gradient.append(QGradientStop(0.49, m_colorFrom));
243 m_gradient.append(QGradientStop(0.5, m_colorTo));
244 m_gradient.append(QGradientStop(1.0, m_colorTo));
245 if (m_listItem) {
246 m_listItem->update();
247 }
248}
249
250QSGNode *UCListItemDivider::paint(QSGNode *node, const QRectF &rect)
251{
252 QSGRectangleNode *dividerNode = static_cast<QSGRectangleNode*>(node);
253 bool lastItem = m_listItem->countOwner ? (m_listItem->index() == (m_listItem->countOwner->property("count").toInt() - 1)): false;
254 if (m_visible && !lastItem && (m_gradient.size() > 0) && ((m_colorFrom.alphaF() >= (1.0f / 255.0f)) || (m_colorTo.alphaF() >= (1.0f / 255.0f)))) {
255 if (!dividerNode) {
256 dividerNode = m_listItem->sceneGraphContext()->createRectangleNode();
257 }
258 QRectF divider(m_leftMargin, rect.height() - m_thickness, rect.width() - m_leftMargin - m_rightMargin, m_thickness);
259 dividerNode->setRect(divider);
260 dividerNode->setGradientStops(m_gradient);
261 dividerNode->update();
262 return dividerNode;
263 } else if (node) {
264 // delete the node
265 delete node;
266 }
267 return 0;
268}
269
270void UCListItemDivider::setVisible(bool visible)
271{
272 if (m_visible == visible) {
273 return;
274 }
275 m_visible = visible;
276 m_listItem->resize();
277 m_listItem->update();
278 Q_EMIT visibleChanged();
279}
280
281void UCListItemDivider::setLeftMargin(qreal leftMargin)
282{
283 if (m_leftMargin == leftMargin) {
284 return;
285 }
286 m_leftMargin = leftMargin;
287 m_leftMarginChanged = true;
288 m_listItem->update();
289 Q_EMIT leftMarginChanged();
290}
291
292void UCListItemDivider::setRightMargin(qreal rightMargin)
293{
294 if (m_rightMargin == rightMargin) {
295 return;
296 }
297 m_rightMargin = rightMargin;
298 m_rightMarginChanged = true;
299 m_listItem->update();
300 Q_EMIT rightMarginChanged();
301}
302
303void UCListItemDivider::setColorFrom(const QColor &color)
304{
305 if (m_colorFrom == color) {
306 return;
307 }
308 m_colorFrom = color;
309 m_colorFromChanged = true;
310 updateGradient();
311 Q_EMIT colorFromChanged();
312}
313
314void UCListItemDivider::setColorTo(const QColor &color)
315{
316 if (m_colorTo == color) {
317 return;
318 }
319 m_colorTo = color;
320 m_colorToChanged = true;
321 updateGradient();
322 Q_EMIT colorToChanged();
323}
324
325/******************************************************************************
326 * ListItemPrivate
327 */
328UCListItemPrivate::UCListItemPrivate()
329 : UCStyledItemBasePrivate()
330 , pressed(false)
331 , contentMoved(false)
332 , highlightColorChanged(false)
333 , swiped(false)
334 , suppressClick(false)
335 , ready(false)
336 , customStyle(false)
337 , customColor(false)
338 , customOvershoot(false)
339 , flicked(false)
340 , xAxisMoveThresholdGU(1.5)
341 , overshoot(0)
342 , color(Qt::transparent)
343 , highlightColor(Qt::transparent)
344 , attachedProperties(0)
345 , contentItem(new QQuickItem)
346 , divider(new UCListItemDivider)
347 , leadingActions(0)
348 , trailingActions(0)
349 , animator(0)
350 , styleComponent(0)
351 , styleItem(0)
352{
353}
354UCListItemPrivate::~UCListItemPrivate()
355{
356}
357
358void UCListItemPrivate::init()
359{
360 Q_Q(UCListItem);
361 contentItem->setObjectName("ListItemHolder");
362 QQml_setParent_noEvent(contentItem, q);
363 contentItem->setParentItem(q);
364 divider->init(q);
365 // content will be redirected to the contentItem, therefore we must report
366 // children changes as it would come from the main component
367 QObject::connect(contentItem, &QQuickItem::childrenChanged,
368 q, &UCListItem::listItemChildrenChanged);
369 q->setFlag(QQuickItem::ItemHasContents);
370 // turn activeFocusOnPress on
371 q->setActiveFocusOnPress(true);
372
373 // catch theme changes
374 QObject::connect(&UCTheme::instance(), SIGNAL(nameChanged()), q, SLOT(_q_updateThemedData()));
375 _q_updateThemedData();
376
377 // watch size change and set implicit size;
378 QObject::connect(&UCUnits::instance(), SIGNAL(gridUnitChanged()), q, SLOT(_q_updateSize()));
379 _q_updateSize();
380}
381
382void UCListItemPrivate::_q_updateThemedData()
383{
384 // update colors, panels
385 if (!customColor) {
386 Q_Q(UCListItem);
387 highlightColor = getPaletteColor("selected", "background");
388 q->update();
389 }
390 loadStyle(true);
391}
392
393void UCListItemPrivate::_q_rebound()
394{
395 setPressed(false);
396 // initiate rebinding only if there were actions tugged
397 Q_Q(UCListItem);
398 if (!UCListItemActionsPrivate::isConnectedTo(leadingActions, q) &&
399 !UCListItemActionsPrivate::isConnectedTo(trailingActions, q)) {
400 return;
401 }
402 setSwiped(false);
403 // rebound to zero
404 animator->snap(0);
405}
406
407void UCListItemPrivate::_q_updateIndex()
408{
409 Q_Q(UCListItem);
410 q->update();
411}
412
413/*!
414 * \qmlproperty Component ListItem::style
415 * Holds the style of the component defining the components visualizing the leading/
416 * trailing actions, selection and dragging mode handlers as well as different
417 * animations. The component does not assume any visuals present in the style,
418 * and will load its content only when requested.
419 * \sa ListItemStyle
420 */
421QQmlComponent *UCListItemPrivate::style() const
422{
423 return styleComponent;
424}
425void UCListItemPrivate::setStyle(QQmlComponent *delegate)
426{
427 if (styleComponent == delegate) {
428 return;
429 }
430 Q_Q(UCListItem);
431 // make sure we're rebound before we change the panel component
432 promptRebound();
433 if (styleItem) {
434 delete styleItem;
435 styleItem = 0;
436 Q_EMIT q->__styleInstanceChanged();
437 }
438 delete styleComponent;
439 customStyle = (delegate == 0);
440 styleComponent = delegate;
441 loadStyle(false);
442 Q_EMIT q->styleChanged();
443}
444
445// update themed components
446bool UCListItemPrivate::loadStyle(bool reload)
447{
448 if (!ready) {
449 return false;
450 }
451 if (!customStyle) {
452 Q_Q(UCListItem);
453 if (reload && styleItem) {
454 delete styleItem;
455 styleItem = 0;
456 Q_EMIT q->__styleInstanceChanged();
457 }
458 delete styleComponent;
459 styleComponent = UCTheme::instance().createStyleComponent("ListItemStyle.qml", q);
460 }
461 return (styleComponent != NULL);
462}
463// creates the style item
464void UCListItemPrivate::initStyleItem()
465{
466 if (!styleItem && !loadStyle(false)) {
467 return;
468 }
469 Q_Q(UCListItem);
470 QObject *object = styleComponent->beginCreate(qmlContext(q));
471 styleItem = qobject_cast<UCListItemStyle*>(object);
472 if (!styleItem) {
473 delete object;
474 styleComponent->completeCreate();
475 return;
476 }
477 QQml_setParent_noEvent(styleItem, q);
478 styleComponent->completeCreate();
479 Q_EMIT q->__styleInstanceChanged();
480
481 // get the overshoot value from the style!
482 if (!customOvershoot) {
483 overshoot = styleItem->m_swipeOvershoot;
484 Q_EMIT q->swipeOvershootChanged();
485 }
486}
487
488/*!
489 * \qmlproperty Item ListItem::__styleInstance
490 * \internal
491 */
492QQuickItem *UCListItemPrivate::styleInstance() const
493{
494 return styleItem;
495}
496
497// rebound without animation
498void UCListItemPrivate::promptRebound()
499{
500 setPressed(false);
501 setSwiped(false);
502 if (animator) {
503 animator->snapOut();
504 }
505}
506
507// set pressed flag and update background
508// called when units size changes
509void UCListItemPrivate::_q_updateSize()
510{
511 Q_Q(UCListItem);
512 QQuickItem *owner = flickable ? flickable : parentItem;
513 q->setImplicitWidth(owner ? owner->width() : UCUnits::instance().gu(40));
514 q->setImplicitHeight(UCUnits::instance().gu(7));
515}
516
517// returns the index of the list item when used in model driven views,
518// and the child index in other cases
519int UCListItemPrivate::index()
520{
521 Q_Q(UCListItem);
522 // is there an index context property?
523 QQmlContext *context = qmlContext(q);
524 QVariant index = context->contextProperty("index");
525 return index.isValid() ?
526 index.toInt() :
527 (parentItem ? QQuickItemPrivate::get(parentItem)->childItems.indexOf(q) : -1);
528}
529
530// returns true if the highlight is possible
531bool UCListItemPrivate::canHighlight(QMouseEvent *event)
532{
533 // localPos is a position relative to ListItem which will give us a child from
534 // from the original coordinates; therefore we must map the position to the contentItem
535 Q_Q(UCListItem);
536 QPointF myPos = q->mapToItem(contentItem, event->localPos());
537 QQuickItem *child = contentItem->childAt(myPos.x(), myPos.y());
538 bool activeComponent = child && (child->acceptedMouseButtons() & event->button()) && !qobject_cast<QQuickText*>(child);
539 // do highlight if not pressed above the active component, and the component has either
540 // action, leading or trailing actions list or at least an active child component declared
541 QQuickMouseArea *ma = q->findChild<QQuickMouseArea*>();
542 bool activeMouseArea = ma && ma->isEnabled();
543 return !activeComponent && (leadingActions || trailingActions || activeMouseArea);
544}
545
546// set pressed flag and update contentItem
547void UCListItemPrivate::setPressed(bool pressed)
548{
549 if (this->pressed != pressed) {
550 this->pressed = pressed;
551 suppressClick = false;
552 Q_Q(UCListItem);
553 q->update();
554 Q_EMIT q->pressedChanged();
555 }
556}
557// toggles the tugged flag and installs/removes event filter
558void UCListItemPrivate::setSwiped(bool swiped)
559{
560 suppressClick = swiped;
561 if (this->swiped == swiped) {
562 return;
563 }
564 this->swiped = swiped;
565 Q_Q(UCListItem);
566 QQuickWindow *window = q->window();
567 if (swiped) {
568 window->installEventFilter(q);
569 } else {
570 window->removeEventFilter(q);
571 }
572}
573
574// grabs the panels from the leading/trailing actions and disables all ascending flickables
575bool UCListItemPrivate::grabPanel(UCListItemActions *actionsList, bool isTugged)
576{
577 Q_Q(UCListItem);
578 if (isTugged) {
579 bool grab = UCListItemActionsPrivate::connectToListItem(actionsList, q, (actionsList == leadingActions));
580 if (attachedProperties) {
581 attachedProperties->disableInteractive(q, true);
582 }
583 return grab;
584 } else {
585 UCListItemActionsPrivate::disconnectFromListItem(actionsList);
586 return false;
587 }
588}
589
590
591// connects/disconnects from the Flickable anchestor to get notified when to do rebound
592void UCListItemPrivate::listenToRebind(bool listen)
593{
594 if (attachedProperties) {
595 Q_Q(UCListItem);
596 attachedProperties->listenToRebind(q, listen);
597 }
598}
599
600void UCListItemPrivate::resize()
601{
602 Q_Q(UCListItem);
603 QRectF rect(q->boundingRect());
604 if (divider && divider->m_visible) {
605 rect.setHeight(rect.height() - divider->m_thickness);
606 }
607 contentItem->setSize(rect.size());
608}
609
610void UCListItemPrivate::update()
611{
612 if (!ready) {
613 return;
614 }
615 Q_Q(UCListItem);
616 q->update();
617}
618
619// clamps the X value and moves the contentItem to the new X value
620void UCListItemPrivate::clampAndMoveX(qreal &x, qreal dx)
621{
622 UCListItemActionsPrivate *leading = UCListItemActionsPrivate::get(leadingActions);
623 UCListItemActionsPrivate *trailing = UCListItemActionsPrivate::get(trailingActions);
624 x += dx;
625 // min cannot be less than the trailing's panel width
626 qreal min = (trailing && trailing->panelItem) ? -trailing->panelItem->width() - overshoot: 0;
627 // max cannot be bigger than 0 or the leading's width in case we have leading panel
628 qreal max = (leading && leading->panelItem) ? leading->panelItem->width() + overshoot: 0;
629 x = CLAMP(x, min, max);
630}
631
632/*!
633 * \qmltype ListItem
634 * \instantiates UCListItem
635 * \inqmlmodule Ubuntu.Components 1.2
636 * \ingroup unstable-ubuntu-listitems
637 * \since Ubuntu.Components 1.2
638 * \brief The ListItem element provides Ubuntu design standards for list or grid
639 * views.
640 * The ListItem component was designed to be used in a list view. It does not
641 * define any specific layout, but while its contents can be freely chosen by
642 * the developer, care must be taken to keep the contents light in order to ensure
643 * good performance when used in long list views.
644 *
645 * The component provides two color properties which configures the item's background
646 * when normal or pressed. This can be configured through \l color and \l highlightColor
647 * properties.
648 *
649 * \c contentItem holds all components and resources declared as child to ListItem.
650 * Being an Item, all properties can be accessed or altered. However, make sure you
651 * never change \c x, \c y, \c width, \c height or \c anchors properties as those are
652 * controlled by the ListItem itself when leading or trailing actions are revealed
653 * and thus might cause the component to misbehave.
654 *
655 * Each ListItem has a thin divider shown on the bottom of the component. This
656 * divider can be configured through the \c divider grouped property, which can
657 * configure its margins from the edges of the ListItem as well as its visibility.
658 * When used in \c ListView or \l UbuntuListView, the last list item will not
659 * show the divider no matter of the visible property value set.
660 *
661 * ListItem can handle actions that can get tugged from front to back of the item.
662 * These actions are Action elements visualized in panels attached to the front
663 * or to the back of the item, and are revealed by swiping the item horizontally.
664 * The tug is started only after the mouse/touch move had passed a given threshold.
665 * These actions are configured through the \l leadingActions as well as \l
666 * trailingActions properties.
667 * \qml
668 * ListItem {
669 * id: listItem
670 * leadingActions: ListItemActions {
671 * actions: [
672 * Action {
673 * iconName: "delete"
674 * onTriggered: listItem.destroy()
675 * }
676 * ]
677 * }
678 * trailingActions: ListItemActions {
679 * actions: [
680 * Action {
681 * iconName: "search"
682 * onTriggered: {
683 * // do some search
684 * }
685 * }
686 * ]
687 * }
688 * }
689 * \endqml
690 * \note When a list item is tugged, it automatically connects both leading and
691 * trailing actions to the list item. This implies that a ListItem cannot use
692 * the same ListItemActions instance for both leading and trailing actions. If
693 * it is desired to have the same action present in both leading and trailing
694 * actions, one of the ListItemActions actions list can use the other's list. In
695 * the following example the list item can be deleted through both leading and
696 * trailing actions:
697 * \qml
698 * ListItem {
699 * id: listItem
700 * leadingActions: ListItemActions {
701 * actions: [
702 * Action {
703 * iconName: "delete"
704 * onTriggered: listItem.destroy()
705 * }
706 * ]
707 * }
708 * trailingActions: ListItemActions {
709 * actions: leadingActions.actions
710 * }
711 * }
712 * \endqml
713 * \sa ListItemActions
714 *
715 * The component is styled using the \l ListItemStyle style interface.
716 */
717
718/*!
719 * \qmlsignal ListItem::clicked()
720 *
721 * The signal is emitted when the component gets released while the \l pressed property
722 * is set. The signal is not emitted if the ListItem content is swiped or when used in
723 * Flickable (or ListView, GridView) and the Flickable gets moved.
724 */
725UCListItem::UCListItem(QQuickItem *parent)
726 : UCStyledItemBase(*(new UCListItemPrivate), parent)
727{
728 Q_D(UCListItem);
729 d->init();
730}
731
732UCListItem::~UCListItem()
733{
734}
735
736UCListItemAttached *UCListItem::qmlAttachedProperties(QObject *owner)
737{
738 return new UCListItemAttached(owner);
739}
740
741void UCListItem::componentComplete()
742{
743 UCStyledItemBase::componentComplete();
744 Q_D(UCListItem);
745 d->ready = true;
746 /* We only deal with ListView, as for other cases we would need to check the children
747 * changes, which would have an enormous impact on performance in case of huge amount
748 * of items. However, if the parent item, or Flickable declares a "count" property,
749 * the ListItem will take use of it!
750 */
751 d->countOwner = (d->flickable && d->flickable->property("count").isValid()) ?
752 d->flickable :
753 (d->parentItem && d->parentItem->property("count").isValid()) ? d->parentItem : 0;
754 if (d->countOwner) {
755 QObject::connect(d->countOwner.data(), SIGNAL(countChanged()),
756 this, SLOT(_q_updateIndex()), Qt::DirectConnection);
757 update();
758 }
759}
760
761void UCListItem::itemChange(ItemChange change, const ItemChangeData &data)
762{
763 UCStyledItemBase::itemChange(change, data);
764 if (change == ItemParentHasChanged) {
765 Q_D(UCListItem);
766 // make sure we are not connected to the previous Flickable
767 d->listenToRebind(false);
768 // check if we are in a positioner, and if that positioner is in a Flickable
769 QQuickBasePositioner *positioner = qobject_cast<QQuickBasePositioner*>(data.item);
770 if (positioner && positioner->parentItem()) {
771 d->flickable = qobject_cast<QQuickFlickable*>(positioner->parentItem()->parentItem());
772 } else if (data.item && data.item->parentItem()){
773 // check if we are in a Flickable then
774 d->flickable = qobject_cast<QQuickFlickable*>(data.item->parentItem());
775 }
776
777 // attach ListItem properties to the flickable or to the parent item
778 if (d->flickable) {
779 // connect to flickable to get width changes
780 d->attachedProperties = static_cast<UCListItemAttached*>(qmlAttachedPropertiesObject<UCListItem>(d->flickable));
781 } else if (data.item) {
782 d->attachedProperties = static_cast<UCListItemAttached*>(qmlAttachedPropertiesObject<UCListItem>(data.item));
783 } else {
784 // mark as not ready, so no action should be performed which depends on readyness
785 d->ready = false;
786 // about to be deleted or reparented, disable attached
787 d->attachedProperties = 0;
788 }
789
790 if (data.item) {
791 QObject::connect(d->flickable ? d->flickable : data.item, SIGNAL(widthChanged()), this, SLOT(_q_updateSize()));
792 }
793
794 // update size
795 d->_q_updateSize();
796 }
797}
798
799void UCListItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
800{
801 UCStyledItemBase::geometryChanged(newGeometry, oldGeometry);
802 // resize contentItem item
803 Q_D(UCListItem);
804 d->resize();
805}
806
807QSGNode *UCListItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
808{
809 Q_UNUSED(data);
810
811 Q_D(UCListItem);
812 QColor color = d->pressed ? d->highlightColor : d->color;
813
814 if (width() <= 0 || height() <= 0) {
815 delete oldNode;
816 return 0;
817 }
818
819 QSGRectangleNode *rectNode = 0;
820 rectNode = static_cast<QSGRectangleNode*>(oldNode);
821 if (!rectNode) {
822 rectNode = QQuickItemPrivate::get(this)->sceneGraphContext()->createRectangleNode();
823 }
824 if (color.alphaF() >= (1.0f / 255.0f)) {
825 rectNode->setColor(color);
826 // cover only the area of the contentItem
827 rectNode->setRect(d->contentItem->boundingRect());
828 rectNode->setGradientStops(QGradientStops());
829 rectNode->setAntialiasing(true);
830 rectNode->setAntialiasing(false);
831 rectNode->update();
832 } else {
833 // delete node, this will delete the divider node as well
834 delete rectNode;
835 rectNode = 0;
836 }
837 oldNode = rectNode;
838 QSGNode *dividerNode = oldNode ? oldNode->childAtIndex(0) : 0;
839 if (d->divider && d->divider->m_visible) {
840 QSGNode *newNode = d->divider->paint(dividerNode, boundingRect());
841 if (newNode != dividerNode && oldNode) {
842 if (dividerNode) {
843 oldNode->removeChildNode(dividerNode);
844 }
845 if (newNode) {
846 oldNode->appendChildNode(newNode);
847 }
848 }
849 if (!oldNode) {
850 oldNode = newNode;
851 }
852 } else if (dividerNode) {
853 // the divider painter node may be still added as child, so remove it
854 oldNode->removeChildNode(dividerNode);
855 }
856 return oldNode;
857}
858
859void UCListItem::mousePressEvent(QMouseEvent *event)
860{
861 UCStyledItemBase::mousePressEvent(event);
862 Q_D(UCListItem);
863 if (d->attachedProperties && d->attachedProperties->isMoving()) {
864 // while moving, we cannot select any items
865 return;
866 }
867 if (event->button() == Qt::LeftButton && d->canHighlight(event)) {
868 d->setPressed(true);
869 d->lastPos = d->pressedPos = event->localPos();
870 // connect the Flickable to know when to rebound
871 d->listenToRebind(true);
872 // if it was moved, grab the panels
873 if (d->swiped) {
874 d->grabPanel(d->leadingActions, true);
875 d->grabPanel(d->trailingActions, true);
876 }
877 }
878 // accept the event so we get the rest of the events as well
879 event->setAccepted(true);
880}
881
882void UCListItem::mouseReleaseEvent(QMouseEvent *event)
883{
884 UCStyledItemBase::mouseReleaseEvent(event);
885 Q_D(UCListItem);
886 // set released
887 if (d->pressed) {
888 d->listenToRebind(false);
889 if (d->attachedProperties) {
890 d->attachedProperties->disableInteractive(this, false);
891 }
892
893 if (!d->suppressClick) {
894 Q_EMIT clicked();
895 d->_q_rebound();
896 }
897 }
898 d->setPressed(false);
899}
900
901void UCListItem::mouseMoveEvent(QMouseEvent *event)
902{
903 Q_D(UCListItem);
904 UCStyledItemBase::mouseMoveEvent(event);
905
906 // accept the tugging only if the move is within the threshold
907 bool leadingAttached = UCListItemActionsPrivate::isConnectedTo(d->leadingActions, this);
908 bool trailingAttached = UCListItemActionsPrivate::isConnectedTo(d->trailingActions, this);
909 if (d->pressed && !(leadingAttached || trailingAttached)) {
910 // check if we can initiate the drag at all
911 // only X direction matters, if Y-direction leaves the threshold, but X not, the tug is not valid
912 qreal threshold = UCUnits::instance().gu(d->xAxisMoveThresholdGU);
913 qreal mouseX = event->localPos().x();
914 qreal pressedX = d->pressedPos.x();
915
916 if ((mouseX < (pressedX - threshold)) || (mouseX > (pressedX + threshold))) {
917 // the press went out of the threshold area, enable move, if the direction allows it
918 d->lastPos = event->localPos();
919 // tries to connect both panels so we do no longer need to take care which
920 // got connected ad which not; this may fail in case of shared ListItemActions,
921 // as then the panel is shared between the list items, and the panel might be
922 // still in use in other panels. See UCListItemActionsPrivate::connectToListItem
923 leadingAttached = d->grabPanel(d->leadingActions, true);
924 trailingAttached = d->grabPanel(d->trailingActions, true);
925 // create animator if not created yet
926 if (!d->animator) {
927 d->animator = new UCListItemSnapAnimator(this);
928 }
929 }
930 }
931
932 if (leadingAttached || trailingAttached) {
933 qreal x = d->contentItem->x();
934 qreal dx = event->localPos().x() - d->lastPos.x();
935 d->lastPos = event->localPos();
936
937 if (dx) {
938 d->setContentMoving(true);
939 // clamp X into allowed dragging area
940 d->clampAndMoveX(x, dx);
941 // block flickable
942 d->setSwiped(true);
943 d->contentItem->setX(x);
944 // decide which panel is visible by checking the contentItem's X coordinates
945 if (d->contentItem->x() > 0) {
946 if (leadingAttached) {
947 UCListItemActionsPrivate::get(d->leadingActions)->panelItem->setVisible(true);
948 }
949 if (trailingAttached) {
950 UCListItemActionsPrivate::get(d->trailingActions)->panelItem->setVisible(false);
951 }
952 } else if (d->contentItem->x() < 0) {
953 // trailing revealed
954 if (leadingAttached) {
955 UCListItemActionsPrivate::get(d->leadingActions)->panelItem->setVisible(false);
956 }
957 if (trailingAttached) {
958 UCListItemActionsPrivate::get(d->trailingActions)->panelItem->setVisible(true);
959 }
960 }
961 }
962 }
963}
964
965bool UCListItem::childMouseEventFilter(QQuickItem *child, QEvent *event)
966{
967 QEvent::Type type = event->type();
968 if (type == QEvent::MouseButtonPress) {
969 // suppress click event if pressed over an active area, except Text, which can also handle
970 // mouse clicks when content is an URL
971 QMouseEvent *mouse = static_cast<QMouseEvent*>(event);
972 if (child->isEnabled() && (child->acceptedMouseButtons() & mouse->button()) && !qobject_cast<QQuickText*>(child)) {
973 Q_D(UCListItem);
974 d->suppressClick = true;
975 // listen for flickable to be able to rebind if movement started there!
976 d->listenToRebind(true);
977 }
978 } else if (type == QEvent::MouseButtonRelease) {
979 Q_D(UCListItem);
980 d->suppressClick = false;
981 }
982 return UCStyledItemBase::childMouseEventFilter(child, event);
983}
984
985bool UCListItem::eventFilter(QObject *target, QEvent *event)
986{
987 QPointF myPos;
988 // only filter press events, and rebound when pressed outside
989 if (event->type() == QEvent::MouseButtonPress) {
990 QMouseEvent *mouse = static_cast<QMouseEvent*>(event);
991 QQuickWindow *window = qobject_cast<QQuickWindow*>(target);
992 if (window) {
993 myPos = window->contentItem()->mapToItem(this, mouse->localPos());
994 }
995 } else if (event->type() == QEvent::TouchBegin) {
996 QTouchEvent *touch = static_cast<QTouchEvent*>(event);
997 QQuickWindow *window = qobject_cast<QQuickWindow*>(target);
998 if (window) {
999 myPos = window->contentItem()->mapToItem(this, touch->touchPoints()[0].pos());
1000 }
1001 }
1002 if (!myPos.isNull() && !contains(myPos)) {
1003 Q_D(UCListItem);
1004 d->_q_rebound();
1005 // only accept event, but let it be handled by the underlying or surrounding Flickables
1006 event->accept();
1007 }
1008 return UCStyledItemBase::eventFilter(target, event);
1009}
1010
1011/*!
1012 * \qmlproperty ListItemActions ListItem::leadingActions
1013 *
1014 * The property holds the actions and its configuration to be revealed when swiped
1015 * from left to right.
1016 *
1017 * \sa trailingActions
1018 */
1019UCListItemActions *UCListItem::leadingActions() const
1020{
1021 Q_D(const UCListItem);
1022 return d->leadingActions;
1023}
1024void UCListItem::setLeadingActions(UCListItemActions *actions)
1025{
1026 Q_D(UCListItem);
1027 if (d->leadingActions == actions) {
1028 return;
1029 }
1030 // snap out before we change the actions
1031 d->promptRebound();
1032 // then delete panelItem
1033 if (d->leadingActions) {
1034 UCListItemActionsPrivate *list = UCListItemActionsPrivate::get(d->leadingActions);
1035 delete list->panelItem;
1036 list->panelItem = 0;
1037 }
1038 d->leadingActions = actions;
1039 if (d->leadingActions == d->trailingActions && d->leadingActions) {
1040 qmlInfo(this) << UbuntuI18n::tr("leadingActions and trailingActions cannot share the same object!");
1041 }
1042 Q_EMIT leadingActionsChanged();
1043}
1044
1045/*!
1046 * \qmlproperty ListItemActions ListItem::trailingActions
1047 *
1048 * The property holds the actions and its configuration to be revealed when swiped
1049 * from right to left.
1050 *
1051 * \sa leadingActions
1052 */
1053UCListItemActions *UCListItem::trailingActions() const
1054{
1055 Q_D(const UCListItem);
1056 return d->trailingActions;
1057}
1058void UCListItem::setTrailingActions(UCListItemActions *actions)
1059{
1060 Q_D(UCListItem);
1061 if (d->trailingActions == actions) {
1062 return;
1063 }
1064 // snap out before we change the actions
1065 d->promptRebound();
1066 // then delete panelItem
1067 if (d->trailingActions) {
1068 UCListItemActionsPrivate *list = UCListItemActionsPrivate::get(d->trailingActions);
1069 delete list->panelItem;
1070 list->panelItem = 0;
1071 }
1072 d->trailingActions = actions;
1073 if (d->leadingActions == d->trailingActions && d->trailingActions) {
1074 qmlInfo(this) << UbuntuI18n::tr("leadingActions and trailingActions cannot share the same object!");
1075 }
1076 Q_EMIT trailingActionsChanged();
1077}
1078
1079/*!
1080 * \qmlproperty Item ListItem::contentItem
1081 *
1082 * contentItem holds the components placed on a ListItem.
1083 */
1084QQuickItem* UCListItem::contentItem() const
1085{
1086 Q_D(const UCListItem);
1087 return d->contentItem;
1088}
1089
1090/*!
1091 * \qmlpropertygroup ::ListItem::divider
1092 * \qmlproperty bool ListItem::divider.visible
1093 * \qmlproperty real ListItem::divider.leftMargin
1094 * \qmlproperty real ListItem::divider.rightMargin
1095 * \qmlproperty real ListItem::divider.colorFrom
1096 * \qmlproperty real ListItem::divider.colorTo
1097 *
1098 * This grouped property configures the thin divider shown in the bottom of the
1099 * component. Configures the visibility and the margins from the left and right
1100 * of the ListItem. When swiped left or right to reveal the actions, it is not
1101 * moved together with the content. \c colorFrom and \c colorTo configure
1102 * the starting and ending colors of the divider.
1103 *
1104 * When \c visible is true, the ListItem's content size gets thinner with the
1105 * divider's \c thickness.
1106 *
1107 * The default values for the properties are:
1108 * \list
1109 * \li \c visible: true
1110 * \li \c leftMargin: 2 GU
1111 * \li \c rightMargin: 2 GU
1112 * \endlist
1113 */
1114UCListItemDivider* UCListItem::divider() const
1115{
1116 Q_D(const UCListItem);
1117 return d->divider;
1118}
1119
1120/*!
1121 * \qmlproperty bool ListItem::pressed
1122 * True when the item is pressed. The items stays pressed when the mouse or touch
1123 * is moved horizontally. When in Flickable (or ListView), the item gets un-pressed
1124 * (false) when the mouse or touch is moved towards the vertical direction causing
1125 * the flickable to move.
1126 */
1127bool UCListItem::pressed() const
1128{
1129 Q_D(const UCListItem);
1130 return d->pressed;
1131}
1132
1133/*!
1134 * \qmlproperty bool ListItem::contentMoving
1135 * \readonly
1136 * The property describes whether the content is moving or not. The content is
1137 * moved when swiped or when snapping in or out, and lasts till the snapping
1138 * animation completes.
1139 */
1140
1141/*!
1142 * \qmlsignal ListItem::contentMovementStarted()
1143 * The signal is emitted when the content movement has started.
1144 */
1145
1146/*!
1147 * \qmlsignal ListItem::contentMovementEnded()
1148 * The signal is emitted when the content movement has ended.
1149 */
1150bool UCListItemPrivate::contentMoving() const
1151{
1152 return contentMoved;
1153}
1154void UCListItemPrivate::setContentMoving(bool moved)
1155{
1156 if (contentMoved == moved) {
1157 return;
1158 }
1159 contentMoved = moved;
1160 Q_Q(UCListItem);
1161 if (contentMoved) {
1162 Q_EMIT q->contentMovementStarted();
1163 } else {
1164 Q_EMIT q->contentMovementEnded();
1165 }
1166 Q_EMIT q->contentMovingChanged();
1167
1168}
1169
1170/*!
1171 * \qmlproperty color ListItem::color
1172 * Configures the color of the normal background. The default value is transparent.
1173 */
1174QColor UCListItem::color() const
1175{
1176 Q_D(const UCListItem);
1177 return d->color;
1178}
1179void UCListItem::setColor(const QColor &color)
1180{
1181 Q_D(UCListItem);
1182 if (d->color == color) {
1183 return;
1184 }
1185 d->color = color;
1186 update();
1187 Q_EMIT colorChanged();
1188}
1189
1190/*!
1191 * \qmlproperty color ListItem::highlightColor
1192 * Configures the color when pressed. Defaults to the theme palette's background color.
1193 */
1194QColor UCListItem::highlightColor() const
1195{
1196 Q_D(const UCListItem);
1197 return d->highlightColor;
1198}
1199void UCListItem::setHighlightColor(const QColor &color)
1200{
1201 Q_D(UCListItem);
1202 if (d->highlightColor == color) {
1203 return;
1204 }
1205 d->highlightColor = color;
1206 // no more theme change watch
1207 d->customColor = true;
1208 update();
1209 Q_EMIT highlightColorChanged();
1210}
1211
1212/*!
1213 * \qmlproperty real ListItem::swipeOvershoot
1214 * The property configures the overshoot value on swiping. Its default value is
1215 * configured by the \l {ListItemStyle}{style}. Any positive value overrides the
1216 * default value, and any negative value resets it back to the default.
1217 */
1218qreal UCListItemPrivate::swipeOvershoot() const
1219{
1220 return overshoot;
1221}
1222void UCListItemPrivate::setSwipeOvershoot(qreal overshoot)
1223{
1224 // same value should be guarded only if the style hasn't been loaded yet
1225 // swipeOvershoot can be set to 0 prior the style is loaded.
1226 if (this->overshoot == overshoot && styleItem) {
1227 return;
1228 }
1229 customOvershoot = (overshoot >= 0.0);
1230 this->overshoot = (overshoot < 0.0) ?
1231 // reset, use style to get the overshoot value
1232 (styleItem ? styleItem->m_swipeOvershoot : 0.0) :
1233 overshoot;
1234 update();
1235 Q_Q(UCListItem);
1236 Q_EMIT q->swipeOvershootChanged();
1237}
1238
1239/*!
1240 * \qmlproperty list<Object> ListItem::listItemData
1241 * \default
1242 * \internal
1243 * Overloaded default property containing all the children and resources.
1244 */
1245QQmlListProperty<QObject> UCListItemPrivate::data()
1246{
1247 return QQuickItemPrivate::get(contentItem)->data();
1248}
1249
1250/*!
1251 * \qmlproperty list<Item> ListItem::listItemChildren
1252 * \internal
1253 * Overloaded default property containing all the visible children of the item.
1254 */
1255QQmlListProperty<QQuickItem> UCListItemPrivate::children()
1256{
1257 return QQuickItemPrivate::get(contentItem)->children();
1258}
1259
1260#include "moc_uclistitem.cpp"
01261
=== added file 'modules/Ubuntu/Components/plugin/uclistitem.h'
--- modules/Ubuntu/Components/plugin/uclistitem.h 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/plugin/uclistitem.h 2014-12-03 06:10:30 +0000
@@ -0,0 +1,126 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef UCLISTITEM_H
18#define UCLISTITEM_H
19
20#include <QtQuick/QQuickItem>
21#include "ucstyleditembase.h"
22
23class UCListItemContent;
24class UCListItemDivider;
25class UCListItemActions;
26class UCListItemAttached;
27class QQuickPropertyAnimation;
28class UCListItemPrivate;
29class UCListItem : public UCStyledItemBase
30{
31 Q_OBJECT
32 Q_PROPERTY(QQuickItem *contentItem READ contentItem CONSTANT)
33 Q_PROPERTY(UCListItemDivider *divider READ divider CONSTANT)
34 Q_PROPERTY(UCListItemActions *leadingActions READ leadingActions WRITE setLeadingActions NOTIFY leadingActionsChanged DESIGNABLE false)
35 Q_PROPERTY(UCListItemActions *trailingActions READ trailingActions WRITE setTrailingActions NOTIFY trailingActionsChanged DESIGNABLE false)
36 Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged)
37 Q_PRIVATE_PROPERTY(UCListItem::d_func(), qreal swipeOvershoot READ swipeOvershoot WRITE setSwipeOvershoot NOTIFY swipeOvershootChanged)
38 Q_PRIVATE_PROPERTY(UCListItem::d_func(), bool contentMoving READ contentMoving NOTIFY contentMovingChanged)
39 Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
40 Q_PROPERTY(QColor highlightColor READ highlightColor WRITE setHighlightColor NOTIFY highlightColorChanged)
41 Q_PRIVATE_PROPERTY(UCListItem::d_func(), QQmlListProperty<QObject> listItemData READ data DESIGNABLE false)
42 Q_PRIVATE_PROPERTY(UCListItem::d_func(), QQmlListProperty<QQuickItem> listItemChildren READ children NOTIFY listItemChildrenChanged DESIGNABLE false)
43 // FIXME move these to StyledItemBase with subtheming
44 Q_PRIVATE_PROPERTY(UCListItem::d_func(), QQmlComponent *style READ style WRITE setStyle NOTIFY styleChanged)
45 Q_PRIVATE_PROPERTY(UCListItem::d_func(), QQuickItem *__styleInstance READ styleInstance NOTIFY __styleInstanceChanged)
46 Q_CLASSINFO("DefaultProperty", "listItemData")
47public:
48 explicit UCListItem(QQuickItem *parent = 0);
49 ~UCListItem();
50
51 static UCListItemAttached *qmlAttachedProperties(QObject *owner);
52
53 QQuickItem *contentItem() const;
54 UCListItemDivider *divider() const;
55 UCListItemActions *leadingActions() const;
56 void setLeadingActions(UCListItemActions *options);
57 UCListItemActions *trailingActions() const;
58 void setTrailingActions(UCListItemActions *options);
59 bool pressed() const;
60 QColor color() const;
61 void setColor(const QColor &color);
62 QColor highlightColor() const;
63 void setHighlightColor(const QColor &color);
64
65protected:
66 void componentComplete();
67 QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data);
68 void itemChange(ItemChange change, const ItemChangeData &data);
69 void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
70 void mousePressEvent(QMouseEvent *event);
71 void mouseReleaseEvent(QMouseEvent *event);
72 void mouseMoveEvent(QMouseEvent *event);
73 bool childMouseEventFilter(QQuickItem *child, QEvent *event);
74 bool eventFilter(QObject *, QEvent *);
75
76Q_SIGNALS:
77 void leadingActionsChanged();
78 void trailingActionsChanged();
79 void pressedChanged();
80 void swipeOvershootChanged();
81 void contentMovingChanged();
82 void colorChanged();
83 void highlightColorChanged();
84 void listItemChildrenChanged();
85
86 void clicked();
87
88 void styleChanged();
89 void __styleInstanceChanged();
90
91 void contentMovementStarted();
92 void contentMovementEnded();
93
94public Q_SLOTS:
95
96private:
97 Q_DECLARE_PRIVATE(UCListItem)
98 Q_PRIVATE_SLOT(d_func(), void _q_updateThemedData())
99 Q_PRIVATE_SLOT(d_func(), void _q_rebound())
100 Q_PRIVATE_SLOT(d_func(), void _q_updateSize())
101 Q_PRIVATE_SLOT(d_func(), void _q_updateIndex())
102};
103
104QML_DECLARE_TYPEINFO(UCListItem, QML_HAS_ATTACHED_PROPERTIES)
105
106class UCListItemAttachedPrivate;
107class UCListItemAttached : public QObject
108{
109 Q_OBJECT
110public:
111 explicit UCListItemAttached(QObject *owner);
112 ~UCListItemAttached();
113
114 bool listenToRebind(UCListItem *item, bool listen);
115 void disableInteractive(UCListItem *item, bool disable);
116 bool isMoving();
117 bool isBoundTo(UCListItem *item);
118
119private Q_SLOTS:
120 void unbindItem();
121private:
122 Q_DECLARE_PRIVATE(UCListItemAttached)
123 QScopedPointer<UCListItemAttachedPrivate> d_ptr;
124};
125
126#endif // UCLISTITEM_H
0127
=== added file 'modules/Ubuntu/Components/plugin/uclistitem_p.h'
--- modules/Ubuntu/Components/plugin/uclistitem_p.h 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/plugin/uclistitem_p.h 2014-12-03 06:10:30 +0000
@@ -0,0 +1,202 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef UCVIEWITEM_P_H
18#define UCVIEWITEM_P_H
19
20#include "uclistitem.h"
21#include "ucstyleditembase_p.h"
22#include <QtCore/QPointer>
23#include <QtQuick/private/qquickrectangle_p.h>
24
25class QQuickFlickable;
26class QQuickPropertyAnimation;
27class UCListItemContent;
28class UCListItemDivider;
29class UCListItemActions;
30class UCListItemSnapAnimator;
31class UCListItemStyle;
32class UCListItemPrivate : public UCStyledItemBasePrivate
33{
34 Q_DECLARE_PUBLIC(UCListItem)
35public:
36 UCListItemPrivate();
37 virtual ~UCListItemPrivate();
38 void init();
39
40 static inline UCListItemPrivate *get(UCListItem *that)
41 {
42 Q_ASSERT(that);
43 return that->d_func();
44 }
45
46 void _q_updateThemedData();
47 void _q_rebound();
48 void promptRebound();
49 void _q_updateSize();
50 void _q_updateIndex();
51 int index();
52 bool canHighlight(QMouseEvent *event);
53 void setPressed(bool pressed);
54 void setSwiped(bool tugged);
55 bool grabPanel(UCListItemActions *optionList, bool isTugged);
56 void listenToRebind(bool listen);
57 void resize();
58 void update();
59 void clampAndMoveX(qreal &x, qreal dx);
60
61 bool pressed:1;
62 bool contentMoved:1;
63 bool highlightColorChanged:1;
64 bool swiped:1;
65 bool suppressClick:1;
66 bool ready:1;
67 bool customStyle:1;
68 bool customColor:1;
69 bool customOvershoot:1;
70 bool flicked:1;
71 qreal xAxisMoveThresholdGU;
72 qreal overshoot;
73 QPointF lastPos;
74 QPointF pressedPos;
75 QColor color;
76 QColor highlightColor;
77 QPointer<QQuickItem> countOwner;
78 QPointer<QQuickFlickable> flickable;
79 QPointer<UCListItemAttached> attachedProperties;
80 QQuickItem *contentItem;
81 UCListItemDivider *divider;
82 UCListItemActions *leadingActions;
83 UCListItemActions *trailingActions;
84 UCListItemSnapAnimator *animator;
85
86 // FIXME move these to StyledItemBase togehther with subtheming.
87 QQmlComponent *styleComponent;
88 UCListItemStyle *styleItem;
89
90 // getters/setters
91 qreal swipeOvershoot() const;
92 void setSwipeOvershoot(qreal overshoot);
93 QQmlListProperty<QObject> data();
94 QQmlListProperty<QQuickItem> children();
95 bool contentMoving() const;
96 void setContentMoving(bool moved);
97 QQmlComponent *style() const;
98 void setStyle(QQmlComponent *delegate);
99 bool loadStyle(bool reload);
100 void initStyleItem();
101 QQuickItem *styleInstance() const;
102};
103
104class PropertyChange;
105class UCListItemAttachedPrivate
106{
107 Q_DECLARE_PUBLIC(UCListItemAttached)
108public:
109 UCListItemAttachedPrivate(UCListItemAttached *qq);
110 ~UCListItemAttachedPrivate();
111
112 void clearFlickablesList();
113 void buildFlickablesList();
114 void clearChangesList();
115 void buildChangesList(const QVariant &newValue);
116
117 UCListItemAttached *q_ptr;
118 bool globalDisabled;
119 QList< QPointer<QQuickFlickable> > flickables;
120 QList< PropertyChange* > changes;
121 QPointer<UCListItem> boundItem;
122 QPointer<UCListItem> disablerItem;
123};
124
125class UCListItemDivider : public QObject
126{
127 Q_OBJECT
128 Q_PROPERTY(bool visible MEMBER m_visible WRITE setVisible NOTIFY visibleChanged)
129 Q_PROPERTY(qreal leftMargin MEMBER m_leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged)
130 Q_PROPERTY(qreal rightMargin MEMBER m_rightMargin WRITE setRightMargin NOTIFY rightMarginChanged)
131 Q_PROPERTY(QColor colorFrom MEMBER m_colorFrom WRITE setColorFrom NOTIFY colorFromChanged)
132 Q_PROPERTY(QColor colorTo MEMBER m_colorTo WRITE setColorTo NOTIFY colorToChanged)
133public:
134 explicit UCListItemDivider(QObject *parent = 0);
135 ~UCListItemDivider();
136 void init(UCListItem *listItem);
137
138Q_SIGNALS:
139 void visibleChanged();
140 void leftMarginChanged();
141 void rightMarginChanged();
142 void colorFromChanged();
143 void colorToChanged();
144
145protected:
146 QSGNode *paint(QSGNode *node, const QRectF &rect);
147
148private Q_SLOTS:
149 void unitsChanged();
150 void paletteChanged();
151
152private:
153 void updateGradient();
154 void setVisible(bool visible);
155 void setLeftMargin(qreal leftMargin);
156 void setRightMargin(qreal rightMargin);
157 void setColorFrom(const QColor &color);
158 void setColorTo(const QColor &color);
159
160 bool m_visible:1;
161 bool m_leftMarginChanged:1;
162 bool m_rightMarginChanged:1;
163 bool m_colorFromChanged:1;
164 bool m_colorToChanged:1;
165 qreal m_thickness;
166 qreal m_leftMargin;
167 qreal m_rightMargin;
168 QColor m_colorFrom;
169 QColor m_colorTo;
170 QGradientStops m_gradient;
171 UCListItemPrivate *m_listItem;
172 friend class UCListItem;
173 friend class UCListItemPrivate;
174};
175
176QColor getPaletteColor(const char *profile, const char *color);
177
178QML_DECLARE_TYPE(UCListItemDivider)
179
180class QQuickPropertyAnimation;
181class UCListItemSnapAnimator : public QObject
182{
183 Q_OBJECT
184public:
185 UCListItemSnapAnimator(UCListItem *item);
186 ~UCListItemSnapAnimator();
187
188 bool snap(qreal to);
189 void complete();
190
191public Q_SLOTS:
192 void snapOut();
193 void snapIn();
194
195 QQuickPropertyAnimation *getDefaultAnimation();
196
197private:
198 bool active;
199 UCListItem *item;
200};
201
202#endif // UCVIEWITEM_P_H
0203
=== added file 'modules/Ubuntu/Components/plugin/uclistitemactions.cpp'
--- modules/Ubuntu/Components/plugin/uclistitemactions.cpp 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/plugin/uclistitemactions.cpp 2014-12-03 06:10:30 +0000
@@ -0,0 +1,541 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "uclistitemactions.h"
18#include "uclistitemactions_p.h"
19#include "uclistitem_p.h"
20#include "quickutils.h"
21#include "i18n.h"
22#include "plugin.h"
23#include <QtQml/QQmlInfo>
24#include "ucaction.h"
25#include "ucunits.h"
26#include "uclistitemstyle.h"
27
28UCListItemActionsPrivate::UCListItemActionsPrivate()
29 : QObjectPrivate()
30 , status(UCListItemActions::Disconnected)
31 , offsetDragged(0)
32 , optionSlotWidth(0.0)
33 , delegate(0)
34 , panelDelegate(0)
35 , panelItem(0)
36{
37}
38UCListItemActionsPrivate::~UCListItemActionsPrivate()
39{
40}
41
42void UCListItemActionsPrivate::_q_updateDraggedOffset()
43{
44 UCListItem *listItem = qobject_cast<UCListItem*>(panelItem->parentItem());
45 if (!listItem) {
46 return;
47 }
48
49 Q_Q(UCListItemActions);
50 offsetDragged = (status == UCListItemActions::Leading) ? panelItem->width() + panelItem->x() :
51 listItem->width() - panelItem->x();
52 if (offsetDragged < 0.0) {
53 offsetDragged = 0.0;
54 }
55}
56
57UCListItemActionsAttached *UCListItemActionsPrivate::attachedObject()
58{
59 if (!panelItem) {
60 return 0;
61 }
62 return static_cast<UCListItemActionsAttached*>(
63 qmlAttachedPropertiesObject<UCListItemActions>(panelItem, false));
64}
65
66/*
67 * Connects a ListItem to the ListItemActions' panel and returns true upon successful connection.
68 * Connection may fail if there is a different ListItem connected. In this case the new ListItem
69 * will be queued and automatically connected when the previous ListItem disconnects.
70 * The panel is only re-created when a different Component is used to create it.
71 * FIXME - despite each ListItem uses the same document to create the panel, theming engine
72 * provides different Component objects for each, due to not caching the components created.
73 * This must be fixed to optimize memory usage.
74 */
75bool UCListItemActionsPrivate::connectToListItem(UCListItemActions *actions, UCListItem *listItem, bool leading)
76{
77 UCListItemActionsPrivate *_this = get(actions);
78 if (!_this) {
79 return false;
80 }
81 // do we have a panel created already?
82 if (_this->panelItem) {
83 if (isConnectedTo(actions, listItem)) {
84 return true;
85 }
86 if (_this->panelItem->parentItem() && _this->panelItem->parentItem() != listItem) {
87 // set the requesting listItem as queuedItem
88 _this->queuedItem = listItem;
89 return false;
90 }
91 }
92 // no parent set or panelItem yet, proceed with panel creation
93 UCListItemPrivate *pItem = UCListItemPrivate::get(listItem);
94 pItem->initStyleItem();
95 if (!pItem->styleItem || (pItem->styleItem && !_this->createPanelItem(pItem->styleItem->m_actionsDelegate))) {
96 return false;
97 }
98
99 // check if the panel is still connected to a ListItem
100 // this may happen if there is a swipe over an other item while the previous
101 // one is rebounding
102 _this->panelItem->setParentItem(listItem);
103 if (_this->attachedObject()) {
104 _this->attachedObject()->connectListItem(listItem, true);
105 }
106 _this->offsetDragged = 0.0;
107 _this->status = leading ? UCListItemActions::Leading : UCListItemActions::Trailing;
108 Q_EMIT actions->statusChanged(_this->status);
109 return true;
110}
111
112void UCListItemActionsPrivate::disconnectFromListItem(UCListItemActions *actions)
113{
114 UCListItemActionsPrivate *_this = get(actions);
115 if (!_this || !_this->panelItem || !_this->panelItem->parentItem()) {
116 return;
117 }
118
119 if (_this->attachedObject()) {
120 _this->attachedObject()->connectListItem(static_cast<UCListItem*>(_this->panelItem->parentItem()), false);
121 }
122 _this->panelItem->setParentItem(0);
123 _this->status = UCListItemActions::Disconnected;
124 Q_EMIT actions->statusChanged(_this->status);
125 // if there was a queuedItem, make it grab the actions list
126 if (_this->queuedItem) {
127 UCListItemPrivate::get(_this->queuedItem.data())->grabPanel(actions, true);
128 // remove item from queue
129 _this->queuedItem.clear();
130 }
131}
132
133bool UCListItemActionsPrivate::isConnectedTo(UCListItemActions *actions, UCListItem *listItem)
134{
135 UCListItemActionsPrivate *_this = get(actions);
136 return _this && _this->panelItem &&
137 (_this->status != UCListItemActions::Disconnected) &&
138 (_this->panelItem->parentItem() == listItem);
139}
140
141QQuickItem *UCListItemActionsPrivate::createPanelItem(QQmlComponent *panel)
142{
143 if (panelItem && panelDelegate == panel) {
144 return panelItem;
145 }
146 // delete the panel if the next item's actionsDelegate differs from the
147 // one this panel was created from
148 if (panelDelegate != panel) {
149 delete panelItem;
150 panelItem = 0;
151 }
152
153 Q_Q(UCListItemActions);
154 if (!panel) {
155 qmlInfo(q) << UbuntuI18n::instance().tr("actionsDelegate not set!");
156 return 0;
157 }
158
159 panelDelegate = panel;
160 if (!panelDelegate->isError()) {
161 panelItem = qobject_cast<QQuickItem*>(panelDelegate->beginCreate(qmlContext(q)));
162 if (panelItem) {
163 QQml_setParent_noEvent(panelItem, q);
164 // add panelItem to data so we can access it in case is needed (i.e. tests)
165 data.append(panelItem);
166 // create attached property!
167 UCListItemActionsAttached *attached = static_cast<UCListItemActionsAttached*>(
168 qmlAttachedPropertiesObject<UCListItemActions>(panelItem));
169 if (!attached->container()) {
170 attached->setList(q);
171 } else {
172 // container is set, but we need to emit the signal again so we get the
173 // attached props updated for those cases when the attached property is
174 // created before the statement above
175 Q_EMIT attached->containerChanged();
176 }
177 panelDelegate->completeCreate();
178
179 // calculate option's slot size
180 offsetDragged = 0.0;
181 // connect to panel to catch dragging
182 QObject::connect(panelItem, SIGNAL(xChanged()), q, SLOT(_q_updateDraggedOffset()));
183 }
184 } else {
185 qmlInfo(q) << panelDelegate->errorString();
186 }
187
188 return panelItem;
189}
190
191/*!
192 * \qmltype ListItemActions
193 * \instantiates UCListItemActions
194 * \inherits QtQObject
195 * \inqmlmodule Ubuntu.Components 1.2
196 * \since Ubuntu.Components 1.2
197 * \ingroup unstable-ubuntu-listitems
198 * \brief Provides configuration for actions to be added to a ListItem.
199 *
200 * ListItem accepts actions that can be configured to appear when swiped to left
201 * or right. The API does not limit the number of actions to be assigned for leading
202 * or trailing actions, however the design constrains are allowing a maximum of
203 * 1 action on leading- and a maximum of 3 actions on trailing side of the ListItem.
204 *
205 * The \l actions are Action instances or elements derived from Action. The default
206 * visualization of the actions can be overridden using the \l delegate property,
207 * and the default implementation uses the \c name property of the Action.
208 *
209 * The leading and trailing actions are placed on a panel item, which is created
210 * the first time the actions are accessed. The colors of the panel is taken from
211 * the theme's palette.
212 *
213 * When swiped, panels reveal the actions one by one. In case an action is revealed
214 * more than 50%, the action will be snapped and revealed completely. This is also
215 * valid for the case when the action is visible less than 50%, in which case the
216 * action is hidden. Actions can be triggered by tapping.
217 *
218 * \note You can use the same ListItemActions for leading and for trailing actions
219 * the same time only if the instance is used by different groups of list items,
220 * where one group uses it as leading and other group as trailing. In any other
221 * circumstances use separate ListItemActions for leading and trailing actions.
222 * \qml
223 * import QtQuick 2.2
224 * import Ubuntu.Components 1.2
225 * MainView {
226 * width: units.gu(40)
227 * height: units.gu(71)
228 *
229 * ListItemActions {
230 * id: sharedActions
231 * actions: [
232 * Action {
233 * iconName: "search"
234 * },
235 * Action {
236 * iconName: "edit"
237 * },
238 * Action {
239 * iconName: "copy"
240 * }
241 * ]
242 * }
243 *
244 * Column {
245 * ListItem {
246 * leadingActions: sharedActions
247 * }
248 * UbuntuListView {
249 * anchors.fill: parent
250 * model: 10000
251 * delegate: ListItem {
252 * trailingActions: sharedActions
253 * }
254 * }
255 * }
256 * }
257 * \endqml
258 *
259 * \section3 Using with ListViews
260 * ListItemActions instances can be shared between ListItem instances within the
261 * same view. When shared, the memory footprint of the view will be lot smaller,
262 * as there will be no individual panel created for each list's actions visualization.
263 * Depending on how long the initialization of the component used in \l {ListItemStyle::actionsDelegate}
264 * {actionsDelegate} takes, creation time will be also reduced to one time per view.
265 * However, this implies that swiping a new ListItem content while another one is
266 * swiped will result in showing the newly swiped item's panel delayed, as the
267 * panel can be shown only after the previous item's snapping is completed. Depending
268 * on the \l {ListItemStyle::snapAnimation}{snapAnimation} duration, this may take some time, and the
269 * response time of the UI can become unacceptable.
270 *
271 * Having individual ListItemActions instances increases the memory footprint,
272 * however the UI will be more responsive as swiping individual ListItems will
273 * not have to wait till the previous ListItem's panel is snapped out (rebound).
274 * On the other hand, memory consumption will increase significantly due to
275 * separate panel creation, and performance may decrease with up to 40%, depending
276 * on what way are the actions declared, within the ListItemActions or as shared
277 * actions.
278 *
279 * The example above illustrates how to share ListItemActions between ListItem
280 * delegates, which can be the worst-performant but most lightwaight memory consumer
281 * setup. The following example illustrates the worst case:
282 * \qml
283 * import QtQuick 2.2
284 * import Ubuntu.Components 1.2
285 * MainView {
286 * width: units.gu(40)
287 * height: units.gu(71)
288 *
289 * UbuntuListView {
290 * anchors.fill: parent
291 * model: 10000
292 * delegate: ListItem {
293 * leadingActions: ListItemActions {
294 * actions: [
295 * Action {
296 * iconName: "delete"
297 * }
298 * ]
299 * }
300 * trailingActions: ListItemActions {
301 * actions: [
302 * Action {
303 * iconName: "search"
304 * },
305 * Action {
306 * iconName: "edit"
307 * },
308 * Action {
309 * iconName: "copy"
310 * }
311 * ]
312 * }
313 * }
314 * }
315 * }
316 * \endqml
317 *
318 * This example can be optimized by sharing the action arrays between the items:
319 * \qml
320 * import QtQuick 2.2
321 * import Ubuntu.Components 1.2
322 * MainView {
323 * width: units.gu(40)
324 * height: units.gu(71)
325 *
326 * property list<Action> leading: [
327 * Action {
328 * iconName: "delete"
329 * }
330 * ]
331 * property list<Action> trailing: [
332 * Action {
333 * iconName: "search"
334 * },
335 * Action {
336 * iconName: "edit"
337 * },
338 * Action {
339 * iconName: "copy"
340 * }
341 * ]
342 *
343 * UbuntuListView {
344 * anchors.fill: parent
345 * model: 10000
346 * delegate: ListItem {
347 * leadingActions: ListItemActions {
348 * actions: leading
349 * }
350 * trailingActions: ListItemActions {
351 * actions: trailing
352 * }
353 * }
354 * }
355 * }
356 * \endqml
357 *
358 * \section3 Action parameter types
359 * Actions handled by the ListItem are all triggered with the ListItem's index
360 * as parameter. This index can either be the model index when used with ListView,
361 * or the child index from the parentItem's childItems list. Actions can use this
362 * parameter to identify the instance of the ListItem on which it was executed,
363 * in which case ListItem will change the type from \c Actions.None to \c Actions.Integer
364 * when it is triggered.
365 *
366 * \section3 Attached properties
367 * ListItemActions provides a set of attached properties to the panels visualizing
368 * the actions. These properties can be used by implementations visualizing the
369 * actions.
370 */
371
372UCListItemActions::UCListItemActions(QObject *parent)
373 : QObject(*(new UCListItemActionsPrivate), parent)
374{
375}
376UCListItemActions::~UCListItemActions()
377{
378}
379
380UCListItemActionsAttached *UCListItemActions::qmlAttachedProperties(QObject *owner)
381{
382 /*
383 * Detect the attachee, whether is it a child item of the panelItem. The panelItem
384 * itself cannot be detected, as the object can be attached during the call of
385 * component.beginCreate().
386 */
387 UCListItemActionsAttached *attached = new UCListItemActionsAttached(owner);
388 QQuickItem *item = qobject_cast<QQuickItem*>(owner);
389 while (item) {
390 // has item our attached property?
391 UCListItemActionsAttached *itemAttached = static_cast<UCListItemActionsAttached*>(
392 qmlAttachedPropertiesObject<UCListItemActions>(item, false));
393 if (itemAttached) {
394 attached->setList(itemAttached->container());
395 break;
396 }
397 item = item->parentItem();
398 }
399 return attached;
400}
401
402/*!
403 * \qmlproperty Component ListItemActions::delegate
404 * The property holds the custom delegate to visualize the actions listed in the
405 * ListItemActions. When set to null, the default delegate specified by the \l
406 * {ListItemStyle::actionsDelegate}{actionsDelegate} will be used.
407 *
408 * ListItemActions provides the \c action context property which contains the
409 * Action instance currently visualized. Using this property delegates can access
410 * the information to be visualized. The action is triggered by the panel item
411 * holding the visualized action, therefore only visualization is needed by the
412 * custom delegate. The other context property exposed to delegates is the \c
413 * index, which specifies the index of the action visualized.
414 *
415 * Specifying a custom delegate will not override the triggering logic of the
416 * action, that will be still handled by the panel itself. However custom delegates
417 * may still need to distinguish the pressed/released state visually. This can
418 * be achieved using the \c pressed context property, which informs the delegate
419 * about the pressed state of the action.
420 *
421 * The delegate height is set automatically by the panel item, only the width must
422 * be specified which will be clamped between height and the maximum width of the
423 * list item divided by the number of actions in the list.
424 * \qml
425 * import QtQuick 2.2
426 * import Ubuntu.Components 1.2
427 *
428 * MainView {
429 * width: units.gu(40)
430 * height: units.gu(71)
431 *
432 * UbuntuListView {
433 * anchors.fill: parent
434 * model: 50
435 * delegate: ListItem {
436 * trailingActions: actionsList
437 * }
438 * ListItemActions {
439 * id: actionsList
440 * delegate: Column {
441 * width: height + units.gu(2)
442 * Icon {
443 * name: action.iconName
444 * width: units.gu(3)
445 * height: width
446 * color: pressed ? "blue" : "lightblue"
447 * anchors.horizontalCenter: parent.horizontalCenter
448 * }
449 * Label {
450 * text: action.text + "#" + index
451 * width: parent.width
452 * horizontalAlignment: Text.AlignHCenter
453 * }
454 * }
455 * actions: Action {
456 * iconName: "starred"
457 * text: "Star"
458 * }
459 * }
460 * }
461 * }
462 * \endqml
463 * \note Putting a Rectangle in the delegate can be used to override the color
464 * of the panel.
465 *
466 * Defaults to null.
467 */
468QQmlComponent *UCListItemActions::delegate() const
469{
470 Q_D(const UCListItemActions);
471 return d->delegate;
472}
473void UCListItemActions::setDelegate(QQmlComponent *delegate)
474{
475 Q_D(UCListItemActions);
476 if (d->delegate == delegate) {
477 return;
478 }
479 d->delegate = delegate;
480 Q_EMIT delegateChanged();
481}
482
483/*!
484 * \qmlproperty list<Action> ListItemActions::actions
485 * The property holds the actions to be displayed. It can hold instances cached or
486 * declared in place. An example of cached actions:
487 * \qml
488 * ListItemActions {
489 * id: cachedActions
490 * actions: [
491 * copyAction, searchAction, cutAction
492 * ]
493 * }
494 * \endqml
495 */
496int UCListItemActionsPrivate::actions_count(QQmlListProperty<UCAction> *p)
497{
498 return reinterpret_cast<QList<UCAction *> *>(p->data)->count();
499}
500void UCListItemActionsPrivate::actions_append(QQmlListProperty<UCAction> *p, UCAction *v)
501{
502 // check action type before adding it
503 if (v->m_parameterType == UCAction::None) {
504 v->setProperty("parameterType", UCAction::Integer);
505 }
506 reinterpret_cast<QList<UCAction *> *>(p->data)->append(v);
507}
508UCAction *UCListItemActionsPrivate::actions_at(QQmlListProperty<UCAction> *p, int i)
509{
510 return reinterpret_cast<QList<UCAction *> *>(p->data)->at(i);
511}
512
513void UCListItemActionsPrivate::actions_clear(QQmlListProperty<UCAction> *p)
514{
515 reinterpret_cast<QList<UCAction *> *>(p->data)->clear();
516}
517
518QQmlListProperty<UCAction> UCListItemActions::actions()
519{
520 Q_D(UCListItemActions);
521 return QQmlListProperty<UCAction>(this, &d->actions,
522 UCListItemActionsPrivate::actions_append,
523 UCListItemActionsPrivate::actions_count,
524 UCListItemActionsPrivate::actions_at,
525 UCListItemActionsPrivate::actions_clear
526 );
527}
528
529/*!
530 * \internal
531 * \qmlproperty list<QtObject> ListItemActions::data
532 * \default
533 * The property holds any additional content added to the ListItemActions.
534 */
535QQmlListProperty<QObject> UCListItemActions::data()
536{
537 Q_D(UCListItemActions);
538 return QQmlListProperty<QObject>(this, d->data);
539}
540
541#include "moc_uclistitemactions.cpp"
0542
=== added file 'modules/Ubuntu/Components/plugin/uclistitemactions.h'
--- modules/Ubuntu/Components/plugin/uclistitemactions.h 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/plugin/uclistitemactions.h 2014-12-03 06:10:30 +0000
@@ -0,0 +1,113 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef UCLISTITEMACTIONS_H
18#define UCLISTITEMACTIONS_H
19
20#include <QtCore/QObject>
21#include "uclistitem_p.h"
22
23class QQmlComponent;
24class UCAction;
25class UCListItemActionsAttached;
26class UCListItemActionsPrivate;
27class UCListItemActions : public QObject
28{
29 Q_OBJECT
30 Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
31 Q_PROPERTY(QQmlListProperty<UCAction> actions READ actions CONSTANT)
32 Q_PROPERTY(QQmlListProperty<QObject> data READ data)
33 Q_CLASSINFO("DefaultProperty", "data")
34 Q_ENUMS(Status)
35public:
36 enum Status {
37 Disconnected,
38 Leading,
39 Trailing
40 };
41 explicit UCListItemActions(QObject *parent = 0);
42 ~UCListItemActions();
43
44 static UCListItemActionsAttached *qmlAttachedProperties(QObject *owner);
45
46 QQmlComponent *delegate() const;
47 void setDelegate(QQmlComponent *delegate);
48 QQmlListProperty<UCAction> actions();
49 QQmlListProperty<QObject> data();
50
51Q_SIGNALS:
52 void delegateChanged();
53 void statusChanged(Status status);
54
55private:
56 Q_DECLARE_PRIVATE(UCListItemActions)
57 Q_PRIVATE_SLOT(d_func(), void _q_updateDraggedOffset())
58};
59
60class UCListItemActionsAttached : public QObject
61{
62 Q_OBJECT
63 Q_PROPERTY(UCListItemActions *container READ container NOTIFY containerChanged)
64 Q_PROPERTY(QQmlListProperty<UCAction> visibleActions READ visibleActions NOTIFY visibleActionsChanged)
65 Q_PROPERTY(UCListItem *listItem READ listItem NOTIFY listItemChanged)
66 Q_PROPERTY(int listItemIndex READ listItemIndex NOTIFY listItemIndexChanged)
67 Q_PROPERTY(qreal offset READ offset NOTIFY offsetChanged)
68 Q_PROPERTY(UCListItemActions::Status status READ status NOTIFY statusChanged)
69 Q_PROPERTY(bool swiping READ swiping NOTIFY swipingChanged)
70public:
71 UCListItemActionsAttached(QObject *parent = 0);
72 ~UCListItemActionsAttached();
73 void setList(UCListItemActions *list);
74 void connectListItem(UCListItem *item, bool connect);
75
76 UCListItemActions *container() const
77 {
78 return m_container.data();
79 }
80 QQmlListProperty<UCAction> visibleActions();
81 UCListItem *listItem();
82 int listItemIndex();
83 bool swiping();
84 qreal offset();
85 UCListItemActions::Status status();
86
87
88public Q_SLOTS:
89 void snapToPosition(qreal position);
90
91Q_SIGNALS:
92 void containerChanged();
93 void visibleActionsChanged();
94 void listItemChanged();
95 void listItemIndexChanged();
96 void offsetChanged();
97 void statusChanged();
98 void swipingChanged();
99
100private:
101 QPointer<UCListItemActions> m_container;
102 QList<UCAction*> m_visibleActions;
103 bool m_swiping;
104 friend class UCListItemAction;
105
106private Q_SLOTS:
107 void updateVisibleActions();
108 void updateSwipeState();
109};
110
111QML_DECLARE_TYPEINFO(UCListItemActions, QML_HAS_ATTACHED_PROPERTIES)
112
113#endif // UCLISTITEMACTIONS_H
0114
=== added file 'modules/Ubuntu/Components/plugin/uclistitemactions_p.h'
--- modules/Ubuntu/Components/plugin/uclistitemactions_p.h 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/plugin/uclistitemactions_p.h 2014-12-03 06:10:30 +0000
@@ -0,0 +1,61 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef UCLISTITEMACTIONS_P_H
18#define UCLISTITEMACTIONS_P_H
19
20#include "uclistitemactions.h"
21#include "QtCore/private/qobject_p.h"
22#include <QtQml/QQmlListProperty>
23
24class UCListItem;
25class UCListItemActionsPrivate : public QObjectPrivate {
26 Q_DECLARE_PUBLIC(UCListItemActions)
27public:
28 UCListItemActionsPrivate();
29 ~UCListItemActionsPrivate();
30 static UCListItemActionsPrivate* get(UCListItemActions *actions)
31 {
32 return actions ? actions->d_func() : 0;
33 }
34
35 UCListItemActions::Status status;
36 qreal offsetDragged;
37 qreal optionSlotWidth;
38
39 QQmlComponent *delegate;
40 QQmlComponent *panelDelegate;
41 QQuickItem *panelItem;
42 QList<UCAction*> actions;
43 QList<QObject*> data;
44 QPointer<UCListItem> queuedItem;
45
46 void _q_updateDraggedOffset();
47 UCListItemActionsAttached *attachedObject();
48
49 static bool connectToListItem(UCListItemActions *options, UCListItem *listItem, bool leading);
50 static void disconnectFromListItem(UCListItemActions *options);
51 static bool isConnectedTo(UCListItemActions *options, UCListItem *listItem);
52
53 static int actions_count(QQmlListProperty<UCAction> *p);
54 static void actions_append(QQmlListProperty<UCAction> *p, UCAction *v);
55 static UCAction *actions_at(QQmlListProperty<UCAction>*, int);
56 static void actions_clear(QQmlListProperty<UCAction>*);
57
58 QQuickItem *createPanelItem(QQmlComponent *delegate);
59};
60
61#endif // UCLISTITEMACTIONS_P_H
062
=== added file 'modules/Ubuntu/Components/plugin/uclistitemactionsattached.cpp'
--- modules/Ubuntu/Components/plugin/uclistitemactionsattached.cpp 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/plugin/uclistitemactionsattached.cpp 2014-12-03 06:10:30 +0000
@@ -0,0 +1,247 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "uclistitem.h"
18#include "uclistitem_p.h"
19#include "uclistitemactions.h"
20#include "uclistitemactions_p.h"
21#include "ucunits.h"
22#include "ucaction.h"
23
24UCListItemActionsAttached::UCListItemActionsAttached(QObject *parent)
25 : QObject(parent)
26 , m_swiping(false)
27{
28}
29
30UCListItemActionsAttached::~UCListItemActionsAttached()
31{
32}
33
34/*!
35 * \qmlattachedproperty ListItemActions ListItemActions::container
36 * \readonly
37 * The property holds the instance of the \l ListItemActions the ListItem's actions
38 * panel is visualizing.
39 */
40void UCListItemActionsAttached::setList(UCListItemActions *list)
41{
42 if (list == m_container) {
43 return;
44 }
45 m_container = list;
46 if (!m_container.isNull()) {
47 // connect statusChanged() to update status, listItem, listItemIndex and overshoot values
48 QObject::connect(m_container.data(), &UCListItemActions::statusChanged,
49 this, &UCListItemActionsAttached::statusChanged);
50 QObject::connect(m_container.data(), &UCListItemActions::statusChanged,
51 this, &UCListItemActionsAttached::listItemChanged);
52 QObject::connect(m_container.data(), &UCListItemActions::statusChanged,
53 this, &UCListItemActionsAttached::listItemIndexChanged);
54
55 UCListItemActionsPrivate *actions = UCListItemActionsPrivate::get(m_container.data());
56 // connect panel's xChanged to update the dragged offset
57 QObject::connect(actions->panelItem, &QQuickItem::xChanged,
58 this, &UCListItemActionsAttached::offsetChanged);
59
60 // connect actions to get updates about visible changes
61 Q_FOREACH(UCAction *action, UCListItemActionsPrivate::get(m_container)->actions) {
62 QObject::connect(action, &UCAction::visibleChanged,
63 this, &UCListItemActionsAttached::updateVisibleActions);
64 }
65 updateVisibleActions();
66 }
67 Q_EMIT containerChanged();
68 Q_EMIT visibleActionsChanged();
69}
70
71void UCListItemActionsAttached::connectListItem(UCListItem *item, bool connect)
72{
73 if (!item) {
74 return;
75 }
76 if (connect) {
77 QObject::connect(item, &UCListItem::pressedChanged,
78 this, &UCListItemActionsAttached::updateSwipeState);
79 QObject::connect(item, &UCListItem::contentMovingChanged,
80 this, &UCListItemActionsAttached::updateSwipeState);
81 } else {
82 QObject::disconnect(item, &UCListItem::pressedChanged,
83 this, &UCListItemActionsAttached::updateSwipeState);
84 QObject::disconnect(item, &UCListItem::contentMovingChanged,
85 this, &UCListItemActionsAttached::updateSwipeState);
86 }
87}
88
89// private slot to update visible actions
90void UCListItemActionsAttached::updateVisibleActions()
91{
92 m_visibleActions.clear();
93 if (!m_container.isNull()) {
94 Q_FOREACH(UCAction *action, UCListItemActionsPrivate::get(m_container)->actions) {
95 if (action->m_visible) {
96 m_visibleActions << action;
97 }
98 }
99 }
100 Q_EMIT visibleActionsChanged();
101}
102
103// private slot updating swipe state
104void UCListItemActionsAttached::updateSwipeState()
105{
106 if (m_container.isNull()) {
107 return;
108 }
109 QQuickItem *panelItem = UCListItemActionsPrivate::get(m_container)->panelItem;
110 if (!panelItem || !panelItem->parentItem()) {
111 return;
112 }
113 UCListItem *item = static_cast<UCListItem*>(panelItem->parentItem());
114 UCListItemPrivate *listItem = UCListItemPrivate::get(item);
115 bool swiped = listItem->pressed && listItem->contentMoved;
116 if (swiped != m_swiping) {
117 m_swiping = swiped;
118 Q_EMIT swipingChanged();
119 }
120}
121
122/*!
123 * \qmlattachedproperty list<Action> ListItemActions::visibleActions
124 * Holds the list of visible actions. This is a convenience property to help action
125 * visualization panel implementations to consider only visible actions.
126 */
127QQmlListProperty<UCAction> UCListItemActionsAttached::visibleActions()
128{
129 return QQmlListProperty<UCAction>(this, m_visibleActions);
130}
131
132/*!
133 * \qmlattachedproperty ListItem ListItemActions::listItem
134 * \readonly
135 * The property reports the connected \l ListItem to the list of actions.
136 */
137UCListItem *UCListItemActionsAttached::listItem()
138{
139 if (m_container.isNull()) {
140 return NULL;
141 }
142 QQuickItem *panelItem = UCListItemActionsPrivate::get(m_container)->panelItem;
143 if (!panelItem) {
144 return NULL;
145 }
146 QQuickItem *listItem = panelItem->parentItem();
147 return static_cast<UCListItem*>(listItem);
148}
149
150/*!
151 * \qmlattachedproperty int ListItemActions::listItemIndex
152 * \readonly
153 * Holds the index of the \l ListItem within a view, if the \l ListItem is used
154 * in a model driven view. Otherwise it is set to -1.
155 */
156int UCListItemActionsAttached::listItemIndex() {
157 if (m_container.isNull()) {
158 return -1;
159 }
160 QQuickItem *panelItem = UCListItemActionsPrivate::get(m_container)->panelItem;
161 if (!panelItem) {
162 return -1;
163 }
164 QQuickItem *listItem = panelItem->parentItem();
165 return listItem ? UCListItemPrivate::get(static_cast<UCListItem*>(listItem))->index() : -1;
166}
167
168/*!
169 * \qmlattachedproperty bool ListItemActions::swiping
170 * \readonly
171 * The property notifies whether the panel is swiped or not. The property does
172 * not notify the rebounding.
173 */
174bool UCListItemActionsAttached::swiping()
175{
176 return m_swiping;
177}
178
179/*!
180 * \qmlattachedproperty real ListItemActions::offset
181 * The property returns the offset the panel item holding the visualized actions
182 * is visible. This can be used to do different animations on the panel and on
183 * the action visualizations.
184 */
185qreal UCListItemActionsAttached::offset()
186{
187 if (m_container.isNull()) {
188 return 0.0;
189 }
190 return UCListItemActionsPrivate::get(m_container)->offsetDragged;
191}
192
193/*!
194 * \qmlattachedproperty enum ListItemActions::status
195 * \readonly
196 * The property holds the status of the ListItemActions, whether is connected
197 * as leading or as trailing action list to a \l ListItem. Possible valueas are:
198 * \list A
199 * \li \b Disconnected - default, the actions list is not connected to any \l ListItem
200 * \li \b Leading - the actions list is connected as leading list
201 * \li \b Trailing - the actions list is connected as trailing list
202 * \endlist
203 */
204UCListItemActions::Status UCListItemActionsAttached::status()
205{
206 if (m_container.isNull()) {
207 return UCListItemActions::Disconnected;
208 }
209 return UCListItemActionsPrivate::get(m_container)->status;
210}
211
212/*!
213 * \qmlattachedmethod void ListItemActions::snapToPosition(real position)
214 * The function can be used to perform custom snapping, or to execute rebounding
215 * and also disconnecting from the connected \l ListItem. This can be achieved by
216 * calling the function with 0.0 value.
217 */
218void UCListItemActionsAttached::snapToPosition(qreal position)
219{
220 //if it is disconnected, leave (this also includes the case when m_container is null)
221 if (status() == UCListItemActions::Disconnected) {
222 return;
223 }
224 QQuickItem *panelItem = UCListItemActionsPrivate::get(m_container)->panelItem;
225 if (!panelItem) {
226 // we don't have the panel created yet
227 return;
228 }
229 UCListItem *item = static_cast<UCListItem*>(panelItem->parentItem());
230 if (!item) {
231 // no ListItem attached
232 return;
233 }
234 UCListItemPrivate *listItem = UCListItemPrivate::get(item);
235 position *= (status() == UCListItemActions::Leading) ? 1 : -1;
236 if (position == 0.0) {
237 listItem->_q_rebound();
238 } else {
239 if (listItem->animator) {
240 listItem->animator->snap(position);
241 } else {
242 listItem->contentItem->setX(position);
243 }
244 }
245}
246
247
0248
=== added file 'modules/Ubuntu/Components/plugin/uclistitemattached.cpp'
--- modules/Ubuntu/Components/plugin/uclistitemattached.cpp 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/plugin/uclistitemattached.cpp 2014-12-03 06:10:30 +0000
@@ -0,0 +1,207 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "ucunits.h"
18#include "uctheme.h"
19#include "uclistitem.h"
20#include "uclistitem_p.h"
21#include "propertychange_p.h"
22#include <QtQuick/private/qquickflickable_p.h>
23
24/*
25 * The properties are attached to the ListItem's parent item or to its closest
26 * Flickable parent, when embedded in ListView or Flickable. There will be only
27 * one attached property per Flickable for all embedded child ListItems, enabling
28 * in this way the controlling of the interactive flag of the Flickable and all
29 * its ascendant Flickables.
30 */
31UCListItemAttachedPrivate::UCListItemAttachedPrivate(UCListItemAttached *qq)
32 : q_ptr(qq)
33 , globalDisabled(false)
34{
35}
36
37UCListItemAttachedPrivate::~UCListItemAttachedPrivate()
38{
39 clearChangesList();
40 clearFlickablesList();
41}
42
43// disconnect all flickables
44void UCListItemAttachedPrivate::clearFlickablesList()
45{
46 Q_Q(UCListItemAttached);
47 Q_FOREACH(const QPointer<QQuickFlickable> &flickable, flickables) {
48 if (flickable.data())
49 QObject::disconnect(flickable.data(), &QQuickFlickable::movementStarted,
50 q, &UCListItemAttached::unbindItem);
51 }
52 flickables.clear();
53}
54
55// connect all flickables
56void UCListItemAttachedPrivate::buildFlickablesList()
57{
58 Q_Q(UCListItemAttached);
59 QQuickItem *item = qobject_cast<QQuickItem*>(q->parent());
60 if (!item) {
61 return;
62 }
63 clearFlickablesList();
64 while (item) {
65 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(item);
66 if (flickable) {
67 QObject::connect(flickable, &QQuickFlickable::movementStarted,
68 q, &UCListItemAttached::unbindItem);
69 flickables << flickable;
70 }
71 item = item->parentItem();
72 }
73}
74
75void UCListItemAttachedPrivate::clearChangesList()
76{
77 // clear property change objects
78 Q_Q(UCListItemAttached);
79 Q_FOREACH(PropertyChange *change, changes) {
80 // deleting PropertyChange will restore the saved property
81 // to its original binding/value
82 delete change;
83 }
84 changes.clear();
85}
86
87void UCListItemAttachedPrivate::buildChangesList(const QVariant &newValue)
88{
89 // collect all ascendant flickables
90 Q_Q(UCListItemAttached);
91 QQuickItem *item = qobject_cast<QQuickItem*>(q->parent());
92 if (!item) {
93 return;
94 }
95 clearChangesList();
96 while (item) {
97 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(item);
98 if (flickable) {
99 PropertyChange *change = new PropertyChange(item, "interactive");
100 PropertyChange::setValue(change, newValue);
101 changes << change;
102 }
103 item = item->parentItem();
104 }
105}
106
107UCListItemAttached::UCListItemAttached(QObject *owner)
108 : QObject(owner)
109 , d_ptr(new UCListItemAttachedPrivate(this))
110{
111}
112
113UCListItemAttached::~UCListItemAttached()
114{
115}
116
117// register item to be rebound
118bool UCListItemAttached::listenToRebind(UCListItem *item, bool listen)
119{
120 // we cannot bind the item until we have an other one bound
121 bool result = false;
122 Q_D(UCListItemAttached);
123 if (listen) {
124 if (d->boundItem.isNull() || (d->boundItem == item)) {
125 d->boundItem = item;
126 // rebuild flickable list
127 d->buildFlickablesList();
128 result = true;
129 }
130 } else if (d->boundItem == item) {
131 d->boundItem.clear();
132 result = true;
133 }
134 return result;
135}
136
137// reports true if any of the ascendant flickables is moving
138bool UCListItemAttached::isMoving()
139{
140 Q_D(UCListItemAttached);
141 Q_FOREACH(const QPointer<QQuickFlickable> &flickable, d->flickables) {
142 if (flickable && flickable->isMoving()) {
143 return true;
144 }
145 }
146 return false;
147}
148
149// returns true if the given ListItem is bound to listen on moving changes
150bool UCListItemAttached::isBoundTo(UCListItem *item)
151{
152 Q_D(UCListItemAttached);
153 return d->boundItem == item;
154}
155
156/*
157 * Disable/enable interactive flag for the ascendant flickables. The item is used
158 * to detect whether the same item is trying to enable the flickables which disabled
159 * it before. The enabled/disabled states are not equivalent to the enabled/disabled
160 * state of the interactive flag.
161 * When disabled, always the last item disabling will be kept as active disabler,
162 * and only the active disabler can enable (restore) the interactive flag state.
163 */
164void UCListItemAttached::disableInteractive(UCListItem *item, bool disable)
165{
166 Q_D(UCListItemAttached);
167 if (disable) {
168 // disabling or re-disabling
169 d->disablerItem = item;
170 if (d->globalDisabled == disable) {
171 // was already disabled, leave
172 return;
173 }
174 d->globalDisabled = true;
175 } else if (d->globalDisabled && d->disablerItem == item) {
176 // the one disabled it will enable
177 d->globalDisabled = false;
178 d->disablerItem.clear();
179 } else {
180 // !disabled && (!globalDisabled || item != d->disablerItem)
181 return;
182 }
183 if (disable) {
184 // (re)build changes list with disabling the interactive value
185 d->buildChangesList(false);
186 } else {
187 d->clearChangesList();
188 }
189}
190
191void UCListItemAttached::unbindItem()
192{
193 Q_D(UCListItemAttached);
194 if (d->boundItem) {
195 // depending on content item's X coordinate, we either do animated or prompt rebind
196 if (d->boundItem->contentItem()->x() != 0.0) {
197 // content is not in origin, rebind
198 UCListItemPrivate::get(d->boundItem.data())->_q_rebound();
199 } else {
200 // do some cleanup
201 UCListItemPrivate::get(d->boundItem.data())->promptRebound();
202 }
203 d->boundItem.clear();
204 }
205 // clear binding list
206 d->clearFlickablesList();
207}
0208
=== added file 'modules/Ubuntu/Components/plugin/uclistitemstyle.cpp'
--- modules/Ubuntu/Components/plugin/uclistitemstyle.cpp 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/plugin/uclistitemstyle.cpp 2014-12-03 06:10:30 +0000
@@ -0,0 +1,70 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "uclistitemstyle.h"
18
19/*!
20 * \qmltype ListItemStyle
21 * \instantiates UCListItemStyle
22 * \inqmlmodule Ubuntu.Components.Styles 1.2
23 * \ingroup style-api
24 * \since Ubuntu.Components.Styles 1.2
25 * \brief Style API for ListItem component.
26 *
27 * Style API for the ListItem component which provides actions, select and
28 * drag handler delegates, and snap animation via its properties.
29 * ListItem treats the style differently compared to the other components,
30 * as it:
31 * \list
32 * \li - loads the style only when needed
33 * \li - gets delegates and snap animation from the style properties
34 * \li - ignores any other visuals defined in the style.
35 * \endlist
36 */
37UCListItemStyle::UCListItemStyle(QQuickItem *parent)
38 : QQuickItem(parent)
39 , m_actionsDelegate(0)
40 , m_selectionDelegate(0)
41 , m_dragHandlerDelegate(0)
42 , m_snapAnimation(0)
43 , m_swipeOvershoot(0)
44{
45}
46
47/*!
48 * \qmlproperty Component ListItemStyle::actionsDelegate
49 * Specifies the component visualizing the leading/trailing actions.
50 */
51
52/*!
53 * \qmlproperty Component ListItemStyle::selectionDelegate
54 * Holds the component handling the selection mode.
55 */
56
57/*!
58 * \qmlproperty Component ListItemStyle::dragHandlerDelegate
59 * Holds the component shown when dragging mode is enabled.
60 */
61
62/*!
63 * \qmlproperty PropertyAnimation ListItemStyle::snapAnimation
64 * Holds the animation used in animating when snapped in or out.
65 */
66
67/*!
68 * \qmlproperty real ListItemStyle::swipeOvershoot
69 * The property specifies the overshoot value of the ListItem.
70 */
071
=== added file 'modules/Ubuntu/Components/plugin/uclistitemstyle.h'
--- modules/Ubuntu/Components/plugin/uclistitemstyle.h 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/plugin/uclistitemstyle.h 2014-12-03 06:10:30 +0000
@@ -0,0 +1,53 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16#ifndef UCLISTITEMSTYLE_H
17#define UCLISTITEMSTYLE_H
18
19#include <QtQuick/QQuickItem>
20
21class QQmlComponent;
22class QQuickPropertyAnimation;
23class UCListItemStyle : public QQuickItem
24{
25 Q_OBJECT
26 Q_PROPERTY(QQmlComponent *actionsDelegate MEMBER m_actionsDelegate NOTIFY actionsDelegateChanged)
27 Q_PROPERTY(QQmlComponent *selectionDelegate MEMBER m_selectionDelegate NOTIFY selectionDelegateChanged)
28 Q_PROPERTY(QQmlComponent *dragHandlerDelegate MEMBER m_dragHandlerDelegate NOTIFY dragHandlerDelegateChanged)
29 Q_PROPERTY(QQuickPropertyAnimation *snapAnimation MEMBER m_snapAnimation NOTIFY snapAnimationChanged)
30 Q_PROPERTY(qreal swipeOvershoot MEMBER m_swipeOvershoot NOTIFY swipeOvershootChanged)
31public:
32 explicit UCListItemStyle(QQuickItem *parent = 0);
33
34Q_SIGNALS:
35 void actionsDelegateChanged();
36 void selectionDelegateChanged();
37 void dragHandlerDelegateChanged();
38 void snapAnimationChanged();
39 void swipeOvershootChanged();
40
41private:
42 QQmlComponent *m_actionsDelegate;
43 QQmlComponent *m_selectionDelegate;
44 QQmlComponent *m_dragHandlerDelegate;
45 QQuickPropertyAnimation *m_snapAnimation;
46 qreal m_swipeOvershoot;
47
48 friend class UCListItemPrivate;
49 friend class UCListItemActionsPrivate;
50 friend class UCListItemSnapAnimator;
51};
52
53#endif // UCLISTITEMSTYLE_H
054
=== modified file 'modules/Ubuntu/Components/plugin/ucstyleditembase.cpp'
--- modules/Ubuntu/Components/plugin/ucstyleditembase.cpp 2014-09-02 12:49:00 +0000
+++ modules/Ubuntu/Components/plugin/ucstyleditembase.cpp 2014-12-03 06:10:30 +0000
@@ -186,13 +186,11 @@
186 // therefore must check containment186 // therefore must check containment
187 QPointF point = mapFromItem(child, mouse->localPos());187 QPointF point = mapFromItem(child, mouse->localPos());
188 if (contains(point)) {188 if (contains(point)) {
189 QMouseEvent press(event->type(), point, mouse->windowPos(), mouse->screenPos(),189 requestFocus(Qt::MouseFocusReason);
190 mouse->button(), mouse->buttons(), mouse->modifiers());
191 mousePressEvent(&press);
192 }190 }
193 }191 }
194 // let the event be passed to children192 // let the event be passed to children
195 return false;193 return QQuickItem::childMouseEventFilter(child, event);
196}194}
197195
198#include "moc_ucstyleditembase.cpp"196#include "moc_ucstyleditembase.cpp"
199197
=== modified file 'modules/Ubuntu/Test/UbuntuTestCase.qml'
--- modules/Ubuntu/Test/UbuntuTestCase.qml 2014-10-10 16:49:22 +0000
+++ modules/Ubuntu/Test/UbuntuTestCase.qml 2014-12-03 06:10:30 +0000
@@ -163,12 +163,9 @@
163 if (pressTimeout !== undefined && pressTimeout > 0) {163 if (pressTimeout !== undefined && pressTimeout > 0) {
164 wait(pressTimeout);164 wait(pressTimeout);
165 }165 }
166 mouseMove(item, x, y, button, modifiers, delay);166 // mouse moves are all processed immediately, without delay in between events
167 for (var i = 1; i <= steps; i++) {167 mouseMoveSlowly(item, x, y, dx, dy, steps, delay);
168 // mouse moves are all processed immediately, without delay in between events168 mouseRelease(item, x + dx, y + dy, button, modifiers);
169 mouseMove(item, x + i * ddx, y + i * ddy, -1, button);
170 }
171 mouseRelease(item, x + dx, y + dy, button, modifiers, delay);
172 // empty event buffer169 // empty event buffer
173 wait(200);170 wait(200);
174 }171 }
175172
=== modified file 'tests/qmlapicheck.sh'
--- tests/qmlapicheck.sh 2014-10-30 10:44:19 +0000
+++ tests/qmlapicheck.sh 2014-12-03 06:10:30 +0000
@@ -33,7 +33,7 @@
33 # https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/125699933 # https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1256999
34 # https://bugreports.qt-project.org/browse/QTBUG-3624334 # https://bugreports.qt-project.org/browse/QTBUG-36243
35 env UBUNTU_UI_TOOLKIT_THEMES_PATH=$SRCDIR/modules ALARM_BACKEND=memory \35 env UBUNTU_UI_TOOLKIT_THEMES_PATH=$SRCDIR/modules ALARM_BACKEND=memory \
36 qmlplugindump $i 0.1 $SRCDIR/modules 1>> $BLDDIR/plugins.qmltypes36 qmlplugindump -noinstantiate $i 0.1 $SRCDIR/modules 1>> $BLDDIR/plugins.qmltypes
37 test $? != 0 && ERRORS=137 test $? != 0 && ERRORS=1
38done38done
39test $ERRORS = 1 && echo Error: qmlplugindump failed && exit 139test $ERRORS = 1 && echo Error: qmlplugindump failed && exit 1
4040
=== added file 'tests/resources/listitems/ListItemTest.qml'
--- tests/resources/listitems/ListItemTest.qml 1970-01-01 00:00:00 +0000
+++ tests/resources/listitems/ListItemTest.qml 2014-12-03 06:10:30 +0000
@@ -0,0 +1,201 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.2
18import Ubuntu.Components 1.2
19
20MainView {
21 id: main
22 width: units.gu(50)
23 height: units.gu(100)
24
25 property bool override: false
26
27 Action {
28 objectName: "stock"
29 id: stock
30 iconName: "starred"
31 text: "Staaaar"
32 onTriggered: print(iconName, "triggered", value)
33 }
34
35 ListItemActions {
36 id: leading
37 objectName: "StockLeading"
38 actions: [
39 Action {
40 iconName: "delete"
41 onTriggered: print(iconName, "triggered", value)
42 },
43 Action {
44 iconName: "alarm-clock"
45 enabled: false
46 onTriggered: print(iconName, "triggered", value)
47 },
48 Action {
49 iconName: "camcorder"
50 onTriggered: print(iconName, "triggered", value)
51 },
52 Action {
53 iconName: "stock_website"
54 onTriggered: print(iconName, "triggered", value)
55 },
56 Action {
57 iconName: "starred"
58 onTriggered: print(iconName, "triggered", value)
59 },
60 Action {
61 iconName: "go-home"
62 onTriggered: print(iconName, "triggered", value)
63 }
64 ]
65 }
66
67 property list<Action> leadingArray: [
68 Action {
69 iconName: "delete"
70 }
71 ]
72 property list<Action> trailingArray: [
73 Action {
74 iconName: "search"
75 },
76 Action {
77 iconName: "edit"
78 },
79 Action {
80 iconName: "email"
81 }
82 ]
83
84 Column {
85 anchors {
86 left: parent.left
87 right: parent.right
88 }
89
90 ListItem {
91 id: testItem
92 objectName: "single"
93 color: "lime"
94 onClicked: {
95 print("click")
96 main.override = !main.override
97 }
98 Label {
99 anchors.fill: parent
100 text: units.gridUnit + "PX/unit"
101 }
102 leadingActions: ListItemActions {
103 objectName: "InlineLeading"
104 actions: [stock]
105 delegate: Column {
106 width: height + units.gu(2)
107 anchors.verticalCenter: parent.verticalCenter
108 Icon {
109 width: units.gu(3)
110 height: width
111 name: action.iconName
112 color: "blue"
113 anchors.horizontalCenter: parent.horizontalCenter
114 }
115 Label {
116 text: action.text + index
117 width: parent.width
118 horizontalAlignment: Text.AlignHCenter
119 }
120 }
121 }
122 trailingActions: leading
123 }
124 ListItem {
125 Label {
126 anchors.fill: parent
127 text: "Another standalone ListItem"
128 }
129 leadingActions: testItem.leadingActions
130 trailingActions: ListItemActions {
131 actions: leading.actions
132 }
133 }
134
135 ListView {
136 id: view
137 clip: true
138 width: parent.width
139 height: units.gu(20)
140 model: 10
141 pressDelay: 0
142 delegate: ListItem {
143 objectName: "ListItem" + index
144 id: listItem
145 onClicked: print(" clicked")
146 leadingActions: leading
147 Label {
148 text: modelData + " item"
149 }
150 states: State {
151 name: "override"
152 when: main.override
153 PropertyChanges {
154 target: listItem
155 highlightColor: "brown"
156 }
157 }
158 }
159 }
160 Flickable {
161 id: flicker
162 width: parent.width
163 height: units.gu(20)
164 clip: true
165 contentHeight: column.childrenRect.height
166 ListItemActions {
167 id: trailing
168 actions: leading.actions
169 }
170
171 Column {
172 id: column
173 width: view.width
174 property alias count: repeater.count
175 Repeater {
176 id: repeater
177 model: 10
178 ListItem {
179 objectName: "InFlickable"+index
180 color: "red"
181 highlightColor: "lime"
182 divider.colorFrom: UbuntuColors.green
183 swipeOvershoot: units.gu(10)
184
185 leadingActions: ListItemActions {
186 actions: leadingArray
187 }
188 trailingActions: ListItemActions {
189 actions: trailingArray
190 }
191
192 Label {
193 text: modelData + " Flickable item"
194 }
195 onClicked: divider.visible = !divider.visible
196 }
197 }
198 }
199 }
200 }
201}
0202
=== added file 'tests/unit/tst_performance/ItemList.qml'
--- tests/unit/tst_performance/ItemList.qml 1970-01-01 00:00:00 +0000
+++ tests/unit/tst_performance/ItemList.qml 2014-12-03 06:10:30 +0000
@@ -0,0 +1,30 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Components 1.1
19
20Column {
21 width: 800
22 height: 600
23 Repeater {
24 model: 5000
25 Item {
26 width: parent.width
27 height: units.gu(6)
28 }
29 }
30}
031
=== added file 'tests/unit/tst_performance/ListItemList.qml'
--- tests/unit/tst_performance/ListItemList.qml 1970-01-01 00:00:00 +0000
+++ tests/unit/tst_performance/ListItemList.qml 2014-12-03 06:10:30 +0000
@@ -0,0 +1,30 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Components 1.2
19
20Column {
21 width: 800
22 height: 600
23 property alias count: repeater.count
24 Repeater {
25 id: repeater
26 model: 5000
27 ListItem {
28 }
29 }
30}
031
=== added file 'tests/unit/tst_performance/ListItemWithActionsList.qml'
--- tests/unit/tst_performance/ListItemWithActionsList.qml 1970-01-01 00:00:00 +0000
+++ tests/unit/tst_performance/ListItemWithActionsList.qml 2014-12-03 06:10:30 +0000
@@ -0,0 +1,43 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Components 1.2
19
20Column {
21 width: 800
22 height: 600
23 ListItemActions {
24 id: actions1
25 actions: [Action {}]
26 }
27 ListItemActions {
28 id: actions2
29 actions: [
30 Action {},
31 Action {},
32 Action {}
33 ]
34 }
35
36 Repeater {
37 model: 5000
38 ListItem {
39 trailingActions: actions1
40 leadingActions: actions2
41 }
42 }
43}
044
=== added file 'tests/unit/tst_performance/ListItemWithInlineActionsList.qml'
--- tests/unit/tst_performance/ListItemWithInlineActionsList.qml 1970-01-01 00:00:00 +0000
+++ tests/unit/tst_performance/ListItemWithInlineActionsList.qml 2014-12-03 06:10:30 +0000
@@ -0,0 +1,41 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Components 1.2
19
20Column {
21 width: 800
22 height: 600
23
24 Repeater {
25 model: 5000
26 ListItem {
27 trailingActions: ListItemActions {
28 actions: [
29 Action {}
30 ]
31 }
32 leadingActions: ListItemActions {
33 actions: [
34 Action {},
35 Action {},
36 Action {}
37 ]
38 }
39 }
40 }
41}
042
=== added file 'tests/unit/tst_performance/ListItemsBaseList.qml'
--- tests/unit/tst_performance/ListItemsBaseList.qml 1970-01-01 00:00:00 +0000
+++ tests/unit/tst_performance/ListItemsBaseList.qml 2014-12-03 06:10:30 +0000
@@ -0,0 +1,29 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Components 1.1
19import Ubuntu.Components.ListItems 1.0 as ListItems
20
21Column {
22 width: 800
23 height: 600
24 Repeater {
25 model: 5000
26 ListItems.Base {
27 }
28 }
29}
030
=== added file 'tests/unit/tst_performance/ListItemsEmptyList.qml'
--- tests/unit/tst_performance/ListItemsEmptyList.qml 1970-01-01 00:00:00 +0000
+++ tests/unit/tst_performance/ListItemsEmptyList.qml 2014-12-03 06:10:30 +0000
@@ -0,0 +1,29 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Components 1.1
19import Ubuntu.Components.ListItems 1.0 as ListItems
20
21Column {
22 width: 800
23 height: 600
24 Repeater {
25 model: 5000
26 ListItems.Empty {
27 }
28 }
29}
030
=== modified file 'tests/unit/tst_performance/tst_performance.cpp'
--- tests/unit/tst_performance/tst_performance.cpp 2013-06-27 15:35:23 +0000
+++ tests/unit/tst_performance/tst_performance.cpp 2014-12-03 06:10:30 +0000
@@ -74,16 +74,15 @@
74 QTest::newRow("grid with Label") << "LabelGrid.qml" << QUrl();74 QTest::newRow("grid with Label") << "LabelGrid.qml" << QUrl();
75 QTest::newRow("grid with UbuntuShape") << "UbuntuShapeGrid.qml" << QUrl();75 QTest::newRow("grid with UbuntuShape") << "UbuntuShapeGrid.qml" << QUrl();
76 QTest::newRow("grid with UbuntuShapePair") << "PairOfUbuntuShapeGrid.qml" << QUrl();76 QTest::newRow("grid with UbuntuShapePair") << "PairOfUbuntuShapeGrid.qml" << QUrl();
77 QTest::newRow("grid with ButtonStyle") << "ButtonStyleGrid.qml" << QUrl();
78 QTest::newRow("grid with Button") << "ButtonGrid.qml" << QUrl();77 QTest::newRow("grid with Button") << "ButtonGrid.qml" << QUrl();
79// QTest::newRow("grid with CheckBoxStyle") << "CheckBoxStyleGrid.qml" << QUrl();
80// QTest::newRow("grid with CheckBox") << "CheckBoxGrid.qml" << QUrl();
81// QTest::newRow("grid with SwitchStyle") << "SwitchStyleGrid.qml" << QUrl();
82// QTest::newRow("grid with Switch") << "SwitchGrid.qml" << QUrl();
83// QTest::newRow("grid with SwitchStyle") << "SwitchStyleGrid.qml" << QUrl();
84// QTest::newRow("grid with Switch") << "SwitchGrid.qml" << QUrl();
85 QTest::newRow("grid with SliderStyle") << "SliderStyleGrid.qml" << QUrl();
86 QTest::newRow("grid with Slider") << "SliderGrid.qml" << QUrl();78 QTest::newRow("grid with Slider") << "SliderGrid.qml" << QUrl();
79 QTest::newRow("list with QtQuick Item") << "ItemList.qml" << QUrl();
80 QTest::newRow("list with new ListItem") << "ListItemList.qml" << QUrl();
81 QTest::newRow("list with new ListItem with actions") << "ListItemWithActionsList.qml" << QUrl();
82 QTest::newRow("list with new ListItem with inline actions") << "ListItemWithInlineActionsList.qml" << QUrl();
83 QTest::newRow("list with ListItems.Empty (equivalent to the new ListItem") << "ListItemsEmptyList.qml" << QUrl();
84 // disable this test as it takes >20 seconds. Kept still for measurements to be done during development
85// QTest::newRow("list with ListItems.Base (one icon, one label and one chevron)") << "ListItemsBaseList.qml" << QUrl();
87 }86 }
8887
89 void benchmark_GridOfComponents()88 void benchmark_GridOfComponents()
9089
=== modified file 'tests/unit/tst_performance/tst_performance.pro'
--- tests/unit/tst_performance/tst_performance.pro 2013-06-27 15:20:12 +0000
+++ tests/unit/tst_performance/tst_performance.pro 2014-12-03 06:10:30 +0000
@@ -19,4 +19,10 @@
19 TextWithImport.qml \19 TextWithImport.qml \
20 TextWithImportGrid.qml \20 TextWithImportGrid.qml \
21 TextWithImportPopupsGrid.qml \21 TextWithImportPopupsGrid.qml \
22 TextWithImportPopups.qml22 TextWithImportPopups.qml \
23 ItemList.qml \
24 ListItemList.qml \
25 ListItemsEmptyList.qml \
26 ListItemsBaseList.qml \
27 ListItemWithInlineActionsList.qml \
28 ListItemWithActionsList.qml
2329
=== added file 'tests/unit_x11/tst_components/tst_listitem.qml'
--- tests/unit_x11/tst_components/tst_listitem.qml 1970-01-01 00:00:00 +0000
+++ tests/unit_x11/tst_components/tst_listitem.qml 2014-12-03 06:10:30 +0000
@@ -0,0 +1,570 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import QtTest 1.0
19import Ubuntu.Test 1.0
20import Ubuntu.Components 1.2
21
22Item {
23 id: main
24 width: units.gu(40)
25 height: units.gu(71)
26
27 Action {
28 id: stockAction
29 iconName: "starred"
30 objectName: "stockAction"
31 }
32 ListItemActions {
33 id: leading
34 actions: [
35 Action {
36 iconName: "starred"
37 objectName: "leading_1"
38 },
39 Action {
40 iconName: "edit"
41 objectName: "leading_2"
42 },
43 Action {
44 iconName: "camcorder"
45 objectName: "leading_3"
46 }
47 ]
48 }
49 ListItemActions {
50 id: trailing
51 actions: [
52 stockAction,
53 ]
54 }
55 ListItemActions {
56 id: actionsDefault
57 }
58
59 Component {
60 id: customDelegate
61 Rectangle {
62 width: units.gu(10)
63 color: "green"
64 objectName: "custom_delegate"
65 }
66 }
67
68 Column {
69 width: parent.width
70 ListItem {
71 id: defaults
72 width: parent.width
73 }
74 ListItem {
75 id: testItem
76 width: parent.width
77 color: "blue"
78 leadingActions: leading
79 trailingActions: trailing
80 Item {
81 id: bodyItem
82 anchors.fill: parent
83 }
84 }
85 ListView {
86 id: listView
87 width: parent.width
88 height: units.gu(28)
89 clip: true
90 model: 10
91 delegate: ListItem {
92 objectName: "listItem" + index
93 color: "lightgray"
94 width: parent.width
95 leadingActions: leading
96 trailingActions: trailing
97 }
98 }
99 }
100
101 UbuntuTestCase {
102 name: "ListItemAPI"
103 when: windowShown
104
105 SignalSpy {
106 id: movingSpy
107 signalName: "contentMovementEnded"
108 }
109
110 SignalSpy {
111 id: pressedSpy
112 signalName: "pressedChanged"
113 target: testItem
114 }
115
116 SignalSpy {
117 id: clickSpy
118 signalName: "clicked"
119 target: testItem;
120 }
121
122 SignalSpy {
123 id: actionSpy
124 signalName: "onTriggered"
125 }
126 SignalSpy {
127 id: interactiveSpy
128 signalName: "interactiveChanged"
129 }
130
131 function panelItem(item, leading) {
132 return findInvisibleChild(item, (leading ? "ListItemPanelLeading" : "ListItemPanelTrailing"));
133 }
134
135 function rebound(item, watchTarget) {
136 if (watchTarget === undefined) {
137 watchTarget = item;
138 }
139
140 movingSpy.target = null;
141 movingSpy.target = watchTarget;
142 movingSpy.clear();
143 mouseClick(item, centerOf(item).x, centerOf(item).y);
144 if (watchTarget.contentMoving) {
145 movingSpy.wait();
146 }
147 movingSpy.target = null;
148 }
149
150 function initTestCase() {
151 TestExtras.registerTouchDevice();
152 waitForRendering(main);
153 }
154
155 function cleanup() {
156 movingSpy.clear();
157 pressedSpy.clear();
158 clickSpy.clear();
159 actionSpy.clear();
160 interactiveSpy.clear();
161 listView.interactive = true;
162 // make sure we collapse
163 mouseClick(defaults, 0, 0)
164 movingSpy.target = null;
165 movingSpy.clear();
166 interactiveSpy.target = null;
167 interactiveSpy.clear();
168 trailing.delegate = null;
169 listView.positionViewAtBeginning();
170 }
171
172 function test_0_defaults() {
173 verify(defaults.contentItem !== null, "Defaults is null");
174 compare(defaults.color, "#000000", "Transparent by default");
175 compare(defaults.highlightColor, Theme.palette.selected.background, "Theme.palette.selected.background color by default")
176 compare(defaults.pressed, false, "Not pressed buy default");
177 compare(defaults.swipeOvershoot, 0.0, "No overshoot till the style is loaded!");
178 compare(defaults.divider.visible, true, "divider is visible by default");
179 compare(defaults.divider.leftMargin, units.dp(2), "divider's left margin is 2GU");
180 compare(defaults.divider.rightMargin, units.dp(2), "divider's right margin is 2GU");
181 compare(defaults.divider.colorFrom, "#000000", "colorFrom differs.");
182 fuzzyCompare(defaults.divider.colorFrom.a, 0.14, 0.01, "colorFrom alpha differs");
183 compare(defaults.divider.colorTo, "#ffffff", "colorTo differs.");
184 fuzzyCompare(defaults.divider.colorTo.a, 0.07, 0.01, "colorTo alpha differs");
185 compare(defaults.contentMoving, false, "default is not moving");
186 compare(defaults.style, null, "Style is loaded upon first use.");
187 compare(defaults.__styleInstance, null, "__styleInstance must be null.");
188
189 compare(actionsDefault.delegate, null, "ListItemActions has no delegate set by default.");
190 compare(actionsDefault.actions.length, 0, "ListItemActions has no actions set.");
191 }
192
193 function test_children_in_content_item() {
194 compare(bodyItem.parent, testItem.contentItem, "Content is not in the right holder!");
195 }
196
197 function test_pressedChanged_on_click() {
198 mousePress(testItem, testItem.width / 2, testItem.height / 2);
199 pressedSpy.wait();
200 mouseRelease(testItem, testItem.width / 2, testItem.height / 2);
201 }
202 function test_pressedChanged_on_tap() {
203 TestExtras.touchPress(0, testItem, centerOf(testItem));
204 pressedSpy.wait();
205 TestExtras.touchRelease(0, testItem, centerOf(testItem));
206 // local cleanup, wait few msecs to suppress double tap
207 wait(400);
208 }
209
210 function test_clicked_on_mouse() {
211 mouseClick(testItem, testItem.width / 2, testItem.height / 2);
212 clickSpy.wait();
213 }
214 function test_clicked_on_tap() {
215 TestExtras.touchClick(0, testItem, centerOf(testItem));
216 clickSpy.wait();
217 }
218
219 function test_mouse_click_on_listitem() {
220 var listItem = findChild(listView, "listItem0");
221 verify(listItem, "Cannot find listItem0");
222
223 mousePress(listItem, listItem.width / 2, 0);
224 compare(listItem.pressed, true, "Item is not pressed?");
225 // do 5 moves to be able to sense it
226 var dy = 0;
227 for (var i = 1; i <= 5; i++) {
228 dy += i * 10;
229 mouseMove(listItem, listItem.width / 2, dy);
230 }
231 compare(listItem.pressed, false, "Item is pressed still!");
232 mouseRelease(listItem, listItem.width / 2, dy);
233 // dismiss
234 rebound(listItem);
235 }
236 function test_touch_click_on_listitem() {
237 var listItem = findChild(listView, "listItem0");
238 verify(listItem, "Cannot find listItem0");
239
240 TestExtras.touchPress(0, listItem, Qt.point(listItem.width / 2, 5));
241 compare(listItem.pressed, true, "Item is not pressed?");
242 // do 5 moves to be able to sense it
243 var dy = 0;
244 for (var i = 1; i <= 5; i++) {
245 dy += i * 10;
246 TestExtras.touchMove(0, listItem, Qt.point(listItem.width / 2, dy));
247 }
248 compare(listItem.pressed, false, "Item is pressed still!");
249 // cleanup, wait few milliseconds to avoid dbl-click collision
250 TestExtras.touchRelease(0, listItem, Qt.point(listItem.width / 2, dy));
251 // dismiss
252 rebound(listItem);
253 }
254
255 function test_background_height_change_on_divider_visible() {
256 // make sure the testItem's divider is shown
257 testItem.divider.visible = true;
258 verify(testItem.contentItem.height < testItem.height, "ListItem's background height must be less than the item itself.");
259 testItem.divider.visible = false;
260 compare(testItem.contentItem.height, testItem.height, "ListItem's background height must be the same as the item itself.");
261 testItem.divider.visible = true;
262 }
263
264 function test_tug_actions_data() {
265 var item = findChild(listView, "listItem0");
266 return [
267 {tag: "Trailing, mouse", item: item, pos: centerOf(item), dx: -units.gu(20), positiveDirection: false, mouse: true},
268 {tag: "Leading, mouse", item: item, pos: centerOf(item), dx: units.gu(20), positiveDirection: true, mouse: true},
269 {tag: "Trailing, touch", item: item, pos: centerOf(item), dx: -units.gu(20), positiveDirection: false, mouse: false},
270 {tag: "Leading, touch", item: item, pos: centerOf(item), dx: units.gu(20), positiveDirection: true, mouse: false},
271 ];
272 }
273 function test_tug_actions(data) {
274 listView.positionViewAtBeginning();
275 movingSpy.target = data.item;
276 if (data.mouse) {
277 flick(data.item, data.pos.x, data.pos.y, data.dx, 0);
278 } else {
279 TestExtras.touchDrag(0, data.item, data.pos, Qt.point(data.dx, 0));
280 }
281 movingSpy.wait();
282 if (data.positiveDirection) {
283 verify(data.item.contentItem.x > 0, data.tag + " actions did not show up");
284 } else {
285 verify(data.item.contentItem.x < 0, data.tag + " actions did not show up");
286 }
287
288 // dismiss
289 rebound(data.item);
290 }
291
292 function test_rebound_when_pressed_outside_or_clicked_data() {
293 var item0 = findChild(listView, "listItem0");
294 var item1 = findChild(listView, "listItem1");
295 return [
296 {tag: "Click on an other Item", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item1, mouse: true},
297 {tag: "Click on the same Item", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item0, mouse: true},
298 {tag: "Tap on an other Item", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item1, mouse: false},
299 {tag: "Tap on the same Item", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item0, mouse: false},
300 ];
301 }
302 function test_rebound_when_pressed_outside_or_clicked(data) {
303 listView.positionViewAtBeginning();
304 movingSpy.target = data.item;
305 if (data.mouse) {
306 flick(data.item, data.pos.x, data.pos.y, data.dx, 0);
307 } else {
308 TestExtras.touchDrag(0, data.item, data.pos, Qt.point(data.dx, 0));
309 }
310 movingSpy.wait();
311 verify(data.item.contentItem.x != 0, "The component wasn't tugged!");
312 // dismiss
313 rebound(data.clickOn, data.item)
314 }
315
316 function test_listview_not_interactive_while_tugged_data() {
317 var item0 = findChild(listView, "listItem0");
318 var item1 = findChild(listView, "listItem1");
319 return [
320 {tag: "Trailing", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item1, mouse: true},
321 {tag: "Leading", item: item0, pos: centerOf(item0), dx: units.gu(20), clickOn: item0.contentItem, mouse: true},
322 {tag: "Trailing", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item1, mouse: false},
323 {tag: "Leading", item: item0, pos: centerOf(item0), dx: units.gu(20), clickOn: item0.contentItem, mouse: false},
324 ];
325 }
326 function test_listview_not_interactive_while_tugged(data) {
327 listView.positionViewAtBeginning();
328 compare(listView.interactive, true, "ListView is not interactive");
329 movingSpy.target = data.item;
330 interactiveSpy.target = listView;
331 if (data.mouse) {
332 flick(data.item, data.pos.x, data.pos.y, data.dx, data.dy);
333 } else {
334 TestExtras.touchDrag(0, data.item, data.pos, Qt.point(data.dx, data.dy));
335 }
336 movingSpy.wait();
337 // animation should no longer be running!
338 verify(!data.item.__styleInstance.snapAnimation.running, "Animation is still running!");
339 compare(listView.interactive, true, "The ListView is still non-interactive!");
340 compare(interactiveSpy.count, 2, "Less/more times changed!");
341 // check if it snapped in
342 verify(data.item.contentItem.x != 0.0, "Not snapped in!!");
343 // dismiss
344 rebound(data.clickOn, data.item);
345 // animation should no longer be running!
346 verify(!data.item.__styleInstance.snapAnimation.running, "Animation is still running!");
347 fuzzyCompare(data.item.contentItem.x, 0.0, 0.1, "Not snapped out!!");
348 }
349
350 function test_visualized_actions_data() {
351 var listItem0 = findChild(listView, "listItem0");
352 var listItem1 = findChild(listView, "listItem1");
353 return [
354 {tag: "Leading actions", item: listItem0, leading: true, expected: ["leading_1", "leading_2", "leading_3"]},
355 {tag: "Trailing actions", item: listItem0, leading: false, expected: ["stockAction"]},
356 ];
357 }
358 function test_visualized_actions(data) {
359 movingSpy.target = data.item;
360 flick(data.item, centerOf(data.item).x, centerOf(data.item).y, data.leading ? units.gu(20) : -units.gu(20), 0);
361 movingSpy.wait();
362
363 // check if the action is visible
364 var panel = panelItem(data.item, data.leading);
365 verify(panel, "Panel not visible");
366 for (var i in data.expected) {
367 var actionItem = findChild(panel, data.expected[i]);
368 verify(actionItem, data.expected[i] + " action not found");
369 }
370 // dismiss
371 rebound(data.item);
372 }
373
374 function test_selecting_action_rebounds_data() {
375 var item0 = findChild(listView, "listItem0");
376 return [
377 {tag: "With mouse", item: item0, pos: centerOf(item0), dx: units.gu(20), leading: true, select: "leading_1", mouse: true},
378 {tag: "With touch", item: item0, pos: centerOf(item0), dx: units.gu(20), leading: true, select: "leading_1", mouse: false},
379 ]
380 }
381 function test_selecting_action_rebounds(data) {
382 listView.positionViewAtBeginning();
383 movingSpy.target = data.item;
384 if (data.mouse) {
385 flick(data.item, data.pos.x, data.pos.y, data.dx, 0);
386 } else {
387 TestExtras.touchDrag(0, data.item, data.pos, Qt.point(data.dx, 0));
388 }
389 movingSpy.wait();
390 verify(data.item.contentItem.x > 0, "Not snapped in!");
391 var panel = panelItem(data.item, data.leading);
392 verify(panel, "panelItem not found");
393 var selectedAction = findChild(panel, data.select);
394 verify(selectedAction, "Cannot select action " + data.select);
395
396 // dismiss
397 movingSpy.clear();
398 if (data.mouse) {
399 mouseClick(selectedAction, centerOf(selectedAction).x, centerOf(selectedAction).y);
400 } else {
401 TestExtras.touchClick(0, selectedAction, centerOf(selectedAction));
402 }
403 movingSpy.wait();
404 fuzzyCompare(data.item.contentItem.x, 0.0, 0.1, "Content not snapped out");
405 }
406
407 function test_custom_trailing_delegate() {
408 trailing.delegate = customDelegate;
409 listView.positionViewAtBeginning();
410 var item = findChild(listView, "listItem0");
411 movingSpy.target = item;
412 flick(item, centerOf(item).x, centerOf(item).y, -units.gu(20), 0);
413 var panel = panelItem(item, false);
414 verify(panel, "Panel is not visible");
415 var custom = findChild(panel, "custom_delegate");
416 verify(custom, "Custom delegate not in use");
417 movingSpy.wait();
418 // cleanup
419 rebound(item);
420 }
421
422 // execute as last so we make sure we have the panel created
423 function test_snap_data() {
424 var listItem = findChild(listView, "listItem0");
425 verify(listItem, "ListItem cannot be found");
426
427 return [
428 // the list snaps out if the panel is dragged in > overshoot GU (hardcoded for now)
429 {tag: "Snap out leading", item: listItem, dx: units.gu(2), snapIn: false},
430 {tag: "Snap in leading", item: listItem, dx: units.gu(4), snapIn: true},
431 {tag: "Snap out trailing", item: listItem, dx: -units.gu(2), snapIn: false},
432 {tag: "Snap in trailing", item: listItem, dx: -units.gu(4), snapIn: true},
433 ];
434 }
435 function test_snap(data) {
436 movingSpy.target = data.item;
437 flick(data.item, centerOf(data.item).x, centerOf(data.item).y, data.dx, 0);
438 movingSpy.wait();
439 waitForRendering(data.item, 400);
440 movingSpy.clear();
441 if (data.snapIn) {
442 verify(data.item.contentItem.x != 0.0, "Not snapped to be visible");
443 // cleanup
444 rebound(data.item);
445 } else {
446 tryCompareFunction(function() { return data.item.contentItem.x; }, 0.0, 1000, "Not snapped back");
447 }
448 }
449
450 function test_snap_gesture_data() {
451 var listItem = findChild(listView, "listItem0");
452 var front = Qt.point(units.gu(1), listItem.height / 2);
453 var rear = Qt.point(listItem.width - units.gu(1), listItem.height / 2);
454 return [
455 // the first dx must be big enough to drag the panel in, it is always the last dx value
456 // which decides the snap direction
457 {tag: "Snap out, leading", item: listItem, grabPos: front, dx: [units.gu(10), -units.gu(2)], snapIn: false},
458 {tag: "Snap in, leading", item: listItem, grabPos: front, dx: [units.gu(10), -units.gu(1), units.gu(1)], snapIn: true},
459 // have less first dx as the trailing panel is shorter
460 {tag: "Snap out, trailing", item: listItem, grabPos: rear, dx: [-units.gu(5), units.gu(2)], snapIn: false},
461 {tag: "Snap in, trailing", item: listItem, grabPos: rear, dx: [-units.gu(5), units.gu(1), -units.gu(1)], snapIn: true},
462 ];
463 }
464 function test_snap_gesture(data) {
465 // performe the moves
466 movingSpy.target = data.item;
467 var pos = data.grabPos;
468 mousePress(data.item.contentItem, pos.x, pos.y);
469 for (var i in data.dx) {
470 var dx = data.dx[i];
471 mouseMoveSlowly(data.item.contentItem, pos.x, pos.y, dx, 0, 5, 100);
472 pos.x += dx;
473 }
474 mouseRelease(data.item.contentItem, pos.x, pos.y);
475 movingSpy.wait();
476
477 if (data.snapIn) {
478 // the contenTitem must be dragged in (snapIn)
479 verify(data.item.contentItem.x != 0.0, "Not snapped in!");
480 // dismiss
481 rebound(data.item);
482 } else {
483 fuzzyCompare(data.item.contentItem.x, 0.0, 0.1, "Not snapped out!");
484 }
485 }
486
487 function test_overshoot_from_style() {
488 // scroll to the last ListView element and test on that, to make sure we don't have the style loaded for that component
489 listView.positionViewAtEnd();
490 var listItem = findChild(listView, "listItem" + (listView.count - 1));
491 verify(listItem, "Cannot get list item for testing");
492
493 compare(listItem.swipeOvershoot, 0.0, "No overshoot should be set yet!");
494 // now swipe
495 movingSpy.target = listItem;
496 flick(listItem.contentItem, centerOf(listItem).x, centerOf(listItem).y, units.gu(5), 0);
497 movingSpy.wait();
498 compare(listItem.swipeOvershoot, listItem.__styleInstance.swipeOvershoot, "Overshoot not taken from style");
499
500 // cleanup
501 rebound(listItem);
502 }
503
504 function test_custom_overshoot_data() {
505 // use different items to make sure the style doesn't update the overshoot values during the test
506 return [
507 {tag: "Positive value", index: listView.count - 1, value: units.gu(10), expected: units.gu(10)},
508 {tag: "Zero value", index: listView.count - 2, value: 0, expected: 0},
509 // synchronize the expected value with the one from Ambiance theme!
510 {tag: "Negative value", index: listView.count - 3, value: -1, expected: units.gu(2)},
511 ];
512 }
513 function test_custom_overshoot(data) {
514 // scroll to the last ListView element and test on that, to make sure we don't have the style loaded for that component
515 listView.positionViewAtEnd();
516 var listItem = findChild(listView, "listItem" + data.index);
517 verify(listItem, "Cannot get list item for testing");
518
519 compare(listItem.swipeOvershoot, 0.0, "No overshoot should be set yet!");
520 listItem.swipeOvershoot = data.value;
521 // now swipe
522 movingSpy.target = listItem;
523 flick(listItem.contentItem, centerOf(listItem).x, centerOf(listItem).y, units.gu(5), 0);
524 movingSpy.wait();
525 compare(listItem.swipeOvershoot, data.expected, "Overshoot differs from one set!");
526
527 // cleanup
528 rebound(listItem);
529 }
530
531 function test_verify_action_value_data() {
532 var item0 = findChild(listView, "listItem0");
533 var item1 = findChild(listView, "listItem1");
534 var item2 = findChild(listView, "listItem2");
535 var item3 = findChild(listView, "listItem3");
536 return [
537 // testItem is the child item @index 1 in the topmost Column.
538 {tag: "Standalone item, child index 1", item: testItem, result: 1},
539 {tag: "ListView, item index 0", item: item0, result: 0},
540 {tag: "ListView, item index 1", item: item1, result: 1},
541 {tag: "ListView, item index 2", item: item2, result: 2},
542 {tag: "ListView, item index 3", item: item3, result: 3},
543 ];
544 }
545 function test_verify_action_value(data) {
546 // tug actions in
547 movingSpy.target = data.item;
548 flick(data.item, centerOf(data.item).x, centerOf(data.item).y, units.gu(20), 0, 100, 10);
549 movingSpy.wait();
550 verify(data.item.contentItem.x != 0.0, "Not snapped in");
551
552 var panel = panelItem(data.item, "Leading");
553 var action = findChild(panel, "leading_2");
554 verify(action, "actions panel cannot be reached");
555 // we test the action closest to the list item's contentItem
556 actionSpy.target = data.item.leadingActions.actions[1];
557
558 // select the action
559 movingSpy.clear();
560 mouseClick(action, centerOf(action).x, centerOf(action).y);
561 movingSpy.wait();
562
563 // check the action param
564 actionSpy.wait();
565 // SignalSpy.signalArguments[0] is an array of arguments, where the index is set as index 0
566 var param = actionSpy.signalArguments[0];
567 compare(param[0], data.result, "Action parameter differs");
568 }
569 }
570}

Subscribers

People subscribed via source and target branches