Merge lp:~martin-borho/ubuntu-weather-app/edit-locations-in-sheets into lp:ubuntu-weather-app/obsolete.trunk

Proposed by Martin Borho
Status: Merged
Approved by: David Planella
Approved revision: 72
Merged at revision: 71
Proposed branch: lp:~martin-borho/ubuntu-weather-app/edit-locations-in-sheets
Merge into: lp:ubuntu-weather-app/obsolete.trunk
Diff against target: 1291 lines (+528/-442)
13 files modified
components/AddLocationPage.qml (+0/-114)
components/AddLocationSheet.qml (+134/-0)
components/CurrentWeather.qml (+6/-1)
components/DateComponent.qml (+3/-2)
components/DayWeatherComponent.qml (+4/-1)
components/LocationManagerPage.qml (+0/-182)
components/LocationManagerSheet.qml (+228/-0)
components/LocationTab.qml (+48/-56)
components/SettingsSheet.qml (+2/-2)
tests/autopilot/ubuntu_weather_app/tests/__init__.py (+9/-0)
tests/autopilot/ubuntu_weather_app/tests/test_locationmanager.py (+61/-22)
tests/autopilot/ubuntu_weather_app/tests/test_settings.py (+5/-13)
ubuntu-weather-app.qml (+28/-49)
To merge this branch: bzr merge lp:~martin-borho/ubuntu-weather-app/edit-locations-in-sheets
Reviewer Review Type Date Requested Status
David Planella Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Review via email: mp+175815@code.launchpad.net

Commit message

LocationManager now implemented with Sheet components, PageStack removed, Suru gradient background added.

Description of the change

* LocationManager is now implemented with Sheet components, like discussed in Bug #1187314
* Flat navigation is used everywhere now, which means the tab header will slide away when a tab gets scrolled down (default behaviour). Solves Bug #1201094
* PageStack removed, since not needed anymore.
* Autopilot tests updated accordingly
* one ActivityIndicator removed
* Suru gradient background added

To post a comment you must log in.
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
71. By Martin Borho

autopilot settings tests now also uses SheetMixin

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
72. By Martin Borho

suru gradient background added

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
David Planella (dpm) wrote :

Nice work, thanks for updating the tests as well!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== removed file 'components/AddLocationPage.qml'
--- components/AddLocationPage.qml 2013-07-16 08:25:05 +0000
+++ components/AddLocationPage.qml 1970-01-01 00:00:00 +0000
@@ -1,114 +0,0 @@
1import QtQuick 2.0
2import Ubuntu.Components 0.1
3import Ubuntu.Components.ListItems 0.1 as ListItem
4
5Page {
6 id: addLocationPage
7 objectName: "AddLocationPage"
8 title: i18n.tr("Add city")
9 visible: false
10 tools: ToolbarItems{
11 locked: true
12 opened: true
13 back: ToolbarButton {
14 objectName: "BackButton"
15 iconSource: Qt.resolvedUrl("../resources/images/back@18.png")
16 text: i18n.tr("Back")
17 onTriggered: {
18 if(pageStack.depth === 3) pageStack.pop()
19 }
20 }
21 }
22
23 WorkerScript {
24 id: searchWorker
25 source: "./WeatherApi.js"
26 onMessage: {
27 if(!messageObject.error) {
28 listView.visible = true
29 messageObject.result.locations.forEach(function(loc) {
30 citiesModel.append(loc);
31 noCityError.visible = false
32 });
33 } else {
34 console.log(messageObject.error.msg+" / "+messageObject.error.request.url)
35 }
36 if (!citiesModel.count) {
37 noCityError.visible = true
38 noCityError.text = i18n.tr("No location was found for ") + locationString.text
39 }
40 }
41 }
42
43 function clear() {
44 locationString.text = '';
45 citiesModel.clear();
46 listView.visible = true;
47 }
48
49 Rectangle {
50 id: searchInput
51 width:parent.width
52 height:units.gu(7)
53 color: "transparent"
54 TextField {
55 id: locationString
56 objectName: "SearchField"
57 width: parent.width-units.gu(2)
58 height:units.gu(5)
59 anchors.centerIn: parent
60 placeholderText: i18n.tr("Enter a city name")
61 hasClearButton: true
62 onAccepted: {
63 citiesModel.clear();
64 searchWorker.sendMessage({
65 action: "searchByName",
66 params: {name:locationString.text, units:"metric"}
67 })
68 }
69 }
70 }
71
72 ListModel {
73 id: citiesModel
74 }
75
76 Rectangle {
77 id: cityList;
78 anchors.top: searchInput.bottom
79 width: parent.width
80 height: units.gu(52)
81 color: "transparent"
82 Label {
83 id: noCityError
84 objectName: "noCityError"
85 visible: false
86 anchors.centerIn: parent;
87 font.pixelSize: 18
88 }
89
90 ListView {
91 id: listView;
92 objectName: "SearchResultList"
93 visible: false
94 clip: true;
95 anchors.fill: parent;
96 model: citiesModel;
97 delegate: ListItem.Standard {
98 text: i18n.tr(name)+((country) ? ', '+i18n.tr(country): '');
99 progression: true;
100 onClicked: {
101 var location = citiesModel.get(index)
102 locationManager.addLocation(location)
103 pageStack.pop()
104 clear()
105 }
106 }
107 Scrollbar {
108 flickableItem: listView;
109 align: Qt.AlignTrailing;
110 }
111 }
112 }
113
114}
1150
=== added file 'components/AddLocationSheet.qml'
--- components/AddLocationSheet.qml 1970-01-01 00:00:00 +0000
+++ components/AddLocationSheet.qml 2013-07-20 16:54:27 +0000
@@ -0,0 +1,134 @@
1/*
2 * Copyright (C) 2013 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 version 3 as
6 * published by the Free Software Foundation.
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 * Authored by: Raúl Yeguas <neokore@gmail.com>
17 * Martin Borho <martin@borho.net>
18 */
19import QtQuick 2.0
20import Ubuntu.Components 0.1
21import Ubuntu.Components.Popups 0.1
22import Ubuntu.Components.ListItems 0.1 as ListItem
23
24Component {
25
26 DefaultSheet {
27 id: addLocationSheet
28 objectName: "AddLocationSheet"
29 title: i18n.tr("Add city")
30 contentsHeight: parent.height
31 doneButton: false
32
33 WorkerScript {
34 id: searchWorker
35 source: "./WeatherApi.js"
36 onMessage: {
37 if(!messageObject.error) {
38 listView.visible = true
39 messageObject.result.locations.forEach(function(loc) {
40 citiesModel.append(loc);
41 noCityError.visible = false
42 });
43 } else {
44 console.log(messageObject.error.msg+" / "+messageObject.error.request.url)
45 }
46 if (!citiesModel.count) {
47 noCityError.visible = true
48 noCityError.text = i18n.tr("No location was found for ") + locationString.text
49 }
50 }
51 }
52
53 Component.onCompleted: {
54
55 }
56
57 function clear() {
58 locationString.text = '';
59 citiesModel.clear();
60 listView.visible = true;
61 }
62
63 ListModel {
64 id: citiesModel
65 }
66
67 container: Item {
68 width: parent.width
69 Rectangle {
70 id: searchInput
71 width:parent.width
72 height:units.gu(7)
73 color: "transparent"
74 TextField {
75 id: locationString
76 objectName: "SearchField"
77 width: parent.width-units.gu(2)
78 height:units.gu(5)
79 anchors.centerIn: parent
80 placeholderText: i18n.tr("Enter a city name")
81 hasClearButton: true
82 onAccepted: {
83 citiesModel.clear();
84 searchWorker.sendMessage({
85 action: "searchByName",
86 params: {name:locationString.text, units:"metric"}
87 })
88 }
89 }
90 }
91
92 Rectangle {
93 id: cityList;
94 anchors.top: searchInput.bottom
95 width: parent.width
96 height: units.gu(52)
97 color: "transparent"
98 Label {
99 id: noCityError
100 objectName: "noCityError"
101 visible: false
102 anchors.centerIn: parent;
103 font.pixelSize: 18
104 }
105
106 ListView {
107 id: listView;
108 objectName: "SearchResultList"
109 visible: false
110 clip: true;
111 anchors.fill: parent;
112 model: citiesModel;
113 delegate: ListItem.Standard {
114 text: i18n.tr(name)+((country) ? ', '+i18n.tr(country): '');
115 progression: true;
116 onClicked: {
117 var location = citiesModel.get(index)
118 //locationManager.addLocation(location)
119 //mainView.newLocationAdded(location)
120 locationManagerSheet.addLocation(location)
121 PopupUtils.close(addLocationSheet)
122 //pageStack.pop()
123 clear()
124 }
125 }
126 Scrollbar {
127 flickableItem: listView;
128 align: Qt.AlignTrailing;
129 }
130 }
131 }
132 }
133 }
134}
0135
=== modified file 'components/CurrentWeather.qml'
--- components/CurrentWeather.qml 2013-06-26 10:43:49 +0000
+++ components/CurrentWeather.qml 2013-07-20 16:54:27 +0000
@@ -4,6 +4,7 @@
44
5Rectangle {5Rectangle {
6 id: currentWeather6 id: currentWeather
7 color: "transparent"
78
8 // For Status I take the same condition codes from OpenWeatherMap (http://openweathermap.org/wiki/API/Weather_Condition_Codes)9 // For Status I take the same condition codes from OpenWeatherMap (http://openweathermap.org/wiki/API/Weather_Condition_Codes)
9 property int condition10 property int condition
@@ -44,7 +45,7 @@
44 anchors.horizontalCenter: parent.horizontalCenter45 anchors.horizontalCenter: parent.horizontalCenter
45 anchors.top: currentCondition.bottom46 anchors.top: currentCondition.bottom
46 anchors.topMargin: 047 anchors.topMargin: 0
47 color: "#00000000"48 color: "transparent"
4849
49 Column {50 Column {
50 id: column151 id: column1
@@ -67,6 +68,7 @@
67 anchors.horizontalCenter: parent.horizontalCenter68 anchors.horizontalCenter: parent.horizontalCenter
68 font.pixelSize: FontUtils.sizeToPixels("medium")69 font.pixelSize: FontUtils.sizeToPixels("medium")
69 horizontalAlignment: Text.AlignHCenter70 horizontalAlignment: Text.AlignHCenter
71 color: Theme.palette.normal.baseText
70 }72 }
71 }73 }
7274
@@ -84,6 +86,7 @@
84 anchors.verticalCenter: parent.verticalCenter86 anchors.verticalCenter: parent.verticalCenter
85 font.pointSize: 5287 font.pointSize: 52
86 horizontalAlignment: Text.AlignHCenter88 horizontalAlignment: Text.AlignHCenter
89 color: Theme.palette.normal.baseText
87 }90 }
8891
89 Column {92 Column {
@@ -99,6 +102,7 @@
99 horizontalAlignment: Text.AlignHCenter102 horizontalAlignment: Text.AlignHCenter
100 anchors.horizontalCenter: parent.horizontalCenter103 anchors.horizontalCenter: parent.horizontalCenter
101 font.pixelSize: FontUtils.sizeToPixels("x-small")104 font.pixelSize: FontUtils.sizeToPixels("x-small")
105 color: Theme.palette.normal.baseText
102 }106 }
103107
104 Label {108 Label {
@@ -107,6 +111,7 @@
107 horizontalAlignment: Text.AlignHCenter111 horizontalAlignment: Text.AlignHCenter
108 anchors.horizontalCenter: parent.horizontalCenter112 anchors.horizontalCenter: parent.horizontalCenter
109 font.pixelSize: FontUtils.sizeToPixels("medium")113 font.pixelSize: FontUtils.sizeToPixels("medium")
114 color: Theme.palette.normal.baseText
110 }115 }
111 }116 }
112 }117 }
113118
=== modified file 'components/DateComponent.qml'
--- components/DateComponent.qml 2013-06-26 11:12:34 +0000
+++ components/DateComponent.qml 2013-07-20 16:54:27 +0000
@@ -4,11 +4,11 @@
4Rectangle {4Rectangle {
5 width: parent.width5 width: parent.width
6 height: childrenRect.height6 height: childrenRect.height
77 color: "transparent"
8 property bool borderTopVisible: true8 property bool borderTopVisible: true
9 property string dateRelative: ""9 property string dateRelative: ""
10 property string dateString: ""10 property string dateString: ""
11 property string borderColor: "#D6D6D6"11 property string borderColor: "transparent"
12 property int borderHeight: units.gu(0.1)12 property int borderHeight: units.gu(0.1)
1313
14 Component.onCompleted: { 14 Component.onCompleted: {
@@ -44,6 +44,7 @@
44 objectName: "DayDateLabel"44 objectName: "DayDateLabel"
45 height: parent.height45 height: parent.height
46 verticalAlignment: Text.AlignVCenter46 verticalAlignment: Text.AlignVCenter
47 color: Theme.palette.normal.baseText
47 }48 }
48 }49 }
4950
5051
=== modified file 'components/DayWeatherComponent.qml'
--- components/DayWeatherComponent.qml 2013-06-26 23:50:16 +0000
+++ components/DayWeatherComponent.qml 2013-07-20 16:54:27 +0000
@@ -8,13 +8,14 @@
8 width: parent.width-units.gu(2)8 width: parent.width-units.gu(2)
9 anchors.horizontalCenter: parent.horizontalCenter9 anchors.horizontalCenter: parent.horizontalCenter
10 height:contentShape.height+units.gu(2)10 height:contentShape.height+units.gu(2)
11 color: "transparent"
1112
12 property int condition13 property int condition
13 property string icon14 property string icon
14 property int temperature15 property int temperature
15 property int temperatureMin16 property int temperatureMin
16 property string scale: (mainView.settings["units"] === "imperial") ? "F" : "C"17 property string scale: (mainView.settings["units"] === "imperial") ? "F" : "C"
17 property string shapeColor: "#D6D6D6"18 property string shapeColor: "transparent"
1819
19 UbuntuShape{20 UbuntuShape{
20 id: contentShape21 id: contentShape
@@ -38,6 +39,7 @@
38 width:parent.width/9*339 width:parent.width/9*3
39 height:parent.height40 height:parent.height
40 text: dayWeatherComponent.temperature+"°"+dayWeatherComponent.scale41 text: dayWeatherComponent.temperature+"°"+dayWeatherComponent.scale
42 color: Theme.palette.normal.baseText
41 }43 }
42 Label {44 Label {
43 id: minTemp45 id: minTemp
@@ -46,6 +48,7 @@
46 width:parent.width/9*348 width:parent.width/9*3
47 height: parent.height49 height: parent.height
48 text: i18n.tr("Min.")+"\n"+dayWeatherComponent.temperatureMin+"°"+dayWeatherComponent.scale50 text: i18n.tr("Min.")+"\n"+dayWeatherComponent.temperatureMin+"°"+dayWeatherComponent.scale
51 color: Theme.palette.normal.baseText
49 }52 }
50 }53 }
51 }54 }
5255
=== removed file 'components/LocationManagerPage.qml'
--- components/LocationManagerPage.qml 2013-07-16 09:39:11 +0000
+++ components/LocationManagerPage.qml 1970-01-01 00:00:00 +0000
@@ -1,182 +0,0 @@
1import QtQuick 2.0
2import Ubuntu.Components 0.1
3import Ubuntu.Components.ListItems 0.1 as ListItem
4import Ubuntu.Components.Popups 0.1
5
6Page {
7 id: locationManagerPage
8 objectName: "LocationManagerPage"
9 title: i18n.tr("Edit locations")
10 visible: false
11
12 property bool locationsChanged: false
13
14 tools: ToolbarItems {
15 locked: true
16 opened: (locationModel.count > 0) ? true : false
17 back: ToolbarButton {
18 objectName: "BackButton"
19 text: i18n.tr("Back")
20 iconSource: Qt.resolvedUrl("../resources/images/back@18.png")
21 onTriggered: {
22 if(locationsChanged) {
23 mainView.refreshData()
24 locationsChanged = false;
25 }
26 // handle dummy "current location" item
27 currentLocationItem.visible = false
28 locationLookupItem.visible = true
29 lookupItemAddButton.visible = true
30
31 if(pageStack.depth === 2) pageStack.pop()
32 }
33 }
34 }
35
36 function loadData() {
37 storage.getLocations(fillLocationsList);
38 }
39
40 function checkLocationExists(location) {
41 for(var x=0;x<locationModel.count;x++) {
42 var loc = locationModel.get(x);
43 if(loc.service === location.service
44 && loc.service_id === location.service_id) {
45 existsNotification.show()
46 return true;
47 }
48 }
49 return false;
50 }
51
52 function fillLocationsList(locations) {
53 locationModel.clear()
54 for(var x=0;x<locations.length;x++) {
55 var dbId = locations[x].db.id,
56 location = locations[x].location;
57 // add db-id to the location object
58 location.dbId = dbId;
59 locationModel.append(locations[x].location);
60 }
61 }
62
63 function addLocation(location) {
64 if(!checkLocationExists(location)) {
65 storage.insertLocation({location:location});
66 loadData()
67 locationsChanged = true;
68 }
69 }
70
71
72 Dialog {
73 id:existsNotification
74 title: i18n.tr("Location already added.")
75 Button {
76 text: i18n.tr("OK")
77 onClicked: PopupUtils.close(existsNotification)
78 }
79 }
80
81 ListModel {
82 id: locationModel
83 }
84
85 Column {
86 anchors.fill: parent
87 Rectangle {
88 color: "#D6D6D6"
89 width:parent.width
90 height: units.gu(0.3)
91 }
92 Rectangle {
93 width: parent.width
94 height: units.gu(5)
95 Label {
96 anchors.verticalCenter: parent.verticalCenter
97 text: " "+i18n.tr("Current location")
98 fontSize: "large"
99 }
100 }
101 Rectangle {
102 color: "#D6D6D6"
103 width:parent.width
104 height: units.gu(0.3)
105 }
106 ListItem.Standard {
107 id: locationLookupItem
108 text: i18n.tr("Lookup location")+" (Dummy)"
109 progression: true
110 onClicked: {
111 print("TODO: lookup current location")
112 visible = false
113 currentLocationItem.visible = true;
114 }
115 }
116 ListItem.Standard {
117 id: currentLocationItem
118 visible: false
119 text: i18n.tr("London")
120 //icon: Qt.resolvedUrl("../resources/images/refresh_icon.png")
121 control: Button {
122 id: lookupItemAddButton
123 anchors.verticalCenter: parent.verticalCenter
124 text: i18n.tr("add")
125 MouseArea {
126 anchors.fill: parent
127 onClicked: {
128 // add dummy location
129 addLocation({"service_id":2643743,"name":"London","country":"GB","service":"openweathermap"})
130 lookupItemAddButton.visible = false;
131 }
132 }
133 }
134 }
135 Rectangle {
136 id:test2
137 width: parent.width
138 height: units.gu(5)
139 Label {
140 anchors.verticalCenter: parent.verticalCenter
141 text: " "+i18n.tr("World")
142 fontSize: "large"
143 }
144 }
145 Rectangle {
146 color: "#D6D6D6"
147 width:parent.width
148 height: units.gu(0.3)
149 }
150 Rectangle {
151 width: parent.width
152 clip:true
153 color:"transparent"
154 height: units.gu(40)
155 ListView {
156 objectName: "LocationList"
157 anchors.fill: parent
158 model: locationModel
159 delegate: ListItem.Standard {
160 text: model.name
161 removable: true
162 onItemRemoved: {
163 var location = locationModel.get(index)
164 locationsChanged = true;
165 storage.clearLocation(location.dbId);
166 locationModel.remove(index)
167 }
168 backgroundIndicator: SwipeDelete {
169 id: swipeDelete
170 state: swipingState
171 }
172 }
173 footer: ListItem.Standard {
174 objectName: "AddCityListItem"
175 text: i18n.tr("Add city")
176 progression: true
177 onClicked: pageStack.push(addLocationPage)
178 }
179 }
180 }
181 }
182}
1830
=== added file 'components/LocationManagerSheet.qml'
--- components/LocationManagerSheet.qml 1970-01-01 00:00:00 +0000
+++ components/LocationManagerSheet.qml 2013-07-20 16:54:27 +0000
@@ -0,0 +1,228 @@
1/*
2 * Copyright (C) 2013 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 version 3 as
6 * published by the Free Software Foundation.
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 * Authored by: Martin Borho <martin@borho.net>
17 Raúl Yeguas <neokore@gmail.com>
18 Riccardo Padovani <ricki.padovani@gmail.com>
19 */
20import QtQuick 2.0
21import Ubuntu.Components 0.1
22import Ubuntu.Components.Popups 0.1
23import Ubuntu.Components.ListItems 0.1 as ListItem
24
25Component {
26 id: locationManagerComponent
27
28 ComposerSheet {
29 id: locationManagerSheet
30 objectName: "LocationManagerSheet"
31 title: i18n.tr("Edit locations")
32 contentsHeight: parent.height
33
34 property int initial_sum: 0
35
36 Component.onCompleted: {
37 locationsRemoved.clear()
38 loadData()
39 }
40
41 function loadData() {
42 storage.getLocations(fillLocationsList);
43 }
44
45 function checkLocationExists(location) {
46 for(var x=0;x<locationModel.count;x++) {
47 var loc = locationModel.get(x);
48 if(loc.service === location.service
49 && loc.service_id === location.service_id) {
50 existsNotification.show()
51 return true;
52 }
53 }
54 return false;
55 }
56
57 function fillLocationsList(locations) {
58 initial_sum = locations.length;
59 locationModel.clear()
60 for(var x=0;x<locations.length;x++) {
61 var dbId = locations[x].db.id,
62 location = locations[x].location;
63 // add db-id to the location object
64 location.dbId = dbId;
65 locationModel.append(locations[x].location);
66 }
67 }
68
69 function addLocation(location) {
70 if(!checkLocationExists(location)) {
71 locationModel.append(location)
72 }
73 }
74
75 container: Column {
76 anchors.fill: parent
77 Rectangle {
78 width: parent.width
79 height: units.gu(5)
80 color: "transparent"
81 Label {
82 anchors.verticalCenter: parent.verticalCenter
83 text: " "+i18n.tr("Current location")
84 fontSize: "large"
85 }
86 }
87 Rectangle {
88 color: "#A9A9A9"
89 width:parent.width
90 height: units.gu(0.1)
91 }
92 ListItem.Standard {
93 id: locationLookupItem
94 text: i18n.tr("Lookup location")+" (Dummy)"
95 progression: true
96 onClicked: {
97 print("TODO: lookup current location")
98 visible = false
99 currentLocationItem.visible = true;
100 }
101 }
102 ListItem.Standard {
103 id: currentLocationItem
104 visible: false
105 text: i18n.tr("London")
106 control: Button {
107 id: lookupItemAddButton
108 anchors.verticalCenter: parent.verticalCenter
109 text: i18n.tr("add")
110 MouseArea {
111 anchors.fill: parent
112 onClicked: {
113 // add dummy location
114 addLocation({"service_id":2643743,"name":"London","country":"GB","service":"openweathermap"})
115 lookupItemAddButton.visible = false;
116 }
117 }
118 }
119 }
120 Rectangle {
121 width: parent.width
122 height: units.gu(5)
123 color: "transparent"
124 Label {
125 anchors.verticalCenter: parent.verticalCenter
126 text: " "+i18n.tr("World")
127 fontSize: "large"
128 }
129 }
130 Rectangle {
131 color: "#A9A9A9"
132 width:parent.width
133 height: units.gu(0.1)
134 }
135 Rectangle {
136 width: parent.width
137 clip:true
138 color:"transparent"
139 height: units.gu(40)
140 ListView {
141 objectName: "LocationList"
142 anchors.fill: parent
143 model: locationModel
144 delegate: ListItem.Standard {
145 text: model.name
146 removable: true
147 onItemRemoved: {
148 var location = locationModel.get(index)
149 // clone location obj
150 location = JSON.parse(JSON.stringify(location));
151 locationsRemoved.append(location)
152 locationModel.remove(index)
153
154 }
155 backgroundIndicator: SwipeDelete {
156 id: swipeDelete
157 state: swipingState
158 }
159 }
160 footer: ListItem.Standard {
161 objectName: "AddCityListItem"
162 text: i18n.tr("Add city")
163 progression: true
164 onClicked: {
165 PopupUtils.open(addLocationSheet)
166 }
167 }
168 }
169 }
170 }
171
172 Dialog {
173 id:existsNotification
174 title: i18n.tr("Location already added.")
175 Button {
176 text: i18n.tr("OK")
177 onClicked: PopupUtils.close(existsNotification)
178 }
179 }
180
181 ListModel {
182 id: locationModel
183 }
184
185 ListModel {
186 id: locationsRemoved
187 }
188
189 onConfirmClicked: {
190 var locationsChanged = false;
191 // remove
192 for(var x=0;x<locationsRemoved.count;x++) {
193 var delLoc = locationsRemoved.get(x);
194 storage.clearLocation(delLoc.dbId);
195 locationsChanged = true;
196 }
197 // add
198 for(var y=0;y<locationModel.count;y++) {
199 var loc = locationModel.get(y);
200 if(loc.dbId === undefined || loc.dbId=== 0) {
201 storage.insertLocation({location:loc});
202 locationsChanged = true;
203 }
204 }
205 // refresh tabs if neccessary
206 if(locationsChanged || locationModel.count === 0) {
207 mainView.refreshData()
208 }
209 // handle dummy "current location" item
210 currentLocationItem.visible = false
211 locationLookupItem.visible = true
212 lookupItemAddButton.visible = true
213 }
214
215 onCancelClicked: {
216 if(initial_sum === 0) {
217 // refresh mainview to land again here,
218 // when no locations were defined when
219 // opening the sheet
220 mainView.refreshData()
221 }
222 }
223
224 AddLocationSheet {
225 id:addLocationSheet
226 }
227 }
228}
0229
=== modified file 'components/LocationTab.qml'
--- components/LocationTab.qml 2013-07-15 18:04:37 +0000
+++ components/LocationTab.qml 2013-07-20 16:54:27 +0000
@@ -75,66 +75,21 @@
75 // Menu for options75 // Menu for options
76 page: Page {76 page: Page {
77 id: locationPage77 id: locationPage
78 tools: ToolbarItems {78 anchors.fill: parent
79 id: pageTools79
80 ToolbarButton {80 ListModel {
81 objectName: "RefreshButton"81 id: dayForecastModel
82 action:Action {
83 id: refreshAction
84 objectName: "RefreshAction"
85 iconSource: Qt.resolvedUrl("../resources/images/refresh_icon.png")
86 text: i18n.tr("Refresh")
87
88 onTriggered: {
89 mainView.refreshData(false, true);
90 }
91 }
92 }
93 ToolbarButton {
94 objectName: "EditButton"
95 action:Action {
96 id: editLocationAction
97 iconSource: Qt.resolvedUrl("../resources/images/add_icon.png")
98 text: i18n.tr("Edit")
99
100 onTriggered: {
101 mainView.showLocationManager()
102 }
103 }
104 }
105 ToolbarButton {
106 objectName: "SettingsButton"
107 action:Action {
108 id: configAction
109 iconSource: Qt.resolvedUrl("../resources/images/settings.png")
110 text: i18n.tr("Settings")
111
112 onTriggered: {
113 mainView.showSettings();
114 }
115 }
116 }
117 }82 }
118 }
119
120 ListModel {
121 id: dayForecastModel
122 }
123
124 Rectangle {
125 id:startPage
126 width: parent.width
127 height: parent.height
12883
129 Flickable {84 Flickable {
130 id: flickable85 id: flickable
131 width: parent.width86 anchors.fill: parent
132 height: parent.height
133 contentHeight: flickContent.height;87 contentHeight: flickContent.height;
134 Rectangle {88 Rectangle {
135 id: flickContent89 id: flickContent
136 width:parent.width90 width:parent.width
137 height: childrenRect.height91 height: childrenRect.height
92 color: "transparent"
138 DateComponent {93 DateComponent {
139 id: todayDateComponent94 id: todayDateComponent
140 //dateRelative: i18n.tr("Today")95 //dateRelative: i18n.tr("Today")
@@ -144,10 +99,6 @@
144 id: currentCondition99 id: currentCondition
145 anchors.top: todayDateComponent.bottom100 anchors.top: todayDateComponent.bottom
146 }101 }
147 /*ChartComponent{
148 id: chartComponent
149 anchors.top: currentCondition.bottom
150 }*/
151 ListView {102 ListView {
152 id: dailyForecastList103 id: dailyForecastList
153 width: parent.width;104 width: parent.width;
@@ -175,6 +126,47 @@
175 }126 }
176 }127 }
177 }128 }
129
130
131 tools: ToolbarItems {
132 id: pageTools
133 ToolbarButton {
134 objectName: "RefreshButton"
135 action:Action {
136 id: refreshAction
137 objectName: "RefreshAction"
138 iconSource: Qt.resolvedUrl("../resources/images/refresh_icon.png")
139 text: i18n.tr("Refresh")
140
141 onTriggered: {
142 mainView.refreshData(false, true);
143 }
144 }
145 }
146 ToolbarButton {
147 objectName: "EditButton"
148 action:Action {
149 id: editLocationAction
150 iconSource: Qt.resolvedUrl("../resources/images/add_icon.png")
151 text: i18n.tr("Edit")
152
153 onTriggered: {
154 mainView.showLocationManager()
155 }
156 }
157 }
158 ToolbarButton {
159 objectName: "SettingsButton"
160 action:Action {
161 id: configAction
162 iconSource: Qt.resolvedUrl("../resources/images/settings.png")
163 text: i18n.tr("Settings")
164
165 onTriggered: {
166 mainView.showSettings();
167 }
168 }
169 }
170 }
178 }171 }
179
180}172}
181173
=== modified file 'components/SettingsSheet.qml'
--- components/SettingsSheet.qml 2013-07-11 18:50:23 +0000
+++ components/SettingsSheet.qml 2013-07-20 16:54:27 +0000
@@ -27,9 +27,9 @@
27 id: sheet27 id: sheet
28 objectName: "SettingsSheet"28 objectName: "SettingsSheet"
29 title: i18n.tr("Settings")29 title: i18n.tr("Settings")
30 contentsHeight: parent.height30 contentsHeight: parent.height
3131
32 container: ListItem.ValueSelector {32 container: ListItem.ValueSelector {
33 id: unitsSelector33 id: unitsSelector
34 objectName: "UnitsSelector"34 objectName: "UnitsSelector"
35 text: i18n.tr("Temperature unit")35 text: i18n.tr("Temperature unit")
3636
=== modified file 'tests/autopilot/ubuntu_weather_app/tests/__init__.py'
--- tests/autopilot/ubuntu_weather_app/tests/__init__.py 2013-07-11 20:03:29 +0000
+++ tests/autopilot/ubuntu_weather_app/tests/__init__.py 2013-07-20 16:54:27 +0000
@@ -76,6 +76,15 @@
76 def main_window(self):76 def main_window(self):
77 return ubuntusdk(self, self.app)#MainWindow(self, self.app)77 return ubuntusdk(self, self.app)#MainWindow(self, self.app)
7878
79class SheetMixin(object):
80 """A mixin to to give access to common sheet elements"""
81 def _click_sheet_confirm(self):
82 """Clicks the confirm button"""
83 self.pointing_device.click_object(self.main_window.app.select_many('Button')[1])
84
85 def _click_sheet_cancel(self):
86 """Clicks the cancel button"""
87 self.pointing_device.click_object(self.main_window.app.select_many('Button')[0])
7988
80class DatabaseMixin(object):89class DatabaseMixin(object):
8190
8291
=== modified file 'tests/autopilot/ubuntu_weather_app/tests/test_locationmanager.py'
--- tests/autopilot/ubuntu_weather_app/tests/test_locationmanager.py 2013-07-16 19:34:12 +0000
+++ tests/autopilot/ubuntu_weather_app/tests/test_locationmanager.py 2013-07-20 16:54:27 +0000
@@ -12,10 +12,10 @@
12from testtools.matchers import Equals12from testtools.matchers import Equals
13from autopilot.matchers import Eventually13from autopilot.matchers import Eventually
1414
15from ubuntu_weather_app.tests import WeatherTestCase, DatabaseMixin15from ubuntu_weather_app.tests import WeatherTestCase, DatabaseMixin, SheetMixin
16from ubuntu_weather_app.tests.weatherdata import locations_data16from ubuntu_weather_app.tests.weatherdata import locations_data
1717
18class TestLocationManager(WeatherTestCase, DatabaseMixin):18class TestLocationManager(WeatherTestCase, DatabaseMixin, SheetMixin):
19 def setUp(self):19 def setUp(self):
20 self.clean_db()20 self.clean_db()
21 super(TestLocationManager, self).setUp()21 super(TestLocationManager, self).setUp()
@@ -30,7 +30,7 @@
30 self.pointing_device.move_to_object(addCityItem)30 self.pointing_device.move_to_object(addCityItem)
31 self.pointing_device.click()31 self.pointing_device.click()
3232
33 addLocPage = self.main_window.get_object("AddLocationPage", "AddLocationPage")33 addLocPage = self.main_window.get_object("DefaultSheet", "AddLocationSheet")
34 self.assertThat(addLocPage.visible, Eventually(Equals(True)))34 self.assertThat(addLocPage.visible, Eventually(Equals(True)))
3535
36 def test_add_location(self):36 def test_add_location(self):
@@ -56,10 +56,17 @@
56 locationList = self.main_window.get_object('QQuickListView', 'LocationList')56 locationList = self.main_window.get_object('QQuickListView', 'LocationList')
57 addedItem = locationList.get_children()[0]57 addedItem = locationList.get_children()[0]
58 self.assertThat(addedItem.get_children()[1].text, Eventually(Equals("London")))58 self.assertThat(addedItem.get_children()[1].text, Eventually(Equals("London")))
5959 self._click_sheet_confirm()
60 # a location is defined, so toolbar should be visible60
61 toolbar = self.main_window.get_toolbar()61 # back to locations, wait till data is loaded
62 self.assertThat(toolbar.opened, Eventually(Equals(True)))62 load_indicator = self.main_window.get_object('ActivityIndicator', 'LoadingSpinner')
63 self.assertThat(load_indicator.running, Eventually(Equals(False)))
64
65 # only location is there
66 loadingPage = self.main_window.get_object("Tabs", "rootTabs")
67 self.assertThat(loadingPage.visible, Eventually(Equals(True)))
68 tabObjects = self.main_window.get_objects('LocationTab','LocationTab')
69 self.assertEqual(1, len(tabObjects))
6370
64 def test_toolbar_hidden_when_no_location_is_defined(self):71 def test_toolbar_hidden_when_no_location_is_defined(self):
65 """Toolbar is not visible when there's no location is defined"""72 """Toolbar is not visible when there's no location is defined"""
@@ -99,7 +106,7 @@
99 def test_cancel_adding_location(self):106 def test_cancel_adding_location(self):
100 """Cancel the cities search"""107 """Cancel the cities search"""
101 self._open_add_location_page()108 self._open_add_location_page()
102 self.main_window.click_toolbar_button("BackButton")109 self._click_sheet_cancel()
103 locationList = self.main_window.get_object('QQuickListView', 'LocationList')110 locationList = self.main_window.get_object('QQuickListView', 'LocationList')
104 self.assertThat(locationList.visible, Eventually(Equals(True)))111 self.assertThat(locationList.visible, Eventually(Equals(True)))
105112
@@ -120,12 +127,13 @@
120 self.assertThat(errorLabel.visible, Eventually(Equals(True), timeout=30))127 self.assertThat(errorLabel.visible, Eventually(Equals(True), timeout=30))
121128
122129
123class TestLocationManagerWithLocation(WeatherTestCase, DatabaseMixin):130class TestLocationManagerWithLocation(WeatherTestCase, DatabaseMixin, SheetMixin):
124 def setUp(self):131 def setUp(self):
125 self.clean_db()132 self.clean_db()
126 self.launch_and_quit_app()133 self.launch_and_quit_app()
127 # add one location to storage134 # add one location to storage
128 self.save_locations_to_storage([locations_data[0]])135 #self.save_locations_to_storage([locations_data[0]])
136 self.save_locations_to_storage(locations_data)
129 super(TestLocationManagerWithLocation, self).setUp()137 super(TestLocationManagerWithLocation, self).setUp()
130 self.assertThat(138 self.assertThat(
131 self.main_window.get_qml_view().visible, Eventually(Equals(True)))139 self.main_window.get_qml_view().visible, Eventually(Equals(True)))
@@ -136,16 +144,8 @@
136 self.assertThat(self.main_window.get_toolbar().opened, Eventually(Equals(True)))144 self.assertThat(self.main_window.get_toolbar().opened, Eventually(Equals(True)))
137 self.main_window.click_toolbar_button("EditButton")145 self.main_window.click_toolbar_button("EditButton")
138146
139 def test_remove_location(self):147 def _swipe_location_to_remove(self):
140 """Removes location"""148 """Swipe right to delete the first location"""
141 # check loading screen diappears
142 loadingPage = self.main_window.get_object("Page", "MainPage")
143 self.assertThat(loadingPage.visible, Eventually(Equals(True)))
144
145 # go to the location manegr
146 self._open_location_manager()
147
148 # swipe right to delete the first location
149 locationList = self.main_window.get_object('QQuickListView', 'LocationList')149 locationList = self.main_window.get_object('QQuickListView', 'LocationList')
150 locItem = locationList.get_children()[0]150 locItem = locationList.get_children()[0]
151 loc_x, loc_y, loc_w, loc_h = locItem.globalRect151 loc_x, loc_y, loc_w, loc_h = locItem.globalRect
@@ -154,8 +154,47 @@
154 self.pointing_device.move(loc_x + loc_w - 10, loc_y + loc_h / 2)154 self.pointing_device.move(loc_x + loc_w - 10, loc_y + loc_h / 2)
155 self.pointing_device.release()155 self.pointing_device.release()
156156
157 # toolbar disappears since no locations defined157 def test_remove_location(self):
158 self.assertThat(self.main_window.get_toolbar().opened, Eventually(Equals(False)))158 """Removes location"""
159 # wait till data is loaded
160 loadingPage = self.main_window.get_object("Tabs", "rootTabs")
161 self.assertThat(loadingPage.visible, Eventually(Equals(True)))
162
163 # count locations at start
164 tabObjects = self.main_window.get_objects('LocationTab','LocationTab')
165 tabsSumStart = len(tabObjects)
166
167 # go to the location manager and remove location
168 self._open_location_manager()
169 self._swipe_location_to_remove()
170 self._click_sheet_confirm()
171
172 # back to locations, only one is left
173 load_indicator = self.main_window.get_object('ActivityIndicator', 'LoadingSpinner')
174 self.assertThat(load_indicator.running, Eventually(Equals(False)))
175 tabObjects = self.main_window.get_objects('LocationTab','LocationTab')
176 self.assertEqual(tabsSumStart-1, len(tabObjects))
177
178 def test_cancel_remove_location(self):
179 """Cancels removing of location"""
180 # wait data is loaded
181 loadingPage = self.main_window.get_object("Tabs", "rootTabs")
182 self.assertThat(loadingPage.visible, Eventually(Equals(True)))
183
184 # count locations at start
185 tabObjects = self.main_window.get_objects('LocationTab','LocationTab')
186 tabsSumStart = len(tabObjects)
187
188 # go to the location manager, remove location, but click cancel
189 self._open_location_manager()
190 self._swipe_location_to_remove()
191 self._click_sheet_cancel()
192
193 # back to locations, no loction is removed
194 loadingPage = self.main_window.get_object("Tabs", "rootTabs")
195 self.assertThat(loadingPage.visible, Eventually(Equals(True)))
196 tabObjects = self.main_window.get_objects('LocationTab','LocationTab')
197 self.assertEqual(tabsSumStart, len(tabObjects))
159198
160 def test_toolbar_opened_when_location_is_defined(self):199 def test_toolbar_opened_when_location_is_defined(self):
161 """Toolbar is visible since there's a location defined"""200 """Toolbar is visible since there's a location defined"""
162201
=== modified file 'tests/autopilot/ubuntu_weather_app/tests/test_settings.py'
--- tests/autopilot/ubuntu_weather_app/tests/test_settings.py 2013-07-16 19:43:28 +0000
+++ tests/autopilot/ubuntu_weather_app/tests/test_settings.py 2013-07-20 16:54:27 +0000
@@ -12,10 +12,10 @@
12from testtools.matchers import Equals12from testtools.matchers import Equals
13from autopilot.matchers import Eventually13from autopilot.matchers import Eventually
1414
15from ubuntu_weather_app.tests import WeatherTestCase, DatabaseMixin15from ubuntu_weather_app.tests import WeatherTestCase, DatabaseMixin, SheetMixin
16from ubuntu_weather_app.tests.weatherdata import locations_data16from ubuntu_weather_app.tests.weatherdata import locations_data
1717
18class TestSettings(WeatherTestCase, DatabaseMixin):18class TestSettings(WeatherTestCase, DatabaseMixin, SheetMixin):
19 def setUp(self):19 def setUp(self):
20 self.clean_db()20 self.clean_db()
21 self.launch_and_quit_app()21 self.launch_and_quit_app()
@@ -35,14 +35,6 @@
35 sheet = self.main_window.get_object('ComposerSheet', 'SettingsSheet')35 sheet = self.main_window.get_object('ComposerSheet', 'SettingsSheet')
36 self.pointing_device.move_to_object(sheet)36 self.pointing_device.move_to_object(sheet)
3737
38 def _click_confirm(self):
39 """Clicks the confirm button"""
40 self.pointing_device.click_object(self.main_window.app.select_many('Button')[1])
41
42 def _click_cancel(self):
43 """Clicks the cancel button"""
44 self.pointing_device.click_object(self.main_window.app.select_many('Button')[0])
45
46 def _check_units(self, units):38 def _check_units(self, units):
47 """Checks selected units by values from the first location tab"""39 """Checks selected units by values from the first location tab"""
48 current_temps = self.main_window.get_objects('QQuickText', 'CurrentTempText')40 current_temps = self.main_window.get_objects('QQuickText', 'CurrentTempText')
@@ -69,7 +61,7 @@
69 imperial_option = units_selector.get_children()[3]61 imperial_option = units_selector.get_children()[3]
70 self.pointing_device.click_object(imperial_option)62 self.pointing_device.click_object(imperial_option)
71 self.assertThat(units_selector.selectedIndex, Eventually(Equals(1)))63 self.assertThat(units_selector.selectedIndex, Eventually(Equals(1)))
72 self._click_confirm()64 self._click_sheet_confirm()
7365
74 # wait for reload and check the imperial values66 # wait for reload and check the imperial values
75 load_indicator = self.main_window.get_object('ActivityIndicator', 'LoadingSpinner')67 load_indicator = self.main_window.get_object('ActivityIndicator', 'LoadingSpinner')
@@ -90,7 +82,7 @@
9082
91 # confirm83 # confirm
92 self.assertThat(units_selector.selectedIndex, Eventually(Equals(0)))84 self.assertThat(units_selector.selectedIndex, Eventually(Equals(0)))
93 self._click_confirm()85 self._click_sheet_confirm()
9486
95 # wait for reload and check the metric values again87 # wait for reload and check the metric values again
96 load_indicator = self.main_window.get_object('ActivityIndicator', 'LoadingSpinner')88 load_indicator = self.main_window.get_object('ActivityIndicator', 'LoadingSpinner')
@@ -114,6 +106,6 @@
114 self.assertThat(units_selector.selectedIndex, Eventually(Equals(1)))106 self.assertThat(units_selector.selectedIndex, Eventually(Equals(1)))
115107
116 # cancel and check108 # cancel and check
117 self._click_cancel()109 self._click_sheet_cancel()
118 self._check_units('metric')110 self._check_units('metric')
119111
120112
=== modified file 'ubuntu-weather-app.qml'
--- ubuntu-weather-app.qml 2013-07-16 19:34:12 +0000
+++ ubuntu-weather-app.qml 2013-07-20 16:54:27 +0000
@@ -31,6 +31,10 @@
31 width: units.gu(50)31 width: units.gu(50)
32 height: units.gu(75)32 height: units.gu(75)
3333
34 headerColor: "#57365E"
35 backgroundColor: "#A55263"
36 footerColor: "#D75669"
37
34 property var locationsList: []38 property var locationsList: []
35 property var tabsObject: null39 property var tabsObject: null
36 // set default values for settings here40 // set default values for settings here
@@ -61,13 +65,14 @@
61 var locLength = locations.length,65 var locLength = locations.length,
62 locBeforeLen = locationsList.length,66 locBeforeLen = locationsList.length,
63 focusToLast = (locBeforeLen > 0 && locLength > locBeforeLen) ? true : false;67 focusToLast = (locBeforeLen > 0 && locLength > locBeforeLen) ? true : false;
68 loading.running = false;
64 // show locationmanager when no location is added69 // show locationmanager when no location is added
65 if(locLength === 0) {70 if(locLength === 0) {
66 showLocationManager()71 showLocationManager()
67 return;72 return;
68 }73 }
6974
70 locationsList = locations;75 locationsList = locations;
71 if(tabsObject !== null) {76 if(tabsObject !== null) {
72 tabsObject.destroy()77 tabsObject.destroy()
73 }78 }
@@ -79,19 +84,13 @@
79 };84 };
80 }85 }
81 tabsString += "}"; // END Tabs componen86 tabsString += "}"; // END Tabs componen
82 if(bigLoading !== null) {
83 bigLoading.destroy();
84 }
85 loading.running = false;
86 tabsObject = Qt.createQmlObject(tabsString, tabPage, "tabs")87 tabsObject = Qt.createQmlObject(tabsString, tabPage, "tabs")
87 if(focusToLast)88 if(focusToLast)
88 tabsObject.selectedTabIndex = locLength -1;89 tabsObject.selectedTabIndex = locLength -1;
89 }90 }
9091
91 function refreshData(from_storage, force_refresh) {92 function refreshData(from_storage, force_refresh) {
92 if(bigLoading === null) {93 loading.running = true;
93 loading.running = true;
94 }
95 //94 //
96 if(from_storage === true && force_refresh !== true) {95 if(from_storage === true && force_refresh !== true) {
97 storage.getLocations(buildTabs);96 storage.getLocations(buildTabs);
@@ -106,8 +105,7 @@
106 }105 }
107106
108 function showLocationManager() {107 function showLocationManager() {
109 pageStack.push(locationManager)108 PopupUtils.open(locationManager)
110 locationManager.loadData()
111 }109 }
112110
113 function showSettings() {111 function showSettings() {
@@ -131,43 +129,24 @@
131 129
132 Components.Storage{130 Components.Storage{
133 id: storage131 id: storage
134 }132 }
135133
136 Components.LocationManagerPage {134 Components.LocationManagerSheet {
137 id:locationManager135 id:locationManager
138 }136 }
139137
140 Components.AddLocationPage {138 ActivityIndicator{
141 id:addLocationPage139 id:loading
142 }140 objectName: "LoadingSpinner"
143141 running: false
144 PageStack {142 z: 1
145 id: pageStack143 anchors.centerIn: parent
146 Component.onCompleted: push(tabPage)144 }
147145
148 Page {146 Item {
149 id: tabPage147 id:tabPage
150 objectName: "MainPage"148 width: parent.width
151 visible: false149 height:parent.height
152 ActivityIndicator{150 }
153 id:loading151
154 objectName: "LoadingSpinner"
155 running: false
156 z: 1
157 anchors{
158 top: parent.top
159 topMargin: units.gu(0.5)
160 right: parent.right
161 rightMargin: units.gu(1)
162 }
163 }
164 ActivityIndicator{
165 id:bigLoading
166 objectName: "LoadingSpinner"
167 running: true
168 z: 1
169 anchors.centerIn: parent
170 }
171 }
172 }
173}152}

Subscribers

People subscribed via source and target branches