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

Proposed by Zsombor Egri
Status: Merged
Approved by: Zsombor Egri
Approved revision: 1311
Merged at revision: 1272
Proposed branch: lp:~zsombi/ubuntu-ui-toolkit/35-options-panel-swipe
Merge into: lp:~zsombi/ubuntu-ui-toolkit/listitem-master
Prerequisite: lp:~zsombi/ubuntu-ui-toolkit/34-snap-animator
Diff against target: 1767 lines (+1074/-69)
22 files modified
components.api (+9/-0)
modules/Ubuntu/Components/Themes/Ambiance/ListItemPanel.qml (+64/-0)
modules/Ubuntu/Components/Themes/Ambiance/ListItemStyle.qml (+34/-0)
modules/Ubuntu/Components/Themes/Ambiance/qmldir (+4/-0)
modules/Ubuntu/Components/plugin/plugin.cpp (+5/-0)
modules/Ubuntu/Components/plugin/plugin.pro (+4/-2)
modules/Ubuntu/Components/plugin/uclistitem.cpp (+389/-47)
modules/Ubuntu/Components/plugin/uclistitem.h (+9/-1)
modules/Ubuntu/Components/plugin/uclistitem_p.h (+24/-3)
modules/Ubuntu/Components/plugin/uclistitemactions.cpp (+158/-3)
modules/Ubuntu/Components/plugin/uclistitemactions.h (+1/-0)
modules/Ubuntu/Components/plugin/uclistitemactions_p.h (+14/-2)
modules/Ubuntu/Components/plugin/uclistitemactionsattached.cpp (+6/-3)
modules/Ubuntu/Components/plugin/uclistitemstyle.cpp (+92/-0)
modules/Ubuntu/Components/plugin/uclistitemstyle.h (+54/-0)
tests/qmlapicheck.sh (+1/-1)
tests/resources/listitems/ListItemTest.qml (+29/-0)
tests/unit/tst_performance/ListItemWithActionsList.qml (+6/-0)
tests/unit/tst_performance/ListItemWithInlineActionsList.qml (+41/-0)
tests/unit/tst_performance/tst_performance.cpp (+2/-1)
tests/unit/tst_performance/tst_performance.pro (+3/-2)
tests/unit_x11/tst_components/tst_listitem.qml (+125/-4)
To merge this branch: bzr merge lp:~zsombi/ubuntu-ui-toolkit/35-options-panel-swipe
Reviewer Review Type Date Requested Status
Tim Peeters Approve
Review via email: mp+241036@code.launchpad.net

Description of the change

Actions panel handling, swiping and snap animation take in use. Uses fix-size panel for testing.

To post a comment you must log in.
1287. By Zsombor Egri

small fixes

1288. By Zsombor Egri

prereq sync

1289. By Zsombor Egri

adjustments to prerequisite changes; documentation fix

1290. By Zsombor Egri

accessing private property fix

1291. By Zsombor Egri

test cases fixed

1292. By Zsombor Egri

prereq sync

1293. By Zsombor Egri

fixing eronous trailingActions assignment

1294. By Zsombor Egri

introduce styling; move ListItemPanel into Ambiance theme, expose ListItemStyle C++ style component from the main plugin

1295. By Zsombor Egri

animation cleanup

1296. By Zsombor Egri

make leadingPanel property readonly

1297. By Zsombor Egri

prereq sync

1298. By Zsombor Egri

adjusting code to prereq changes

1299. By Zsombor Egri

prereq sync

1300. By Zsombor Egri

staging in test fixing

1301. By Zsombor Egri

prereq sync

1302. By Zsombor Egri

prereq sync

1303. By Zsombor Egri

test fix

1304. By Zsombor Egri

fixing tests to comply with prereq changes

1305. By Zsombor Egri

prereq sync

1306. By Zsombor Egri

prereq sync

1307. By Zsombor Egri

method name fixed

1308. By Zsombor Egri

prereq sync

1309. By Zsombor Egri

prereq sync

1310. By Zsombor Egri

prereq sync

Revision history for this message
Tim Peeters (tpeeters) wrote :

I don't see icons with this code: http://pastebin.ubuntu.com/9145869/

Revision history for this message
Tim Peeters (tpeeters) wrote :

with the code above, the list item text never becomes red, so swiping never becomes true?

Revision history for this message
Tim Peeters (tpeeters) wrote :

66 + // for testing
67 + objectName: "ListItemPanel" + (leadingPanel ? "Leading" : "Trailing")

That might appear like you left some debugging code that should not be in the final version.

Change the comment to // Used for autopilot testing in filename.py?
or something similar.

Revision history for this message
Tim Peeters (tpeeters) wrote :

74 + Specifies whether the panel is used to visualize leading or trailing options.
75 + */
76 + readonly property bool leadingPanel: panel.ListItemActions.status == panel.ListItemActions.Leading

I prefer readonly property bool leading.
We know we are a panel.

Revision history for this message
Zsombor Egri (zsombi) wrote :

> 66 + // for testing
> 67 + objectName: "ListItemPanel" + (leadingPanel ? "Leading" :
> "Trailing")
>
> That might appear like you left some debugging code that should not be in the
> final version.
>
> Change the comment to // Used for autopilot testing in filename.py?
> or something similar.

It is used in module testing.

Revision history for this message
Tim Peeters (tpeeters) wrote :

321 + , xAxisMoveThresholdGU(1.5)
322 , overshoot(UCUnits::instance().gu(2))

let's do the x-axis threshold like the overshoot, so:
xAxisMoveThreshold(UCUnits::instance().gu(1.5))

Revision history for this message
Tim Peeters (tpeeters) wrote :

372 + if (!UCListItemActionsPrivate::isConnectedTo(leadingActions, q) && !UCListItemActionsPrivate::isConnectedTo(trailingActions, q)) {

add a linebreak

Revision history for this message
Tim Peeters (tpeeters) wrote :

383 + * animations. The component does not assume any visuals present in the style,
384 + * and will load its content only when requested, i.e. either of the mentioned
385 + * components are visualized.

the part from i.e. is a bit unclear to me. What do you try to say there?

Revision history for this message
Tim Peeters (tpeeters) wrote :

458 + setTugged(false);

didn't we switch from "tug" to "swipe" in a previous MR?

Revision history for this message
Tim Peeters (tpeeters) wrote :

should we include a link to ListItemStyle in the ListItem docs?

Revision history for this message
Tim Peeters (tpeeters) wrote :

Can we improve this text?

> The component defines the style API for the ListItem component.
> Provides the default actions visualization panel, snap animation,
> selection mode and drag handler delegates. ListItem treats the
> style differently compared to the other components, as it will
> load it only when needed, to load the components defining the
> look and feel of the different configurable elements of the component,
> and will ignore any visuals declared in the style.

Style API for the ListItem component which provides actions, select and
drag handler delegates, and snap animation via its properties.
ListItem treats the style differently compared to the other components,
as it:
- loads the style only when needed
- gets delegates and snap animation from the style properties
- ignores any other visuals defined in the style

Revision history for this message
Tim Peeters (tpeeters) wrote :

replace "tug" by "swipe" everywhere?

Revision history for this message
Tim Peeters (tpeeters) wrote :

Remove this completely?
I understand the example, but I think it is bad practice to show the same action on both sides, so we should not include it as a code example.

 If
568 + * it is desired to have the same action present in both leading and trailing
569 + * actions, one of the ListItemActions actions list can use the other's list. In
570 + * the following example the list item can be deleted through both leading and
571 + * trailing actions:
572 + * \qml
573 + * ListItem {
574 + * id: listItem
575 + * leadingActions: ListItemActions {
576 + * actions: [
577 + * Action {
578 + * iconName: "delete"
579 + * onTriggered: listItem.destroy()
580 + * }
581 + * ]
582 + * }
583 + * trailingActions: ListItemActions {
584 + * actions: leadingActions.actions
585 + * }
586 + * }

Revision history for this message
Tim Peeters (tpeeters) wrote :

1478 === added file 'tests/unit/tst_performance/ListItemWithInlineOptionsList.qml'

filename options-->actions

Revision history for this message
Zsombor Egri (zsombi) wrote :

> with the code above, the list item text never becomes red, so swiping never
> becomes true?

First, ListItem attached properties are only valid when attached to their parent, or if used in ListView, then attached to ListView, or when used in Flickables, attached to Flickables.

Second, swiping is not defined for ListItem, but for ListItemAttached. Now that property is valid only for leadin/trailing panels.

It is unfortunate that you can attach the property to any object, but this is something we cannot change. The same happens with ListView attached properties, it doesn't give you any warning/error (as it cannot) if attached to other elements but the ListView's delegate, but only works with the delegate.

Revision history for this message
Zsombor Egri (zsombi) wrote :

> 321 + , xAxisMoveThresholdGU(1.5)
> 322 , overshoot(UCUnits::instance().gu(2))
>
> let's do the x-axis threshold like the overshoot, so:
> xAxisMoveThreshold(UCUnits::instance().gu(1.5))

No, I would not. Especially that this is not configurable. If we decide to make it configurable, we can do it later. Yet, there's no reason to watch the units change to update this property, as we need the threshold only when swiping. And if we don't configure it, we can always get the proper pixel amount by converting in place.

Revision history for this message
Zsombor Egri (zsombi) wrote :

> 383 + * animations. The component does not assume any visuals present in
> the style,
> 384 + * and will load its content only when requested, i.e. either of the
> mentioned
> 385 + * components are visualized.
>
> the part from i.e. is a bit unclear to me. What do you try to say there?

Perhaps that si not even needed... Will remove it.

Revision history for this message
Zsombor Egri (zsombi) wrote :

> 458 + setTugged(false);
>
> didn't we switch from "tug" to "swipe" in a previous MR?

The concept yes, but this staid... I'll change it to swipe

Revision history for this message
Zsombor Egri (zsombi) wrote :

> should we include a link to ListItemStyle in the ListItem docs?

Yep. Added.

Revision history for this message
Zsombor Egri (zsombi) wrote :

> Can we improve this text?
>
> > The component defines the style API for the ListItem component.
> > Provides the default actions visualization panel, snap animation,
> > selection mode and drag handler delegates. ListItem treats the
> > style differently compared to the other components, as it will
> > load it only when needed, to load the components defining the
> > look and feel of the different configurable elements of the component,
> > and will ignore any visuals declared in the style.
>
> Style API for the ListItem component which provides actions, select and
> drag handler delegates, and snap animation via its properties.
> ListItem treats the style differently compared to the other components,
> as it:
> - loads the style only when needed
> - gets delegates and snap animation from the style properties
> - ignores any other visuals defined in the style

Done.

Revision history for this message
Zsombor Egri (zsombi) wrote :

> replace "tug" by "swipe" everywhere?

No. We agreed with design that they call it tug, so we will, at least in the doc.

Revision history for this message
Zsombor Egri (zsombi) wrote :

> Remove this completely?
> I understand the example, but I think it is bad practice to show the same
> action on both sides, so we should not include it as a code example.
>
>
> If
> 568 + * it is desired to have the same action present in both leading and
> trailing
> 569 + * actions, one of the ListItemActions actions list can use the
> other's list. In
> 570 + * the following example the list item can be deleted through both
> leading and
> 571 + * trailing actions:
> 572 + * \qml
> 573 + * ListItem {
> 574 + * id: listItem
> 575 + * leadingActions: ListItemActions {
> 576 + * actions: [
> 577 + * Action {
> 578 + * iconName: "delete"
> 579 + * onTriggered: listItem.destroy()
> 580 + * }
> 581 + * ]
> 582 + * }
> 583 + * trailingActions: ListItemActions {
> 584 + * actions: leadingActions.actions
> 585 + * }
> 586 + * }

Why not? this is a practice some apps may apply. So why shouldn't we have it documented on how to do it? This way we can be sure we won't spend useless time by triaging these kind of bugs...

1311. By Zsombor Egri

review comments applied

Revision history for this message
Tim Peeters (tpeeters) wrote :

okay, thanks

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'components.api'
2--- components.api 2014-11-21 14:40:26 +0000
3+++ components.api 2014-11-21 14:40:28 +0000
4@@ -876,6 +876,8 @@
5 Property { name: "highlightColor"; type: "QColor" }
6 Property { name: "data"; type: "QObject"; isList: true; isReadonly: true }
7 Property { name: "children"; type: "QQuickItem"; isList: true; isReadonly: true }
8+ Property { name: "style"; type: "QQmlComponent"; isPointer: true }
9+ Property { name: "__styleInstance"; type: "QQuickItem"; isReadonly: true; isPointer: true }
10 Signal { name: "clicked" }
11 Signal { name: "contentMovementStarted" }
12 Signal { name: "contentMovementEnded" }
13@@ -910,6 +912,13 @@
14 Property { name: "rightMargin"; type: "double" }
15 Property { name: "colorFrom"; type: "QColor" }
16 Property { name: "colorTo"; type: "QColor" }
17+ name: "UCListItemStyle"
18+ prototype: "QQuickItem"
19+ exports: ["Ubuntu.Components.Styles/ListItemStyle 1.2"]
20+ Property { name: "actionsDelegate"; type: "QQmlComponent"; isPointer: true }
21+ Property { name: "selectionDelegate"; type: "QQmlComponent"; isPointer: true }
22+ Property { name: "dragHandlerDelegate"; type: "QQmlComponent"; isPointer: true }
23+ Property { name: "snapAnimation"; type: "QQuickPropertyAnimation"; isPointer: true }
24 name: "UCMouse"
25 prototype: "QObject"
26 exports: ["Mouse 0.1", "Mouse 1.0"]
27
28=== added file 'modules/Ubuntu/Components/Themes/Ambiance/ListItemPanel.qml'
29--- modules/Ubuntu/Components/Themes/Ambiance/ListItemPanel.qml 1970-01-01 00:00:00 +0000
30+++ modules/Ubuntu/Components/Themes/Ambiance/ListItemPanel.qml 2014-11-21 14:40:28 +0000
31@@ -0,0 +1,64 @@
32+/*
33+ * Copyright 2014 Canonical Ltd.
34+ *
35+ * This program is free software; you can redistribute it and/or modify
36+ * it under the terms of the GNU Lesser General Public License as published by
37+ * the Free Software Foundation; version 3.
38+ *
39+ * This program is distributed in the hope that it will be useful,
40+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
41+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42+ * GNU Lesser General Public License for more details.
43+ *
44+ * You should have received a copy of the GNU Lesser General Public License
45+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
46+ */
47+
48+import QtQuick 2.2
49+import Ubuntu.Components 1.2
50+
51+/*
52+ This component is the holder of the ListItem options.
53+ */
54+Item {
55+ id: panel
56+
57+ // styling properties
58+ /*
59+ Color of teh background.
60+ */
61+ // FIXME: use Palette colors instead when available
62+ property color backgroundColor: (leading ? UbuntuColors.red : "white")
63+
64+ width: units.gu(20)
65+
66+ // used for module/autopilot testing
67+ objectName: "ListItemPanel" + (leading ? "Leading" : "Trailing")
68+
69+ /*
70+ Property holding the ListItem's contentItem instance
71+ */
72+ readonly property Item contentItem: parent ? parent.contentItem : null
73+ /*
74+ Specifies whether the panel is used to visualize leading or trailing options.
75+ */
76+ readonly property bool leading: panel.ListItemActions.status == panel.ListItemActions.Leading
77+
78+ anchors {
79+ left: contentItem ? (leading ? undefined : contentItem.right) : undefined
80+ right: contentItem ? (leading ? contentItem.left : undefined) : undefined
81+ top: contentItem ? contentItem.top : undefined
82+ bottom: contentItem ? contentItem.bottom : undefined
83+ }
84+
85+ Rectangle {
86+ objectName: "panel_background"
87+ anchors {
88+ fill: parent
89+ // add 4 times the overshoot margins to cover the background when tugged
90+ leftMargin: leading ? -units.gu(4 * panel.ListItemActions.overshoot) : 0
91+ rightMargin: leading ? 0 : -units.gu(4 * panel.ListItemActions.overshoot)
92+ }
93+ color: panel.backgroundColor
94+ }
95+}
96
97=== added file 'modules/Ubuntu/Components/Themes/Ambiance/ListItemStyle.qml'
98--- modules/Ubuntu/Components/Themes/Ambiance/ListItemStyle.qml 1970-01-01 00:00:00 +0000
99+++ modules/Ubuntu/Components/Themes/Ambiance/ListItemStyle.qml 2014-11-21 14:40:28 +0000
100@@ -0,0 +1,34 @@
101+import QtQuick 2.3
102+import Ubuntu.Components.Styles 1.2 as Styles
103+import Ubuntu.Components 1.2
104+/*
105+ * Copyright 2014 Canonical Ltd.
106+ *
107+ * This program is free software; you can redistribute it and/or modify
108+ * it under the terms of the GNU Lesser General Public License as published by
109+ * the Free Software Foundation; version 3.
110+ *
111+ * This program is distributed in the hope that it will be useful,
112+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
113+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
114+ * GNU Lesser General Public License for more details.
115+ *
116+ * You should have received a copy of the GNU Lesser General Public License
117+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
118+ */
119+
120+Styles.ListItemStyle {
121+
122+ actionsDelegate: ListItemPanel{}
123+
124+ snapAnimation: PropertyAnimation {
125+ property: "x"
126+ easing {
127+ type: Easing.OutElastic
128+ period: 0.5
129+ }
130+ duration: UbuntuAnimation.BriskDuration
131+ alwaysRunToEnd: true
132+ }
133+
134+}
135
136=== modified file 'modules/Ubuntu/Components/Themes/Ambiance/qmldir'
137--- modules/Ubuntu/Components/Themes/Ambiance/qmldir 2014-11-10 17:08:05 +0000
138+++ modules/Ubuntu/Components/Themes/Ambiance/qmldir 2014-11-21 14:40:28 +0000
139@@ -55,3 +55,7 @@
140 internal OverflowPanel OverflowPanel.qml
141 internal HeadSeparatorImageStyle HeadSeparatorImageStyle.qml
142 internal HeadDividerStyle HeadDividerStyle.qml
143+
144+#version 1.2
145+ListItemPanel 1.2 ListItemPanel.qml
146+ListItemStyle 1.2 ListItemStyle.qml
147
148=== modified file 'modules/Ubuntu/Components/plugin/plugin.cpp'
149--- modules/Ubuntu/Components/plugin/plugin.cpp 2014-11-21 14:40:26 +0000
150+++ modules/Ubuntu/Components/plugin/plugin.cpp 2014-11-21 14:40:28 +0000
151@@ -55,6 +55,7 @@
152 #include "uclistitem.h"
153 #include "uclistitem_p.h"
154 #include "uclistitemactions.h"
155+#include "uclistitemstyle.h"
156
157 #include <sys/types.h>
158 #include <unistd.h>
159@@ -176,6 +177,10 @@
160 // initialize baseURL
161 m_baseUrl = QUrl(baseUrl().toString() + '/');
162
163+ // register internal styles
164+ const char *styleUri = "Ubuntu.Components.Styles";
165+ qmlRegisterType<UCListItemStyle, 2>(styleUri, 1, 2, "ListItemStyle");
166+
167 QQmlExtensionPlugin::initializeEngine(engine, uri);
168 QQmlContext* context = engine->rootContext();
169
170
171=== modified file 'modules/Ubuntu/Components/plugin/plugin.pro'
172--- modules/Ubuntu/Components/plugin/plugin.pro 2014-11-21 14:40:26 +0000
173+++ modules/Ubuntu/Components/plugin/plugin.pro 2014-11-21 14:40:28 +0000
174@@ -73,7 +73,8 @@
175 uclistitem_p.h \
176 uclistitemactions.h \
177 uclistitemactions_p.h \
178- propertychange_p.h
179+ propertychange_p.h \
180+ uclistitemstyle.h
181
182 SOURCES += plugin.cpp \
183 uctheme.cpp \
184@@ -115,7 +116,8 @@
185 uclistitemactions.cpp \
186 uclistitemactionsattached.cpp \
187 uclistitemattached.cpp \
188- propertychange_p.cpp
189+ propertychange_p.cpp \
190+ uclistitemstyle.cpp
191
192 # adapters
193 SOURCES += adapters/alarmsadapter_organizer.cpp
194
195=== modified file 'modules/Ubuntu/Components/plugin/uclistitem.cpp'
196--- modules/Ubuntu/Components/plugin/uclistitem.cpp 2014-11-21 14:40:26 +0000
197+++ modules/Ubuntu/Components/plugin/uclistitem.cpp 2014-11-21 14:40:28 +0000
198@@ -18,12 +18,20 @@
199 #include "uctheme.h"
200 #include "uclistitem.h"
201 #include "uclistitem_p.h"
202+#include "uclistitemactions.h"
203+#include "uclistitemactions_p.h"
204+#include "ucubuntuanimation.h"
205+#include "propertychange_p.h"
206 #include <QtQml/QQmlInfo>
207 #include <QtQuick/private/qquickitem_p.h>
208 #include <QtQuick/private/qquickflickable_p.h>
209 #include <QtQuick/private/qquickpositioners_p.h>
210 #include <QtQuick/private/qquickanimation_p.h>
211-#include "ucubuntuanimation.h"
212+#include "uclistitemstyle.h"
213+
214+#define MIN(x, y) ((x < y) ? x : y)
215+#define MAX(x, y) ((x > y) ? x : y)
216+#define CLAMP(v, min, max) (min <= max) ? MAX(min, MIN(v, max)) : MAX(max, MIN(v, min))
217
218 QColor getPaletteColor(const char *profile, const char *color)
219 {
220@@ -90,8 +98,7 @@
221 snap->setFrom(listItem->contentItem->property(snap->property().toLocal8Bit().constData()));
222 snap->setTo(to);
223 snap->setAlwaysRunToEnd(true);
224- // FIXME - this will be used later
225-// listItem->setContentMoved(true);
226+ listItem->setContentMoving(true);
227 snap->start();
228 return true;
229 }
230@@ -127,11 +134,11 @@
231 // no need to listen flickables any longer
232 listItem->attachedProperties->listenToRebind(item, false);
233 }
234- // disconnect actions - FIXME this will be used later
235-// listItem->grabPanel(listItem->leadingActions, false);
236-// listItem->grabPanel(listItem->trailingActions, false);
237- // set contentMoving to false - FIXME used later
238-// listItem->setContentMoving(false);
239+ // disconnect actions
240+ listItem->grabPanel(listItem->leadingActions, false);
241+ listItem->grabPanel(listItem->trailingActions, false);
242+ // set contentMoved to false
243+ listItem->setContentMoving(false);
244 }
245
246 /*
247@@ -145,9 +152,9 @@
248 QQuickAbstractAnimation *snap = getDefaultAnimation();
249 QObject::disconnect(snap, 0, 0, 0);
250 }
251- // turn content moving off - FIXME used later
252-// UCListItemPrivate *listItem = UCListItemPrivate::get(item);
253-// listItem->setContentMoving(false);
254+ // turn content moving off
255+ UCListItemPrivate *listItem = UCListItemPrivate::get(item);
256+ listItem->setContentMoving(false);
257 }
258
259 /*
260@@ -155,8 +162,11 @@
261 */
262 QQuickPropertyAnimation *UCListItemSnapAnimator::getDefaultAnimation()
263 {
264- // FIXME - return the animation from the style
265- return 0;
266+ UCListItemPrivate *listItem = UCListItemPrivate::get(item);
267+ if (!listItem->styleItem && listItem->loadStyle()) {
268+ listItem->initStyleItem();
269+ }
270+ return listItem->styleItem ? listItem->styleItem->m_snapAnimation : 0;
271 }
272
273 /******************************************************************************
274@@ -236,22 +246,23 @@
275 }
276 }
277
278-QSGNode *UCListItemDivider::paint(const QRectF &rect)
279+QSGNode *UCListItemDivider::paint(QSGNode *node, const QRectF &rect)
280 {
281- if (m_visible && (m_gradient.size() > 0)) {
282- // the parent always recreates the node, so no worries for the existing child node
283- QSGRectangleNode *rectNode = m_listItem->sceneGraphContext()->createRectangleNode();
284- // margins are only applied when the ListItem is in normal state, when pressed,
285- // the divider is painted from edge to edge
286- qreal left = (m_listItem && m_listItem->pressed) ? 0 : m_leftMargin;
287- qreal right = (m_listItem && m_listItem->pressed) ? rect.width() : rect.width() - m_leftMargin - m_rightMargin;
288- rectNode->setRect(QRectF(left, rect.height() - m_thickness, right, m_thickness));
289- rectNode->setGradientStops(m_gradient);
290- rectNode->update();
291- return rectNode;
292- } else {
293- return 0;
294+ QSGRectangleNode *dividerNode = static_cast<QSGRectangleNode*>(node);
295+ if (m_visible && (m_gradient.size() > 0) && ((m_colorFrom.alphaF() >= (1.0f / 255.0f)) || (m_colorTo.alphaF() >= (1.0f / 255.0f)))) {
296+ if (!dividerNode) {
297+ dividerNode = m_listItem->sceneGraphContext()->createRectangleNode();
298+ }
299+ QRectF divider(m_leftMargin, rect.height() - m_thickness, rect.width() - m_leftMargin - m_rightMargin, m_thickness);
300+ dividerNode->setRect(divider);
301+ dividerNode->setGradientStops(m_gradient);
302+ dividerNode->update();
303+ return dividerNode;
304+ } else if (node) {
305+ // delete the node
306+ delete node;
307 }
308+ return 0;
309 }
310
311 void UCListItemDivider::setVisible(bool visible)
312@@ -317,7 +328,12 @@
313 , pressed(false)
314 , contentMoved(false)
315 , highlightColorChanged(false)
316+ , swiped(false)
317+ , suppressClick(false)
318 , ready(false)
319+ , customStyle(false)
320+ , customColor(false)
321+ , xAxisMoveThresholdGU(1.5)
322 , overshoot(UCUnits::instance().gu(2))
323 , color(Qt::transparent)
324 , highlightColor(Qt::transparent)
325@@ -327,6 +343,8 @@
326 , leadingActions(0)
327 , trailingActions(0)
328 , animator(0)
329+ , styleComponent(0)
330+ , styleItem(0)
331 {
332 }
333 UCListItemPrivate::~UCListItemPrivate()
334@@ -348,33 +366,122 @@
335 // turn activeFocusOnPress on
336 q->setActiveFocusOnPress(true);
337
338- // catch theme palette changes
339- QObject::connect(&UCTheme::instance(), SIGNAL(paletteChanged()), q, SLOT(_q_updateColors()));
340- _q_updateColors();
341+ // catch theme changes
342+ QObject::connect(&UCTheme::instance(), SIGNAL(nameChanged()), q, SLOT(_q_updateThemedData()));
343+ _q_updateThemedData();
344
345 // watch size change and set implicit size;
346 QObject::connect(&UCUnits::instance(), SIGNAL(gridUnitChanged()), q, SLOT(_q_updateSize()));
347 _q_updateSize();
348 }
349
350-void UCListItemPrivate::_q_updateColors()
351+void UCListItemPrivate::_q_updateThemedData()
352 {
353- Q_Q(UCListItem);
354- highlightColor = getPaletteColor("selected", "background");
355- q->update();
356+ // update colors, panels
357+ if (!customColor) {
358+ Q_Q(UCListItem);
359+ highlightColor = getPaletteColor("selected", "background");
360+ q->update();
361+ }
362+ loadStyle();
363 }
364
365 void UCListItemPrivate::_q_rebound()
366 {
367 setPressed(false);
368- // disconnect the flickable
369- listenToRebind(false);
370+ // initiate rebinding only if there were actions tugged
371+ Q_Q(UCListItem);
372+ if (!UCListItemActionsPrivate::isConnectedTo(leadingActions, q) &&
373+ !UCListItemActionsPrivate::isConnectedTo(trailingActions, q)) {
374+ return;
375+ }
376+ setSwiped(false);
377+ // rebound to zero
378+ animator->snap(0);
379+}
380+/*!
381+ * \qmlproperty Component ListItem::style
382+ * Holds the style of the component defining the components visualizing the leading/
383+ * trailing actions, selection and dragging mode handlers as well as different
384+ * animations. The component does not assume any visuals present in the style,
385+ * and will load its content only when requested.
386+ * \sa ListItemStyle
387+ */
388+QQmlComponent *UCListItemPrivate::style() const
389+{
390+ return styleComponent;
391+}
392+void UCListItemPrivate::setStyle(QQmlComponent *delegate)
393+{
394+ if (styleComponent == delegate) {
395+ return;
396+ }
397+ Q_Q(UCListItem);
398+ // make sure we're rebound before we change the panel component
399+ promptRebound();
400+ if (styleItem) {
401+ delete styleItem;
402+ styleItem = 0;
403+ Q_EMIT q->__styleInstanceChanged();
404+ }
405+ delete styleComponent;
406+ customStyle = (delegate == 0);
407+ styleComponent = delegate;
408+ loadStyle();
409+ Q_EMIT q->styleChanged();
410+}
411+
412+// update themed components
413+bool UCListItemPrivate::loadStyle()
414+{
415+ if (!ready) {
416+ return false;
417+ }
418+ if (!customStyle && !styleComponent) {
419+ Q_Q(UCListItem);
420+ if (styleItem) {
421+ delete styleItem;
422+ styleItem = 0;
423+ Q_EMIT q->__styleInstanceChanged();
424+ }
425+ delete styleComponent;
426+ styleComponent = UCTheme::instance().createStyleComponent("ListItemStyle.qml", q);
427+ }
428+ return (styleComponent != NULL);
429+}
430+// creates the style item
431+void UCListItemPrivate::initStyleItem()
432+{
433+ if (!styleComponent || styleItem) {
434+ return;
435+ }
436+ Q_Q(UCListItem);
437+ QObject *object = styleComponent->beginCreate(qmlContext(q));
438+ styleItem = qobject_cast<UCListItemStyle*>(object);
439+ if (!styleItem) {
440+ delete object;
441+ }
442+ QQml_setParent_noEvent(styleItem, q);
443+ styleComponent->completeCreate();
444+}
445+
446+/*!
447+ * \qmlproperty Item ListItem::__styleInstance
448+ * \internal
449+ */
450+QQuickItem *UCListItemPrivate::styleInstance() const
451+{
452+ return styleItem;
453 }
454
455 // rebound without animation
456 void UCListItemPrivate::promptRebound()
457 {
458 setPressed(false);
459+ setSwiped(false);
460+ if (animator) {
461+ animator->snapOut();
462+ }
463 }
464
465 // called when units size changes
466@@ -406,11 +513,45 @@
467 {
468 if (this->pressed != pressed) {
469 this->pressed = pressed;
470+ suppressClick = false;
471 Q_Q(UCListItem);
472 q->update();
473 Q_EMIT q->pressedChanged();
474 }
475 }
476+// toggles the tugged flag and installs/removes event filter
477+void UCListItemPrivate::setSwiped(bool swiped)
478+{
479+ suppressClick = swiped;
480+ if (this->swiped == swiped) {
481+ return;
482+ }
483+ this->swiped = swiped;
484+ Q_Q(UCListItem);
485+ QQuickWindow *window = q->window();
486+ if (swiped) {
487+ window->installEventFilter(q);
488+ } else {
489+ window->removeEventFilter(q);
490+ }
491+}
492+
493+// grabs the panels from the leading/trailing actions and disables all ascending flickables
494+bool UCListItemPrivate::grabPanel(UCListItemActions *actionsList, bool isTugged)
495+{
496+ Q_Q(UCListItem);
497+ if (isTugged) {
498+ bool grab = UCListItemActionsPrivate::connectToListItem(actionsList, q, (actionsList == leadingActions));
499+ if (attachedProperties) {
500+ attachedProperties->disableInteractive(q, true);
501+ }
502+ return grab;
503+ } else {
504+ UCListItemActionsPrivate::disconnectFromListItem(actionsList);
505+ return false;
506+ }
507+}
508+
509
510 // connects/disconnects from the Flickable anchestor to get notified when to do rebound
511 void UCListItemPrivate::listenToRebind(bool listen)
512@@ -440,6 +581,19 @@
513 q->update();
514 }
515
516+// clamps the X value and moves the contentItem to the new X value
517+void UCListItemPrivate::clampAndMoveX(qreal &x, qreal dx)
518+{
519+ UCListItemActionsPrivate *leading = UCListItemActionsPrivate::get(leadingActions);
520+ UCListItemActionsPrivate *trailing = UCListItemActionsPrivate::get(trailingActions);
521+ x += dx;
522+ // min cannot be less than the trailing's panel width
523+ qreal min = (trailing && trailing->panelItem) ? -trailing->panelItem->width() : 0;
524+ // max cannot be bigger than 0 or the leading's width in case we have leading panel
525+ qreal max = (leading && leading->panelItem) ? leading->panelItem->width() : 0;
526+ x = CLAMP(x, min, max);
527+}
528+
529 /*!
530 * \qmltype ListItem
531 * \instantiates UCListItem
532@@ -466,6 +620,62 @@
533 * Each ListItem has a thin divider shown on the bottom of the component. This
534 * divider can be configured through the \c divider grouped property, which can
535 * configure its margins from the edges of the ListItem as well as its visibility.
536+ *
537+ * ListItem can handle actions that can get tugged from front to back of the item.
538+ * These actions are Action elements visualized in panels attached to the front
539+ * or to the back of the item, and are revealed by swiping the item horizontally.
540+ * The tug is started only after the mouse/touch move had passed a given threshold.
541+ * These actions are configured through the \l leadingActions as well as \l
542+ * trailingActions properties.
543+ * \qml
544+ * ListItem {
545+ * id: listItem
546+ * leadingActions: ListItemActions {
547+ * actions: [
548+ * Action {
549+ * iconName: "delete"
550+ * onTriggered: listItem.destroy()
551+ * }
552+ * ]
553+ * }
554+ * trailingActions: ListItemActions {
555+ * actions: [
556+ * Action {
557+ * iconName: "search"
558+ * onTriggered: {
559+ * // do some search
560+ * }
561+ * }
562+ * ]
563+ * }
564+ * }
565+ * \endqml
566+ * \note When a list item is tugged, it automatically connects both leading and
567+ * trailing actions to the list item. This implies that a ListItem cannot use
568+ * the same ListItemActions instance for both leading and trailing actions. If
569+ * it is desired to have the same action present in both leading and trailing
570+ * actions, one of the ListItemActions actions list can use the other's list. In
571+ * the following example the list item can be deleted through both leading and
572+ * trailing actions:
573+ * \qml
574+ * ListItem {
575+ * id: listItem
576+ * leadingActions: ListItemActions {
577+ * actions: [
578+ * Action {
579+ * iconName: "delete"
580+ * onTriggered: listItem.destroy()
581+ * }
582+ * ]
583+ * }
584+ * trailingActions: ListItemActions {
585+ * actions: leadingActions.actions
586+ * }
587+ * }
588+ * \endqml
589+ * \sa ListItemActions
590+ *
591+ * The component is styled using the \l ListItemStyle style interface.
592 */
593
594 /*!
595@@ -494,6 +704,8 @@
596 void UCListItem::componentComplete()
597 {
598 UCStyledItemBase::componentComplete();
599+ // must load the theme!
600+ Q_D(UCListItem);
601 d_func()->ready = true;
602 }
603
604@@ -515,6 +727,7 @@
605
606 // attach ListItem properties to the flickable or to the parent item
607 if (d->flickable) {
608+ // connect to flickable to get width changes
609 d->attachedProperties = static_cast<UCListItemAttached*>(qmlAttachedPropertiesObject<UCListItem>(d->flickable));
610 } else if (data.item) {
611 d->attachedProperties = static_cast<UCListItemAttached*>(qmlAttachedPropertiesObject<UCListItem>(data.item));
612@@ -535,7 +748,7 @@
613 void UCListItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
614 {
615 UCStyledItemBase::geometryChanged(newGeometry, oldGeometry);
616- // resize background item
617+ // resize contentItem item
618 Q_D(UCListItem);
619 d->resize();
620 }
621@@ -547,27 +760,47 @@
622 Q_D(UCListItem);
623 QColor color = d->pressed ? d->highlightColor : d->color;
624
625- delete oldNode;
626 if (width() <= 0 || height() <= 0) {
627+ delete oldNode;
628 return 0;
629 }
630
631 QSGRectangleNode *rectNode = 0;
632- if (color.alpha() > 0) {
633+ rectNode = static_cast<QSGRectangleNode*>(oldNode);
634+ if (!rectNode) {
635 rectNode = QQuickItemPrivate::get(this)->sceneGraphContext()->createRectangleNode();
636+ }
637+ if (color.alphaF() >= (1.0f / 255.0f)) {
638 rectNode->setColor(color);
639 // cover only the area of the contentItem
640 rectNode->setRect(d->contentItem->boundingRect());
641+ rectNode->setGradientStops(QGradientStops());
642+ rectNode->setAntialiasing(true);
643+ rectNode->setAntialiasing(false);
644 rectNode->update();
645+ } else {
646+ // delete node, this will delete the divider node as well
647+ delete rectNode;
648+ rectNode = 0;
649 }
650 oldNode = rectNode;
651+ QSGNode *dividerNode = oldNode ? oldNode->childAtIndex(0) : 0;
652 if (d->divider && d->divider->m_visible) {
653- QSGNode * dividerNode = d->divider->paint(boundingRect());
654- if (dividerNode && oldNode) {
655- oldNode->appendChildNode(dividerNode);
656- } else if (dividerNode) {
657- oldNode = dividerNode;
658- }
659+ QSGNode *newNode = d->divider->paint(dividerNode, boundingRect());
660+ if (newNode != dividerNode && oldNode) {
661+ if (dividerNode) {
662+ oldNode->removeChildNode(dividerNode);
663+ }
664+ if (newNode) {
665+ oldNode->appendChildNode(newNode);
666+ }
667+ }
668+ if (!oldNode) {
669+ oldNode = newNode;
670+ }
671+ } else if (dividerNode) {
672+ // the divider painter node may be still added as child, so remove it
673+ oldNode->removeChildNode(dividerNode);
674 }
675 return oldNode;
676 }
677@@ -582,8 +815,14 @@
678 }
679 if (event->button() == Qt::LeftButton) {
680 d->setPressed(true);
681+ d->lastPos = d->pressedPos = event->localPos();
682 // connect the Flickable to know when to rebound
683 d->listenToRebind(true);
684+ // if it was moved, grab the panels
685+ if (d->swiped) {
686+ d->grabPanel(d->leadingActions, true);
687+ d->grabPanel(d->trailingActions, true);
688+ }
689 }
690 // accept the event so we get the rest of the events as well
691 event->setAccepted(true);
692@@ -596,11 +835,113 @@
693 // set released
694 if (d->pressed) {
695 d->listenToRebind(false);
696- Q_EMIT clicked();
697+ if (d->attachedProperties) {
698+ d->attachedProperties->disableInteractive(this, false);
699+ }
700+
701+ if (!d->suppressClick) {
702+ Q_EMIT clicked();
703+ d->_q_rebound();
704+ } else if (d->contentItem->x() == 0.0) {
705+ // if no actions list is connected, release flickable blockade
706+ d->promptRebound();
707+ } else {
708+ d->setContentMoving(false);
709+ }
710 }
711 d->setPressed(false);
712 }
713
714+void UCListItem::mouseMoveEvent(QMouseEvent *event)
715+{
716+ Q_D(UCListItem);
717+ UCStyledItemBase::mouseMoveEvent(event);
718+
719+ // accept the tugging only if the move is within the threshold
720+ bool leadingAttached = UCListItemActionsPrivate::isConnectedTo(d->leadingActions, this);
721+ bool trailingAttached = UCListItemActionsPrivate::isConnectedTo(d->trailingActions, this);
722+ if (d->pressed && !(leadingAttached || trailingAttached)) {
723+ // check if we can initiate the drag at all
724+ // only X direction matters, if Y-direction leaves the threshold, but X not, the tug is not valid
725+ qreal threshold = UCUnits::instance().gu(d->xAxisMoveThresholdGU);
726+ qreal mouseX = event->localPos().x();
727+ qreal pressedX = d->pressedPos.x();
728+
729+ if ((mouseX < (pressedX - threshold)) || (mouseX > (pressedX + threshold))) {
730+ // the press went out of the threshold area, enable move, if the direction allows it
731+ d->lastPos = event->localPos();
732+ // tries to connect both panels so we do no longer need to take care which
733+ // got connected ad which not; this may fail in case of shared ListItemActions,
734+ // as then the panel is shared between the list items, and the panel might be
735+ // still in use in other panels. See UCListItemActionsPrivate::connectToListItem
736+ leadingAttached = d->grabPanel(d->leadingActions, true);
737+ trailingAttached = d->grabPanel(d->trailingActions, true);
738+ // create animator if not created yet
739+ if (!d->animator) {
740+ d->animator = new UCListItemSnapAnimator(this);
741+ }
742+ }
743+ }
744+
745+ if (leadingAttached || trailingAttached) {
746+ qreal x = d->contentItem->x();
747+ qreal dx = event->localPos().x() - d->lastPos.x();
748+ d->lastPos = event->localPos();
749+
750+ if (dx) {
751+ d->setContentMoving(true);
752+ // clamp X into allowed dragging area
753+ d->clampAndMoveX(x, dx);
754+ // block flickable
755+ d->setSwiped(true);
756+ d->contentItem->setX(x);
757+ // decide which panel is visible by checking the contentItem's X coordinates
758+ if (d->contentItem->x() > 0) {
759+ if (leadingAttached) {
760+ UCListItemActionsPrivate::get(d->leadingActions)->panelItem->setVisible(true);
761+ }
762+ if (trailingAttached) {
763+ UCListItemActionsPrivate::get(d->trailingActions)->panelItem->setVisible(false);
764+ }
765+ } else if (d->contentItem->x() < 0) {
766+ // trailing revealed
767+ if (leadingAttached) {
768+ UCListItemActionsPrivate::get(d->leadingActions)->panelItem->setVisible(false);
769+ }
770+ if (trailingAttached) {
771+ UCListItemActionsPrivate::get(d->trailingActions)->panelItem->setVisible(true);
772+ }
773+ }
774+ }
775+ }
776+}
777+
778+bool UCListItem::eventFilter(QObject *target, QEvent *event)
779+{
780+ QPointF myPos;
781+ // only filter press events, and rebound when pressed outside
782+ if (event->type() == QEvent::MouseButtonPress) {
783+ QMouseEvent *mouse = static_cast<QMouseEvent*>(event);
784+ QQuickWindow *window = qobject_cast<QQuickWindow*>(target);
785+ if (window) {
786+ myPos = window->contentItem()->mapToItem(this, mouse->localPos());
787+ }
788+ } else if (event->type() == QEvent::TouchBegin) {
789+ QTouchEvent *touch = static_cast<QTouchEvent*>(event);
790+ QQuickWindow *window = qobject_cast<QQuickWindow*>(target);
791+ if (window) {
792+ myPos = window->contentItem()->mapToItem(this, touch->touchPoints()[0].pos());
793+ }
794+ }
795+ if (!myPos.isNull() && !contains(myPos)) {
796+ Q_D(UCListItem);
797+ d->_q_rebound();
798+ // only accept event, but let it be handled by the underlying or surrounding Flickables
799+ event->accept();
800+ }
801+ return UCStyledItemBase::eventFilter(target, event);
802+}
803+
804 /*!
805 * \qmlproperty ListItemActions ListItem::leadingActions
806 *
807@@ -771,7 +1112,7 @@
808 }
809 d->highlightColor = color;
810 // no more theme change watch
811- disconnect(&UCTheme::instance(), SIGNAL(paletteChanged()), this, SLOT(_q_updateColors()));
812+ d->customColor = true;
813 update();
814 Q_EMIT highlightColorChanged();
815 }
816@@ -789,6 +1130,7 @@
817
818 /*!
819 * \qmlproperty list<Item> ListItem::children
820+ * \internal
821 * Overloaded default property containing all the visible children of the item.
822 */
823 QQmlListProperty<QQuickItem> UCListItem::children()
824
825=== modified file 'modules/Ubuntu/Components/plugin/uclistitem.h'
826--- modules/Ubuntu/Components/plugin/uclistitem.h 2014-11-21 14:40:26 +0000
827+++ modules/Ubuntu/Components/plugin/uclistitem.h 2014-11-21 14:40:28 +0000
828@@ -39,6 +39,9 @@
829 Q_PROPERTY(QColor highlightColor READ highlightColor WRITE setHighlightColor NOTIFY highlightColorChanged)
830 Q_PROPERTY(QQmlListProperty<QObject> data READ data DESIGNABLE false)
831 Q_PROPERTY(QQmlListProperty<QQuickItem> children READ children NOTIFY childrenChanged DESIGNABLE false)
832+ // FIXME move these to StyledItemBase with subtheming
833+ Q_PRIVATE_PROPERTY(UCListItem::d_func(), QQmlComponent *style READ style WRITE setStyle NOTIFY styleChanged)
834+ Q_PRIVATE_PROPERTY(UCListItem::d_func(), QQuickItem *__styleInstance READ styleInstance NOTIFY __styleInstanceChanged)
835 Q_CLASSINFO("DefaultProperty", "data")
836 public:
837 explicit UCListItem(QQuickItem *parent = 0);
838@@ -65,6 +68,8 @@
839 void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
840 void mousePressEvent(QMouseEvent *event);
841 void mouseReleaseEvent(QMouseEvent *event);
842+ void mouseMoveEvent(QMouseEvent *event);
843+ bool eventFilter(QObject *, QEvent *);
844
845 Q_SIGNALS:
846 void leadingActionsChanged();
847@@ -77,6 +82,9 @@
848
849 void clicked();
850
851+ void styleChanged();
852+ void __styleInstanceChanged();
853+
854 void contentMovementStarted();
855 void contentMovementEnded();
856
857@@ -86,7 +94,7 @@
858 Q_DECLARE_PRIVATE(UCListItem)
859 QQmlListProperty<QObject> data();
860 QQmlListProperty<QQuickItem> children();
861- Q_PRIVATE_SLOT(d_func(), void _q_updateColors())
862+ Q_PRIVATE_SLOT(d_func(), void _q_updateThemedData())
863 Q_PRIVATE_SLOT(d_func(), void _q_rebound())
864 Q_PRIVATE_SLOT(d_func(), void _q_updateSize())
865 };
866
867=== modified file 'modules/Ubuntu/Components/plugin/uclistitem_p.h'
868--- modules/Ubuntu/Components/plugin/uclistitem_p.h 2014-11-21 14:40:26 +0000
869+++ modules/Ubuntu/Components/plugin/uclistitem_p.h 2014-11-21 14:40:28 +0000
870@@ -23,10 +23,12 @@
871 #include <QtQuick/private/qquickrectangle_p.h>
872
873 class QQuickFlickable;
874+class QQuickPropertyAnimation;
875 class UCListItemContent;
876 class UCListItemDivider;
877 class UCListItemActions;
878 class UCListItemSnapAnimator;
879+class UCListItemStyle;
880 class UCListItemPrivate : public UCStyledItemBasePrivate
881 {
882 Q_DECLARE_PUBLIC(UCListItem)
883@@ -41,36 +43,55 @@
884 return that->d_func();
885 }
886
887- void _q_updateColors();
888+ void _q_updateThemedData();
889 void _q_rebound();
890 void promptRebound();
891 void _q_updateSize();
892 int index();
893 void setPressed(bool pressed);
894+ void setSwiped(bool tugged);
895+ bool grabPanel(UCListItemActions *optionList, bool isTugged);
896 void listenToRebind(bool listen);
897 void resize();
898 void update();
899+ void clampAndMoveX(qreal &x, qreal dx);
900
901 bool pressed:1;
902 bool contentMoved:1;
903 bool highlightColorChanged:1;
904+ bool swiped:1;
905+ bool suppressClick:1;
906 bool ready:1;
907+ bool customStyle:1;
908+ bool customColor:1;
909+ qreal xAxisMoveThresholdGU;
910 qreal overshoot;
911+ QPointF lastPos;
912+ QPointF pressedPos;
913 QColor color;
914 QColor highlightColor;
915 QPointer<QQuickFlickable> flickable;
916- UCListItemAttached *attachedProperties;
917+ QPointer<UCListItemAttached> attachedProperties;
918 QQuickItem *contentItem;
919 UCListItemDivider *divider;
920 UCListItemActions *leadingActions;
921 UCListItemActions *trailingActions;
922 UCListItemSnapAnimator *animator;
923
924+ // FIXME move these to StyledItemBase togehther with subtheming.
925+ QQmlComponent *styleComponent;
926+ UCListItemStyle *styleItem;
927+
928 // getter/setters
929 bool contentMoving() const;
930 void setContentMoving(bool moved);
931 QQuickPropertyAnimation *snapAnimation() const;
932 void setSnapAnimation(QQuickPropertyAnimation *animation);
933+ QQmlComponent *style() const;
934+ void setStyle(QQmlComponent *delegate);
935+ bool loadStyle();
936+ void initStyleItem();
937+ QQuickItem *styleInstance() const;
938 };
939
940 class PropertyChange;
941@@ -115,7 +136,7 @@
942 void colorToChanged();
943
944 protected:
945- QSGNode *paint(const QRectF &rect);
946+ QSGNode *paint(QSGNode *node, const QRectF &rect);
947
948 private Q_SLOTS:
949 void unitsChanged();
950
951=== modified file 'modules/Ubuntu/Components/plugin/uclistitemactions.cpp'
952--- modules/Ubuntu/Components/plugin/uclistitemactions.cpp 2014-11-21 14:40:26 +0000
953+++ modules/Ubuntu/Components/plugin/uclistitemactions.cpp 2014-11-21 14:40:28 +0000
954@@ -19,13 +19,17 @@
955 #include "uclistitem_p.h"
956 #include "quickutils.h"
957 #include "i18n.h"
958+#include "plugin.h"
959 #include <QtQml/QQmlInfo>
960+#include "ucaction.h"
961+#include "uclistitemstyle.h"
962
963 UCListItemActionsPrivate::UCListItemActionsPrivate()
964 : QObjectPrivate()
965 , status(UCListItemActions::Disconnected)
966 , offsetDragged(0)
967 , delegate(0)
968+ , panelDelegate(0)
969 , panelItem(0)
970 {
971 }
972@@ -33,7 +37,157 @@
973 {
974 }
975
976-// FIXME - fis ListItemStyle::snapAnimation to a link when property available
977+void UCListItemActionsPrivate::_q_updateDraggedOffset()
978+{
979+ UCListItem *listItem = qobject_cast<UCListItem*>(panelItem->parentItem());
980+ if (!listItem) {
981+ return;
982+ }
983+
984+ Q_Q(UCListItemActions);
985+ offsetDragged = (status == UCListItemActions::Leading) ? panelItem->width() + panelItem->x() :
986+ listItem->width() - panelItem->x();
987+ if (offsetDragged < 0.0) {
988+ offsetDragged = 0.0;
989+ }
990+}
991+
992+UCListItemActionsAttached *UCListItemActionsPrivate::attachedObject()
993+{
994+ if (!panelItem) {
995+ return 0;
996+ }
997+ return static_cast<UCListItemActionsAttached*>(
998+ qmlAttachedPropertiesObject<UCListItemActions>(panelItem, false));
999+}
1000+
1001+/*
1002+ * Connects a ListItem to the ListItemActions' panel and returns true upon successful connection.
1003+ * Connection may fail if there is a different ListItem connected. In this case the new ListItem
1004+ * will be queued and automatically connected when the previous ListItem disconnects.
1005+ * The panel is only re-created when a different Component is used to create it.
1006+ * FIXME - despite each ListItem uses the same document to create the panel, theming engine
1007+ * provides different Component objects for each, due to not caching the components created.
1008+ * This must be fixed to optimize memory usage.
1009+ */
1010+bool UCListItemActionsPrivate::connectToListItem(UCListItemActions *actions, UCListItem *listItem, bool leading)
1011+{
1012+ UCListItemActionsPrivate *_this = get(actions);
1013+ if (!_this) {
1014+ return false;
1015+ }
1016+ // do we have a panel created already?
1017+ if (_this->panelItem) {
1018+ if (isConnectedTo(actions, listItem)) {
1019+ return true;
1020+ }
1021+ if (_this->panelItem->parentItem() && _this->panelItem->parentItem() != listItem) {
1022+ // set the requesting listItem as queuedItem
1023+ _this->queuedItem = listItem;
1024+ return false;
1025+ }
1026+ }
1027+ // no parent set or panelItem yet, proceed with panel creation
1028+ UCListItemPrivate *pItem = UCListItemPrivate::get(listItem);
1029+ if (!pItem->styleItem && pItem->loadStyle()) {
1030+ pItem->initStyleItem();
1031+ }
1032+ if (pItem->styleItem && !_this->createPanelItem(pItem->styleItem->m_actionsDelegate)) {
1033+ return false;
1034+ }
1035+
1036+ // check if the panel is still connected to a ListItem
1037+ // this may happen if there is a swipe over an other item while the previous
1038+ // one is rebounding
1039+ _this->panelItem->setParentItem(listItem);
1040+ if (_this->attachedObject()) {
1041+ _this->attachedObject()->connectListItem(listItem, true);
1042+ }
1043+ _this->offsetDragged = 0.0;
1044+ _this->status = leading ? UCListItemActions::Leading : UCListItemActions::Trailing;
1045+ Q_EMIT actions->statusChanged(_this->status);
1046+ return true;
1047+}
1048+
1049+void UCListItemActionsPrivate::disconnectFromListItem(UCListItemActions *actions)
1050+{
1051+ UCListItemActionsPrivate *_this = get(actions);
1052+ if (!_this || !_this->panelItem || !_this->panelItem->parentItem()) {
1053+ return;
1054+ }
1055+
1056+ if (_this->attachedObject()) {
1057+ _this->attachedObject()->connectListItem(static_cast<UCListItem*>(_this->panelItem->parentItem()), false);
1058+ }
1059+ _this->panelItem->setParentItem(0);
1060+ _this->status = UCListItemActions::Disconnected;
1061+ Q_EMIT actions->statusChanged(_this->status);
1062+ // if there was a queuedItem, make it grab the actions list
1063+ if (_this->queuedItem) {
1064+ UCListItemPrivate::get(_this->queuedItem.data())->grabPanel(actions, true);
1065+ // remove item from queue
1066+ _this->queuedItem.clear();
1067+ }
1068+}
1069+
1070+bool UCListItemActionsPrivate::isConnectedTo(UCListItemActions *actions, UCListItem *listItem)
1071+{
1072+ UCListItemActionsPrivate *_this = get(actions);
1073+ return _this && _this->panelItem &&
1074+ (_this->status != UCListItemActions::Disconnected) &&
1075+ (_this->panelItem->parentItem() == listItem);
1076+}
1077+
1078+QQuickItem *UCListItemActionsPrivate::createPanelItem(QQmlComponent *panel)
1079+{
1080+ if (panelItem && panelDelegate == panel) {
1081+ return panelItem;
1082+ }
1083+ // delete the panel if the next item's actionsDelegate differs from the
1084+ // one this panel was created from
1085+ if (panelDelegate != panel) {
1086+ delete panelItem;
1087+ panelItem = 0;
1088+ }
1089+
1090+ Q_Q(UCListItemActions);
1091+ if (!panel) {
1092+ qmlInfo(q) << UbuntuI18n::instance().tr("actionsDelegate not set!");
1093+ return 0;
1094+ }
1095+
1096+ panelDelegate = panel;
1097+ if (!panelDelegate->isError()) {
1098+ panelItem = qobject_cast<QQuickItem*>(panelDelegate->beginCreate(qmlContext(q)));
1099+ if (panelItem) {
1100+ QQml_setParent_noEvent(panelItem, q);
1101+ // add panelItem to data so we can access it in case is needed (i.e. tests)
1102+ data.append(panelItem);
1103+ // create attached property!
1104+ UCListItemActionsAttached *attached = static_cast<UCListItemActionsAttached*>(
1105+ qmlAttachedPropertiesObject<UCListItemActions>(panelItem));
1106+ if (!attached->container()) {
1107+ attached->setList(q);
1108+ } else {
1109+ // container is set, but we need to emit the signal again so we get the
1110+ // attached props updated for those cases when the attached property is
1111+ // created before the statement above
1112+ Q_EMIT attached->containerChanged();
1113+ }
1114+ panelDelegate->completeCreate();
1115+
1116+ // calculate option's slot size
1117+ offsetDragged = 0.0;
1118+ // connect to panel to catch dragging
1119+ QObject::connect(panelItem, SIGNAL(xChanged()), q, SLOT(_q_updateDraggedOffset()));
1120+ }
1121+ } else {
1122+ qmlInfo(q) << panelDelegate->errorString();
1123+ }
1124+
1125+ return panelItem;
1126+}
1127+
1128 /*!
1129 * \qmltype ListItemActions
1130 * \instantiates UCListItemActions
1131@@ -111,7 +265,7 @@
1132 * However, this implies that swiping a new ListItem content while another one is
1133 * swiped will result in showing the newly swiped item's panel delayed, as the
1134 * panel can be shown only after the previous item's snapping is completed. Depending
1135- * on the \c ListItemStyle::snapAnimation duration, this may take some time, and the
1136+ * on the \l {ListItemStyle::snapAnimation}{snapAnimation} duration, this may take some time, and the
1137 * response time of the UI can become unacceptable.
1138 *
1139 * Having individual ListItemActions instances increases the memory footprint,
1140@@ -261,7 +415,6 @@
1141
1142 /*!
1143 * \qmlproperty list<Action> ListItemActions::actions
1144- * \default
1145 * The property holds the actions to be displayed. It can hold instances cached or
1146 * declared in place. An example of cached actions:
1147 * \qml
1148@@ -290,3 +443,5 @@
1149 Q_D(UCListItemActions);
1150 return QQmlListProperty<QObject>(this, d->data);
1151 }
1152+
1153+#include "moc_uclistitemactions.cpp"
1154
1155=== modified file 'modules/Ubuntu/Components/plugin/uclistitemactions.h'
1156--- modules/Ubuntu/Components/plugin/uclistitemactions.h 2014-11-21 14:40:26 +0000
1157+++ modules/Ubuntu/Components/plugin/uclistitemactions.h 2014-11-21 14:40:28 +0000
1158@@ -54,6 +54,7 @@
1159
1160 private:
1161 Q_DECLARE_PRIVATE(UCListItemActions)
1162+ Q_PRIVATE_SLOT(d_func(), void _q_updateDraggedOffset())
1163 };
1164
1165 class UCListItemActionsAttached : public QObject
1166
1167=== modified file 'modules/Ubuntu/Components/plugin/uclistitemactions_p.h'
1168--- modules/Ubuntu/Components/plugin/uclistitemactions_p.h 2014-11-21 14:40:26 +0000
1169+++ modules/Ubuntu/Components/plugin/uclistitemactions_p.h 2014-11-21 14:40:28 +0000
1170@@ -20,6 +20,7 @@
1171 #include "uclistitemactions.h"
1172 #include "QtCore/private/qobject_p.h"
1173
1174+class UCListItem;
1175 class UCListItemActionsPrivate : public QObjectPrivate {
1176 Q_DECLARE_PUBLIC(UCListItemActions)
1177 public:
1178@@ -27,16 +28,27 @@
1179 ~UCListItemActionsPrivate();
1180 static UCListItemActionsPrivate* get(UCListItemActions *actions)
1181 {
1182- Q_ASSERT(actions);
1183- return actions->d_func();
1184+ return actions ? actions->d_func() : 0;
1185 }
1186
1187 UCListItemActions::Status status;
1188 qreal offsetDragged;
1189+
1190 QQmlComponent *delegate;
1191+ QQmlComponent *panelDelegate;
1192 QQuickItem *panelItem;
1193 QList<UCAction*> actions;
1194 QList<QObject*> data;
1195+ QPointer<UCListItem> queuedItem;
1196+
1197+ void _q_updateDraggedOffset();
1198+ UCListItemActionsAttached *attachedObject();
1199+
1200+ static bool connectToListItem(UCListItemActions *options, UCListItem *listItem, bool leading);
1201+ static void disconnectFromListItem(UCListItemActions *options);
1202+ static bool isConnectedTo(UCListItemActions *options, UCListItem *listItem);
1203+
1204+ QQuickItem *createPanelItem(QQmlComponent *delegate);
1205 };
1206
1207 #endif // UCLISTITEMACTIONS_P_H
1208
1209=== modified file 'modules/Ubuntu/Components/plugin/uclistitemactionsattached.cpp'
1210--- modules/Ubuntu/Components/plugin/uclistitemactionsattached.cpp 2014-11-21 14:40:26 +0000
1211+++ modules/Ubuntu/Components/plugin/uclistitemactionsattached.cpp 2014-11-21 14:40:28 +0000
1212@@ -121,7 +121,7 @@
1213 }
1214
1215 /*!
1216- * \qmlproperty list<Action> ListItemActions::visibleActions
1217+ * \qmlattachedproperty list<Action> ListItemActions::visibleActions
1218 * Holds the list of visible actions. This is a convenience property to help action
1219 * visualization panel implementations to consider only visible actions.
1220 */
1221@@ -259,8 +259,11 @@
1222 if (position == 0.0) {
1223 listItem->_q_rebound();
1224 } else {
1225- // FIXME: implement snaping
1226- listItem->contentItem->setX(position);
1227+ if (listItem->animator) {
1228+ listItem->animator->snap(position);
1229+ } else {
1230+ listItem->contentItem->setX(position);
1231+ }
1232 }
1233 }
1234
1235
1236=== added file 'modules/Ubuntu/Components/plugin/uclistitemstyle.cpp'
1237--- modules/Ubuntu/Components/plugin/uclistitemstyle.cpp 1970-01-01 00:00:00 +0000
1238+++ modules/Ubuntu/Components/plugin/uclistitemstyle.cpp 2014-11-21 14:40:28 +0000
1239@@ -0,0 +1,92 @@
1240+/*
1241+ * Copyright 2014 Canonical Ltd.
1242+ *
1243+ * This program is free software; you can redistribute it and/or modify
1244+ * it under the terms of the GNU Lesser General Public License as published by
1245+ * the Free Software Foundation; version 3.
1246+ *
1247+ * This program is distributed in the hope that it will be useful,
1248+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1249+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1250+ * GNU Lesser General Public License for more details.
1251+ *
1252+ * You should have received a copy of the GNU Lesser General Public License
1253+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1254+ */
1255+
1256+#include "uclistitemstyle.h"
1257+
1258+/*!
1259+ * \qmltype ListItemStyle
1260+ * \instantiates UCListItemStyle
1261+ * \inqmlmodule Ubuntu.Components.Styles 1.2
1262+ * \ingroup style-api
1263+ * \since Ubuntu.Components.Styles 1.2
1264+ * \brief Style API for ListItem component.
1265+ *
1266+ * Style API for the ListItem component which provides actions, select and
1267+ * drag handler delegates, and snap animation via its properties.
1268+ * ListItem treats the style differently compared to the other components,
1269+ * as it:
1270+ * \list
1271+ * \li - loads the style only when needed
1272+ * \li - gets delegates and snap animation from the style properties
1273+ * \li - ignores any other visuals defined in the style.
1274+ * \endlist
1275+ */
1276+UCListItemStyle::UCListItemStyle(QQuickItem *parent) :
1277+ QQuickItem(parent)
1278+{
1279+}
1280+
1281+/*!
1282+ * \qmlproperty Component ListItemStyle::actionsDelegate
1283+ * Specifies the component visualizing the leading/trailing actions.
1284+ */
1285+void UCListItemStyle::setActionsDelegate(QQmlComponent *delegate)
1286+{
1287+ if (m_actionsDelegate == delegate) {
1288+ return;
1289+ }
1290+ m_actionsDelegate = delegate;
1291+ Q_EMIT actionsDelegateChanged();
1292+}
1293+
1294+/*!
1295+ * \qmlproperty Component ListItemStyle::selectionDelegate
1296+ * Holds the component handling the selection mode.
1297+ */
1298+void UCListItemStyle::setSelectionDelegate(QQmlComponent *delegate)
1299+{
1300+ if (m_selectionDelegate == delegate) {
1301+ return;
1302+ }
1303+ m_selectionDelegate = delegate;
1304+ Q_EMIT selectionDelegateChanged();
1305+}
1306+
1307+/*!
1308+ * \qmlproperty Component ListItemStyle::dragHandlerDelegate
1309+ * Holds the component shown when dragging mode is enabled.
1310+ */
1311+void UCListItemStyle::setDragHandlerDelegate(QQmlComponent *delegate)
1312+{
1313+ if (m_dragHandlerDelegate == delegate) {
1314+ return;
1315+ }
1316+ m_dragHandlerDelegate = delegate;
1317+ Q_EMIT dragHandlerDelegateChanged();
1318+}
1319+
1320+/*!
1321+ * \qmlproperty PropertyAnimation ListItemStyle::snapAnimation
1322+ * Holds the animation used in animating when snapped in or out.
1323+ */
1324+void UCListItemStyle::setSnapAnimation(QQuickPropertyAnimation *animation)
1325+{
1326+ if (m_snapAnimation == animation) {
1327+ return;
1328+ }
1329+ m_snapAnimation = animation;
1330+ Q_EMIT snapAnimationChanged();
1331+}
1332
1333=== added file 'modules/Ubuntu/Components/plugin/uclistitemstyle.h'
1334--- modules/Ubuntu/Components/plugin/uclistitemstyle.h 1970-01-01 00:00:00 +0000
1335+++ modules/Ubuntu/Components/plugin/uclistitemstyle.h 2014-11-21 14:40:28 +0000
1336@@ -0,0 +1,54 @@
1337+/*
1338+ * Copyright 2014 Canonical Ltd.
1339+ *
1340+ * This program is free software; you can redistribute it and/or modify
1341+ * it under the terms of the GNU Lesser General Public License as published by
1342+ * the Free Software Foundation; version 3.
1343+ *
1344+ * This program is distributed in the hope that it will be useful,
1345+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1346+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1347+ * GNU Lesser General Public License for more details.
1348+ *
1349+ * You should have received a copy of the GNU Lesser General Public License
1350+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1351+ */
1352+#ifndef UCLISTITEMSTYLE_H
1353+#define UCLISTITEMSTYLE_H
1354+
1355+#include <QtQuick/QQuickItem>
1356+
1357+class QQmlComponent;
1358+class QQuickPropertyAnimation;
1359+class UCListItemStyle : public QQuickItem
1360+{
1361+ Q_OBJECT
1362+ Q_PROPERTY(QQmlComponent *actionsDelegate MEMBER m_actionsDelegate WRITE setActionsDelegate NOTIFY actionsDelegateChanged)
1363+ Q_PROPERTY(QQmlComponent *selectionDelegate MEMBER m_selectionDelegate WRITE setSelectionDelegate NOTIFY selectionDelegateChanged)
1364+ Q_PROPERTY(QQmlComponent *dragHandlerDelegate MEMBER m_dragHandlerDelegate WRITE setDragHandlerDelegate NOTIFY dragHandlerDelegateChanged)
1365+ Q_PROPERTY(QQuickPropertyAnimation *snapAnimation MEMBER m_snapAnimation WRITE setSnapAnimation NOTIFY snapAnimationChanged)
1366+public:
1367+ explicit UCListItemStyle(QQuickItem *parent = 0);
1368+
1369+ void setActionsDelegate(QQmlComponent *delegate);
1370+ void setSelectionDelegate(QQmlComponent *delegate);
1371+ void setDragHandlerDelegate(QQmlComponent *delegate);
1372+ void setSnapAnimation(QQuickPropertyAnimation *animation);
1373+
1374+Q_SIGNALS:
1375+ void actionsDelegateChanged();
1376+ void selectionDelegateChanged();
1377+ void dragHandlerDelegateChanged();
1378+ void snapAnimationChanged();
1379+
1380+private:
1381+ QQmlComponent *m_actionsDelegate;
1382+ QQmlComponent *m_selectionDelegate;
1383+ QQmlComponent *m_dragHandlerDelegate;
1384+ QQuickPropertyAnimation *m_snapAnimation;
1385+
1386+ friend class UCListItemActionsPrivate;
1387+ friend class UCListItemSnapAnimator;
1388+};
1389+
1390+#endif // UCLISTITEMSTYLE_H
1391
1392=== modified file 'tests/qmlapicheck.sh'
1393--- tests/qmlapicheck.sh 2014-10-30 10:44:19 +0000
1394+++ tests/qmlapicheck.sh 2014-11-21 14:40:28 +0000
1395@@ -33,7 +33,7 @@
1396 # https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1256999
1397 # https://bugreports.qt-project.org/browse/QTBUG-36243
1398 env UBUNTU_UI_TOOLKIT_THEMES_PATH=$SRCDIR/modules ALARM_BACKEND=memory \
1399- qmlplugindump $i 0.1 $SRCDIR/modules 1>> $BLDDIR/plugins.qmltypes
1400+ qmlplugindump -noinstantiate $i 0.1 $SRCDIR/modules 1>> $BLDDIR/plugins.qmltypes
1401 test $? != 0 && ERRORS=1
1402 done
1403 test $ERRORS = 1 && echo Error: qmlplugindump failed && exit 1
1404
1405=== modified file 'tests/resources/listitems/ListItemTest.qml'
1406--- tests/resources/listitems/ListItemTest.qml 2014-11-21 14:40:26 +0000
1407+++ tests/resources/listitems/ListItemTest.qml 2014-11-21 14:40:28 +0000
1408@@ -31,6 +31,7 @@
1409
1410 ListItemActions {
1411 id: leading
1412+ objectName: "StockLeading"
1413 actions: [
1414 Action {
1415 },
1416@@ -41,6 +42,23 @@
1417 ]
1418 }
1419
1420+ property list<Action> leadingArray: [
1421+ Action {
1422+ iconName: "delete"
1423+ }
1424+ ]
1425+ property list<Action> trailingArray: [
1426+ Action {
1427+ iconName: "search"
1428+ },
1429+ Action {
1430+ iconName: "edit"
1431+ },
1432+ Action {
1433+ iconName: "copy"
1434+ }
1435+ ]
1436+
1437 Column {
1438 anchors {
1439 left: parent.left
1440@@ -49,6 +67,7 @@
1441
1442 ListItem {
1443 id: testItem
1444+ objectName: "single"
1445 color: "lime"
1446 onClicked: {
1447 print("click")
1448@@ -59,6 +78,7 @@
1449 text: units.gridUnit + "PX/unit"
1450 }
1451 leadingActions: ListItemActions {
1452+ objectName: "InlineLeading"
1453 actions: [stock]
1454 }
1455 trailingActions: leading
1456@@ -72,6 +92,7 @@
1457 model: 100
1458 pressDelay: 0
1459 delegate: ListItem {
1460+ objectName: "ListItem" + index
1461 id: listItem
1462 onClicked: print(" clicked")
1463 leadingActions: leading
1464@@ -100,10 +121,18 @@
1465 Repeater {
1466 model: 100
1467 ListItem {
1468+ objectName: "InFlickable"+index
1469 color: "red"
1470 highlightColor: "lime"
1471 divider.colorFrom: UbuntuColors.green
1472
1473+ leadingActions: ListItemActions {
1474+ actions: leadingArray
1475+ }
1476+ trailingActions: ListItemActions {
1477+ actions: trailingArray
1478+ }
1479+
1480 Label {
1481 text: modelData + " Flickable item"
1482 }
1483
1484=== renamed file 'tests/unit/tst_performance/ListItemWithOptionsList.qml' => 'tests/unit/tst_performance/ListItemWithActionsList.qml'
1485--- tests/unit/tst_performance/ListItemWithOptionsList.qml 2014-11-21 14:40:26 +0000
1486+++ tests/unit/tst_performance/ListItemWithActionsList.qml 2014-11-21 14:40:28 +0000
1487@@ -22,9 +22,15 @@
1488 height: 600
1489 ListItemActions {
1490 id: actions1
1491+ actions: [Action {}]
1492 }
1493 ListItemActions {
1494 id: actions2
1495+ actions: [
1496+ Action {},
1497+ Action {},
1498+ Action {}
1499+ ]
1500 }
1501
1502 Repeater {
1503
1504=== added file 'tests/unit/tst_performance/ListItemWithInlineActionsList.qml'
1505--- tests/unit/tst_performance/ListItemWithInlineActionsList.qml 1970-01-01 00:00:00 +0000
1506+++ tests/unit/tst_performance/ListItemWithInlineActionsList.qml 2014-11-21 14:40:28 +0000
1507@@ -0,0 +1,41 @@
1508+/*
1509+ * Copyright 2014 Canonical Ltd.
1510+ *
1511+ * This program is free software; you can redistribute it and/or modify
1512+ * it under the terms of the GNU Lesser General Public License as published by
1513+ * the Free Software Foundation; version 3.
1514+ *
1515+ * This program is distributed in the hope that it will be useful,
1516+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1517+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1518+ * GNU Lesser General Public License for more details.
1519+ *
1520+ * You should have received a copy of the GNU Lesser General Public License
1521+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1522+ */
1523+
1524+import QtQuick 2.0
1525+import Ubuntu.Components 1.2
1526+
1527+Column {
1528+ width: 800
1529+ height: 600
1530+
1531+ Repeater {
1532+ model: 5000
1533+ ListItem {
1534+ trailingActions: ListItemActions {
1535+ actions: [
1536+ Action {}
1537+ ]
1538+ }
1539+ leadingActions: ListItemActions {
1540+ actions: [
1541+ Action {},
1542+ Action {},
1543+ Action {}
1544+ ]
1545+ }
1546+ }
1547+ }
1548+}
1549
1550=== modified file 'tests/unit/tst_performance/tst_performance.cpp'
1551--- tests/unit/tst_performance/tst_performance.cpp 2014-11-21 14:40:26 +0000
1552+++ tests/unit/tst_performance/tst_performance.cpp 2014-11-21 14:40:28 +0000
1553@@ -78,7 +78,8 @@
1554 QTest::newRow("grid with Slider") << "SliderGrid.qml" << QUrl();
1555 QTest::newRow("list with QtQuick Item") << "ItemList.qml" << QUrl();
1556 QTest::newRow("list with new ListItem") << "ListItemList.qml" << QUrl();
1557- QTest::newRow("list with new ListItem with options") << "ListItemWithOptionsList.qml" << QUrl();
1558+ QTest::newRow("list with new ListItem with actions") << "ListItemWithActionsList.qml" << QUrl();
1559+ QTest::newRow("list with new ListItem with inline actions") << "ListItemWithInlineActionsList.qml" << QUrl();
1560 QTest::newRow("list with ListItems.Empty (equivalent to the new ListItem") << "ListItemsEmptyList.qml" << QUrl();
1561 // disable this test as it takes >20 seconds. Kept still for measurements to be done during development
1562 // QTest::newRow("list with ListItems.Base (one icon, one label and one chevron)") << "ListItemsBaseList.qml" << QUrl();
1563
1564=== modified file 'tests/unit/tst_performance/tst_performance.pro'
1565--- tests/unit/tst_performance/tst_performance.pro 2014-11-21 14:40:26 +0000
1566+++ tests/unit/tst_performance/tst_performance.pro 2014-11-21 14:40:28 +0000
1567@@ -22,6 +22,7 @@
1568 TextWithImportPopups.qml \
1569 ItemList.qml \
1570 ListItemList.qml \
1571- ListItemWithOptionsList.qml \
1572 ListItemsEmptyList.qml \
1573- ListItemsBaseList.qml
1574+ ListItemsBaseList.qml \
1575+ ListItemWithInlineActionsList.qml \
1576+ ListItemWithActionsList.qml
1577
1578=== modified file 'tests/unit_x11/tst_components/tst_listitem.qml'
1579--- tests/unit_x11/tst_components/tst_listitem.qml 2014-11-21 14:40:26 +0000
1580+++ tests/unit_x11/tst_components/tst_listitem.qml 2014-11-21 14:40:28 +0000
1581@@ -58,6 +58,8 @@
1582 id: testItem
1583 width: parent.width
1584 color: "blue"
1585+ leadingActions: leading
1586+ trailingActions: trailing
1587 Item {
1588 id: bodyItem
1589 anchors.fill: parent
1590@@ -71,7 +73,10 @@
1591 model: 10
1592 delegate: ListItem {
1593 objectName: "listItem" + index
1594+ color: "lightgray"
1595 width: parent.width
1596+ leadingActions: leading
1597+ trailingActions: trailing
1598 }
1599 }
1600 }
1601@@ -81,6 +86,11 @@
1602 when: windowShown
1603
1604 SignalSpy {
1605+ id: movingSpy
1606+ signalName: "contentMovementEnded"
1607+ }
1608+
1609+ SignalSpy {
1610 id: pressedSpy
1611 signalName: "pressedChanged"
1612 target: testItem
1613@@ -92,8 +102,24 @@
1614 target: testItem;
1615 }
1616
1617- function centerOf(item) {
1618- return Qt.point(item.width / 2, item.height / 2);
1619+ SignalSpy {
1620+ id: interactiveSpy
1621+ signalName: "interactiveChanged"
1622+ }
1623+
1624+ function rebound(item, watchTarget) {
1625+ if (watchTarget === undefined) {
1626+ watchTarget = item;
1627+ }
1628+
1629+ movingSpy.target = null;
1630+ movingSpy.target = watchTarget;
1631+ movingSpy.clear();
1632+ mouseClick(item, centerOf(item).x, centerOf(item).y);
1633+ if (watchTarget.contentMoving) {
1634+ movingSpy.wait();
1635+ }
1636+ movingSpy.target = null;
1637 }
1638
1639 function initTestCase() {
1640@@ -102,10 +128,16 @@
1641 }
1642
1643 function cleanup() {
1644+ movingSpy.clear();
1645 pressedSpy.clear();
1646 clickSpy.clear();
1647- // make sure all events are processed
1648- wait(200);
1649+ listView.interactive = true;
1650+ // make sure we collapse
1651+ mouseClick(defaults, 0, 0)
1652+ movingSpy.target = null;
1653+ movingSpy.clear();
1654+ interactiveSpy.target = null;
1655+ interactiveSpy.clear();
1656 }
1657
1658 function test_0_defaults() {
1659@@ -120,6 +152,9 @@
1660 fuzzyCompare(defaults.divider.colorFrom.a, 0.14, 0.01, "colorFrom alpha differs");
1661 compare(defaults.divider.colorTo, "#ffffff", "colorTo differs.");
1662 fuzzyCompare(defaults.divider.colorTo.a, 0.07, 0.01, "colorTo alpha differs");
1663+ compare(defaults.contentMoving, false, "default is not moving");
1664+ compare(defaults.style, null, "Style is loaded upon first use.");
1665+ compare(defaults.__styleInstance, null, "__styleInstance must be null.");
1666
1667 compare(actionsDefault.delegate, null, "ListItemActions has no delegate set by default.");
1668 compare(actionsDefault.actions.length, 0, "ListItemActions has no options set.");
1669@@ -179,6 +214,7 @@
1670 TestExtras.touchMove(0, listItem, Qt.point(listItem.width / 2, dy));
1671 }
1672 compare(listItem.pressed, false, "Item is pressed still!");
1673+ // cleanup, wait few milliseconds to avoid dbl-click collision
1674 TestExtras.touchRelease(0, listItem, Qt.point(listItem.width / 2, dy));
1675 }
1676
1677@@ -190,5 +226,90 @@
1678 compare(testItem.contentItem.height, testItem.height, "ListItem's background height must be the same as the item itself.");
1679 testItem.divider.visible = true;
1680 }
1681+
1682+ function test_touch_tug_options_data() {
1683+ var item = findChild(listView, "listItem0");
1684+ return [
1685+ {tag: "Trailing, mouse", item: item, pos: centerOf(item), dx: -units.gu(20), positiveDirection: false, mouse: true},
1686+ {tag: "Leading, mouse", item: item, pos: centerOf(item), dx: units.gu(20), positiveDirection: true, mouse: true},
1687+ {tag: "Trailing, touch", item: item, pos: centerOf(item), dx: -units.gu(20), positiveDirection: false, mouse: false},
1688+ {tag: "Leading, touch", item: item, pos: centerOf(item), dx: units.gu(20), positiveDirection: true, mouse: false},
1689+ ];
1690+ }
1691+ function test_touch_tug_options(data) {
1692+ listView.positionViewAtBeginning();
1693+ movingSpy.target = data.item;
1694+ if (data.mouse) {
1695+ flick(data.item, data.pos.x, data.pos.y, data.dx, 0);
1696+ } else {
1697+ TestExtras.touchDrag(0, data.item, data.pos, Qt.point(data.dx, 0));
1698+ }
1699+ movingSpy.wait();
1700+ if (data.positiveDirection) {
1701+ verify(data.item.contentItem.x > 0, data.tag + " options did not show up");
1702+ } else {
1703+ verify(data.item.contentItem.x < 0, data.tag + " options did not show up");
1704+ }
1705+
1706+ // dismiss
1707+ rebound(data.item);
1708+ }
1709+
1710+ function test_rebound_when_pressed_outside_or_clicked_data() {
1711+ var item0 = findChild(listView, "listItem0");
1712+ var item1 = findChild(listView, "listItem1");
1713+ return [
1714+ {tag: "Click on an other Item", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item1, mouse: true},
1715+ {tag: "Click on the same Item", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item0.contentItem, mouse: true},
1716+ {tag: "Tap on an other Item", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item1, mouse: false},
1717+ {tag: "Tap on the same Item", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item0.contentItem, mouse: false},
1718+ ];
1719+ }
1720+ function test_rebound_when_pressed_outside_or_clicked(data) {
1721+ listView.positionViewAtBeginning();
1722+ movingSpy.target = data.item;
1723+ if (data.mouse) {
1724+ flick(data.item, data.pos.x, data.pos.y, data.dx, 0);
1725+ } else {
1726+ TestExtras.touchDrag(0, data.item, data.pos, Qt.point(data.dx, 0));
1727+ }
1728+ movingSpy.wait();
1729+ verify(data.item.contentItem.x != 0, "The component wasn't tugged!");
1730+ // dismiss
1731+ rebound(data.clickOn, data.item)
1732+ }
1733+
1734+ function test_listview_not_interactive_while_tugged_data() {
1735+ var item0 = findChild(listView, "listItem0");
1736+ var item1 = findChild(listView, "listItem1");
1737+ return [
1738+ {tag: "Trailing", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item1, mouse: true},
1739+ {tag: "Leading", item: item0, pos: centerOf(item0), dx: units.gu(20), clickOn: item0.contentItem, mouse: true},
1740+ {tag: "Trailing", item: item0, pos: centerOf(item0), dx: -units.gu(20), clickOn: item1, mouse: false},
1741+ {tag: "Leading", item: item0, pos: centerOf(item0), dx: units.gu(20), clickOn: item0.contentItem, mouse: false},
1742+ ];
1743+ }
1744+ function test_listview_not_interactive_while_tugged(data) {
1745+ listView.positionViewAtBeginning();
1746+ movingSpy.target = data.item;
1747+ interactiveSpy.target = listView;
1748+ if (data.mouse) {
1749+ flick(data.item, data.pos.x, data.pos.y, data.dx, 0);
1750+ } else {
1751+ TestExtras.touchDrag(0, data.item, data.pos, Qt.point(data.dx, 0));
1752+ }
1753+ movingSpy.wait();
1754+ // animation should no longer be running!
1755+ verify(!data.item.__styleInstance.snapAnimation.running, "Animation is still running!");
1756+ compare(listView.interactive, true, "The ListView is still non-interactive!");
1757+ compare(interactiveSpy.count, 2, "Less/more times changed!");
1758+ // check if it snapped in
1759+ verify(data.item.contentItem.x != 0.0, "Not snapped in!!");
1760+ // dismiss
1761+ rebound(data.clickOn, data.item);
1762+ // animation should no longer be running!
1763+ verify(!data.item.__styleInstance.snapAnimation.running, "Animation is still running!");
1764+ fuzzyCompare(data.item.contentItem.x, 0.0, 0.1, "Not snapped out!!");
1765+ }
1766 }
1767 }

Subscribers

People subscribed via source and target branches

to all changes: