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