Merge lp:~ahayzen/ubuntu-weather-app/uc1.3-listitems into lp:ubuntu-weather-app

Proposed by Andrew Hayzen
Status: Merged
Approved by: Andrew Hayzen
Approved revision: 271
Merged at revision: 271
Proposed branch: lp:~ahayzen/ubuntu-weather-app/uc1.3-listitems
Merge into: lp:ubuntu-weather-app
Diff against target: 1447 lines (+246/-960)
9 files modified
app/components/HeadState/MultiSelectHeadState.qml (+6/-9)
app/components/ListItemReorderComponent.qml (+0/-106)
app/components/ListItemWithActions.qml (+0/-496)
app/components/MultiSelectListView.qml (+33/-14)
app/components/WeatherListItem.qml (+31/-111)
app/ui/LocationPane.qml (+72/-74)
app/ui/LocationsPage.qml (+95/-131)
tests/autopilot/ubuntu_weather_app/__init__.py (+7/-17)
tests/autopilot/ubuntu_weather_app/tests/test_locations_page.py (+2/-2)
To merge this branch: bzr merge lp:~ahayzen/ubuntu-weather-app/uc1.3-listitems
Reviewer Review Type Date Requested Status
Michael Sheldon (community) Approve
Jenkins Bot continuous-integration Approve
Ubuntu Weather Developers Pending
Review via email: mp+323134@code.launchpad.net

Commit message

* Migrate remaining old style listitems to UC1.3 ListItems

Description of the change

* Migrate remaining old style listitems to UC1.3 ListItems

To post a comment you must log in.
Revision history for this message
Andrew Hayzen (ahayzen) wrote :

$ autopilot3 run ubuntu_weather_app
Loading tests from: /home/andrew/Workspace/Work/Canonical/code/ubuntu-weather-app/uc1.3-listitems/tests/autopilot

Tests running...

Ran 15 tests in 99.820s
OK
$ pep8 .
$ pyflakes .

Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michael Sheldon (michael-sheldon) wrote :

Looks good :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'app/components/HeadState/MultiSelectHeadState.qml'
--- app/components/HeadState/MultiSelectHeadState.qml 2016-07-19 16:14:05 +0000
+++ app/components/HeadState/MultiSelectHeadState.qml 2017-04-25 12:59:22 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 20152 * Copyright (C) 2015, 2017
3 * Andrew Hayzen <ahayzen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>4 * Victor Thompson <victor.thompson@gmail.com>
5 *5 *
@@ -31,10 +31,7 @@
31 Action {31 Action {
32 text: i18n.tr("Cancel selection")32 text: i18n.tr("Cancel selection")
33 iconName: "back"33 iconName: "back"
34 onTriggered: {34 onTriggered: listview.closeSelection()
35 listview.clearSelection()
36 listview.state = "normal"
37 }
38 }35 }
39 ]36 ]
40 }37 }
@@ -45,7 +42,7 @@
45 iconName: "select"42 iconName: "select"
46 text: i18n.tr("Select All")43 text: i18n.tr("Select All")
47 onTriggered: {44 onTriggered: {
48 if (listview.selectedItems.length === listview.model.count) {45 if (listview.getSelectedIndices().length === listview.model.count) {
49 listview.clearSelection()46 listview.clearSelection()
50 } else {47 } else {
51 listview.selectAll()48 listview.selectAll()
@@ -53,13 +50,13 @@
53 }50 }
54 },51 },
55 Action {52 Action {
56 enabled: listview.selectedItems.length > 053 enabled: listview.getSelectedIndices().length > 0
57 iconName: "delete"54 iconName: "delete"
58 text: i18n.tr("Delete")55 text: i18n.tr("Delete")
59 visible: removable56 visible: removable
6057
61 onTriggered: {58 onTriggered: {
62 removed(listview.selectedItems)59 removed(listview.getSelectedIndices())
6360
64 listview.closeSelection()61 listview.closeSelection()
65 }62 }
@@ -74,7 +71,7 @@
74 property bool removable: false71 property bool removable: false
75 property Page thisPage72 property Page thisPage
7673
77 signal removed(var selectedItems)74 signal removed(var selectedIndices)
7875
79 PropertyChanges {76 PropertyChanges {
80 target: thisPage77 target: thisPage
8178
=== removed file 'app/components/ListItemReorderComponent.qml'
--- app/components/ListItemReorderComponent.qml 2015-11-02 21:28:34 +0000
+++ app/components/ListItemReorderComponent.qml 1970-01-01 00:00:00 +0000
@@ -1,106 +0,0 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Nekhelesh Ramananthan <krnekhelesh@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20import QtQuick 2.4
21import Ubuntu.Components 1.3
22
23
24Item {
25 id: actionReorder
26 width: units.gu(4)
27
28 Icon {
29 anchors {
30 horizontalCenter: parent.horizontalCenter
31 verticalCenter: parent.verticalCenter
32 }
33 name: "navigation-menu" // TODO: use proper image
34 height: width
35 width: units.gu(3)
36 }
37
38 MouseArea {
39 id: actionReorderMouseArea
40 anchors {
41 fill: parent
42 }
43 property int startY: 0
44 property int startContentY: 0
45
46 onPressed: {
47 root.parent.parent.interactive = false; // stop scrolling of listview
48 startY = root.y;
49 startContentY = root.parent.parent.contentY;
50 root.z += 10; // force ontop of other elements
51
52 console.debug("Reorder listitem pressed", root.y)
53 }
54 onMouseYChanged: root.y += mouse.y - (root.height / 2);
55 onReleased: {
56 console.debug("Reorder diff by position", getDiff());
57
58 var diff = getDiff();
59
60 // Remove the height of the actual item if moved down
61 if (diff > 0) {
62 diff -= 1;
63 }
64
65 root.parent.parent.interactive = true; // reenable scrolling
66
67 if (diff === 0) {
68 // Nothing has changed so reset the item
69 // z index is restored after animation
70 resetListItemYAnimation.start();
71 }
72 else {
73 var newIndex = index + diff;
74
75 if (newIndex < 0) {
76 newIndex = 0;
77 }
78 else if (newIndex > root.parent.parent.count - 1) {
79 newIndex = root.parent.parent.count - 1;
80 }
81
82 root.z -= 10; // restore z index
83 reorder(index, newIndex)
84 }
85 }
86
87 function getDiff() {
88 // Get the amount of items that have been passed over (by centre)
89 return Math.round((((root.y - startY) + (root.parent.parent.contentY - startContentY)) / root.height) + 0.5);
90 }
91 }
92
93 SequentialAnimation {
94 id: resetListItemYAnimation
95 UbuntuNumberAnimation {
96 target: root;
97 property: "y";
98 to: actionReorderMouseArea.startY
99 }
100 ScriptAction {
101 script: {
102 root.z -= 10; // restore z index
103 }
104 }
105 }
106}
1070
=== removed file 'app/components/ListItemWithActions.qml'
--- app/components/ListItemWithActions.qml 2015-11-02 21:28:34 +0000
+++ app/components/ListItemWithActions.qml 1970-01-01 00:00:00 +0000
@@ -1,496 +0,0 @@
1/*
2 * Copyright (C) 2012-2015 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.4
18import Ubuntu.Components 1.3
19import Ubuntu.Components.ListItems 1.0 as ListItem
20
21
22Item {
23 id: root
24 width: parent.width
25
26 property Action leftSideAction: null
27 property list<Action> rightSideActions
28 property double defaultHeight: units.gu(8)
29 property bool locked: false
30 property Action activeAction: null
31 property var activeItem: null
32 property bool triggerActionOnMouseRelease: false
33 property color color: Theme.palette.normal.background
34 property color selectedColor: "#E6E6E6"
35 property bool selected: false
36 property bool selectionMode: false
37 property alias internalAnchors: mainContents.anchors
38 default property alias contents: mainContents.children
39
40 readonly property double actionWidth: units.gu(4)
41 readonly property double leftActionWidth: units.gu(10)
42 readonly property double actionThreshold: actionWidth * 0.4
43 readonly property double threshold: 0.4
44 readonly property string swipeState: main.x == 0 ? "Normal" : main.x > 0 ? "LeftToRight" : "RightToLeft"
45 readonly property alias swipping: mainItemMoving.running
46 readonly property bool _showActions: mouseArea.pressed || swipeState != "Normal" || swipping
47
48 property alias _main: main // CUSTOM
49 property alias pressed: mouseArea.pressed // CUSTOM
50
51 /* internal */
52 property var _visibleRightSideActions: filterVisibleActions(rightSideActions)
53
54 signal itemClicked(var mouse)
55 signal itemPressAndHold(var mouse)
56
57 function returnToBoundsRTL(direction)
58 {
59 var actionFullWidth = actionWidth + units.gu(2)
60
61 // go back to normal state if swipping reverse
62 if (direction === "LTR") {
63 updatePosition(0)
64 return
65 } else if (!triggerActionOnMouseRelease) {
66 updatePosition(-rightActionsView.width + units.gu(2))
67 return
68 }
69
70 var xOffset = Math.abs(main.x)
71 var index = Math.min(Math.floor(xOffset / actionFullWidth), _visibleRightSideActions.length)
72 var newX = 0
73
74 if (index === _visibleRightSideActions.length) {
75 newX = -(rightActionsView.width - units.gu(2))
76 } else if (index >= 1) {
77 newX = -(actionFullWidth * index)
78 }
79
80 updatePosition(newX)
81 }
82
83 function returnToBoundsLTR(direction)
84 {
85 var finalX = leftActionWidth
86 if ((direction === "RTL") || (main.x <= (finalX * root.threshold)))
87 finalX = 0
88 updatePosition(finalX)
89 }
90
91 function returnToBounds(direction)
92 {
93 if (main.x < 0) {
94 returnToBoundsRTL(direction)
95 } else if (main.x > 0) {
96 returnToBoundsLTR(direction)
97 } else {
98 updatePosition(0)
99 }
100 }
101
102 function contains(item, point, marginX)
103 {
104 var itemStartX = item.x - marginX
105 var itemEndX = item.x + item.width + marginX
106 return (point.x >= itemStartX) && (point.x <= itemEndX) &&
107 (point.y >= item.y) && (point.y <= (item.y + item.height));
108 }
109
110 function getActionAt(point)
111 {
112 if (leftSideAction && contains(leftActionViewLoader.item, point, 0)) {
113 return leftSideAction
114 } else if (contains(rightActionsView, point, 0)) {
115 var newPoint = root.mapToItem(rightActionsView, point.x, point.y)
116 for (var i = 0; i < rightActionsRepeater.count; i++) {
117 var child = rightActionsRepeater.itemAt(i)
118 if (contains(child, newPoint, units.gu(1))) {
119 return i
120 }
121 }
122 }
123 return -1
124 }
125
126 function updateActiveAction()
127 {
128 if (triggerActionOnMouseRelease &&
129 (main.x <= -(root.actionWidth + units.gu(2))) &&
130 (main.x > -(rightActionsView.width - units.gu(2)))) {
131 var actionFullWidth = actionWidth + units.gu(2)
132 var xOffset = Math.abs(main.x)
133 var index = Math.min(Math.floor(xOffset / actionFullWidth), _visibleRightSideActions.length)
134 index = index - 1
135 if (index > -1) {
136 root.activeItem = rightActionsRepeater.itemAt(index)
137 root.activeAction = root._visibleRightSideActions[index]
138 }
139 } else {
140 root.activeAction = null
141 }
142 }
143
144 function resetSwipe()
145 {
146 updatePosition(0)
147 }
148
149 function filterVisibleActions(actions)
150 {
151 var visibleActions = []
152 for(var i = 0; i < actions.length; i++) {
153 var action = actions[i]
154 if (action.visible) {
155 visibleActions.push(action)
156 }
157 }
158 return visibleActions
159 }
160
161 function updatePosition(pos)
162 {
163 if (!root.triggerActionOnMouseRelease && (pos !== 0)) {
164 mouseArea.state = pos > 0 ? "RightToLeft" : "LeftToRight"
165 } else {
166 mouseArea.state = ""
167 }
168 main.x = pos
169 }
170
171 // CUSTOM remove animation
172 SequentialAnimation {
173 id: removeAnimation
174
175 property var action
176
177 UbuntuNumberAnimation {
178 target: root
179 duration: UbuntuAnimation.BriskDuration
180 property: "height";
181 to: 0
182 }
183 ScriptAction {
184 script: removeAnimation.action.trigger()
185 }
186 }
187
188 states: [
189 State {
190 name: "select"
191 when: selectionMode || selected
192 PropertyChanges {
193 target: selectionIcon
194 source: Qt.resolvedUrl("ListItemActions/CheckBox.qml")
195 anchors.leftMargin: units.gu(2)
196 }
197 PropertyChanges {
198 target: root
199 locked: true
200 }
201 PropertyChanges {
202 target: main
203 x: 0
204 }
205 }
206 ]
207
208 height: defaultHeight
209 //clip: height !== defaultHeight // CUSTOM
210
211 Loader { // CUSTOM
212 id: leftActionViewLoader
213 anchors {
214 top: parent.top
215 bottom: parent.bottom
216 right: main.left
217 }
218 asynchronous: true
219 sourceComponent: leftSideAction ? leftActionViewComponent : undefined
220 }
221
222 Component { // CUSTOM
223 id: leftActionViewComponent
224
225 Rectangle {
226 id: leftActionView
227 width: root.leftActionWidth + actionThreshold
228 color: UbuntuColors.red
229
230 Icon {
231 id: leftActionIcon
232 anchors {
233 centerIn: parent
234 horizontalCenterOffset: actionThreshold / 2
235 }
236 objectName: "swipeDeleteAction" // CUSTOM
237 name: leftSideAction && _showActions ? leftSideAction.iconName : ""
238 color: Theme.palette.selected.field
239 height: units.gu(3)
240 width: units.gu(3)
241 }
242 }
243 }
244
245 //Rectangle {
246 Item { // CUSTOM
247 id: rightActionsView
248
249 anchors {
250 top: main.top
251 left: main.right
252 bottom: main.bottom
253 }
254 visible: _visibleRightSideActions.length > 0
255 width: rightActionsRepeater.count > 0 ? rightActionsRepeater.count * (root.actionWidth + units.gu(2)) + root.actionThreshold + units.gu(2) : 0
256 // color: "white" // CUSTOM
257
258 Row {
259 anchors{
260 top: parent.top
261 left: parent.left
262 leftMargin: units.gu(2)
263 right: parent.right
264 rightMargin: units.gu(2)
265 bottom: parent.bottom
266 }
267 spacing: units.gu(2)
268 Repeater {
269 id: rightActionsRepeater
270
271 model: _showActions ? _visibleRightSideActions : []
272 Item {
273 property alias image: img
274
275 height: rightActionsView.height
276 width: root.actionWidth
277
278 Icon {
279 id: img
280
281 anchors.centerIn: parent
282 objectName: rightSideActions[index].objectName // CUSTOM
283 width: units.gu(3)
284 height: units.gu(3)
285 name: modelData.iconName
286 color: root.activeAction === modelData ? UbuntuColors.orange : UbuntuColors.coolGrey // CUSTOM
287 }
288 }
289 }
290 }
291 }
292
293 Rectangle {
294 id: main
295 objectName: "mainItem"
296
297 anchors {
298 top: parent.top
299 bottom: parent.bottom
300 }
301
302 width: parent.width
303 color: root.selected ? root.selectedColor : root.color
304
305 Loader {
306 id: selectionIcon
307
308 anchors {
309 left: main.left
310 verticalCenter: main.verticalCenter
311 }
312 asynchronous: true // CUSTOM
313 width: (status === Loader.Ready) ? item.implicitWidth : 0
314 visible: (status === Loader.Ready) && (item.width === item.implicitWidth)
315
316 Behavior on width {
317 NumberAnimation {
318 duration: UbuntuAnimation.SnapDuration
319 }
320 }
321 }
322
323 Item {
324 id: mainContents
325
326 anchors {
327 left: selectionIcon.right
328 //leftMargin: units.gu(2) // CUSTOM
329 top: parent.top
330 //topMargin: units.gu(1) // CUSTOM
331 right: parent.right
332 //rightMargin: units.gu(2) // CUSTOM
333 bottom: parent.bottom
334 //bottomMargin: units.gu(1) // CUSTOM
335 }
336 }
337
338 Behavior on x {
339 UbuntuNumberAnimation {
340 id: mainItemMoving
341
342 easing.type: Easing.OutElastic
343 duration: UbuntuAnimation.SlowDuration
344 }
345 }
346 }
347
348 SequentialAnimation {
349 id: triggerAction
350
351 property var currentItem: root.activeItem ? root.activeItem.image : null
352
353 running: false
354 ParallelAnimation {
355 UbuntuNumberAnimation {
356 target: triggerAction.currentItem
357 property: "opacity"
358 from: 1.0
359 to: 0.0
360 duration: UbuntuAnimation.SlowDuration
361 easing {type: Easing.InOutBack; }
362 }
363 UbuntuNumberAnimation {
364 target: triggerAction.currentItem
365 properties: "width, height"
366 from: units.gu(3)
367 to: root.actionWidth
368 duration: UbuntuAnimation.SlowDuration
369 easing {type: Easing.InOutBack; }
370 }
371 }
372 PropertyAction {
373 target: triggerAction.currentItem
374 properties: "width, height"
375 value: units.gu(3)
376 }
377 PropertyAction {
378 target: triggerAction.currentItem
379 properties: "opacity"
380 value: 1.0
381 }
382 ScriptAction {
383 script: {
384 root.activeAction.triggered(root)
385 mouseArea.state = ""
386 }
387 }
388 PauseAnimation {
389 duration: 500
390 }
391 UbuntuNumberAnimation {
392 target: main
393 property: "x"
394 to: 0
395 }
396 }
397
398 MouseArea {
399 id: mouseArea
400
401 property bool locked: root.locked || ((root.leftSideAction === null) && (root._visibleRightSideActions.count === 0)) // CUSTOM
402 property bool manual: false
403 property string direction: "None"
404 property real lastX: -1
405
406 anchors.fill: parent
407 drag {
408 target: locked ? null : main
409 axis: Drag.XAxis
410 minimumX: rightActionsView.visible ? -(rightActionsView.width) : 0
411 maximumX: leftSideAction ? leftActionViewLoader.item.width : 0
412 threshold: root.actionThreshold
413 }
414
415 states: [
416 State {
417 name: "LeftToRight"
418 PropertyChanges {
419 target: mouseArea
420 drag.maximumX: 0
421 }
422 },
423 State {
424 name: "RightToLeft"
425 PropertyChanges {
426 target: mouseArea
427 drag.minimumX: 0
428 }
429 }
430 ]
431
432 onMouseXChanged: {
433 var offset = (lastX - mouseX)
434 if (Math.abs(offset) <= root.actionThreshold) {
435 return
436 }
437 lastX = mouseX
438 direction = offset > 0 ? "RTL" : "LTR";
439 }
440
441 onPressed: {
442 lastX = mouse.x
443 }
444
445 onReleased: {
446 if (root.triggerActionOnMouseRelease && root.activeAction) {
447 triggerAction.start()
448 } else {
449 root.returnToBounds()
450 root.activeAction = null
451 }
452 lastX = -1
453 direction = "None"
454 }
455 onClicked: {
456 if (selectionMode) { // CUSTOM - selecting a listitem should toggle selection if in selectionMode
457 selected = !selected
458 return
459 } else if (main.x === 0) {
460 root.itemClicked(mouse)
461 } else if (main.x > 0) {
462 var action = getActionAt(Qt.point(mouse.x, mouse.y))
463 if (action && action !== -1) {
464 //action.triggered(root)
465 removeAnimation.action = action // CUSTOM - use our animation instead
466 removeAnimation.start() // CUSTOM
467 }
468 } else {
469 var actionIndex = getActionAt(Qt.point(mouse.x, mouse.y))
470
471 if (actionIndex !== -1 && actionIndex !== leftSideAction) { // CUSTOM - can be leftAction
472 root.activeItem = rightActionsRepeater.itemAt(actionIndex)
473 root.activeAction = root.rightSideActions[actionIndex]
474 triggerAction.start()
475 return
476 }
477 }
478 root.resetSwipe()
479 }
480
481 onPositionChanged: {
482 if (mouseArea.pressed) {
483 updateActiveAction()
484
485 listItemSwiping(index) // CUSTOM - tells other listitems to dismiss any swipe
486 }
487 }
488 onPressAndHold: {
489 if (main.x === 0) {
490 root.itemPressAndHold(mouse)
491 }
492 }
493
494 z: -1
495 }
496}
4970
=== modified file 'app/components/MultiSelectListView.qml'
--- app/components/MultiSelectListView.qml 2015-11-02 21:28:34 +0000
+++ app/components/MultiSelectListView.qml 2017-04-25 12:59:22 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013, 2014, 20152 * Copyright (C) 2013, 2014, 2015, 2017
3 * Andrew Hayzen <ahayzen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@gmail.com>4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>5 * Victor Thompson <victor.thompson@gmail.com>
@@ -20,33 +20,52 @@
20import QtQuick 2.420import QtQuick 2.4
21import Ubuntu.Components 1.321import Ubuntu.Components 1.3
2222
23
24WeatherListView {23WeatherListView {
25 property var selectedItems: []24 // Can't access ViewItems externally
25 // so we need to expose if in multiselect mode for the header states
26 state: ViewItems.selectMode ? "multiselectable" : "default"
2627
27 signal clearSelection()28 signal clearSelection()
28 signal closeSelection()29 signal closeSelection()
30 signal reorder(int from, int to)
29 signal selectAll()31 signal selectAll()
3032
31 onClearSelection: selectedItems = []33 onClearSelection: ViewItems.selectedIndices = []
32 onCloseSelection: {34 onCloseSelection: {
33 clearSelection()35 clearSelection()
34 state = "normal"36 ViewItems.selectMode = false
37 ViewItems.dragMode = false
35 }38 }
36 onSelectAll: {39 onSelectAll: {
37 var tmp = selectedItems40 var tmp = []
3841
39 for (var i=0; i < model.count; i++) {42 for (var i=0; i < model.count; i++) {
40 if (tmp.indexOf(i) === -1) {43 tmp.push(i)
41 tmp.push(i)
42 }
43 }44 }
4445
45 selectedItems = tmp46 ViewItems.selectedIndices = tmp
46 }47 }
47 onVisibleChanged: {48
48 if (!visible) {49 // Can't access ViewItems externally
49 closeSelection()50 // so for the header actions we need to expose the selectedIndices
51 function getSelectedIndices() {
52 var indicies = ViewItems.selectedIndices.slice();
53
54 indicies.sort(); // ensure indicies are in-order
55
56 return indicies;
57 }
58
59 ViewItems.selectMode: false
60 ViewItems.dragMode: false
61 ViewItems.onDragUpdated: {
62 // Only update the model when the listitem is dropped, not 'live'
63 if (event.status == ListItemDrag.Moving) {
64 event.accept = false
65 } else if (event.status == ListItemDrag.Dropped) {
66 model.move(event.from, event.to, 1);
67
68 reorder(event.from, event.to)
50 }69 }
51 }70 }
52}71}
5372
=== modified file 'app/components/WeatherListItem.qml'
--- app/components/WeatherListItem.qml 2015-11-02 21:28:34 +0000
+++ app/components/WeatherListItem.qml 2017-04-25 12:59:22 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013, 2014, 20152 * Copyright (C) 2013, 2014, 2015, 2017
3 * Andrew Hayzen <ahayzen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Nekhelesh Ramananthan <krnekhelesh@gmail.com>4 * Nekhelesh Ramananthan <krnekhelesh@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>5 * Victor Thompson <victor.thompson@gmail.com>
@@ -20,119 +20,39 @@
20import QtQuick 2.420import QtQuick 2.4
21import Ubuntu.Components 1.321import Ubuntu.Components 1.3
2222
23ListItemWithActions {23ListItem {
24 id: root
25 color: "transparent"24 color: "transparent"
2625 divider {
27 property int listItemIndex: index26 visible: true
27 }
28 highlightColor: Qt.lighter(color, 1.2)
29
30 // Store the currentColor so that actions can bind to it
31 property var currentColor: highlighted ? highlightColor : color
32
28 property bool multiselectable: false33 property bool multiselectable: false
29 property int previousListItemIndex: -134 property bool pressAndHoldEnabled: true
30 property bool reorderable: false35 property bool reorderable: false
3136
32 signal reorder(int from, int to)37 signal itemClicked()
3338
34 onItemPressAndHold: {39 onClicked: {
35 if (multiselectable) {40 if (selectMode) {
36 selectionMode = true41 selected = !selected;
37 }42 } else {
38 }43 itemClicked()
3944 }
40 onListItemIndexChanged: {45 }
41 var i = parent.parent.selectedItems.lastIndexOf(previousListItemIndex)46
4247 onPressAndHold: {
43 if (i !== -1) {48 if (pressAndHoldEnabled) {
44 parent.parent.selectedItems[i] = listItemIndex49 if (reorderable) {
45 }50 ListView.view.ViewItems.dragMode = !ListView.view.ViewItems.dragMode
4651 }
47 previousListItemIndex = listItemIndex52
48 }53 if (multiselectable) {
4954 ListView.view.ViewItems.selectMode = !ListView.view.ViewItems.selectMode
50 onSelectedChanged: {55 }
51 if (selectionMode) {56 }
52 var tmp = parent.parent.selectedItems
53
54 if (selected) {
55 if (parent.parent.selectedItems.indexOf(listItemIndex) === -1) {
56 tmp.push(listItemIndex)
57 parent.parent.selectedItems = tmp
58 }
59 } else {
60 tmp.splice(parent.parent.selectedItems.indexOf(listItemIndex), 1)
61 parent.parent.selectedItems = tmp
62 }
63 }
64 }
65
66 onSelectionModeChanged: {
67 if (reorderable && selectionMode) {
68 resetSwipe()
69 }
70
71 for (var j=0; j < _main.children.length; j++) {
72 if (_main.children[j] !== actionReorderLoader) {
73 _main.children[j].anchors.rightMargin = reorderable && selectionMode ? actionReorderLoader.width + units.gu(2) : 0
74 }
75 }
76
77 parent.parent.state = selectionMode ? "multiselectable" : "normal"
78
79 if (!selectionMode) {
80 selected = false
81 }
82 }
83
84 /* Highlight the listitem on press */
85 Rectangle {
86 id: listItemBrighten
87 color: root.pressed ? UbuntuColors.coolGrey : "transparent"
88 opacity: 0.1
89 height: root.height
90 x: root.x - parent.x // -parent.x due to selectionIcon in ListItemWithActions
91 width: root.width
92 }
93
94 /* Reorder Component */
95 Loader {
96 id: actionReorderLoader
97 active: reorderable && selectionMode && root.parent.parent.selectedItems.length === 0
98 anchors {
99 bottom: parent.bottom
100 right: parent.right
101 rightMargin: units.gu(1)
102 top: parent.top
103 }
104 asynchronous: true
105 source: "ListItemReorderComponent.qml"
106 }
107
108 Item {
109 Connections { // Only allow one ListItem to be swiping at any time
110 target: weatherApp
111 onListItemSwiping: {
112 if (i !== index) {
113 root.resetSwipe();
114 }
115 }
116 }
117
118 Connections { // Connections from signals in the ListView
119 target: root.parent.parent
120 onClearSelection: selected = false
121 onFlickingChanged: {
122 if (root.parent.parent.flicking) {
123 root.resetSwipe()
124 }
125 }
126 onSelectAll: selected = true
127 onStateChanged: selectionMode = root.parent.parent.state === "multiselectable"
128 }
129 }
130
131 Component.onCompleted: { // reload settings as delegates are destroyed
132 if (parent.parent.selectedItems.indexOf(index) !== -1) {
133 selected = true
134 }
135
136 selectionMode = root.parent.parent.state === "multiselectable"
137 }57 }
138}58}
13959
=== modified file 'app/ui/LocationPane.qml'
--- app/ui/LocationPane.qml 2017-04-12 09:55:14 +0000
+++ app/ui/LocationPane.qml 2017-04-25 12:59:22 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2015 Canonical Ltd2 * Copyright (C) 2015, 2017 Canonical Ltd
3 *3 *
4 * This file is part of Ubuntu Weather App4 * This file is part of Ubuntu Weather App
5 *5 *
@@ -18,7 +18,6 @@
1818
19import QtQuick 2.419import QtQuick 2.4
20import Ubuntu.Components 1.320import Ubuntu.Components 1.3
21import Ubuntu.Components.ListItems 0.1 as ListItem
22import "../components"21import "../components"
23import "../data/suncalc.js" as SunCalc22import "../data/suncalc.js" as SunCalc
2423
@@ -55,79 +54,78 @@
5554
56 modelData: model55 modelData: model
57 }56 }
58 header: Column {57 header: ListItem {
59 id: locationTop58 divider {
6059 visible: true
61 anchors {60 }
62 left: parent.left61 height: locationTop.height
63 right: parent.right62
64 margins: units.gu(2)63 Column {
65 }64 id: locationTop
66 spacing: units.gu(1)65
67
68 Row { // spacing at top
69 height: units.gu(1)
70 width: parent.width
71 }
72
73 HeaderRow {
74 id: headerRow
75 locationName: mainPageWeekdayListView.name
76 }
77
78 HomeGraphic {
79 id: homeGraphic
80 icon: mainPageWeekdayListView.icon
81 visible: graphicVisible;
82 MouseArea {
83 anchors.fill: parent
84 onClicked: {
85 graphicVisible = false;
86 }
87 }
88 }
89
90 Loader {
91 id: homeHourlyLoader
92 active: !homeGraphic.visible
93 asynchronous: true
94 height: units.gu(32)
95 source: "../components/HomeHourly.qml"
96 visible: active
97 width: parent.width
98 }
99
100 HomeTempInfo {
101 id: homeTempInfo
102 modelData: todayData
103 now: mainPageWeekdayListView.currentTemp
104 updatedAt: mainPageWeekdayListView.lastFetch
105 }
106
107 // TODO: Migrate this to using the new SDK list item when possible.
108 ListItem.ThinDivider {
109 anchors {66 anchors {
110 leftMargin: units.gu(-2);67 left: parent.left
111 rightMargin: units.gu(-2)68 right: parent.right
112 }69 margins: units.gu(2)
113 }70 }
11471 spacing: units.gu(1)
115 NumberAnimation {72
116 id: scrollToTopAnimation73 Row { // spacing at top
117 target: mainPageWeekdayListView;74 height: units.gu(1)
118 property: "contentY";75 width: parent.width
119 duration: 200;76 }
120 easing.type: Easing.InOutQuad77
121 to: -height78 HeaderRow {
122 }79 id: headerRow
12380 locationName: mainPageWeekdayListView.name
124 Connections {81 }
125 target: locationPages82
126 onCurrentIndexChanged: {83 HomeGraphic {
127 if (locationPages.currentIndex !== index) {84 id: homeGraphic
128 scrollToTopAnimation.start()85 icon: mainPageWeekdayListView.icon
129 } else {86 visible: graphicVisible;
130 mainPageWeekdayListView.contentY = -locationTop.height87 MouseArea {
88 anchors.fill: parent
89 onClicked: {
90 graphicVisible = false;
91 }
92 }
93 }
94
95 Loader {
96 id: homeHourlyLoader
97 active: !homeGraphic.visible
98 asynchronous: true
99 height: units.gu(32)
100 source: "../components/HomeHourly.qml"
101 visible: active
102 width: parent.width
103 }
104
105 HomeTempInfo {
106 id: homeTempInfo
107 modelData: todayData
108 now: mainPageWeekdayListView.currentTemp
109 updatedAt: mainPageWeekdayListView.lastFetch
110 }
111
112 NumberAnimation {
113 id: scrollToTopAnimation
114 target: mainPageWeekdayListView;
115 property: "contentY";
116 duration: 200;
117 easing.type: Easing.InOutQuad
118 to: -height
119 }
120
121 Connections {
122 target: locationPages
123 onCurrentIndexChanged: {
124 if (locationPages.currentIndex !== index) {
125 scrollToTopAnimation.start()
126 } else {
127 mainPageWeekdayListView.contentY = -locationTop.height
128 }
131 }129 }
132 }130 }
133 }131 }
134132
=== modified file 'app/ui/LocationsPage.qml'
--- app/ui/LocationsPage.qml 2017-04-18 09:08:08 +0000
+++ app/ui/LocationsPage.qml 2017-04-25 12:59:22 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2015 Canonical Ltd2 * Copyright (C) 2015, 2017 Canonical Ltd
3 *3 *
4 * This file is part of Ubuntu Weather App4 * This file is part of Ubuntu Weather App
5 *5 *
@@ -18,7 +18,6 @@
1818
19import QtQuick 2.419import QtQuick 2.4
20import Ubuntu.Components 1.320import Ubuntu.Components 1.3
21import Ubuntu.Components.ListItems 0.1 as ListItem
22import "../components"21import "../components"
23import "../components/HeadState"22import "../components/HeadState"
24import "../components/ListItemActions"23import "../components/ListItemActions"
@@ -37,7 +36,7 @@
37 removable: true36 removable: true
38 thisPage: locationsPage37 thisPage: locationsPage
3938
40 onRemoved: storage.removeMultiLocations(selectedItems.slice());39 onRemoved: storage.removeMultiLocations(selectedIndices.slice());
41 }40 }
42 ]41 ]
4342
@@ -65,93 +64,91 @@
65 left: parent.left64 left: parent.left
66 right: parent.right65 right: parent.right
67 }66 }
68 height: settings.addedCurrentLocation && settings.detectCurrentLocation ? units.gu(8) : units.gu(0)67 height: settings.addedCurrentLocation && settings.detectCurrentLocation ? contentHeight : units.gu(0)
69 interactive: false68 interactive: false
70 model: currentLocationModel69 model: currentLocationModel
71 delegate: WeatherListItem {70 delegate: WeatherListItem {
72 id: currentLocationListItem71 id: currentLocationListItem
72 height: currentListItemLayout.height + (divider.visible ? divider.height : 0) + (fakeDivider.visible ? fakeDivider.height : 0)
7373
74 onItemClicked: {74 onItemClicked: {
75 settings.current = index;75 settings.current = index;
76
77 // Ensure any selections are closed
78 locationsListView.closeSelection();
79
76 locationsPage.pop()80 locationsPage.pop()
77 }81 }
7882
79 Column {83 ListItemLayout {
80 anchors {84 id: currentListItemLayout
81 left: parent.left85 subtitle {
82 right: currentWeatherImage.left86 elide: Text.ElideRight
83 rightMargin: units.gu(3)87 fontSize: "small"
84 verticalCenter: parent.verticalCenter88 text: name + ", " + (adminName1 == name ? countryName : adminName1)
85 }89 }
8690 title {
87 Label {
88 id: currentLocationName
89
90 anchors {
91 left: parent.left
92 leftMargin: units.gu(2)
93 }
94
95 elide: Text.ElideRight91 elide: Text.ElideRight
96 fontSize: "medium"92 fontSize: "medium"
97 text: i18n.tr("Current Location")93 text: i18n.tr("Current Location")
98 width: parent.width94 }
99 }95
96 Icon {
97 anchors {
98 verticalCenter: parent.verticalCenter
99 }
100 height: parent.height / 2
101 name: icon
102 SlotsLayout.position: SlotsLayout.Trailing
103 SlotsLayout.overrideVerticalPositioning: true
104 width: height
105 }
106
100 Label {107 Label {
101 id: currentLocationName2
102
103 anchors {108 anchors {
104 left: parent.left109 verticalCenter: parent.verticalCenter
105 leftMargin: units.gu(2)
106 }110 }
107111 color: UbuntuColors.orange
108 color: UbuntuColors.graphite
109 elide: Text.ElideRight112 elide: Text.ElideRight
110 fontSize: "small"113 font {
111 font.weight: Font.Light114 pixelSize: parent.height / 2
112 text: name + ", " + (adminName1 == name ? countryName : adminName1)115 weight: Font.Light
113 width: parent.width116 }
114 }117 horizontalAlignment: Text.AlignRight
115 }118 SlotsLayout.position: SlotsLayout.Trailing
116119 SlotsLayout.overrideVerticalPositioning: true
117 Icon {120 text: temp + settings.tempScale
118 id: currentWeatherImage121 }
119 anchors {122 }
120 right: parent.right123
121 rightMargin: units.gu(14)124 // Inject extra divider when we are the last as the SDK hides it
122 verticalCenter: parent.verticalCenter125 // but we need one as we have another listview directly below
123 }126 ListItem {
124 name: icon127 id: fakeDivider
125 height: units.gu(3)128 anchors {
126 width: units.gu(3)129 bottom: parent.bottom
127 }130 }
128131 divider {
129 Label {132 visible: true
130 id: currentTemperatureLabel133 }
131 anchors {134 height: divider.height
132 left: currentWeatherImage.right135 visible: currentLocationModel.count - 1 == index
133 leftMargin: units.gu(1)
134 right: parent.right
135 rightMargin: units.gu(2)
136 verticalCenter: parent.verticalCenter
137 }
138 color: UbuntuColors.orange
139 elide: Text.ElideRight
140 font.pixelSize: units.gu(4)
141 font.weight: Font.Light
142 horizontalAlignment: Text.AlignRight
143 text: temp + settings.tempScale
144 }136 }
145 }137 }
146 }138 }
147139
148 delegate: WeatherListItem {140 delegate: WeatherListItem {
149 id: locationsListItem141 id: locationsListItem
142 height: listItemLayout.height + (divider.visible ? divider.height : 0)
143 leadingActions: ListItemActions {
144 actions: [
145 Remove {
146 onTriggered: storage.removeLocation(index)
147 }
148 ]
149 }
150 multiselectable: true
150 objectName: "location" + index151 objectName: "location" + index
151 leftSideAction: Remove {
152 onTriggered: storage.removeLocation(index)
153 }
154 multiselectable: true
155 reorderable: true152 reorderable: true
156153
157 onItemClicked: {154 onItemClicked: {
@@ -163,89 +160,56 @@
163160
164 locationsPage.pop()161 locationsPage.pop()
165 }162 }
166 onReorder: {163
167 console.debug("Move: ", from, to);164 ListItemLayout {
168165 id: listItemLayout
169 storage.moveLocation(from, to);166 subtitle {
170 }167 elide: Text.ElideRight
171168 fontSize: "small"
172 ListItem.ThinDivider {169 text: adminName1 == name ? countryName : adminName1
173 anchors {170 }
174 top: parent.top171 title {
175 }172 elide: Text.ElideRight
176 visible: index == 0173 fontSize: "medium"
177 }174 objectName: "name"
178175 text: name
179 Item {
180 anchors {
181 fill: parent
182 leftMargin: units.gu(2)
183 rightMargin: units.gu(2)
184 }
185
186 Column {
187 anchors {
188 left: parent.left
189 right: weatherImage.visible ? weatherImage.left : parent.right
190 rightMargin: units.gu(1)
191 verticalCenter: parent.verticalCenter
192 }
193
194 Label {
195 id: locationName
196 objectName: "name"
197 elide: Text.ElideRight
198 fontSize: "medium"
199 text: name
200 width: parent.width
201 }
202 Label {
203 id: locationName2
204 color: UbuntuColors.graphite
205 elide: Text.ElideRight
206 fontSize: "small"
207 font.weight: Font.Light
208 text: adminName1 == name ? countryName : adminName1
209 width: parent.width
210 }
211 }176 }
212177
213 Icon {178 Icon {
214 id: weatherImage
215 anchors {179 anchors {
216 right: parent.right
217 rightMargin: units.gu(12)
218 verticalCenter: parent.verticalCenter180 verticalCenter: parent.verticalCenter
219 }181 }
182 height: parent.height / 2
220 name: icon183 name: icon
221 height: units.gu(3)184 SlotsLayout.position: SlotsLayout.Trailing
222 width: units.gu(3)185 SlotsLayout.overrideVerticalPositioning: true
223 visible: locationsPage.state === "default"186 width: height
187 visible: locationsListView.state === "default"
224 }188 }
225189
226 Label {190 Label {
227 id: temperatureLabel
228 anchors {191 anchors {
229 left: weatherImage.right
230 leftMargin: units.gu(1)
231 right: parent.right
232 verticalCenter: parent.verticalCenter192 verticalCenter: parent.verticalCenter
233 }193 }
234 color: UbuntuColors.orange194 color: UbuntuColors.orange
235 elide: Text.ElideRight195 elide: Text.ElideRight
236 font.pixelSize: units.gu(4)196 font {
237 font.weight: Font.Light197 pixelSize: parent.height / 2
198 weight: Font.Light
199 }
238 horizontalAlignment: Text.AlignRight200 horizontalAlignment: Text.AlignRight
201 SlotsLayout.position: SlotsLayout.Trailing
202 SlotsLayout.overrideVerticalPositioning: true
239 text: temp + settings.tempScale203 text: temp + settings.tempScale
240 visible: locationsPage.state === "default"204 visible: locationsListView.state === "default"
241 }205 }
242 }206 }
243207 }
244 ListItem.ThinDivider {208
245 anchors {209 onReorder: {
246 bottom: parent.bottom210 console.debug("Move: ", from, to);
247 }211
248 }212 storage.moveLocation(from, to);
249 }213 }
250 }214 }
251215
252216
=== modified file 'tests/autopilot/ubuntu_weather_app/__init__.py'
--- tests/autopilot/ubuntu_weather_app/__init__.py 2017-04-13 10:38:05 +0000
+++ tests/autopilot/ubuntu_weather_app/__init__.py 2017-04-25 12:59:22 +0000
@@ -1,5 +1,5 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2# Copyright 2013, 2014, 2015 Canonical2# Copyright 2013, 2014, 2015, 2017 Canonical
3#3#
4# This program is free software: you can redistribute it and/or modify it4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published5# under the terms of the GNU General Public License version 3, as published
@@ -8,7 +8,7 @@
8"""ubuntu-weather-app tests and emulators - top level package."""8"""ubuntu-weather-app tests and emulators - top level package."""
9from autopilot.introspection import dbus9from autopilot.introspection import dbus
10import logging10import logging
11from ubuntuuitoolkit import (MainView, QQuickListView,11from ubuntuuitoolkit import (MainView, QQuickListView, UCListItem,
12 UbuntuUIToolkitCustomProxyObjectBase)12 UbuntuUIToolkitCustomProxyObjectBase)
1313
14logger = logging.getLogger(__name__)14logger = logging.getLogger(__name__)
@@ -329,20 +329,10 @@
329 "StandardListItem", iconVisible=showIcon)329 "StandardListItem", iconVisible=showIcon)
330330
331331
332class WeatherListItem(UbuntuUIToolkitCustomProxyObjectBase):332class WeatherListItem(UCListItem):
333 def click_remove_action(self):
334 return self.trigger_leading_action("swipeDeleteAction",
335 self.wait_until_destroyed)
336
333 def get_name(self):337 def get_name(self):
334 return self.select_single("UCLabel", objectName="name").text338 return self.select_single("UCLabel", objectName="name").text
335
336 @click_object
337 def select_remove(self):
338 return self.select_single(objectName="swipeDeleteAction")
339
340 def swipe_and_select_remove(self):
341 x, y, width, height = self.globalRect
342 start_x = x + (width * 0.2)
343 stop_x = x + (width * 0.8)
344 start_y = stop_y = y + (height // 2)
345
346 self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
347
348 self.select_remove()
349339
=== modified file 'tests/autopilot/ubuntu_weather_app/tests/test_locations_page.py'
--- tests/autopilot/ubuntu_weather_app/tests/test_locations_page.py 2015-08-10 03:13:48 +0000
+++ tests/autopilot/ubuntu_weather_app/tests/test_locations_page.py 2017-04-25 12:59:22 +0000
@@ -1,5 +1,5 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2# Copyright 2013, 2014, 2015 Canonical2# Copyright 2013, 2014, 2015, 2017 Canonical
3#3#
4# This program is free software: you can redistribute it and/or modify it4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published5# under the terms of the GNU General Public License version 3, as published
@@ -48,7 +48,7 @@
48 self.assertThat(list_item.get_name(), Equals("London"))48 self.assertThat(list_item.get_name(), Equals("London"))
4949
50 # Remove the location via the list item action50 # Remove the location via the list item action
51 list_item.swipe_and_select_remove()51 list_item.click_remove_action()
5252
53 # Check that the location was removed53 # Check that the location was removed
54 self.assertThat(self.home_page.get_location_count,54 self.assertThat(self.home_page.get_location_count,

Subscribers

People subscribed via source and target branches

to all changes: