Merge lp:~nik90/ubuntu-clock-app/13-next-alarm-bottomedge into lp:ubuntu-clock-app

Proposed by Nekhelesh Ramananthan
Status: Superseded
Proposed branch: lp:~nik90/ubuntu-clock-app/13-next-alarm-bottomedge
Merge into: lp:ubuntu-clock-app
Diff against target: 1034 lines (+355/-290)
14 files modified
app/alarm/AlarmDelegate.qml (+7/-31)
app/alarm/AlarmList.qml (+2/-5)
app/alarm/AlarmPage.qml (+4/-1)
app/alarm/AlarmUtils.qml (+98/-15)
app/alarm/EditAlarmPage.qml (+1/-2)
app/clock/ClockPage.qml (+20/-4)
app/clock/MainClock.qml (+0/-39)
app/components/Clock.qml (+1/-1)
app/components/Utils.js (+0/-25)
app/ubuntu-clock-app.qml (+36/-11)
app/upstreamcomponents/PageWithBottomEdge.qml (+167/-154)
app/worldclock/UserWorldCityList.qml (+0/-2)
tests/autopilot/ubuntu_clock_app/emulators.py (+2/-0)
tests/unit/tst_alarm.qml (+17/-0)
To merge this branch: bzr merge lp:~nik90/ubuntu-clock-app/13-next-alarm-bottomedge
Reviewer Review Type Date Requested Status
Ubuntu Clock Developers Pending
Review via email: mp+237187@code.launchpad.net

This proposal has been superseded by a proposal from 2014-10-05.

Commit message

Dynamically sets the bottom edge title with the time to the next active alarm.

Description of the change

Dynamically sets the bottom edge title with the time to the next active alarm.

To post a comment you must log in.
110. By Nekhelesh Ramananthan

Fixed failing qml tests

111. By Nekhelesh Ramananthan

merged pre-requisite branch changes

112. By Nekhelesh Ramananthan

Added unit tests for bottom edge title

113. By Nekhelesh Ramananthan

Split qml unit test into smaller unit test for better debugging when a test fails

114. By Nekhelesh Ramananthan

Merged pre-requisite branch changes

115. By Nekhelesh Ramananthan

merged prerequisite branch

116. By Nekhelesh Ramananthan

updated changelog

117. By Nekhelesh Ramananthan

Updated if loop according to riccardo's suggestion

118. By Nekhelesh Ramananthan

merged prerequisite branch

119. By Nekhelesh Ramananthan

Fixed AP test

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'app/alarm/AlarmDelegate.qml'
2--- app/alarm/AlarmDelegate.qml 2014-09-03 22:51:48 +0000
3+++ app/alarm/AlarmDelegate.qml 2014-10-05 15:35:22 +0000
4@@ -23,6 +23,8 @@
5 ListItemWithActions {
6 id: root
7
8+ property var localTime
9+
10 width: parent ? parent.width : 0
11 height: units.gu(6)
12 color: "Transparent"
13@@ -39,6 +41,7 @@
14
15 fontSize: "medium"
16 text: Qt.formatTime(date)
17+ opacity: model.enabled ? 1.0 : 0.8
18 }
19
20 Column {
21@@ -70,9 +73,9 @@
22
23 fontSize: "xx-small"
24 width: parent.width
25- visible: type === Alarm.Repeating || _internalTimerLoader.sourceComponent != undefined
26 wrapMode: Text.WrapAtWordBoundaryOrAnywhere
27- text: alarmUtils.format_day_string(daysOfWeek, type)
28+ text: type === Alarm.Repeating ? alarmUtils.format_day_string(daysOfWeek, type)
29+ : alarmUtils.get_time_to_next_alarm(model.date - localTime)
30 }
31 }
32
33@@ -126,35 +129,8 @@
34 if (model.status === Alarm.Ready) {
35 alarmStatus.checked = model.enabled;
36
37- if(alarmStatus.checked) {
38- var timeObject = alarmUtils.get_time_to_next_alarm(model.date - new Date())
39- var alarmETA
40-
41- // TRANSLATORS: the first argument is the number of days,
42- // followed by hour and minute (eg. in 1d 20h 3m)
43- if(timeObject.days) {
44- alarmETA = i18n.tr("in %1d %1h %2m")
45- .arg(timeObject.days)
46- .arg(timeObject.hours)
47- .arg(timeObject.minutes)
48- }
49-
50- // TRANSLATORS: the first argument is the number of
51- // hours followed by the minutes (eg. in 4h 3m)
52- else if (timeObject.hours) {
53- alarmETA = i18n.tr("in %1h %2m")
54- .arg(timeObject.hours)
55- .arg(timeObject.minutes)
56- }
57-
58- // TRANSLATORS: the argument is the number of
59- // minutes to the alarm (eg. in 3m)
60- else {
61- alarmETA = i18n.tr("in %1m")
62- .arg(timeObject.minutes)
63- }
64-
65- alarmSubtitle.text = alarmETA
66+ if(alarmStatus.checked && type === Alarm.Repeating) {
67+ alarmSubtitle.text = alarmUtils.get_time_to_next_alarm(model.date - localTime)
68 _internalTimerLoader.sourceComponent = _internalTimerComponent
69 }
70 }
71
72=== modified file 'app/alarm/AlarmList.qml'
73--- app/alarm/AlarmList.qml 2014-09-25 11:26:43 +0000
74+++ app/alarm/AlarmList.qml 2014-10-05 15:35:22 +0000
75@@ -21,13 +21,13 @@
76 import Ubuntu.Components.ListItems 1.0 as ListItem
77 import "../components"
78 import "../upstreamcomponents"
79-import "../components/Utils.js" as Utils
80
81 MultipleSelectionListView {
82 id: alarmListView
83 objectName: "alarmListView"
84
85 property var _currentSwipedItem: null
86+ property var localTime
87
88 function _updateSwipeState(item)
89 {
90@@ -51,15 +51,12 @@
91 clip: true
92 anchors.fill: parent
93
94- AlarmUtils {
95- id: alarmUtils
96- }
97-
98 listDelegate: AlarmDelegate {
99 id: alarmDelegate
100 objectName: "alarm" + index
101
102 property var removalAnimation
103+ localTime: alarmListView.localTime
104
105 function remove() {
106 removalAnimation.start()
107
108=== modified file 'app/alarm/AlarmPage.qml'
109--- app/alarm/AlarmPage.qml 2014-09-26 12:24:11 +0000
110+++ app/alarm/AlarmPage.qml 2014-10-05 15:35:22 +0000
111@@ -23,10 +23,12 @@
112 Page {
113 id: alarmPage
114
115+ property var alarmModel
116+
117 title: i18n.tr("Alarms")
118 objectName: 'AlarmPage'
119
120- Component.onCompleted: console.log("Alarm Page loaded")
121+ Component.onCompleted: console.log("[LOG]: Alarm Page loaded")
122
123 flickable: null
124
125@@ -127,6 +129,7 @@
126 id: alarmListView
127 listModel: alarmModel
128 anchors.fill: parent
129+ localTime: clockTime
130 }
131
132 EmptyState {
133
134=== modified file 'app/alarm/AlarmUtils.qml'
135--- app/alarm/AlarmUtils.qml 2014-09-03 22:36:07 +0000
136+++ app/alarm/AlarmUtils.qml 2014-10-05 15:35:22 +0000
137@@ -50,21 +50,65 @@
138 }
139 }
140
141- function get_time_to_next_alarm ( datetime_offset ) {
142- // increase by a minute, so we could make a nicer time
143- // to the next alarm, otherwise a minute always missing
144- // which makes it look odd
145- datetime_offset += 60000;
146-
147- var days_in_offset = Math.floor( datetime_offset / ( 3600000 * 24 ) );
148- var hours_in_offset = Math.floor( datetime_offset / 3600000 % 24 );
149- var minutes_in_offset = Math.floor( datetime_offset / 60000 % 60 );
150-
151- return {
152- days : days_in_offset,
153- hours : hours_in_offset,
154- minutes : minutes_in_offset
155- }
156+ // Function to set the bottom edge title with "Next Active in..."
157+ function set_bottom_edge_title(alarmModel, clockTime) {
158+ var bottom_edge_title = i18n.tr("No active alarms")
159+
160+ /*
161+ Check if alarm model received is valid and has saved alarms and only
162+ then proceed to find the next active alarm.
163+ */
164+ if (alarmModel && alarmModel.count) {
165+ var activeAlarmDate = _get_next_active_alarm(alarmModel, clockTime)
166+
167+ // Return immediately if there are no active alarms found
168+ if (!activeAlarmDate) {
169+ return bottom_edge_title
170+ }
171+ }
172+
173+ else {
174+ return bottom_edge_title
175+ }
176+
177+ bottom_edge_title = i18n.tr("Next Alarm %1").arg(get_time_to_next_alarm(activeAlarmDate - clockTime))
178+ return bottom_edge_title
179+ }
180+
181+ // Function to format the time to next alarm into a string
182+ function get_time_to_next_alarm(totalTime) {
183+ if(totalTime < 0) {
184+ return i18n.tr("Alarm Passed")
185+ }
186+
187+ var timeObject = _split_time(totalTime);
188+ var alarmETA
189+
190+ // TRANSLATORS: the first argument is the number of days,
191+ // followed by hour and minute (eg. in 1d 20h 3m)
192+ if(timeObject.days) {
193+ alarmETA = i18n.tr("in %1d %2h %3m")
194+ .arg(timeObject.days)
195+ .arg(timeObject.hours)
196+ .arg(timeObject.minutes)
197+ }
198+
199+ // TRANSLATORS: the first argument is the number of
200+ // hours followed by the minutes (eg. in 4h 3m)
201+ else if (timeObject.hours) {
202+ alarmETA = i18n.tr("in %1h %2m")
203+ .arg(timeObject.hours)
204+ .arg(timeObject.minutes)
205+ }
206+
207+ // TRANSLATORS: the argument is the number of
208+ // minutes to the alarm (eg. in 3m)
209+ else {
210+ alarmETA = i18n.tr("in %1m")
211+ .arg(timeObject.minutes)
212+ }
213+
214+ return alarmETA;
215 }
216
217 // Function return the alarm dayOfWeek according to the day provided
218@@ -84,6 +128,45 @@
219 INTERNAL FUNCTIONS
220 */
221
222+ /*
223+ Function to get the next active alarm. This function ignores alarms in the
224+ past and also iteratively looks through every alarm since the alarm model
225+ does not always list the active alarms in chronological order.
226+ */
227+ function _get_next_active_alarm(alarmModel, clockTime) {
228+ var activeAlarmDate = undefined
229+
230+ for (var i=0; i<alarmModel.count; i++) {
231+ var currentAlarm = alarmModel.get(i)
232+ if (currentAlarm.enabled && currentAlarm.date > clockTime) {
233+ if (activeAlarmDate === undefined ||
234+ currentAlarm.date < activeAlarmDate) {
235+ activeAlarmDate = currentAlarm.date
236+ }
237+ }
238+ }
239+
240+ return activeAlarmDate
241+ }
242+
243+ // Function to split time (in ms) into days, hours and minutes
244+ function _split_time(totalTime) {
245+ // increase by a minute, so we could make a nicer time
246+ // to the next alarm, otherwise a minute always missing
247+ // which makes it look odd
248+ totalTime += 60000;
249+
250+ var days_in_offset = Math.floor(totalTime / ( 3600000 * 24));
251+ var hours_in_offset = Math.floor(totalTime / 3600000 % 24);
252+ var minutes_in_offset = Math.floor(totalTime / 60000 % 60);
253+
254+ return {
255+ days : days_in_offset,
256+ hours : hours_in_offset,
257+ minutes : minutes_in_offset
258+ }
259+ }
260+
261 // Function to determine the locale's weekdays value
262 function _get_weekdays() {
263 var weekDays = 0
264
265=== modified file 'app/alarm/EditAlarmPage.qml'
266--- app/alarm/EditAlarmPage.qml 2014-09-25 11:26:43 +0000
267+++ app/alarm/EditAlarmPage.qml 2014-10-05 15:35:22 +0000
268@@ -23,7 +23,6 @@
269 import Ubuntu.Components.Pickers 1.0
270 import Ubuntu.Components.ListItems 1.0 as ListItem
271 import "../components"
272-import "../components/Utils.js" as Utils
273
274 Page {
275 id: _addAlarmPage
276@@ -177,7 +176,7 @@
277
278 onErrorChanged: {
279 if (error !== Alarm.NoError) {
280- Utils.log(debugMode, "Error saving alarm, code: " + error)
281+ console.log("[LOG]: Error saving alarm, code: " + error)
282 }
283 }
284
285
286=== modified file 'app/clock/ClockPage.qml'
287--- app/clock/ClockPage.qml 2014-09-26 11:36:12 +0000
288+++ app/clock/ClockPage.qml 2014-10-05 15:35:22 +0000
289@@ -17,12 +17,11 @@
290 */
291
292 import QtQuick 2.3
293-import U1db 1.0 as U1db
294 import Ubuntu.Components 1.1
295+import "../alarm"
296 import "../components"
297 import "../upstreamcomponents"
298 import "../worldclock"
299-import "../components/Utils.js" as Utils
300
301 PageWithBottomEdge {
302 id: _clockPage
303@@ -31,15 +30,32 @@
304 // Property to keep track of the clock mode
305 property alias isDigital: clock.isDigital
306
307+ // Property to keep track of the clock time
308+ property alias clockTime: clock.analogTime
309+
310+ property var alarmModel
311+
312 flickable: null
313-
314- Component.onCompleted: Utils.log(debugMode, "Clock Page loaded")
315+ bottomEdgeTitle: alarmUtils.set_bottom_edge_title(alarmModel, clockTime)
316+
317+ Component.onCompleted: {
318+ console.log("[LOG]: Clock Page loaded")
319+ _clockPage.setBottomEdgePage(Qt.resolvedUrl("../alarm/AlarmPage.qml"), { alarmModel: alarmModel })
320+ }
321+
322+ AlarmUtils {
323+ id: alarmUtils
324+ }
325
326 Flickable {
327 id: _flickable
328
329 Component.onCompleted: otherElementsStartUpAnimation.start()
330
331+ onFlickStarted: {
332+ forceActiveFocus()
333+ }
334+
335 anchors.fill: parent
336 contentWidth: parent.width
337 contentHeight: clock.height + date.height + locationRow.height
338
339=== modified file 'app/clock/MainClock.qml'
340--- app/clock/MainClock.qml 2014-08-24 12:47:50 +0000
341+++ app/clock/MainClock.qml 2014-10-05 15:35:22 +0000
342@@ -17,7 +17,6 @@
343 */
344
345 import QtQuick 2.3
346-import DateTime 1.0
347 import Ubuntu.Components 1.1
348 import "../components"
349
350@@ -30,44 +29,6 @@
351
352 isMainClock: true
353
354- Connections {
355- target: clockApp
356- onApplicationStateChanged: {
357- localTimeSource.update()
358- }
359- }
360-
361- DateTime {
362- id: localTimeSource
363- updateInterval: isDigital ? 1000 : 10
364- }
365-
366- /*
367- Create a new Date() object and pass the date, month, year, hour, minute
368- and second received from the DateTime plugin manually to ensure the
369- timezone info is set correctly.
370-
371- Javascript Month is 0-11 while QDateTime month is 1-12. Hence the -1
372- is required.
373- */
374-
375- /*
376- FIXME: When the upstream QT bug at
377- https://bugreports.qt-project.org/browse/QTBUG-40275 is fixed it will be
378- possible to receive a datetime object directly instead of using this hack.
379- */
380- analogTime: new Date
381- (
382- localTimeSource.localDateString.split(":")[0],
383- localTimeSource.localDateString.split(":")[1]-1,
384- localTimeSource.localDateString.split(":")[2],
385- localTimeSource.localTimeString.split(":")[0],
386- localTimeSource.localTimeString.split(":")[1],
387- localTimeSource.localTimeString.split(":")[2],
388- localTimeSource.localTimeString.split(":")[3]
389- )
390- time: Qt.formatTime(analogTime)
391-
392 isDigital: clockModeDocument.contents.digitalMode ? true : false
393
394 Component.onCompleted: {
395
396=== modified file 'app/components/Clock.qml'
397--- app/components/Clock.qml 2014-09-06 15:16:43 +0000
398+++ app/components/Clock.qml 2014-10-05 15:35:22 +0000
399@@ -18,7 +18,6 @@
400
401 import QtQuick 2.3
402 import Ubuntu.Components 1.1
403-import "Utils.js" as Utils
404
405 /*
406 Generic clock component which has a digital and analog mode. A flip animation
407@@ -148,6 +147,7 @@
408 enabled: isMainClock
409 anchors.fill: parent
410 onClicked: {
411+ forceActiveFocus()
412 clockFlipAnimation.start()
413 }
414 }
415
416=== removed file 'app/components/Utils.js'
417--- app/components/Utils.js 2014-07-17 12:21:45 +0000
418+++ app/components/Utils.js 1970-01-01 00:00:00 +0000
419@@ -1,25 +0,0 @@
420-/*
421- * Copyright (C) 2014 Canonical Ltd
422- *
423- * This file is part of Ubuntu Clock App
424- *
425- * Ubuntu Clock App is free software: you can redistribute it and/or modify
426- * it under the terms of the GNU General Public License version 3 as
427- * published by the Free Software Foundation.
428- *
429- * Ubuntu Clock App is distributed in the hope that it will be useful,
430- * but WITHOUT ANY WARRANTY; without even the implied warranty of
431- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
432- * GNU General Public License for more details.
433- *
434- * You should have received a copy of the GNU General Public License
435- * along with this program. If not, see <http://www.gnu.org/licenses/>.
436- */
437-
438-.pragma library
439-
440-function log (debugMode, message) {
441- if (debugMode) {
442- console.log("[LOG]" + message)
443- }
444-}
445
446=== modified file 'app/ubuntu-clock-app.qml'
447--- app/ubuntu-clock-app.qml 2014-08-26 20:20:37 +0000
448+++ app/ubuntu-clock-app.qml 2014-10-05 15:35:22 +0000
449@@ -17,12 +17,11 @@
450 */
451
452 import QtQuick 2.3
453+import DateTime 1.0
454 import U1db 1.0 as U1db
455 import Ubuntu.Components 1.1
456 import "clock"
457-import "alarm"
458 import "components"
459-import "components/Utils.js" as Utils
460
461 MainView {
462 id: clockApp
463@@ -30,9 +29,6 @@
464 // Property to store the state of an application (active or suspended)
465 property bool applicationState: Qt.application.active
466
467- // Property to enable/disable the debug mode to show more console output
468- property bool debugMode: true
469-
470 // objectName for functional testing purposes (autopilot-qt5)
471 objectName: "clock"
472
473@@ -77,7 +73,16 @@
474
475 AlarmModel {
476 id: alarmModel
477- Component.onCompleted: Utils.log(debugMode, "Alarm Database loaded")
478+ Component.onCompleted: console.log("[LOG]: Alarm Database loaded")
479+ }
480+
481+ DateTime {
482+ id: localTimeSource
483+ updateInterval: clockModeDocument.contents.digitalMode ? 1000 : 10
484+ }
485+
486+ onApplicationStateChanged: {
487+ localTimeSource.update()
488 }
489
490 PageStack {
491@@ -97,11 +102,31 @@
492 */
493
494 /*
495- #TODO: The bottom edge title should reflect the time to the next
496- alarm. For instance it should read "Next alarm in 9h23m".
497- */
498- bottomEdgeTitle: i18n.tr("Alarms")
499- bottomEdgePageComponent: AlarmPage {}
500+ Create a new Date() object and pass the date, month, year, hour, minute
501+ and second received from the DateTime plugin manually to ensure the
502+ timezone info is set correctly.
503+
504+ Javascript Month is 0-11 while QDateTime month is 1-12. Hence the -1
505+ is required.
506+ */
507+
508+ /*
509+ FIXME: When the upstream QT bug at
510+ https://bugreports.qt-project.org/browse/QTBUG-40275 is fixed it will be
511+ possible to receive a datetime object directly instead of using this hack.
512+ */
513+
514+ alarmModel: alarmModel
515+ clockTime: new Date
516+ (
517+ localTimeSource.localDateString.split(":")[0],
518+ localTimeSource.localDateString.split(":")[1]-1,
519+ localTimeSource.localDateString.split(":")[2],
520+ localTimeSource.localTimeString.split(":")[0],
521+ localTimeSource.localTimeString.split(":")[1],
522+ localTimeSource.localTimeString.split(":")[2],
523+ localTimeSource.localTimeString.split(":")[3]
524+ )
525 }
526 }
527 }
528
529=== modified file 'app/upstreamcomponents/PageWithBottomEdge.qml'
530--- app/upstreamcomponents/PageWithBottomEdge.qml 2014-09-20 10:47:21 +0000
531+++ app/upstreamcomponents/PageWithBottomEdge.qml 2014-10-05 15:35:22 +0000
532@@ -62,7 +62,7 @@
533
534 */
535
536-import QtQuick 2.3
537+import QtQuick 2.2
538 import Ubuntu.Components 1.1
539
540 Page {
541@@ -71,7 +71,7 @@
542 property alias bottomEdgePageComponent: edgeLoader.sourceComponent
543 property alias bottomEdgePageSource: edgeLoader.source
544 property alias bottomEdgeTitle: tipLabel.text
545- property alias bottomEdgeEnabled: bottomEdge.visible
546+ property bool bottomEdgeEnabled: true
547 property int bottomEdgeExpandThreshold: page.height * 0.2
548 property int bottomEdgeExposedArea: bottomEdge.state !== "expanded" ? (page.height - bottomEdge.y - bottomEdge.tipHeight) : _areaWhenExpanded
549 property bool reloadBottomEdgePage: true
550@@ -87,6 +87,9 @@
551 signal bottomEdgeReleased()
552 signal bottomEdgeDismissed()
553
554+ function giveFocus() {
555+ tip.forceActiveFocus()
556+ }
557
558 function showBottomEdgePage(source, properties)
559 {
560@@ -142,13 +145,123 @@
561 z: 1
562 }
563
564- Timer {
565- id: hideIndicator
566-
567- interval: 3000
568- running: true
569- repeat: false
570- onTriggered: tip.hiden = true
571+ UbuntuShape {
572+ id: tip
573+ objectName: "bottomEdgeTip"
574+
575+ property bool hiden: (activeFocus === false) ||
576+ ((bottomEdge.y - units.gu(1)) < tip.y)
577+
578+ enabled: mouseArea.enabled
579+ visible: page.bottomEdgeEnabled
580+ anchors {
581+ bottom: parent.bottom
582+ horizontalCenter: bottomEdge.horizontalCenter
583+ bottomMargin: hiden ? - height + units.gu(1) : -units.gu(1)
584+ Behavior on bottomMargin {
585+ SequentialAnimation {
586+ // wait some msecs in case of the focus change again, to avoid flickering
587+ PauseAnimation {
588+ duration: 300
589+ }
590+ UbuntuNumberAnimation {
591+ duration: UbuntuAnimation.SnapDuration
592+ }
593+ }
594+ }
595+ }
596+
597+ z: 1
598+ width: tipLabel.paintedWidth + units.gu(6)
599+ height: bottomEdge.tipHeight + units.gu(1)
600+ color: Theme.palette.normal.overlay
601+ Label {
602+ id: tipLabel
603+
604+ anchors {
605+ top: parent.top
606+ left: parent.left
607+ right: parent.right
608+ }
609+ height: bottomEdge.tipHeight
610+ verticalAlignment: Text.AlignVCenter
611+ horizontalAlignment: Text.AlignHCenter
612+ opacity: tip.hiden ? 0.0 : 1.0
613+ Behavior on opacity {
614+ UbuntuNumberAnimation {
615+ duration: UbuntuAnimation.SnapDuration
616+ }
617+ }
618+ }
619+ }
620+
621+ Rectangle {
622+ id: shadow
623+
624+ anchors {
625+ left: parent.left
626+ right: parent.right
627+ bottom: parent.bottom
628+ }
629+ height: units.gu(1)
630+ z: 1
631+ opacity: 0.0
632+ gradient: Gradient {
633+ GradientStop { position: 0.0; color: "transparent" }
634+ GradientStop { position: 1.0; color: Qt.rgba(0, 0, 0, 0.2) }
635+ }
636+ }
637+
638+ MouseArea {
639+ id: mouseArea
640+
641+ property real previousY: -1
642+ property string dragDirection: "None"
643+
644+ preventStealing: true
645+ drag {
646+ axis: Drag.YAxis
647+ target: bottomEdge
648+ minimumY: bottomEdge.pageStartY
649+ maximumY: page.height
650+ }
651+ enabled: edgeLoader.status == Loader.Ready
652+ visible: page.bottomEdgeEnabled
653+
654+ anchors {
655+ left: parent.left
656+ right: parent.right
657+ bottom: parent.bottom
658+ }
659+ height: bottomEdge.tipHeight
660+ z: 1
661+
662+ onReleased: {
663+ page.bottomEdgeReleased()
664+ if ((dragDirection === "BottomToTop") &&
665+ bottomEdge.y < (page.height - bottomEdgeExpandThreshold - bottomEdge.tipHeight)) {
666+ bottomEdge.state = "expanded"
667+ } else {
668+ bottomEdge.state = "collapsed"
669+ }
670+ previousY = -1
671+ dragDirection = "None"
672+ }
673+
674+ onPressed: {
675+ previousY = mouse.y
676+ tip.forceActiveFocus()
677+ }
678+
679+ onMouseYChanged: {
680+ var yOffset = previousY - mouseY
681+ // skip if was a small move
682+ if (Math.abs(yOffset) <= units.gu(2)) {
683+ return
684+ }
685+ previousY = mouseY
686+ dragDirection = yOffset > 0 ? "BottomToTop" : "TopToBottom"
687+ }
688 }
689
690 FakeHeader {
691@@ -177,7 +290,7 @@
692
693 z: 1
694 color: Theme.palette.normal.background
695- parent: page
696+ clip: true
697 anchors {
698 left: parent.left
699 right: parent.right
700@@ -185,99 +298,7 @@
701 height: page.height
702 y: height
703
704- UbuntuShape {
705- id: tip
706- objectName: "bottomEdgeTip"
707-
708- property bool hiden: false
709-
710- readonly property double visiblePosition: (page.height - bottomEdge.y) < units.gu(1) ? -bottomEdge.tipHeight + (page.height - bottomEdge.y) : 0
711- readonly property double invisiblePosition: (page.height - bottomEdge.y) < units.gu(1) ? -units.gu(1) : 0
712-
713- z: -1
714- anchors.horizontalCenter: parent.horizontalCenter
715- y: hiden ? invisiblePosition : visiblePosition
716-
717- width: tipLabel.paintedWidth + units.gu(6)
718- height: bottomEdge.tipHeight + units.gu(1)
719- color: Theme.palette.normal.overlay
720- Label {
721- id: tipLabel
722-
723- anchors {
724- top: parent.top
725- left: parent.left
726- right: parent.right
727- }
728- height: bottomEdge.tipHeight
729- verticalAlignment: Text.AlignVCenter
730- horizontalAlignment: Text.AlignHCenter
731- opacity: tip.hiden ? 0.0 : 1.0
732- Behavior on opacity {
733- UbuntuNumberAnimation {
734- duration: UbuntuAnimation.SnapDuration
735- }
736- }
737- }
738- Behavior on y {
739- UbuntuNumberAnimation {
740- duration: UbuntuAnimation.SnapDuration
741- }
742- }
743- }
744-
745- Rectangle {
746- id: shadow
747-
748- anchors {
749- left: parent.left
750- right: parent.right
751- }
752- height: units.gu(1)
753- y: -height
754- z: -2
755- opacity: 0.0
756- gradient: Gradient {
757- GradientStop { position: 0.0; color: "transparent" }
758- GradientStop { position: 1.0; color: Qt.rgba(0, 0, 0, 0.2) }
759- }
760- }
761-
762- MouseArea {
763- id: mouseArea
764-
765- preventStealing: true
766- drag {
767- axis: Drag.YAxis
768- target: bottomEdge
769- minimumY: bottomEdge.pageStartY
770- maximumY: page.height
771- threshold: 100
772- }
773-
774- anchors {
775- left: parent.left
776- right: parent.right
777- }
778- height: bottomEdge.tipHeight
779- y: -height
780-
781- onReleased: {
782- page.bottomEdgeReleased()
783- if (bottomEdge.y < (page.height - bottomEdgeExpandThreshold - bottomEdge.tipHeight)) {
784- bottomEdge.state = "expanded"
785- } else {
786- bottomEdge.state = "collapsed"
787- bottomEdge.y = bottomEdge.height
788- }
789- }
790-
791- onClicked: {
792- tip.hiden = false
793- hideIndicator.restart()
794- }
795- }
796-
797+ visible: !page.isCollapsed
798 state: "collapsed"
799 states: [
800 State {
801@@ -290,14 +311,6 @@
802 target: fakeHeader
803 y: -fakeHeader.height
804 }
805- PropertyChanges {
806- target: tip
807- opacity: 1.0
808- }
809- PropertyChanges {
810- target: hideIndicator
811- running: true
812- }
813 },
814 State {
815 name: "expanded"
816@@ -309,10 +322,6 @@
817 target: fakeHeader
818 y: 0
819 }
820- PropertyChanges {
821- target: hideIndicator
822- running: false
823- }
824 },
825 State {
826 name: "floating"
827@@ -321,14 +330,6 @@
828 target: shadow
829 opacity: 1.0
830 }
831- PropertyChanges {
832- target: hideIndicator
833- running: false
834- }
835- PropertyChanges {
836- target: tip
837- hiden: false
838- }
839 }
840 ]
841
842@@ -336,18 +337,35 @@
843 Transition {
844 to: "expanded"
845 SequentialAnimation {
846+ alwaysRunToEnd: true
847 ParallelAnimation {
848- UbuntuNumberAnimation {
849+ SmoothedAnimation {
850 target: bottomEdge
851 property: "y"
852- duration: UbuntuAnimation.SlowDuration
853+ duration: UbuntuAnimation.FastDuration
854+ easing.type: Easing.Linear
855 }
856- UbuntuNumberAnimation {
857+ SmoothedAnimation {
858 target: fakeHeader
859 property: "y"
860- duration: UbuntuAnimation.SlowDuration
861+ duration: UbuntuAnimation.FastDuration
862+ easing.type: Easing.Linear
863 }
864 }
865+ SmoothedAnimation {
866+ target: edgeLoader
867+ property: "anchors.topMargin"
868+ to: - units.gu(4)
869+ duration: UbuntuAnimation.FastDuration
870+ easing.type: Easing.Linear
871+ }
872+ SmoothedAnimation {
873+ target: edgeLoader
874+ property: "anchors.topMargin"
875+ to: 0
876+ duration: UbuntuAnimation.FastDuration
877+ easing: UbuntuAnimation.StandardEasing
878+ }
879 ScriptAction {
880 script: page._pushPage()
881 }
882@@ -357,6 +375,8 @@
883 from: "expanded"
884 to: "collapsed"
885 SequentialAnimation {
886+ alwaysRunToEnd: true
887+
888 ScriptAction {
889 script: {
890 Qt.inputMethod.hide()
891@@ -366,12 +386,12 @@
892 }
893 }
894 ParallelAnimation {
895- UbuntuNumberAnimation {
896+ SmoothedAnimation {
897 target: bottomEdge
898 property: "y"
899 duration: UbuntuAnimation.SlowDuration
900 }
901- UbuntuNumberAnimation {
902+ SmoothedAnimation {
903 target: fakeHeader
904 property: "y"
905 duration: UbuntuAnimation.SlowDuration
906@@ -382,14 +402,14 @@
907 // destroy current bottom page
908 if (page.reloadBottomEdgePage) {
909 edgeLoader.active = false
910+ } else {
911+ tip.forceActiveFocus()
912 }
913
914 // notify
915 page.bottomEdgeDismissed()
916
917 edgeLoader.active = true
918- tip.hiden = false
919- hideIndicator.restart()
920 }
921 }
922 }
923@@ -404,30 +424,23 @@
924 }
925 ]
926
927- Item {
928+ Loader {
929+ id: edgeLoader
930+
931+ asynchronous: true
932 anchors.fill: parent
933- clip: true
934-
935- Loader {
936- id: edgeLoader
937-
938- z: 1
939- active: true
940- asynchronous: true
941- anchors.fill: parent
942-
943- //WORKAROUND: The SDK move the page contents down to allocate space for the header we need to avoid that during the page dragging
944- Binding {
945- target: edgeLoader.status === Loader.Ready ? edgeLoader : null
946- property: "anchors.topMargin"
947- value: edgeLoader.item && edgeLoader.item.flickable ? edgeLoader.item.flickable.contentY : 0
948- when: !page.isReady
949- }
950-
951- onLoaded: {
952- if (page.isReady && edgeLoader.item.active !== true) {
953- page._pushPage()
954- }
955+ //WORKAROUND: The SDK move the page contents down to allocate space for the header we need to avoid that during the page dragging
956+ Binding {
957+ target: edgeLoader.status === Loader.Ready ? edgeLoader : null
958+ property: "anchors.topMargin"
959+ value: edgeLoader.item && edgeLoader.item.flickable ? edgeLoader.item.flickable.contentY : 0
960+ when: !page.isReady
961+ }
962+
963+ onLoaded: {
964+ tip.forceActiveFocus()
965+ if (page.isReady && edgeLoader.item.active !== true) {
966+ page._pushPage()
967 }
968 }
969 }
970
971=== modified file 'app/worldclock/UserWorldCityList.qml'
972--- app/worldclock/UserWorldCityList.qml 2014-08-24 12:47:50 +0000
973+++ app/worldclock/UserWorldCityList.qml 2014-10-05 15:35:22 +0000
974@@ -21,8 +21,6 @@
975 import U1db 1.0 as U1db
976 import Ubuntu.Components 1.1
977
978-import "../components/Utils.js" as Utils
979-
980 Column {
981 id: worldCityColumn
982
983
984=== modified file 'tests/autopilot/ubuntu_clock_app/emulators.py'
985--- tests/autopilot/ubuntu_clock_app/emulators.py 2014-09-12 19:44:18 +0000
986+++ tests/autopilot/ubuntu_clock_app/emulators.py 2014-10-05 15:35:22 +0000
987@@ -91,6 +91,8 @@
988 self.bottomEdgePageLoaded.wait_for(True)
989 try:
990 action_item = self.wait_select_single(objectName='bottomEdgeTip')
991+ action_item.hiden.wait_for(False)
992+ action_item.enabled.wait_for(True)
993 start_x = (action_item.globalRect.x +
994 (action_item.globalRect.width * 0.5))
995 start_y = (action_item.globalRect.y +
996
997=== modified file 'tests/unit/tst_alarm.qml'
998--- tests/unit/tst_alarm.qml 2014-09-25 22:10:27 +0000
999+++ tests/unit/tst_alarm.qml 2014-10-05 15:35:22 +0000
1000@@ -18,6 +18,7 @@
1001
1002 import QtQuick 2.3
1003 import QtTest 1.0
1004+import DateTime 1.0
1005 import Ubuntu.Test 1.0
1006 import Ubuntu.Components 1.1
1007 import "../../app/alarm"
1008@@ -29,10 +30,26 @@
1009 height: units.gu(70)
1010 useDeprecatedToolbar: false
1011
1012+ property var clockTime: new Date
1013+ (
1014+ localTimeSource.localDateString.split(":")[0],
1015+ localTimeSource.localDateString.split(":")[1]-1,
1016+ localTimeSource.localDateString.split(":")[2],
1017+ localTimeSource.localTimeString.split(":")[0],
1018+ localTimeSource.localTimeString.split(":")[1],
1019+ localTimeSource.localTimeString.split(":")[2],
1020+ localTimeSource.localTimeString.split(":")[3]
1021+ )
1022+
1023 AlarmModel {
1024 id: alarmModel
1025 }
1026
1027+ DateTime {
1028+ id: localTimeSource
1029+ updateInterval: 1000
1030+ }
1031+
1032 PageStack {
1033 id: pageStack
1034 Component.onCompleted: push(alarmPage)

Subscribers

People subscribed via source and target branches