Unity 8
50-timezone.qml
1 /*
2  * Copyright (C) 2015-2016 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 
17 import QtQuick 2.4
18 import QtQuick.Layouts 1.1
19 import Ubuntu.Components 1.3
20 import Wizard 0.1
21 import Ubuntu.SystemSettings.TimeDate 1.0
22 import Utils 0.1 as Utils
23 import ".." as LocalComponents
24 
25 LocalComponents.Page {
26  id: tzPage
27  objectName: "tzPage"
28 
29  title: i18n.tr("Time Zone")
30  forwardButtonSourceComponent: forwardButton
31 
32  property string selectedTimeZone: ""
33  readonly property bool showingMap: wideMode && width >= units.gu(110)
34 
35  // for testing
36  readonly property alias tdModule: timeDatePanel
37 
38  function highlightTimezone(offset) {
39  highlightImage.source = "data/timezonemap/timezone_" + offset + ".png";
40  }
41 
42  // geo coords conversion functions (adapted from libtimezonemap)
43  function radians(degrees) {
44  return degrees * Math.PI / 180;
45  }
46 
47  function longitudeToX(longitude, map_width) {
48  const xdeg_offset = -6;
49  const x = (map_width * (180.0 + longitude) / 360.0) + (map_width * xdeg_offset / 180.0);
50  return x;
51  }
52 
53  function latitudeToY(latitude, map_height) {
54  const bottom_lat = -59;
55  const top_lat = 81;
56  const top_per = top_lat / 180.0;
57 
58  var y = 1.25 * Math.log(Math.tan(0.25*Math.PI + 0.4 * radians(latitude)));
59  const full_range = 4.6068250867599998;
60  const top_offset = full_range * top_per;
61  const map_range = Math.abs(1.25 * Math.log(Math.tan(0.25*Math.PI + 0.4 * radians(bottom_lat))) - top_offset);
62  y = Math.abs(y - top_offset);
63  y = y / map_range;
64  y = y * map_height;
65  return y;
66  }
67 
68  function resetViews() {
69  selectedTimeZone = ""
70  tzList.currentIndex = -1
71  highlightImage.source = ""
72  pinImage.x = 0;
73  pinImage.y = 0;
74  }
75 
76  UbuntuTimeDatePanel {
77  id: timeDatePanel
78  }
79 
80  onContentAnimationRunningChanged: {
81  if (!contentAnimationRunning) {
82  if (tzList.count == 1) { // preselect the first (and only) TZ
83  var tz = tzList.itemAt(0,0);
84  if (!!tz) {
85  tz.clicked();
86  }
87  }
88 
89  resetViews();
90  searchField.forceActiveFocus();
91  }
92  }
93 
94  Component {
95  id: tzComponent
96  ListItem {
97  id: tz
98  objectName: "tz" + index
99  highlightColor: backgroundColor
100  divider.colorFrom: dividerColor
101  divider.colorTo: backgroundColor
102  readonly property bool currentTz: ListView.view.currentIndex === index
103 
104  Column {
105  anchors.verticalCenter: parent.verticalCenter
106  anchors.left: parent.left
107  anchors.leftMargin: !wideMode ? staticMargin : 0
108  anchors.right: image.left
109  anchors.rightMargin: units.gu(2)
110 
111  Label {
112  id: cityLabel
113  text: displayName
114  font.weight: tz.currentTz ? Font.Normal : Font.Light
115  fontSize: "medium"
116  color: textColor
117  elide: Text.ElideMiddle
118  maximumLineCount: 1
119  width: parent.width
120  }
121  Label {
122  id: timeLabel
123  text: Utils.TimezoneFormatter.currentTimeInTimezoneWithAbbrev(timeZone)
124  font.weight: tz.currentTz ? Font.Normal : Font.Light
125  fontSize: "small"
126  color: textColor
127  }
128  }
129  Image {
130  id: image
131  anchors {
132  right: parent.right
133  verticalCenter: parent.verticalCenter
134  rightMargin: !wideMode ? staticMargin : 0
135  }
136  fillMode: Image.PreserveAspectFit
137  height: units.gu(1.5)
138 
139  source: "data/Tick@30.png"
140  visible: tz.currentTz
141  }
142 
143  onClicked: {
144  highlightTimezone(offset);
145  ListView.view.currentIndex = index;
146  selectedTimeZone = timeZone;
147  //print("Clicked at city with coords:", longitude, latitude);
148  //print("Clicked on TZ:", timeZone);
149  //print("Highlight at (x,y):", longitudeToX(longitude, map.width), latitudeToY(latitude, map.height));
150  pinImage.x = Qt.binding(function() { return longitudeToX(longitude, map.width) - pinImage.width; });
151  pinImage.y = Qt.binding(function() { return latitudeToY(latitude, map.height) - pinImage.height; });
152  }
153  }
154  }
155 
156 
157  ColumnLayout {
158  id: leftColumn
159  anchors {
160  left: content.left
161  top: content.top
162  bottom: content.bottom
163  right: !showingMap ? content.right : undefined
164  leftMargin: showingMap ? staticMargin : (wideMode ? tzPage.leftMargin : 0)
165  rightMargin: showingMap ? staticMargin : (wideMode ? tzPage.rightMargin : 0)
166  topMargin: customMargin
167  }
168 
169  width: Math.min(parent.width, units.gu(34))
170 
171  LocalComponents.WizardTextField {
172  id: searchField
173  objectName: "tzFilter"
174  anchors.left: parent.left
175  anchors.right: parent.right
176  anchors.leftMargin: !showingMap && !wideMode ? staticMargin : undefined
177  anchors.rightMargin: !showingMap && !wideMode ? staticMargin : undefined
178  placeholderText: i18n.tr("Enter your city")
179  inputMethodHints: Qt.ImhNoPredictiveText
180  onTextChanged: resetViews();
181  }
182 
183  ListView {
184  Layout.fillHeight: true
185  id: tzList
186  objectName: "tzList"
187  clip: true
188  anchors.left: parent.left
189  anchors.right: parent.right
190  currentIndex: -1
191  model: TimeZoneModel {
192  id: timeZoneModel
193  filter: searchField.text
194  country: i18n.language.split('_')[1].split('.')[0]
195  }
196  delegate: tzComponent
197  }
198 
199  ActivityIndicator {
200  anchors.centerIn: tzList
201  running: tzList.count == 0 &&
202  searchField.length > 0 &&
203  timeZoneModel.listUpdating
204  visible: running
205  }
206  }
207 
208  Item {
209  id: mapContainer
210  visible: showingMap && !contentAnimationRunning
211  enabled: visible
212 
213  anchors {
214  left: leftColumn.right
215  leftMargin: units.gu(4)
216  right: content.right
217  rightMargin: staticMargin
218  top: content.top
219  topMargin: customMargin
220  bottom: parent.bottom
221  bottomMargin: buttonBarHeight
222  }
223 
224  Item {
225  id: map
226  width: Math.min(parent.width, height * 1.95) // keep our aspect ratio
227  height: parent.height
228  anchors {
229  centerIn: parent
230  }
231 
232  Image {
233  id: backgroundImage
234  source: "data/timezonemap/map.png"
235  sourceSize: Qt.size(map.width, map.height)
236  fillMode: Image.PreserveAspectFit
237  smooth: false
238  visible: mapContainer.visible
239  asynchronous: true
240  anchors.fill: parent
241  }
242 
243  Image {
244  id: highlightImage
245  sourceSize: Qt.size(map.width, map.height)
246  fillMode: Image.PreserveAspectFit
247  smooth: false
248  visible: selectedTimeZone != ""
249  asynchronous: true
250  anchors.fill: backgroundImage
251  }
252 
253  Image {
254  id: pinImage
255  source: "data/timezonemap/pin.png"
256  visible: x != 0 && y != 0
257  width: units.dp(12)
258  height: units.dp(20)
259  z: map.z + 1
260  }
261  }
262  }
263 
264  Component {
265  id: forwardButton
266  LocalComponents.StackButton {
267  text: i18n.tr("Next")
268  enabled: selectedTimeZone != ""
269  onClicked: {
270  timeDatePanel.timeZone = selectedTimeZone;
271  pageStack.next();
272  }
273  }
274  }
275 }