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