Merge lp:~zsombi/ubuntu-ui-toolkit/35-options-panel-swipe into lp:~zsombi/ubuntu-ui-toolkit/listitem-master
- 35-options-panel-swipe
- Merge into listitem-master
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 |
Related bugs: | |
Related blueprints: |
SDK: Design a new ListItem and layouts
(Undefined)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tim Peeters | Approve | ||
Review via email: mp+241036@code.launchpad.net |
Commit message
Description of the change
Actions panel handling, swiping and snap animation take in use. Uses fix-size panel for testing.
- 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
Tim Peeters (tpeeters) wrote : | # |
Tim Peeters (tpeeters) wrote : | # |
with the code above, the list item text never becomes red, so swiping never becomes true?
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.
Tim Peeters (tpeeters) wrote : | # |
74 + Specifies whether the panel is used to visualize leading or trailing options.
75 + */
76 + readonly property bool leadingPanel: panel.ListItemA
I prefer readonly property bool leading.
We know we are a panel.
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.
Tim Peeters (tpeeters) wrote : | # |
321 + , xAxisMoveThresh
322 , overshoot(
let's do the x-axis threshold like the overshoot, so:
xAxisMoveThresh
Tim Peeters (tpeeters) wrote : | # |
372 + if (!UCListItemAct
add a linebreak
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?
Tim Peeters (tpeeters) wrote : | # |
458 + setTugged(false);
didn't we switch from "tug" to "swipe" in a previous MR?
Tim Peeters (tpeeters) wrote : | # |
should we include a link to ListItemStyle in the ListItem docs?
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
Tim Peeters (tpeeters) wrote : | # |
replace "tug" by "swipe" everywhere?
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.
585 + * }
586 + * }
Tim Peeters (tpeeters) wrote : | # |
1478 === added file 'tests/
filename options-->actions
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.
Zsombor Egri (zsombi) wrote : | # |
> 321 + , xAxisMoveThresh
> 322 , overshoot(
>
> let's do the x-axis threshold like the overshoot, so:
> xAxisMoveThresh
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.
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.
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
Zsombor Egri (zsombi) wrote : | # |
> should we include a link to ListItemStyle in the ListItem docs?
Yep. Added.
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.
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.
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.
> 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
Preview Diff
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 | } |
I don't see icons with this code: http:// pastebin. ubuntu. com/9145869/