Merge lp:~renatofilho/ubuntu-calendar-app/optimize into lp:ubuntu-calendar-app
- optimize
- Merge into trunk
Status: | Merged | ||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Approved by: | Bill Filler | ||||||||||||||||||||||||||||||||||||
Approved revision: | 735 | ||||||||||||||||||||||||||||||||||||
Merged at revision: | 768 | ||||||||||||||||||||||||||||||||||||
Proposed branch: | lp:~renatofilho/ubuntu-calendar-app/optimize | ||||||||||||||||||||||||||||||||||||
Merge into: | lp:ubuntu-calendar-app | ||||||||||||||||||||||||||||||||||||
Prerequisite: | lp:~renatofilho/ubuntu-calendar-app/optimize-page-load | ||||||||||||||||||||||||||||||||||||
Diff against target: |
6303 lines (+3052/-1463) 33 files modified
AgendaView.qml (+14/-13) AllDayEventComponent.qml (+71/-36) ContactChoicePopup.qml (+57/-15) DayView.qml (+127/-87) EventActions.qml (+2/-13) EventBubble.qml (+61/-71) EventLayoutHelper.js (+0/-132) EventListModel.qml (+17/-5) KeyboardRectangle.qml (+1/-7) MonthComponent.qml (+178/-125) MonthComponentDateDelegate.qml (+1/-140) MonthComponentWithEventsDateDelegate.qml (+80/-0) MonthView.qml (+46/-82) MonthWithEventsComponent.qml (+80/-0) NewEvent.qml (+228/-131) NewEventBottomEdge.qml (+115/-0) PageWithBottomEdge.qml (+44/-0) PathViewBase.qml (+12/-4) TimeLineBase.qml (+276/-190) TimeLineBaseComponent.qml (+159/-75) TimeLineHeader.qml (+6/-0) WeekView.qml (+130/-60) YearView.qml (+64/-48) YearViewDelegate.qml (+39/-74) calendar.qml (+150/-27) calendar_canvas.js (+52/-0) calendar_canvas_worker.js (+111/-0) click/calendar-helper-apparmor.json (+1/-1) click/calendar.apparmor (+1/-1) click/manifest.json.in (+1/-1) dateExt.js (+15/-2) po/com.ubuntu.calendar.pot (+241/-123) tests/unittests/tst_calendar_canvas.qml (+672/-0) |
||||||||||||||||||||||||||||||||||||
To merge this branch: | bzr merge lp:~renatofilho/ubuntu-calendar-app/optimize | ||||||||||||||||||||||||||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jenkins Bot | continuous-integration | Approve | |
Ubuntu Phone Apps Jenkins Bot | continuous-integration | Pending | |
Kunal Parmar | Pending | ||
Review via email: mp+283858@code.launchpad.net |
This proposal supersedes a proposal from 2016-01-25.
Commit message
Optimize Application;
Rewrite event layout functions;
Fixed several bugs;
Description of the change
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : Posted in a previous version of this proposal | # |
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:623
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Kunal Parmar (pkunal-parmar) wrote : Posted in a previous version of this proposal | # |
added inline comment
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:623
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:624
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:627
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:627
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
FAILED: Continuous integration, rev:629
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
FAILED: Continuous integration, rev:630
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
FAILED: Continuous integration, rev:632
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
FAILED: Continuous integration, rev:634
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
FAILED: Continuous integration, rev:635
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
FAILED: Continuous integration, rev:636
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:728
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
- 729. By Renato Araujo Oliveira Filho
-
Use events word instead of 'ev.' for all day events.
- 730. By Renato Araujo Oliveira Filho
-
Update events filter if the collections changed.
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:730
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
- 731. By Renato Araujo Oliveira Filho
-
Update apparmor policy version.
- 732. By Renato Araujo Oliveira Filho
-
Disable model update while sync is in progress to avoid several updates.
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:732
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
- 733. By Renato Araujo Oliveira Filho
-
Keep event bubble color in sync with calendar event color.
- 734. By Renato Araujo Oliveira Filho
-
Fixed day view title.
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:734
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
- 735. By Renato Araujo Oliveira Filho
-
Fixed uri handler for eventId.
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:735
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Jenkins Bot (ubuntu-core-apps-jenkins-bot) : | # |
Preview Diff
1 | === modified file 'AgendaView.qml' |
2 | --- AgendaView.qml 2016-02-03 08:06:25 +0000 |
3 | +++ AgendaView.qml 2016-03-07 15:59:03 +0000 |
4 | @@ -23,16 +23,14 @@ |
5 | import "dateExt.js" as DateExt |
6 | import "./3rd-party/lunar.js" as Lunar |
7 | |
8 | -Page{ |
9 | +PageWithBottomEdge { |
10 | id: root |
11 | objectName: "AgendaView" |
12 | |
13 | - property var currentDay: new Date() |
14 | + property var anchorDate: new Date() |
15 | |
16 | signal dateSelected(var date); |
17 | |
18 | - Keys.forwardTo: [eventList] |
19 | - |
20 | function goToBeginning() { |
21 | eventList.positionViewAtBeginning(); |
22 | } |
23 | @@ -45,13 +43,17 @@ |
24 | return !!enabled_calendars.length; |
25 | } |
26 | |
27 | + |
28 | + Keys.forwardTo: [eventList] |
29 | + createEventAt: anchorDate |
30 | + |
31 | Action { |
32 | id: calendarTodayAction |
33 | objectName:"todaybutton" |
34 | iconName: "calendar-today" |
35 | text: i18n.tr("Today") |
36 | onTriggered: { |
37 | - currentDay = new Date() |
38 | + anchorDate = new Date() |
39 | goToBeginning() |
40 | } |
41 | } |
42 | @@ -63,7 +65,6 @@ |
43 | leadingActionBar.actions: tabs.tabsAction |
44 | trailingActionBar.actions: [ |
45 | calendarTodayAction, |
46 | - commonHeaderActions.newEventAction, |
47 | commonHeaderActions.showCalendarAction, |
48 | commonHeaderActions.reloadAction, |
49 | commonHeaderActions.syncCalendarAction, |
50 | @@ -74,9 +75,9 @@ |
51 | |
52 | EventListModel { |
53 | id: eventListModel |
54 | - startPeriod: currentDay.midnight(); |
55 | - endPeriod: currentDay.addDays(7).endOfDay() |
56 | - filter: eventModel.filter |
57 | + startPeriod: anchorDate.midnight(); |
58 | + endPeriod: anchorDate.addDays(7).endOfDay() |
59 | + filter: model.filter |
60 | |
61 | sortOrders: [ |
62 | SortOrder{ |
63 | @@ -106,7 +107,7 @@ |
64 | |
65 | return default_title; |
66 | } |
67 | - visible: (eventListModel.count === 0) && !eventListModel.isLoading |
68 | + visible: (eventList.count === 0) && !eventListModel.isLoading |
69 | anchors.centerIn: parent |
70 | } |
71 | |
72 | @@ -119,12 +120,12 @@ |
73 | color: UbuntuColors.orange |
74 | |
75 | onClicked: { |
76 | - pageStack.push(Qt.resolvedUrl("CalendarChoicePopup.qml"),{"model":eventModel}); |
77 | - pageStack.currentPage.collectionUpdated.connect(eventModel.delayedApplyFilter); |
78 | + pageStack.push(Qt.resolvedUrl("CalendarChoicePopup.qml"),{"model": model}); |
79 | + pageStack.currentPage.collectionUpdated.connect(model.delayedApplyFilter); |
80 | } |
81 | } |
82 | |
83 | - ListView{ |
84 | + ListView { |
85 | id: eventList |
86 | objectName: "eventList" |
87 | model: eventListModel |
88 | |
89 | === modified file 'AllDayEventComponent.qml' |
90 | --- AllDayEventComponent.qml 2016-02-03 08:06:25 +0000 |
91 | +++ AllDayEventComponent.qml 2016-03-07 15:59:03 +0000 |
92 | @@ -31,12 +31,14 @@ |
93 | property var allDayEvents; |
94 | property var model; |
95 | |
96 | + signal pressAndHold(var date) |
97 | + |
98 | width: parent.width |
99 | height: units.gu(5) |
100 | |
101 | function getAllDayEvents(startDate, endDate) { |
102 | var map = {}; |
103 | - var items = model.getItems(startDate,endDate); |
104 | + var items = model.itemsByTimePeriod(startDate,endDate); |
105 | for(var i = 0 ; i < items.length ; ++i) { |
106 | var event = items[(i)]; |
107 | if( event && event.allDay ) { |
108 | @@ -62,6 +64,10 @@ |
109 | } |
110 | |
111 | Repeater{ |
112 | + id: repeater |
113 | + |
114 | + readonly property bool compactView: (root.width / repeater.count) < units.gu(15) |
115 | + |
116 | model: type == ViewType.ViewTypeWeek ? 7 : 1 |
117 | delegate: Item { |
118 | id: allDayButton |
119 | @@ -71,29 +77,86 @@ |
120 | height: units.gu(5) |
121 | width: parent.width / (type == ViewType.ViewTypeWeek ? 7 : 1) |
122 | |
123 | + Rectangle { |
124 | + id: temporaryEvent |
125 | + |
126 | + anchors.fill: parent |
127 | + visible: mouseArea.mouseHold |
128 | + Label { |
129 | + anchors.fill: parent |
130 | + verticalAlignment: Text.AlignVCenter |
131 | + horizontalAlignment: Text.AlignHCenter |
132 | + text: i18n.tr("New event") |
133 | + } |
134 | + z: 100 |
135 | + } |
136 | + |
137 | MouseArea { |
138 | + id: mouseArea |
139 | + |
140 | + property bool mouseHold: false |
141 | + |
142 | + preventStealing: mouseHold |
143 | anchors.fill: parent |
144 | onClicked: { |
145 | if(!allDayButton.events || allDayButton.events.length === 0) { |
146 | return; |
147 | } |
148 | |
149 | - if(type == ViewType.ViewTypeWeek) { |
150 | - PopupUtils.open(popoverComponent, root,{"events": allDayButton.events}) |
151 | + if(repeater.compactView) { |
152 | + PopupUtils.open(popoverComponent, allDayButton,{"events": allDayButton.events}) |
153 | } else { |
154 | if( allDayButton.events.length > 1 ) { |
155 | - PopupUtils.open(popoverComponent, root,{"events": allDayButton.events}) |
156 | + PopupUtils.open(popoverComponent, allDayButton,{"events": allDayButton.events}) |
157 | } else { |
158 | pageStack.push(Qt.resolvedUrl("EventDetails.qml"),{"event":allDayButton.events[0],"model": root.model}); |
159 | } |
160 | } |
161 | } |
162 | + |
163 | + onReleased: { |
164 | + if (mouseHold && containsMouse) { |
165 | + root.pressAndHold(startDay.midnight().addDays(index)) |
166 | + } |
167 | + mouseHold = false |
168 | + } |
169 | + |
170 | + |
171 | + onPressAndHold: { |
172 | + mouseHold = true |
173 | + Haptics.play() |
174 | + } |
175 | } |
176 | |
177 | - Loader { |
178 | - id: eventLabelLoader |
179 | - anchors.fill: parent |
180 | - sourceComponent : !allDayButton.events || allDayButton.events.length === 0 ? undefined : eventComponent |
181 | + Label { |
182 | + id: eventLabel |
183 | + anchors { |
184 | + fill: parent |
185 | + margins: units.gu(0.5) |
186 | + } |
187 | + verticalAlignment: Text.AlignVCenter |
188 | + horizontalAlignment: Text.AlignHCenter |
189 | + elide: Text.ElideRight |
190 | + text: { |
191 | + if(!events || events.length === 0) { |
192 | + return ""; |
193 | + } |
194 | + |
195 | + if(repeater.compactView) { |
196 | + // TRANSLATORS: the first parameter refers to the number of all-day events |
197 | + // on a given day. "Ev." is short form for "Events". |
198 | + // Please keep the translation of "Ev." to 3 characters only, as the week view |
199 | + // where it's shown has limited space |
200 | + return i18n.tr("%1 event", "%1 events", events.length).arg(events.length) |
201 | + } else { |
202 | + if( events.length > 1) { |
203 | + // TRANSLATORS: the argument refers to the number of all day events |
204 | + return i18n.tr("%1 all day event", "%1 all day events", events.length).arg(events.length) |
205 | + } else { |
206 | + return events[0].displayLabel; |
207 | + } |
208 | + } |
209 | + } |
210 | } |
211 | |
212 | Loader{ |
213 | @@ -110,39 +173,11 @@ |
214 | sd = sd.addDays(index); |
215 | var key = Qt.formatDateTime(sd, "dd-MMM-yyyy"); |
216 | events = allDayEvents[key]; |
217 | - |
218 | - if(!events || events.length === 0) { |
219 | - return; |
220 | - } |
221 | - |
222 | - if(type == ViewType.ViewTypeWeek) { |
223 | - // TRANSLATORS: the first parameter refers to the number of all-day events |
224 | - // on a given day. "Ev." is short form for "Events". |
225 | - // Please keep the translation of "Ev." to 3 characters only, as the week view |
226 | - // where it's shown has limited space |
227 | - eventLabelLoader.item.text = i18n.tr("%1 ev.").arg(events.length) |
228 | - } else { |
229 | - if( events.length > 1) { |
230 | - // TRANSLATORS: the argument refers to the number of all day events |
231 | - eventLabelLoader.item.text = i18n.tr("%1 all day event", "%1 all day events", events.length).arg(events.length) |
232 | - } else { |
233 | - eventLabelLoader.item.text = events[0].displayLabel; |
234 | - } |
235 | - } |
236 | } |
237 | } |
238 | } |
239 | } |
240 | |
241 | - |
242 | - Component{ |
243 | - id: eventComponent |
244 | - Label { |
245 | - verticalAlignment: Text.AlignVCenter |
246 | - horizontalAlignment: Text.AlignHCenter |
247 | - } |
248 | - } |
249 | - |
250 | Component { |
251 | id: dividerComponent |
252 | SimpleDivider{ |
253 | |
254 | === modified file 'ContactChoicePopup.qml' |
255 | --- ContactChoicePopup.qml 2016-01-29 14:35:14 +0000 |
256 | +++ ContactChoicePopup.qml 2016-03-07 15:59:03 +0000 |
257 | @@ -17,7 +17,7 @@ |
258 | */ |
259 | import QtQuick 2.4 |
260 | import Ubuntu.Components 1.3 |
261 | -import Ubuntu.Components.Popups 1.0 |
262 | +import Ubuntu.Components.Popups 1.3 |
263 | import Ubuntu.Components.ListItems 1.0 |
264 | import Ubuntu.Components.Themes.Ambiance 1.0 |
265 | import QtOrganizer 5.0 |
266 | @@ -29,7 +29,7 @@ |
267 | id: root |
268 | objectName: "contactPopover" |
269 | |
270 | - signal contactSelected(var contact); |
271 | + signal contactSelected(var contact, string emailAddress); |
272 | |
273 | Label { |
274 | id: noContact |
275 | @@ -40,24 +40,27 @@ |
276 | |
277 | UnionFilter { |
278 | id: filter |
279 | + |
280 | + property string searchString: "" |
281 | + |
282 | filters: [ |
283 | DetailFilter{ |
284 | detail: ContactDetail.Name |
285 | field: Name.FirstName |
286 | matchFlags: Filter.MatchContains |
287 | - value: searchBox.text |
288 | + value: filter.searchString |
289 | }, |
290 | DetailFilter{ |
291 | detail: ContactDetail.Name |
292 | field: Name.LastName |
293 | matchFlags: Filter.MatchContains |
294 | - value: searchBox.text |
295 | + value: filter.searchString |
296 | }, |
297 | DetailFilter{ |
298 | detail: ContactDetail.DisplayLabel |
299 | field: DisplayLabel.Label |
300 | matchFlags: Filter.MatchContains |
301 | - value: searchBox.text |
302 | + value: filter.searchString |
303 | } |
304 | ] |
305 | } |
306 | @@ -69,6 +72,16 @@ |
307 | autoUpdate: true |
308 | } |
309 | |
310 | + Timer { |
311 | + id: idleSearch |
312 | + |
313 | + interval: 500 |
314 | + repeat: false |
315 | + onTriggered: { |
316 | + filter.searchString = searchBox.text |
317 | + } |
318 | + } |
319 | + |
320 | Column { |
321 | anchors.top: parent.top |
322 | anchors.left: parent.left |
323 | @@ -81,12 +94,16 @@ |
324 | focus: true |
325 | width: parent.width |
326 | placeholderText: i18n.tr("Search contact") |
327 | + inputMethodHints: Qt.ImhNoPredictiveText |
328 | primaryItem: Icon { |
329 | height: parent.height*0.5 |
330 | width: parent.height*0.5 |
331 | anchors.verticalCenter: parent.verticalCenter |
332 | name:"find" |
333 | - } |
334 | + } |
335 | + onTextChanged: { |
336 | + idleSearch.restart() |
337 | + } |
338 | } |
339 | |
340 | ListView { |
341 | @@ -96,17 +113,42 @@ |
342 | model: contactModel |
343 | height: units.gu(15) |
344 | clip: true |
345 | - delegate: Standard{ |
346 | - objectName: "contactPopoverList%1".arg(index) |
347 | - property var item: contactModel.contacts[index] |
348 | - height: units.gu(4) |
349 | - text: item ? item.displayLabel.label : "" |
350 | - |
351 | - onClicked: { |
352 | - root.contactSelected(item); |
353 | - onClicked: PopupUtils.close(root) |
354 | + focus: false |
355 | + delegate: Column { |
356 | + width: contactList.width |
357 | + Repeater { |
358 | + anchors { |
359 | + left: parent.left |
360 | + right: parent.right |
361 | + } |
362 | + height: childrenRect.height |
363 | + |
364 | + model: Math.max(1, contact.emails.length) |
365 | + delegate: ListItem { |
366 | + property string emailAddress: contact.emails.length > index ? contact.emails[index].emailAddress : "" |
367 | + |
368 | + activeFocusOnPress: false |
369 | + opacity: emailAddress.length > 0 ? 1.0 : 0.3 |
370 | + width: contactList.width |
371 | + objectName: "contactPopoverList%1".arg(index) |
372 | + ListItemLayout { |
373 | + title.text: contact.displayLabel.label |
374 | + subtitle.text: emailAddress |
375 | + } |
376 | + MouseArea { |
377 | + anchors.fill: parent |
378 | + onClicked: { |
379 | + if (emailAddress.length > 0) { |
380 | + root.contactSelected(contact, emailAddress); |
381 | + PopupUtils.close(root) |
382 | + } |
383 | + } |
384 | + } |
385 | + } |
386 | } |
387 | } |
388 | } |
389 | } |
390 | + |
391 | + Component.onCompleted: searchBox.forceActiveFocus() |
392 | } |
393 | |
394 | === modified file 'DayView.qml' |
395 | --- DayView.qml 2016-02-03 08:06:25 +0000 |
396 | +++ DayView.qml 2016-03-07 15:59:03 +0000 |
397 | @@ -18,21 +18,52 @@ |
398 | |
399 | import QtQuick 2.4 |
400 | import Ubuntu.Components 1.3 |
401 | + |
402 | import "dateExt.js" as DateExt |
403 | import "ViewType.js" as ViewType |
404 | import "./3rd-party/lunar.js" as Lunar |
405 | |
406 | -Page{ |
407 | +PageWithBottomEdge { |
408 | id: dayViewPage |
409 | objectName: "dayViewPage" |
410 | |
411 | - property var currentDay: new Date() |
412 | - property bool isCurrentPage: false |
413 | + |
414 | + property bool displayLunarCalendar: false |
415 | + property var anchorDate: new Date() |
416 | + readonly property var currentDate: dayViewPath.currentItem.startDay |
417 | |
418 | signal dateSelected(var date); |
419 | + signal pressAndHoldAt(var date, bool allDay) |
420 | + |
421 | + function delayScrollToDate(date, scrollTime) { |
422 | + idleScroll.scrollToTime = scrollTime != undefined ? scrollTime : true |
423 | + idleScroll.scrollToDate = new Date(date) |
424 | + idleScroll.restart() |
425 | + } |
426 | |
427 | Keys.forwardTo: [dayViewPath] |
428 | - flickable: null |
429 | + |
430 | + createEventAt: currentDate |
431 | + onAnchorDateChanged: { |
432 | + dayViewPath.scrollToBegginer() |
433 | + } |
434 | + |
435 | + onEventCreated: { |
436 | + var scrollDate = new Date(event.startDateTime) |
437 | + var needScroll = false |
438 | + if ((currentDate.getFullYear() !== scrollDate.getFullYear()) || |
439 | + (currentDate.getMonth() !== scrollDate.getMonth()) || |
440 | + (currentDate.getDate() !== scrollDate.getDate())) { |
441 | + anchorDate = new Date(scrollDate) |
442 | + needScroll = true |
443 | + } else if (!dayViewPath.currentItem.timeIsVisible(scrollDate)) { |
444 | + needScroll = true |
445 | + } |
446 | + |
447 | + if (needScroll) { |
448 | + delayScrollToDate(scrollDate, !event.allDay) |
449 | + } |
450 | + } |
451 | |
452 | Action { |
453 | id: calendarTodayAction |
454 | @@ -40,17 +71,38 @@ |
455 | iconName: "calendar-today" |
456 | text: i18n.tr("Today") |
457 | onTriggered: { |
458 | - currentDay = new Date() |
459 | + anchorDate = new Date() |
460 | + delayScrollToDate(anchorDate) |
461 | + } |
462 | + } |
463 | + |
464 | + |
465 | + Timer { |
466 | + id: idleScroll |
467 | + |
468 | + property var scrollToDate: null |
469 | + property bool scrollToTime: true |
470 | + |
471 | + interval: 200 |
472 | + repeat:false |
473 | + onTriggered: { |
474 | + if (scrollToDate && scrollToTime) { |
475 | + dayViewPath.currentItem.scrollToTime(scrollToDate) |
476 | + } else { |
477 | + dayViewPath.currentItem.scrollToBegin() |
478 | + } |
479 | + scrollToDate = null |
480 | + scrollToTime = true |
481 | } |
482 | } |
483 | |
484 | header: PageHeader { |
485 | id: pageHeader |
486 | |
487 | + flickable: null |
488 | leadingActionBar.actions: tabs.tabsAction |
489 | trailingActionBar.actions: [ |
490 | calendarTodayAction, |
491 | - commonHeaderActions.newEventAction, |
492 | commonHeaderActions.showCalendarAction, |
493 | commonHeaderActions.reloadAction, |
494 | commonHeaderActions.syncCalendarAction, |
495 | @@ -58,105 +110,93 @@ |
496 | ] |
497 | |
498 | title: { |
499 | - // TRANSLATORS: this is a time formatting string, |
500 | - // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions. |
501 | - // It's used in the header of the month and week views |
502 | - var monthName = currentDay.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy")) |
503 | - return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1) |
504 | + if(dayViewPage.displayLunarCalendar){ |
505 | + var lunarDate = Lunar.calendar.solar2lunar(currentDate.getFullYear(), |
506 | + currentDate.getMonth() + 1, |
507 | + currentDate.getDate()) |
508 | + return ("%1 %2").arg(lunarDate .IMonthCn).arg(lunarDate.gzYear) |
509 | + } else { |
510 | + // TRANSLATORS: this is a time formatting string, |
511 | + // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions. |
512 | + // It's used in the header of the month and week views |
513 | + var monthName = currentDate.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy")) |
514 | + return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1) |
515 | + } |
516 | } |
517 | + } |
518 | |
519 | + onBottomEdgeCommitStarted: { |
520 | + var eventAt = new Date() |
521 | + if (dayViewPath.currentItem) { |
522 | + eventAt.setFullYear(currentDate.getFullYear()) |
523 | + eventAt.setMonth(currentDate.getMonth()) |
524 | + eventAt.setDate(currentDate.getDate()) |
525 | + } |
526 | + createEventAt = eventAt |
527 | } |
528 | |
529 | PathViewBase{ |
530 | id: dayViewPath |
531 | objectName: "dayViewPath" |
532 | |
533 | - property var startDay: currentDay |
534 | + property var startDay: currentDate |
535 | //This is used to scroll all view together when currentItem scrolls |
536 | - property var childContentY; |
537 | + property real childContentY; |
538 | |
539 | anchors { |
540 | fill: parent |
541 | topMargin: header.height |
542 | } |
543 | |
544 | - onNextItemHighlighted: { |
545 | - //next day |
546 | - currentDay = currentDay.addDays(1); |
547 | - } |
548 | - |
549 | - onPreviousItemHighlighted: { |
550 | - //previous day |
551 | - currentDay = currentDay.addDays(-1); |
552 | - } |
553 | - |
554 | - delegate: Loader { |
555 | + delegate: TimeLineBaseComponent { |
556 | + id: timeLineView |
557 | + objectName: "DayComponent-"+index |
558 | + |
559 | width: parent.width |
560 | height: parent.height |
561 | - asynchronous: !dayViewPath.isCurrentItem |
562 | - sourceComponent: delegateComponent |
563 | - |
564 | - Component { |
565 | - id: delegateComponent |
566 | - |
567 | - TimeLineBaseComponent { |
568 | - id: timeLineView |
569 | - objectName: "DayComponent-"+index |
570 | - |
571 | - type: ViewType.ViewTypeDay |
572 | - anchors.fill: parent |
573 | - |
574 | - isActive: parent.PathView.isCurrentItem |
575 | - contentInteractive: parent.PathView.isCurrentItem |
576 | - startDay: dayViewPath.startDay.addDays(dayViewPath.indexType(index)) |
577 | - keyboardEventProvider: dayViewPath |
578 | - |
579 | - Component.onCompleted: { |
580 | - if(dayViewPage.isCurrentPage){ |
581 | - timeLineView.scrollToCurrentTime(); |
582 | - } |
583 | - } |
584 | - |
585 | - Connections{ |
586 | - target: dayViewPage |
587 | - onIsCurrentPageChanged:{ |
588 | - if(dayViewPage.isCurrentPage){ |
589 | - timeLineView.scrollToCurrentTime(); |
590 | - } |
591 | - } |
592 | - } |
593 | - |
594 | - //get contentY value from PathView, if its not current Item |
595 | - Binding{ |
596 | - target: timeLineView |
597 | - property: "contentY" |
598 | - value: dayViewPath.childContentY; |
599 | - when: !parent.PathView.isCurrentItem |
600 | - } |
601 | - |
602 | - //set PathView's contentY property, if its current item |
603 | - Binding{ |
604 | - target: dayViewPath |
605 | - property: "childContentY" |
606 | - value: contentY |
607 | - when: parent.PathView.isCurrentItem |
608 | - } |
609 | - } |
610 | + |
611 | + type: ViewType.ViewTypeDay |
612 | + isCurrentItem: PathView.isCurrentItem |
613 | + isActive: !dayViewPath.moving && !dayViewPath.flicking |
614 | + contentInteractive: PathView.isCurrentItem |
615 | + startDay: anchorDate.addDays(dayViewPath.loopCurrentIndex + dayViewPath.indexType(index)) |
616 | + keyboardEventProvider: dayViewPath |
617 | + modelFilter: dayViewPage.model ? dayViewPage.model.filter : null |
618 | + |
619 | + onPressAndHoldAt: { |
620 | + dayViewPage.pressAndHoldAt(date, allDay) |
621 | + } |
622 | + |
623 | + Component.onCompleted: { |
624 | + if(dayViewPage.tabSelected){ |
625 | + idleScroll.restart() |
626 | + } |
627 | + } |
628 | + |
629 | + Connections{ |
630 | + target: dayViewPage |
631 | + onTabSelectedChanged: { |
632 | + if(tabSelected){ |
633 | + timeLineView.scrollToTime(new Date()); |
634 | + } |
635 | + } |
636 | + } |
637 | + |
638 | + //get contentY value from PathView, if its not current Item |
639 | + Binding{ |
640 | + target: timeLineView |
641 | + property: "contentY" |
642 | + value: dayViewPath.childContentY; |
643 | + when: !timeLineView.isCurrentItem |
644 | + } |
645 | + |
646 | + //set PathView's contentY property, if its current item |
647 | + Binding{ |
648 | + target: dayViewPath |
649 | + property: "childContentY" |
650 | + value: timeLineView.contentY |
651 | + when: timeLineView.isCurrentItem |
652 | } |
653 | } |
654 | } |
655 | - |
656 | - Component.onCompleted: { |
657 | - pageHeader.title = Qt.binding(function(){ |
658 | - if(mainView.displayLunarCalendar){ |
659 | - var lunarDate = Lunar.calendar.solar2lunar(currentDay.getFullYear(), |
660 | - currentDay.getMonth() + 1, |
661 | - currentDay.getDate()) |
662 | - return i18n.tr("%1 %2").arg(lunarDate .IMonthCn).arg(lunarDate.gzYear) |
663 | - } else { |
664 | - var monthName = currentDay.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy")) |
665 | - return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1) |
666 | - } |
667 | - }) |
668 | - } |
669 | } |
670 | |
671 | === modified file 'EventActions.qml' |
672 | --- EventActions.qml 2016-02-01 19:49:33 +0000 |
673 | +++ EventActions.qml 2016-03-07 15:59:03 +0000 |
674 | @@ -23,10 +23,10 @@ |
675 | Item { |
676 | id: actionPool |
677 | |
678 | - property alias newEventAction: _newEventAction |
679 | property alias showCalendarAction: _showCalendarAction |
680 | property alias syncCalendarAction: _syncCalendarAction |
681 | property alias settingsAction: _settingsAction |
682 | + readonly property bool syncInProgress: (syncMonitor.state === "syncing") |
683 | |
684 | Action { |
685 | id: _syncCalendarAction |
686 | @@ -36,7 +36,7 @@ |
687 | // Currently ,there is no way we can increase width of action menu currently. |
688 | text: enabled ? i18n.tr("Sync") : i18n.tr("Syncing") |
689 | onTriggered: syncMonitor.sync(["calendar"]) |
690 | - enabled: (syncMonitor.state !== "syncing") |
691 | + enabled: !syncInProgress |
692 | visible: syncMonitor.enabledServices ? syncMonitor.serviceIsEnabled("calendar") : false |
693 | } |
694 | |
695 | @@ -44,17 +44,6 @@ |
696 | id: syncMonitor |
697 | } |
698 | |
699 | - Action { |
700 | - id: _newEventAction |
701 | - objectName: "neweventbutton" |
702 | - name: "neweventbutton" |
703 | - iconName: "new-event" |
704 | - text: i18n.tr("New Event") |
705 | - onTriggered: { |
706 | - pageStack.push(Qt.resolvedUrl("NewEvent.qml"),{"date":tabs.currentDay,"model":eventModel}); |
707 | - } |
708 | - } |
709 | - |
710 | Action{ |
711 | id: _showCalendarAction |
712 | objectName: "calendarsbutton" |
713 | |
714 | === modified file 'EventBubble.qml' |
715 | --- EventBubble.qml 2016-01-29 14:35:14 +0000 |
716 | +++ EventBubble.qml 2016-03-07 15:59:03 +0000 |
717 | @@ -20,54 +20,39 @@ |
718 | import Ubuntu.Components 1.3 |
719 | import QtOrganizer 5.0 |
720 | |
721 | +import "calendar_canvas.js" as CanlendarCanvas |
722 | + |
723 | Item{ |
724 | id: infoBubble |
725 | |
726 | + property var anchorDate; |
727 | property var event; |
728 | - property var model; |
729 | - |
730 | + property var model: null |
731 | + property int depthInRow: 0 |
732 | + property real sizeOfRow:0.0 |
733 | + property real minuteHeight: 1.0 |
734 | property int type: narrowType |
735 | property int wideType: 1; |
736 | property int narrowType: 2; |
737 | - |
738 | - property int depthInRow: 0; |
739 | - property int sizeOfRow:0 |
740 | - |
741 | property bool isLiveEditing: false |
742 | - |
743 | property Flickable flickable; |
744 | + property bool isEventBubble: true |
745 | |
746 | readonly property int minimumHeight: type == wideType |
747 | ? detailsItems.timeLabelHeight + /*top-bottom margin*/ units.gu(2) |
748 | : units.gu(2) |
749 | |
750 | - z: depthInRow |
751 | + readonly property real startTimeInMinutes: event ? CanlendarCanvas.minutesSince(infoBubble.anchorDate, event.startDateTime) : 0.0 |
752 | + readonly property real endTimeInMinutes: event ? CanlendarCanvas.minutesSince(infoBubble.anchorDate, event.endDateTime) : 0.0 |
753 | + readonly property real durationInMinutes: endTimeInMinutes - startTimeInMinutes |
754 | |
755 | signal clicked(var event); |
756 | |
757 | - Rectangle{ |
758 | - id: bg |
759 | - anchors.fill: parent |
760 | - border.color: isLiveEditing ? "red" : "white" |
761 | - } |
762 | - |
763 | - function resize() { |
764 | - var offset = parent.width/sizeOfRow; |
765 | - x = (depthInRow) * offset; |
766 | - width = parent.width - x; |
767 | - } |
768 | - |
769 | - Connections{ |
770 | - target: parent |
771 | - onWidthChanged:{ |
772 | - resize(); |
773 | - } |
774 | - } |
775 | - |
776 | - onEventChanged: { |
777 | - resize(); |
778 | - assingnBgColor(); |
779 | - setDetails(); |
780 | + // keep color up-to-date |
781 | + Connections { |
782 | + target: model |
783 | + ignoreUnknownSignals: true |
784 | + onCollectionsChanged: assingnBgColor() |
785 | } |
786 | |
787 | function assingnBgColor() { |
788 | @@ -102,20 +87,6 @@ |
789 | } |
790 | } |
791 | |
792 | - function layoutBubbleDetails() { |
793 | - if( !flickable || flickable === undefined ) { |
794 | - return; |
795 | - } |
796 | - |
797 | - if( infoBubble.y < flickable.contentY && infoBubble.height > flickable.height) { |
798 | - var y = (flickable.contentY - infoBubble.y) * 1.2; |
799 | - if( ( y + detailsItems.height + units.gu(2)) > infoBubble.height) { |
800 | - y = infoBubble.height - detailsItems.height - units.gu(2); |
801 | - } |
802 | - detailsItems.y = y; |
803 | - } |
804 | - } |
805 | - |
806 | function setDetails() { |
807 | if(event === null || event === undefined) { |
808 | return; |
809 | @@ -153,8 +124,38 @@ |
810 | timeLabel.horizontalAlignment = Text.AlignHCenter |
811 | timeLabel.wrapMode = Text.WrapAtWordBoundaryOrAnywhere |
812 | } |
813 | - |
814 | - layoutBubbleDetails(); |
815 | + } |
816 | + |
817 | + function resize() |
818 | + { |
819 | + width = parent ? parent.width * sizeOfRow : 0 |
820 | + x = depthInRow * width |
821 | + z = depthInRow |
822 | + height = Math.max(30, (durationInMinutes * parent.minuteHeight)) |
823 | + } |
824 | + |
825 | + onEventChanged: { |
826 | + assingnBgColor(); |
827 | + setDetails(); |
828 | + resize() |
829 | + } |
830 | + |
831 | + Connections { |
832 | + target: parent |
833 | + onWidthChanged: resize() |
834 | + } |
835 | + |
836 | + Binding { |
837 | + target: infoBubble |
838 | + property: "y" |
839 | + value: (startTimeInMinutes * parent.minuteHeight) |
840 | + when: !infoBubble.isLiveEditing |
841 | + } |
842 | + |
843 | + Rectangle{ |
844 | + id: bg |
845 | + anchors.fill: parent |
846 | + border.color: isLiveEditing ? "red" : "white" |
847 | } |
848 | |
849 | Item { |
850 | @@ -193,25 +194,6 @@ |
851 | wrapMode: Text.WrapAtWordBoundaryOrAnywhere |
852 | } |
853 | } |
854 | - |
855 | - onHeightChanged: { |
856 | - layoutBubbleDetails(); |
857 | - } |
858 | - |
859 | - Connections { |
860 | - target: infoBubble |
861 | - onFlickableChanged: { |
862 | - if (flickable && infoBubble.height > flickable.height) { |
863 | - flickable.onContentYChanged.connect(layoutBubbleDetails); |
864 | - } |
865 | - } |
866 | - |
867 | - onHeightChanged: { |
868 | - if(flickable && infoBubble.height > flickable.height) { |
869 | - flickable.onContentYChanged.connect(layoutBubbleDetails); |
870 | - } |
871 | - } |
872 | - } |
873 | } |
874 | |
875 | Drag.active: dragArea.drag.active |
876 | @@ -219,11 +201,19 @@ |
877 | MouseArea { |
878 | id: dragArea |
879 | anchors.fill: parent |
880 | - drag.target: isLiveEditing ? infoBubble : null |
881 | - drag.axis: Drag.YAxis |
882 | - drag.minimumY: flickable ? flickable.y : 0 |
883 | - drag.maximumY: flickable ? flickable.contentHeight - infoBubble.height : infoBubble.height |
884 | - onReleased: parent.Drag.drop() |
885 | + drag { |
886 | + target: isLiveEditing ? infoBubble : null |
887 | + axis: Drag.YAxis |
888 | + minimumY: flickable ? flickable.y : 0 |
889 | + maximumY: flickable ? flickable.contentHeight - infoBubble.height : infoBubble.height |
890 | + } |
891 | + onReleased: { |
892 | + if (isLiveEditing) { |
893 | + isLiveEditing = false; |
894 | + infoBubble.z -= 1; |
895 | + } |
896 | + parent.Drag.drop() |
897 | + } |
898 | onClicked: { |
899 | if( isLiveEditing ) { |
900 | isLiveEditing = false; |
901 | |
902 | === removed file 'EventLayoutHelper.js' |
903 | --- EventLayoutHelper.js 2015-11-27 01:34:46 +0000 |
904 | +++ EventLayoutHelper.js 1970-01-01 00:00:00 +0000 |
905 | @@ -1,132 +0,0 @@ |
906 | -/* |
907 | - * Copyright (C) 2013-2014 Canonical Ltd |
908 | - * |
909 | - * This file is part of Ubuntu Calendar App |
910 | - * |
911 | - * Ubuntu Calendar App is free software: you can redistribute it and/or modify |
912 | - * it under the terms of the GNU General Public License version 3 as |
913 | - * published by the Free Software Foundation. |
914 | - * |
915 | - * Ubuntu Calendar App is distributed in the hope that it will be useful, |
916 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
917 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
918 | - * GNU General Public License for more details. |
919 | - * |
920 | - * You should have received a copy of the GNU General Public License |
921 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
922 | - */ |
923 | -WorkerScript.onMessage = function(events) { |
924 | - |
925 | - //returns sorted array of schedules |
926 | - //schedule is start time and duration |
927 | - var allSchs = processEvents(events); |
928 | - |
929 | - while( allSchs.length > 0) { |
930 | - var sch = allSchs.shift(); |
931 | - |
932 | - //finds all schedules overlapping with current schedule and remove from original array |
933 | - var schs = findOverlappingSchedules(sch, allSchs); |
934 | - |
935 | - //insert original schedule first, so array remain sorted |
936 | - schs.unshift(sch); |
937 | - |
938 | - //assign position to schedules with respest to their duration and start time |
939 | - var array = []; |
940 | - var maxDepth = assignDepth(schs, array); |
941 | - WorkerScript.sendMessage({ 'schedules': array,"maxDepth":maxDepth}); |
942 | - } |
943 | -} |
944 | - |
945 | -function processEvents(events) { |
946 | - var array = []; |
947 | - for( var i = 0; i < events.length ; ++i) { |
948 | - var event = events[i] |
949 | - var sch = {}; |
950 | - sch["start"] = event.startDateTime.getTime() |
951 | - sch["duration"] = event.endDateTime - event.startDateTime |
952 | - sch["id"] = event.id; |
953 | - sch["depth"] = 0; |
954 | - sortedInsert(array,sch); |
955 | - } |
956 | - return array; |
957 | -} |
958 | - |
959 | -//insert in to array using insertion sort |
960 | -function sortedInsert(array, sch) { |
961 | - for(var i = array.length-1; i >=0 ; --i) { |
962 | - var temp = array[i]; |
963 | - if( sch.duration < array[i].duration) { |
964 | - array.splice(i+1,0,sch); |
965 | - return; |
966 | - } |
967 | - } |
968 | - array.push(sch); |
969 | -} |
970 | - |
971 | -//find all overlapping schedules respect to provided schedule |
972 | -function findOverlappingSchedules( sch, schs) { |
973 | - var array = []; |
974 | - var i = 0; |
975 | - while( i < schs.length && schs.length > 0) { |
976 | - var otherSch = schs[i]; |
977 | - if( doesOverlap(sch, otherSch) ) { |
978 | - schs.splice(i,1); |
979 | - array.push(otherSch); |
980 | - sch = mergeSchedules(sch,otherSch); |
981 | - } else { |
982 | - i++; |
983 | - } |
984 | - } |
985 | - return array; |
986 | -} |
987 | - |
988 | -//merges tow schedules and creates a schedule which is long engouth to contain both the of them |
989 | -function mergeSchedules(sch1,sch2) { |
990 | - var sch = {}; |
991 | - sch["start"] = Math.min(sch1.start,sch2.start); |
992 | - sch["duration"] = Math.max(sch1.duration,sch2.duration); |
993 | - sch["depth"] = 1; |
994 | - sch["id"] = "DUMMY"; |
995 | - return sch; |
996 | -} |
997 | - |
998 | - |
999 | -//check if two schedules overlap each other |
1000 | -//is start time of one schedule is between |
1001 | -//start and end time of other schedule then it's considered as overlap |
1002 | -function doesOverlap( sch1, sch2) { |
1003 | - if( sch1.start >= sch2.start && sch1.start < sch2.start + sch2.duration ) { |
1004 | - return true; |
1005 | - } |
1006 | - |
1007 | - if( sch2.start >= sch1.start && sch2.start < sch1.start + sch1.duration ) { |
1008 | - return true; |
1009 | - } |
1010 | - |
1011 | - return false; |
1012 | -} |
1013 | - |
1014 | -//assign depth(position) of schedule with respect to other |
1015 | -function assignDepth(schs, array) { |
1016 | - var maxDepth = 0; |
1017 | - while( schs.length > 0 ) { |
1018 | - var sch = schs.shift() |
1019 | - array.push(sch); |
1020 | - var depth = findDepth(sch, schs); |
1021 | - maxDepth = Math.max(maxDepth,depth); |
1022 | - } |
1023 | - return maxDepth; |
1024 | -} |
1025 | - |
1026 | -function findDepth( sch, schs) { |
1027 | - var maxDepth = 0; |
1028 | - for( var i = 0; i < schs.length ; ++i) { |
1029 | - var otherSch = schs[i]; |
1030 | - if( doesOverlap(sch, otherSch) ) { |
1031 | - otherSch.depth ++; |
1032 | - maxDepth = Math.max(maxDepth,otherSch.depth); |
1033 | - sch = mergeSchedules(sch,otherSch); |
1034 | - } |
1035 | - } |
1036 | - return maxDepth; |
1037 | -} |
1038 | |
1039 | === modified file 'EventListModel.qml' |
1040 | --- EventListModel.qml 2016-01-25 13:20:32 +0000 |
1041 | +++ EventListModel.qml 2016-03-07 15:59:03 +0000 |
1042 | @@ -80,7 +80,6 @@ |
1043 | var collections = eventModel.collections; |
1044 | for(var i = 0 ; i < collections.length ; ++i) { |
1045 | var cal = collections[i]; |
1046 | - print(cal.name + " ---- " + cal.extendedMetaData("collection-readonly")); |
1047 | if( cal.extendedMetaData("collection-type") === "Calendar" && |
1048 | cal.extendedMetaData("collection-readonly") === false ) { |
1049 | cals.push(cal); |
1050 | @@ -90,16 +89,21 @@ |
1051 | } |
1052 | |
1053 | function getDefaultCollection() { |
1054 | + var defaultCol = defaultCollection(); |
1055 | + if (defaultCol.extendedMetaData("collection-selected") === true) { |
1056 | + return defaultCol |
1057 | + } |
1058 | + |
1059 | var cals = getCollections(); |
1060 | - for(var i = 0 ; i < cals.length ; ++i) { |
1061 | + for(var i = 0 ; i < cals.length ; ++i) { |
1062 | var cal = cals[i] |
1063 | - var val = cal.extendedMetaData("X-CAL-DEFAULT-CALENDAR") |
1064 | - if( val ) { |
1065 | + var val = cal.extendedMetaData("collection-selected") |
1066 | + if (val === true) { |
1067 | return cal; |
1068 | } |
1069 | } |
1070 | |
1071 | - return defaultCollection(); |
1072 | + return cals[0] |
1073 | } |
1074 | |
1075 | function setDefaultCollection( collectionId ) { |
1076 | @@ -122,4 +126,12 @@ |
1077 | startLoadingTimer(); |
1078 | } |
1079 | } |
1080 | + |
1081 | + // disable update while syncing to avoid tons of unecessary update |
1082 | + autoUpdate: !mainView.syncInProgress |
1083 | + onAutoUpdateChanged: { |
1084 | + if (autoUpdate) { |
1085 | + eventModel.update() |
1086 | + } |
1087 | + } |
1088 | } |
1089 | |
1090 | === modified file 'KeyboardRectangle.qml' |
1091 | --- KeyboardRectangle.qml 2016-01-25 13:20:32 +0000 |
1092 | +++ KeyboardRectangle.qml 2016-03-07 15:59:03 +0000 |
1093 | @@ -20,18 +20,12 @@ |
1094 | |
1095 | Item { |
1096 | id: keyboardRect |
1097 | + |
1098 | anchors.left: parent.left |
1099 | anchors.right: parent.right |
1100 | anchors.bottom: parent.bottom |
1101 | height: Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0 |
1102 | |
1103 | - Behavior on height { |
1104 | - NumberAnimation { |
1105 | - duration: 300 |
1106 | - easing.type: Easing.InOutQuad |
1107 | - } |
1108 | - } |
1109 | - |
1110 | states: [ |
1111 | State { |
1112 | name: "hidden" |
1113 | |
1114 | === modified file 'MonthComponent.qml' |
1115 | --- MonthComponent.qml 2016-02-03 08:06:25 +0000 |
1116 | +++ MonthComponent.qml 2016-03-07 15:59:03 +0000 |
1117 | @@ -17,78 +17,45 @@ |
1118 | */ |
1119 | import QtQuick 2.4 |
1120 | import Ubuntu.Components 1.3 |
1121 | + |
1122 | import "dateExt.js" as DateExt |
1123 | import "colorUtils.js" as Color |
1124 | +import "./3rd-party/lunar.js" as Lunar |
1125 | |
1126 | Item{ |
1127 | id: root |
1128 | objectName: "MonthComponent" |
1129 | |
1130 | property bool isCurrentItem; |
1131 | - |
1132 | - property bool showEvents: false |
1133 | - |
1134 | - property var currentMonth; |
1135 | + property int currentYear; |
1136 | + property int currentMonth; |
1137 | + |
1138 | property var isYearView; |
1139 | - property var selectedDay; |
1140 | - property bool displayWeekNumber:false; |
1141 | - property bool displayLunarCalendar: false; |
1142 | - |
1143 | - property string subLabelFontSize: "small" |
1144 | + property var highlightedDate; |
1145 | + property bool displayWeekNumber:false |
1146 | + property bool displayLunarCalendar: false |
1147 | + |
1148 | + property alias dayLabelDelegate : dayLabelRepeater.delegate |
1149 | + property alias dateLabelDelegate : dateLabelRepeater.delegate |
1150 | + readonly property alias monthStartDate: intern.monthStart |
1151 | + |
1152 | property string dayLabelFontSize: "medium" |
1153 | property string dateLabelFontSize: "large" |
1154 | property string monthLabelFontSize: "large" |
1155 | property string yearLabelFontSize: "large" |
1156 | |
1157 | - property alias dayLabelDelegate : dayLabelRepeater.delegate |
1158 | - property alias dateLabelDelegate : dateLabelRepeater.delegate |
1159 | - |
1160 | signal monthSelected(var date); |
1161 | signal dateSelected(var date); |
1162 | signal dateHighlighted(var date); |
1163 | |
1164 | - //creatng timer only if we need to show events in month |
1165 | - Loader { |
1166 | - id: timerLoader |
1167 | - sourceComponent: showEvents ? timerComp : undefined |
1168 | - } |
1169 | - |
1170 | - // Timer to delay creation of Model, There seems some problem fetching events if we create Model immediatly |
1171 | - Component { |
1172 | - id: timerComp |
1173 | - Timer{ |
1174 | - interval: 200; running: true; repeat: false |
1175 | - onTriggered: { |
1176 | - modelLoader.sourceComponent = modelComponent |
1177 | - } |
1178 | - } |
1179 | - } |
1180 | - |
1181 | - Loader{ |
1182 | - id: modelLoader |
1183 | - } |
1184 | - |
1185 | - Component{ |
1186 | - id: modelComponent |
1187 | - EventListModel { |
1188 | - id: mainModel |
1189 | - startPeriod: intern.monthStart.midnight(); |
1190 | - endPeriod: intern.monthStart.addDays((/*monthGrid.rows * cols */ 42 )-1).endOfDay() |
1191 | - filter: eventModel.filter |
1192 | - onModelChanged: { |
1193 | - intern.eventStatus = Qt.binding(function() { return mainModel.containsItems(startPeriod, endPeriod, 86400/*24*60*60*/)}); |
1194 | - } |
1195 | - } |
1196 | + function updateEvents(events) { |
1197 | + intern.eventStatus = events |
1198 | } |
1199 | |
1200 | QtObject{ |
1201 | id: intern |
1202 | |
1203 | - property var eventStatus; |
1204 | - |
1205 | - property int curMonthDate: currentMonth.getDate() |
1206 | - property int curMonth: currentMonth.getMonth() |
1207 | - property int curMonthYear: currentMonth.getFullYear() |
1208 | + property var eventStatus: new Array(42) |
1209 | |
1210 | property var today: DateExt.today() |
1211 | property int todayDate: today.getDate() |
1212 | @@ -96,36 +63,42 @@ |
1213 | property int todayYear: today.getFullYear() |
1214 | |
1215 | //date from month will start, this date might be from previous month |
1216 | - property var monthStart: currentMonth.weekStart( Qt.locale().firstDayOfWeek ) |
1217 | + property var currentDate: new Date(root.currentYear, root.currentMonth, 1, 0, 0, 0, 0) |
1218 | + property var monthStart: currentDate.weekStart( Qt.locale().firstDayOfWeek ) |
1219 | property int monthStartDate: monthStart.getDate() |
1220 | property int monthStartMonth: monthStart.getMonth() |
1221 | property int monthStartYear: monthStart.getFullYear() |
1222 | - |
1223 | - property int daysInStartMonth: Date.daysInMonth(monthStartYear, monthStartMonth) |
1224 | - property int daysInCurMonth: Date.daysInMonth(curMonthYear,curMonth) |
1225 | + readonly property int daysInStartMonth: Date.daysInMonth(monthStartYear, monthStartMonth) |
1226 | |
1227 | //check if current month is start month |
1228 | - property bool isCurMonthStartMonth: curMonthDate === monthStartDate |
1229 | - && curMonth === monthStartMonth |
1230 | - && curMonthYear === monthStartYear |
1231 | + property bool isCurMonthStartMonth: root.currentMonth === monthStartMonth && |
1232 | + root.currentYear === monthStartYear |
1233 | |
1234 | //check current month is same as today's month |
1235 | - property bool isCurMonthTodayMonth: todayYear === curMonthYear && todayMonth == curMonth |
1236 | + property bool isCurMonthTodayMonth: todayYear === root.currentYear && |
1237 | + todayMonth == root.currentMonth |
1238 | //offset from current month's first date to start date of current month |
1239 | property int offset: isCurMonthStartMonth ? -1 : (daysInStartMonth - monthStartDate) |
1240 | |
1241 | property int dateFontSize: FontUtils.sizeToPixels(root.dateLabelFontSize) |
1242 | property int dayFontSize: FontUtils.sizeToPixels(root.dayLabelFontSize) |
1243 | |
1244 | - property int selectedIndex: -1 |
1245 | + property int highlightedIndex: root.isCurrentItem && |
1246 | + root.highlightedDate ? |
1247 | + intern.indexByDate(root.highlightedDate) : -1 |
1248 | + property int todayIndex: root.isCurrentItem && |
1249 | + isCurMonthTodayMonth ? |
1250 | + intern.indexByDate(intern.today) : -1 |
1251 | |
1252 | - function findSelectedDayIndex(){ |
1253 | - if(!selectedDay) { |
1254 | + function indexByDate(date){ |
1255 | + if (!date) { |
1256 | return -1; |
1257 | } |
1258 | |
1259 | - if( todayMonth === selectedDay.getMonth() && selectedDay.getFullYear() === todayYear){ |
1260 | - return selectedDay.getDate() + |
1261 | + if ((root.currentMonth === date.getMonth()) && |
1262 | + (root.currentYear === date.getFullYear())) { |
1263 | + |
1264 | + return date.getDate() + |
1265 | (Date.daysInMonth(monthStartYear, monthStartMonth) - monthStartDate); |
1266 | } else { |
1267 | return -1; |
1268 | @@ -133,14 +106,40 @@ |
1269 | } |
1270 | } |
1271 | |
1272 | - onSelectedDayChanged: { |
1273 | - if( isCurrentItem ) { |
1274 | - intern.selectedIndex = intern.findSelectedDayIndex(); |
1275 | + UbuntuShape{ |
1276 | + id: todayShape |
1277 | + |
1278 | + visible: (monthGrid.todayItem != null) |
1279 | + color: (monthGrid.highlightedItem === monthGrid.todayItem) ? UbuntuColors.darkGrey : UbuntuColors.orange |
1280 | + width: parent ? Math.min(parent.height, parent.width) / 1.3 : 0 |
1281 | + height: width |
1282 | + parent: monthGrid.todayItem |
1283 | + anchors.centerIn: parent |
1284 | + z: -1 |
1285 | + Rectangle { |
1286 | + anchors.fill: parent |
1287 | + anchors.margins: units.gu(0.5) |
1288 | + color: UbuntuColors.orange |
1289 | + radius: 5 |
1290 | } |
1291 | } |
1292 | |
1293 | - onCurrentMonthChanged: { |
1294 | - intern.selectedIndex = -1; |
1295 | + UbuntuShape{ |
1296 | + id: highlightedShape |
1297 | + |
1298 | + visible: monthGrid.highlightedItem && (monthGrid.highlightedItem != monthGrid.todayItem) |
1299 | + color: UbuntuColors.darkGrey |
1300 | + width: parent ? Math.min(parent.height, parent.width) / 1.3 : 0 |
1301 | + height: width |
1302 | + parent: monthGrid.highlightedItem |
1303 | + anchors.centerIn: parent |
1304 | + z: -1 |
1305 | + Rectangle { |
1306 | + anchors.fill: parent |
1307 | + anchors.margins: units.gu(0.5) |
1308 | + color: UbuntuColors.lightGrey |
1309 | + radius: 5 |
1310 | + } |
1311 | } |
1312 | |
1313 | Column{ |
1314 | @@ -166,9 +165,9 @@ |
1315 | ViewHeader{ |
1316 | id: monthHeader |
1317 | anchors.fill: parent |
1318 | - month: intern.curMonth |
1319 | - year: intern.curMonthYear |
1320 | - daysInMonth: intern.daysInCurMonth |
1321 | + month: root.currentMonth |
1322 | + year: root.currentYear |
1323 | + daysInMonth: intern.daysInStartMonth |
1324 | |
1325 | monthLabelFontSize: root.monthLabelFontSize |
1326 | yearLabelFontSize: root.yearLabelFontSize |
1327 | @@ -186,9 +185,11 @@ |
1328 | |
1329 | property int dayWidth: width / 7; |
1330 | |
1331 | - width: parent.width |
1332 | - anchors.horizontalCenter: parent.horizontalCenter |
1333 | - anchors.verticalCenter: parent.verticalCenter |
1334 | + anchors{ |
1335 | + left: parent.left |
1336 | + right: parent.right |
1337 | + verticalCenter: parent.verticalCenter |
1338 | + } |
1339 | |
1340 | Repeater{ |
1341 | id: dayLabelRepeater |
1342 | @@ -198,23 +199,75 @@ |
1343 | } |
1344 | } |
1345 | |
1346 | - Grid{ |
1347 | + Grid { |
1348 | id: monthGrid |
1349 | objectName: "monthGrid" |
1350 | |
1351 | - width: parent.width |
1352 | - height: parent.height - monthGrid.y |
1353 | - |
1354 | property int dayWidth: width / 7 /*cols*/; |
1355 | property int dayHeight: height / 6/*rows*/; |
1356 | + readonly property var todayItem: (intern.todayIndex != -1) && |
1357 | + (monthGrid.children.length > intern.todayIndex) ? |
1358 | + dateLabelRepeater.itemAt(intern.todayIndex) : null |
1359 | + readonly property var highlightedItem: (intern.highlightedIndex != -1) && |
1360 | + (monthGrid.children.length > intern.highlightedIndex) ? |
1361 | + dateLabelRepeater.itemAt(intern.highlightedIndex) : null |
1362 | |
1363 | - rows: 6 |
1364 | + anchors { |
1365 | + left: parent.left |
1366 | + right: parent.right |
1367 | + } |
1368 | + height: parent.height - monthGrid.y |
1369 | columns: 7 |
1370 | |
1371 | Repeater{ |
1372 | id: dateLabelRepeater |
1373 | - model: 42 //monthGrid.rows * monthGrid.columns |
1374 | - delegate: defaultDateLabelComponent |
1375 | + model: 42 |
1376 | + delegate: isYearView ? monthWithoutEventsDelegate : monthWithEventsDelegate |
1377 | + } |
1378 | + } |
1379 | + } |
1380 | + |
1381 | + MouseArea { |
1382 | + id: mouseArea |
1383 | + |
1384 | + function getItemAt(x, y) |
1385 | + { |
1386 | + var clickPosition = mouseArea.mapToItem(monthGrid, x, y) |
1387 | + return monthGrid.childAt(clickPosition.x, clickPosition.y) |
1388 | + } |
1389 | + |
1390 | + anchors { |
1391 | + fill: column |
1392 | + topMargin: monthGrid.y |
1393 | + } |
1394 | + |
1395 | + onPressAndHold: { |
1396 | + var dayItem = getItemAt(mouse.x, mouse.y) |
1397 | + |
1398 | + if( dayItem.isSelected ) { |
1399 | + var selectedDate = new Date(); |
1400 | + selectedDate.setFullYear(intern.monthStartYear) |
1401 | + selectedDate.setMonth(intern.monthStartMonth + 1) |
1402 | + selectedDate.setDate(dayItem.date) |
1403 | + selectedDate.setMinutes(60, 0, 0) |
1404 | + pageStack.push(Qt.resolvedUrl("NewEvent.qml"), {"date":selectedDate, "model":eventModel}); |
1405 | + } |
1406 | + } |
1407 | + onClicked: { |
1408 | + var dayItem = getItemAt(mouse.x, mouse.y) |
1409 | + var selectedDate = new Date(intern.monthStartYear, |
1410 | + intern.monthStartMonth + 1, |
1411 | + dayItem.date, 0, 0, 0, 0) |
1412 | + if (root.isYearView) { |
1413 | + //If yearView is clicked then open selected MonthView |
1414 | + root.monthSelected(selectedDate); |
1415 | + } else { |
1416 | + if (dayItem.isSelected) { |
1417 | + //If monthView is clicked then open selected DayView |
1418 | + root.dateSelected(selectedDate); |
1419 | + } else { |
1420 | + root.dateHighlighted(selectedDate) |
1421 | + } |
1422 | } |
1423 | } |
1424 | } |
1425 | @@ -301,53 +354,9 @@ |
1426 | } |
1427 | |
1428 | Component{ |
1429 | - id: defaultDateLabelComponent |
1430 | - MonthComponentDateDelegate{ |
1431 | - date: { |
1432 | - //try to find date from index and month's first week's first date |
1433 | - var temp = intern.daysInStartMonth - intern.offset + index |
1434 | - //date exceeds days in startMonth, |
1435 | - //this means previous month is over and we are now in current month |
1436 | - //to get actual date we need to remove number of days in startMonth |
1437 | - if( temp > intern.daysInStartMonth ) { |
1438 | - temp = temp - intern.daysInStartMonth |
1439 | - //date exceeds days in current month |
1440 | - // this means date is from next month |
1441 | - //to get actual date we need to remove number of days in current month |
1442 | - if( temp > intern.daysInCurMonth ) { |
1443 | - temp = temp - intern.daysInCurMonth |
1444 | - } |
1445 | - } |
1446 | - return temp; |
1447 | - } |
1448 | - |
1449 | - isCurrentMonth: { |
1450 | - //remove offset from index |
1451 | - //if index falls in 1 to no of days in current month |
1452 | - //then date is inside current month |
1453 | - var temp = index - intern.offset |
1454 | - return (temp >= 1 && temp <= intern.daysInCurMonth) |
1455 | - } |
1456 | - |
1457 | - isToday: intern.todayDate == date && intern.isCurMonthTodayMonth |
1458 | - |
1459 | - isSelected: showEvents && intern.selectedIndex == index |
1460 | - |
1461 | - width: parent.dayWidth |
1462 | - height: parent.dayHeight |
1463 | - fontSize: intern.dateFontSize |
1464 | - showLunarCalendar: displayLunarCalendar |
1465 | - showEvent: showEvents |
1466 | - && intern.eventStatus !== undefined |
1467 | - && intern.eventStatus[index] !== undefined |
1468 | - && intern.eventStatus[index] |
1469 | - } |
1470 | - } |
1471 | - |
1472 | - Component{ |
1473 | id: dafaultDayLabelComponent |
1474 | |
1475 | - Label{ |
1476 | + Text { |
1477 | id: weekDay |
1478 | objectName: "weekDay" + index |
1479 | width: parent.dayWidth |
1480 | @@ -359,4 +368,48 @@ |
1481 | color: "black" |
1482 | } |
1483 | } |
1484 | + Component { |
1485 | + id: monthWithEventsDelegate |
1486 | + |
1487 | + MonthComponentWithEventsDateDelegate { |
1488 | + property var delegateDate: intern.monthStart.addDays(index) |
1489 | + |
1490 | + date: delegateDate.getDate() |
1491 | + isCurrentMonth: delegateDate.getMonth() === root.currentMonth |
1492 | + showEvent: intern.eventStatus[index] === true |
1493 | + lunarData: { |
1494 | + if (!root.displayLunarCalendar) |
1495 | + return null |
1496 | + |
1497 | + var lunar = Lunar.calendar.solar2lunar(intern.monthStartYear, |
1498 | + intern.monthStartMonth + 1, |
1499 | + intern.monthStartDate + index) |
1500 | + if (lunar.isTerm) { |
1501 | + return {"lunarText": lunar.Term, "isTerm" :lunar.isTerm}; |
1502 | + } else { |
1503 | + return {"lunarText": lunar.IDayCn, "isTerm" :lunar.isTerm}; |
1504 | + } |
1505 | + } |
1506 | + isToday: intern.todayDate == date && intern.isCurMonthTodayMonth |
1507 | + isSelected: intern.highlightedIndex == index |
1508 | + width: monthGrid.dayWidth |
1509 | + height: monthGrid.dayHeight |
1510 | + } |
1511 | + } |
1512 | + |
1513 | + Component { |
1514 | + id: monthWithoutEventsDelegate |
1515 | + |
1516 | + MonthComponentDateDelegate { |
1517 | + property var delegateDate: intern.monthStart.addDays(index) |
1518 | + |
1519 | + date: delegateDate.getDate() |
1520 | + isCurrentMonth: delegateDate.getMonth() === root.currentMonth |
1521 | + |
1522 | + isToday: intern.todayDate == date && intern.isCurMonthTodayMonth |
1523 | + isSelected: intern.highlightedIndex == index |
1524 | + width: monthGrid.dayWidth |
1525 | + height: monthGrid.dayHeight |
1526 | + } |
1527 | + } |
1528 | } |
1529 | |
1530 | === modified file 'MonthComponentDateDelegate.qml' |
1531 | --- MonthComponentDateDelegate.qml 2016-02-03 08:06:25 +0000 |
1532 | +++ MonthComponentDateDelegate.qml 2016-03-07 15:59:03 +0000 |
1533 | @@ -1,6 +1,5 @@ |
1534 | import QtQuick 2.4 |
1535 | import Ubuntu.Components 1.3 |
1536 | -import "./3rd-party/lunar.js" as Lunar |
1537 | |
1538 | Item{ |
1539 | id: dateRootItem |
1540 | @@ -8,30 +7,13 @@ |
1541 | property int date; |
1542 | property bool isCurrentMonth; |
1543 | property bool isToday; |
1544 | - property bool showEvent; |
1545 | - property bool showLunarCalendar; |
1546 | property alias fontSize: dateLabel.font.pixelSize |
1547 | - |
1548 | property bool isSelected: false |
1549 | |
1550 | - Loader { |
1551 | - sourceComponent: (isToday && isCurrentMonth) || isSelected ? highLightComp : undefined |
1552 | - |
1553 | - onSourceComponentChanged: { |
1554 | - width = Qt.binding( function() { |
1555 | - var width = dateRootItem.height > dateRootItem.width ? dateRootItem.width :dateRootItem.height |
1556 | - return ( width / 1.3 ); |
1557 | - }); |
1558 | - height = Qt.binding ( function() { return width} ); |
1559 | - anchors.centerIn = Qt.binding( function() { return dateLabel}); |
1560 | - } |
1561 | - } |
1562 | - |
1563 | - Label { |
1564 | + Text { |
1565 | id: dateLabel |
1566 | anchors.centerIn: parent |
1567 | text: date |
1568 | - fontSize: root.dateLabelFontSize |
1569 | color: { |
1570 | if( isCurrentMonth ) { |
1571 | if( isToday || isSelected ) { |
1572 | @@ -48,125 +30,4 @@ |
1573 | } |
1574 | } |
1575 | } |
1576 | - |
1577 | - Loader{ |
1578 | - sourceComponent: showLunarCalendar ? reservedFiled : undefined |
1579 | - onSourceComponentChanged: { |
1580 | - if (item != undefined) { |
1581 | - item.reservedData = Qt.binding(function(){ |
1582 | - var lunarDate = Lunar.calendar.solar2lunar(intern.monthStartYear, |
1583 | - intern.monthStartMonth + 1, |
1584 | - intern.monthStartDate + index) |
1585 | - if (lunarDate.isTerm) { |
1586 | - return {"lunarText": lunarDate.Term, "isTerm" :lunarDate.isTerm}; |
1587 | - } else { |
1588 | - return {"lunarText": lunarDate.IDayCn, "isTerm" :lunarDate.isTerm}; |
1589 | - } |
1590 | - }) |
1591 | - } |
1592 | - |
1593 | - width = Qt.binding( function() { return units.gu(0.8)}) |
1594 | - height = Qt.binding( function() { return width }) |
1595 | - anchors.horizontalCenter = Qt.binding( function() { return parent.horizontalCenter }) |
1596 | - anchors.top = Qt.binding( function() { return parent.verticalCenter }) |
1597 | - anchors.topMargin = Qt.binding( function() { |
1598 | - return (dateRootItem.height > dateRootItem.width ? dateRootItem.width :dateRootItem.height)/ 4.0 + units.gu(0.25) |
1599 | - }); |
1600 | - } |
1601 | - } |
1602 | - |
1603 | - //this component is reserved for extra information display |
1604 | - Component { |
1605 | - id: reservedFiled |
1606 | - Label { |
1607 | - id: reservedLabel |
1608 | - property var reservedData |
1609 | - onReservedDataChanged: { |
1610 | - text = reservedData.lunarText |
1611 | - if (reservedData.isTerm) |
1612 | - color = "red"; |
1613 | - } |
1614 | - |
1615 | - horizontalAlignment: Text.AlignHCenter |
1616 | - fontSize: root.subLabelFontSize |
1617 | - color: "#5D5D5D" |
1618 | - } |
1619 | - } |
1620 | - |
1621 | - Loader{ |
1622 | - sourceComponent: showEvent ? eventIndicatorComp : undefined |
1623 | - onSourceComponentChanged: { |
1624 | - width = Qt.binding( function() { return units.gu(0.8)}) |
1625 | - height = Qt.binding( function() { return width }) |
1626 | - anchors.horizontalCenter = Qt.binding( function() { return parent.horizontalCenter }) |
1627 | - anchors.top = Qt.binding( function() { return parent.verticalCenter }) |
1628 | - anchors.topMargin = Qt.binding( function() { |
1629 | - if (showLunarCalendar) { |
1630 | - return (dateRootItem.height > dateRootItem.width ? dateRootItem.width :dateRootItem.height) / 2 + units.gu(1.5) |
1631 | - } else { |
1632 | - var w = (dateRootItem.height > dateRootItem.width ? dateRootItem.width :dateRootItem.height)/1.3 |
1633 | - return (w/2) + units.gu(0.1) |
1634 | - } |
1635 | - }); |
1636 | - } |
1637 | - } |
1638 | - |
1639 | - Component{ |
1640 | - id: eventIndicatorComp |
1641 | - Rectangle { |
1642 | - anchors.fill: parent |
1643 | - radius: height/2 |
1644 | - color: "black" |
1645 | - } |
1646 | - } |
1647 | - |
1648 | - Component{ |
1649 | - id: highLightComp |
1650 | - UbuntuShape{ |
1651 | - color: { |
1652 | - if( isToday && !isSelected ) { |
1653 | - "#DD4814" |
1654 | - } else { |
1655 | - "gray" |
1656 | - } |
1657 | - } |
1658 | - |
1659 | - Rectangle{ |
1660 | - anchors.fill: parent |
1661 | - anchors.margins: units.gu(0.5) |
1662 | - color: isToday ? "#DD4814" : "darkgray" |
1663 | - } |
1664 | - } |
1665 | - } |
1666 | - |
1667 | - MouseArea { |
1668 | - anchors.fill: parent |
1669 | - onPressAndHold: { |
1670 | - if( isSelected ) { |
1671 | - var selectedDate = new Date(); |
1672 | - selectedDate.setFullYear(intern.monthStartYear) |
1673 | - selectedDate.setMonth(intern.monthStartMonth + 1) |
1674 | - selectedDate.setDate(date) |
1675 | - selectedDate.setMinutes(60, 0, 0) |
1676 | - pageStack.push(Qt.resolvedUrl("NewEvent.qml"), {"date":selectedDate, "model":eventModel}); |
1677 | - } |
1678 | - } |
1679 | - onClicked: { |
1680 | - var selectedDate = new Date(intern.monthStartYear, |
1681 | - intern.monthStartMonth, |
1682 | - intern.monthStartDate + index, 0, 0, 0, 0) |
1683 | - if( isYearView ) { |
1684 | - //If yearView is clicked then open selected MonthView |
1685 | - root.monthSelected(selectedDate); |
1686 | - } else { |
1687 | - if( isSelected ) { |
1688 | - //If monthView is clicked then open selected DayView |
1689 | - root.dateSelected(selectedDate); |
1690 | - } else { |
1691 | - intern.selectedIndex = index |
1692 | - root.dateHighlighted(selectedDate) |
1693 | - } |
1694 | - } |
1695 | - } |
1696 | - } |
1697 | } |
1698 | |
1699 | === added file 'MonthComponentWithEventsDateDelegate.qml' |
1700 | --- MonthComponentWithEventsDateDelegate.qml 1970-01-01 00:00:00 +0000 |
1701 | +++ MonthComponentWithEventsDateDelegate.qml 2016-03-07 15:59:03 +0000 |
1702 | @@ -0,0 +1,80 @@ |
1703 | +import QtQuick 2.4 |
1704 | +import Ubuntu.Components 1.3 |
1705 | + |
1706 | +Item{ |
1707 | + id: dateRootItem |
1708 | + |
1709 | + property int date; |
1710 | + property bool isCurrentMonth; |
1711 | + property bool isToday; |
1712 | + property bool showEvent; |
1713 | + property alias fontSize: dateLabel.font.pixelSize |
1714 | + property bool isSelected: false |
1715 | + property alias lunarData: lunarLabel.lunarData |
1716 | + |
1717 | + Text { |
1718 | + id: dateLabel |
1719 | + anchors.centerIn: parent |
1720 | + text: date |
1721 | + color: { |
1722 | + if( isCurrentMonth ) { |
1723 | + if( isToday || isSelected ) { |
1724 | + "white" |
1725 | + } else { |
1726 | + "#5D5D5D" |
1727 | + } |
1728 | + } else { |
1729 | + if(isSelected) { |
1730 | + "white" |
1731 | + } else { |
1732 | + "#AEA79F" |
1733 | + } |
1734 | + } |
1735 | + } |
1736 | + } |
1737 | + |
1738 | + Label { |
1739 | + id: lunarLabel |
1740 | + |
1741 | + property var lunarData: null |
1742 | + |
1743 | + text: lunarData ? lunarData.lunarText : "" |
1744 | + color: { |
1745 | + if (lunarData && lunarData.isTerm) { |
1746 | + if (isCurrentMonth && isToday) |
1747 | + return "black" |
1748 | + else |
1749 | + return UbuntuColors.red |
1750 | + } else { |
1751 | + if (isSelected) |
1752 | + return "white" |
1753 | + else |
1754 | + return "#5D5D5D" |
1755 | + } |
1756 | + } |
1757 | + fontSize: "small" |
1758 | + visible: (lunarData != null) |
1759 | + horizontalAlignment: Text.AlignHCenter |
1760 | + width: parent.width |
1761 | + anchors { |
1762 | + top: dateLabel.bottom |
1763 | + topMargin: units.gu(0.5) |
1764 | + } |
1765 | + } |
1766 | + |
1767 | + Rectangle { |
1768 | + id: eventIndicator |
1769 | + |
1770 | + width: visible ? units.gu(0.8) : 0 |
1771 | + height: width |
1772 | + anchors { |
1773 | + horizontalCenter: parent.horizontalCenter |
1774 | + //top: parent.verticalCenter |
1775 | + //topMargin: ((Math.min(parent.height, dateRootItem.width) / 1.3) / 2) + units.gu(0.1) |
1776 | + bottom: parent.bottom |
1777 | + } |
1778 | + radius: height/2 |
1779 | + color:"black" |
1780 | + visible: showEvent |
1781 | + } |
1782 | +} |
1783 | |
1784 | === modified file 'MonthView.qml' |
1785 | --- MonthView.qml 2016-02-03 08:06:25 +0000 |
1786 | +++ MonthView.qml 2016-03-07 15:59:03 +0000 |
1787 | @@ -17,21 +17,31 @@ |
1788 | */ |
1789 | import QtQuick 2.4 |
1790 | import Ubuntu.Components 1.3 |
1791 | + |
1792 | import "dateExt.js" as DateExt |
1793 | import "colorUtils.js" as Color |
1794 | import "./3rd-party/lunar.js" as Lunar |
1795 | |
1796 | -Page { |
1797 | +PageWithBottomEdge { |
1798 | id: monthViewPage |
1799 | objectName: "monthViewPage" |
1800 | |
1801 | - property var currentMonth: DateExt.today(); |
1802 | + property var anchorDate: DateExt.today(); |
1803 | + readonly property var firstDayOfAnchorDate: new Date(anchorDate.getFullYear(), |
1804 | + anchorDate.getMonth(), |
1805 | + 1, |
1806 | + 0, 0, 0) |
1807 | + readonly property var currentDate: monthViewPath.currentItem.indexDate |
1808 | + |
1809 | property var selectedDay; |
1810 | + property var highlightedDate; |
1811 | + property bool displayLunarCalendar: false |
1812 | |
1813 | signal dateSelected(var date); |
1814 | - signal dateHighlighted(var date); |
1815 | |
1816 | Keys.forwardTo: [monthViewPath] |
1817 | + createEventAt: highlightedDate ? highlightedDate : currentDate |
1818 | + onAnchorDateChanged: monthViewPath.scrollToBegginer() |
1819 | |
1820 | Action { |
1821 | id: calendarTodayAction |
1822 | @@ -39,7 +49,7 @@ |
1823 | iconName: "calendar-today" |
1824 | text: i18n.tr("Today") |
1825 | onTriggered: { |
1826 | - currentMonth = new Date().midnight() |
1827 | + anchorDate = new Date().midnight() |
1828 | } |
1829 | } |
1830 | |
1831 | @@ -49,20 +59,26 @@ |
1832 | leadingActionBar.actions: tabs.tabsAction |
1833 | trailingActionBar.actions: [ |
1834 | calendarTodayAction, |
1835 | - commonHeaderActions.newEventAction, |
1836 | commonHeaderActions.showCalendarAction, |
1837 | commonHeaderActions.reloadAction, |
1838 | commonHeaderActions.syncCalendarAction, |
1839 | commonHeaderActions.settingsAction |
1840 | ] |
1841 | title: { |
1842 | - // TRANSLATORS: this is a time formatting string, |
1843 | - // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions. |
1844 | - // It's used in the header of the month and week views |
1845 | - var monthName = currentMonth.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy")) |
1846 | - return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1) |
1847 | + if (displayLunarCalendar) { |
1848 | + var year = currentDate.getFullYear() |
1849 | + var month = currentDate.getMonth() |
1850 | + var day = Math.floor(Date.daysInMonth(year, month) / 2.0) |
1851 | + var lunarDate = Lunar.calendar.solar2lunar(year, month + 1, day) |
1852 | + return i18n.tr("%1 %2").arg(lunarDate .IMonthCn).arg(lunarDate.gzYear) |
1853 | + } else { |
1854 | + // TRANSLATORS: this is a time formatting string, |
1855 | + // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions. |
1856 | + // It's used in the header of the month and week views |
1857 | + var monthName = currentDate.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy")) |
1858 | + return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1) |
1859 | + } |
1860 | } |
1861 | - |
1862 | flickable: null |
1863 | } |
1864 | |
1865 | @@ -70,85 +86,33 @@ |
1866 | id: monthViewPath |
1867 | objectName: "monthViewPath" |
1868 | |
1869 | - property var startMonth: currentMonth; |
1870 | - |
1871 | anchors { |
1872 | fill: parent |
1873 | topMargin: header.height |
1874 | } |
1875 | |
1876 | - onNextItemHighlighted: { |
1877 | - nextMonth(); |
1878 | - } |
1879 | - |
1880 | - onPreviousItemHighlighted: { |
1881 | - previousMonth(); |
1882 | - } |
1883 | - |
1884 | - function nextMonth() { |
1885 | - currentMonth = addMonth(currentMonth, 1); |
1886 | - } |
1887 | - |
1888 | - function previousMonth() { |
1889 | - currentMonth = addMonth(currentMonth, -1); |
1890 | - } |
1891 | - |
1892 | - function addMonth(date,month) { |
1893 | - return new Date(date.getFullYear(), date.getMonth() + month, 1, 0, 0, 0); |
1894 | - } |
1895 | - |
1896 | - delegate: Loader { |
1897 | + delegate: MonthWithEventsComponent { |
1898 | + property var indexDate: firstDayOfAnchorDate.addMonths(monthViewPath.loopCurrentIndex + monthViewPath.indexType(index)) |
1899 | + |
1900 | + currentMonth: indexDate.getMonth() |
1901 | + currentYear: indexDate.getFullYear() |
1902 | + displayLunarCalendar: monthViewPage.displayLunarCalendar |
1903 | + |
1904 | + modelFilter: eventModel.filter |
1905 | width: parent.width - units.gu(4) |
1906 | height: parent.height |
1907 | - |
1908 | - sourceComponent: delegateComponent |
1909 | - asynchronous: index !== monthViewPath.currentIndex |
1910 | - |
1911 | - Component { |
1912 | - id: delegateComponent |
1913 | - |
1914 | - MonthComponent { |
1915 | - isCurrentItem: index === monthViewPath.currentIndex |
1916 | - |
1917 | - showEvents: true |
1918 | - |
1919 | - displayWeekNumber: mainView.displayWeekNumber |
1920 | - displayLunarCalendar: mainView.displayLunarCalendar |
1921 | - anchors.fill: parent |
1922 | - |
1923 | - currentMonth: monthViewPath.addMonth(monthViewPath.startMonth, |
1924 | - monthViewPath.indexType(index)); |
1925 | - |
1926 | - selectedDay: monthViewPage.selectedDay |
1927 | - isYearView: false |
1928 | - |
1929 | - onDateSelected: { |
1930 | - monthViewPage.dateSelected(date); |
1931 | - } |
1932 | - |
1933 | - onDateHighlighted: { |
1934 | - monthViewPage.dateHighlighted(date); |
1935 | - } |
1936 | - } |
1937 | + isCurrentItem: PathView.isCurrentItem |
1938 | + isActive: !monthViewPath.moving && !monthViewPath.flicking |
1939 | + displayWeekNumber: mainView.displayWeekNumber |
1940 | + highlightedDate: monthViewPage.highlightedDate |
1941 | + isYearView: false |
1942 | + |
1943 | + onDateSelected: { |
1944 | + monthViewPage.dateSelected(date); |
1945 | + } |
1946 | + onDateHighlighted: { |
1947 | + monthViewPage.highlightedDate = date |
1948 | } |
1949 | } |
1950 | } |
1951 | - |
1952 | - Component.onCompleted: { |
1953 | - pageHeader.title = Qt.binding(function(){ |
1954 | - if(mainView.displayLunarCalendar){ |
1955 | - var year = currentMonth.getFullYear() |
1956 | - var month = currentMonth.getMonth() |
1957 | - var day = Math.floor(Date.daysInMonth(year, month) / 2.0) |
1958 | - var lunarDate = Lunar.calendar.solar2lunar( year, month + 1, day) |
1959 | - return i18n.tr("%1 %2").arg(lunarDate .IMonthCn).arg(lunarDate.gzYear) |
1960 | - } else { |
1961 | - // TRANSLATORS: this is a time formatting string, |
1962 | - // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions. |
1963 | - // It's used in the header of the month and week views |
1964 | - var monthName = currentMonth.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy")) |
1965 | - return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1) |
1966 | - } |
1967 | - }) |
1968 | - } |
1969 | } |
1970 | |
1971 | === added file 'MonthWithEventsComponent.qml' |
1972 | --- MonthWithEventsComponent.qml 1970-01-01 00:00:00 +0000 |
1973 | +++ MonthWithEventsComponent.qml 2016-03-07 15:59:03 +0000 |
1974 | @@ -0,0 +1,80 @@ |
1975 | +/* |
1976 | + * Copyright (C) 2013-2014 Canonical Ltd |
1977 | + * |
1978 | + * This file is part of Ubuntu Calendar App |
1979 | + * |
1980 | + * Ubuntu Calendar App is free software: you can redistribute it and/or modify |
1981 | + * it under the terms of the GNU General Public License version 3 as |
1982 | + * published by the Free Software Foundation. |
1983 | + * |
1984 | + * Ubuntu Calendar App is distributed in the hope that it will be useful, |
1985 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1986 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1987 | + * GNU General Public License for more details. |
1988 | + * |
1989 | + * You should have received a copy of the GNU General Public License |
1990 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1991 | + */ |
1992 | +import QtQuick 2.4 |
1993 | +import Ubuntu.Components 1.3 |
1994 | +import QtOrganizer 5.0 |
1995 | +import "dateExt.js" as DateExt |
1996 | +import "colorUtils.js" as Color |
1997 | + |
1998 | +MonthComponent { |
1999 | + id: root |
2000 | + objectName: "MonthComponent" |
2001 | + |
2002 | + property bool isActive: false |
2003 | + property var modelFilter: invalidFilter |
2004 | + |
2005 | + onIsActiveChanged: { |
2006 | + if (isActive && (mainModel.filter === invalidFilter)) { |
2007 | + idleRefresh.reset() |
2008 | + } |
2009 | + } |
2010 | + |
2011 | + Timer { |
2012 | + id: idleRefresh |
2013 | + |
2014 | + function reset() |
2015 | + { |
2016 | + mainModel.filter = invalidFilter |
2017 | + restart() |
2018 | + } |
2019 | + |
2020 | + interval: root.isCurrentItem ? 1000 : 2000 |
2021 | + repeat: false |
2022 | + onTriggered: { |
2023 | + mainModel.filter = Qt.binding(function() { return root.modelFilter } ) |
2024 | + } |
2025 | + } |
2026 | + |
2027 | + InvalidFilter { |
2028 | + id: invalidFilter |
2029 | + } |
2030 | + |
2031 | + EventListModel { |
2032 | + id: mainModel |
2033 | + |
2034 | + startPeriod: root.monthStartDate.midnight(); |
2035 | + endPeriod: root.monthStartDate.addDays((/*monthGrid.rows * cols */ 42 )-1).endOfDay() |
2036 | + filter: invalidFilter |
2037 | + fetchHint: FetchHint { |
2038 | + detailTypesHint: [ Detail.EventTime, |
2039 | + Detail.JournalTime, |
2040 | + Detail.TodoTime |
2041 | + ] |
2042 | + } |
2043 | + |
2044 | + onModelChanged: { |
2045 | + var eventStatus = mainModel.containsItems(startPeriod, |
2046 | + endPeriod, |
2047 | + 86400/*24*60*60*/); |
2048 | + root.updateEvents(eventStatus) |
2049 | + } |
2050 | + |
2051 | + onStartPeriodChanged: idleRefresh.reset() |
2052 | + onEndPeriodChanged: idleRefresh.reset() |
2053 | + } |
2054 | +} |
2055 | |
2056 | === modified file 'NewEvent.qml' |
2057 | --- NewEvent.qml 2016-02-02 22:54:03 +0000 |
2058 | +++ NewEvent.qml 2016-03-07 15:59:03 +0000 |
2059 | @@ -20,7 +20,7 @@ |
2060 | import QtOrganizer 5.0 |
2061 | import Ubuntu.Components 1.3 |
2062 | import Ubuntu.Components.Popups 1.0 |
2063 | -import Ubuntu.Components.ListItems 1.0 as ListItem |
2064 | +import Ubuntu.Components.ListItems 1.0 as ListItems |
2065 | import Ubuntu.Components.Themes.Ambiance 1.0 |
2066 | import Ubuntu.Components.Pickers 1.0 |
2067 | import QtOrganizer 5.0 |
2068 | @@ -31,10 +31,11 @@ |
2069 | objectName: 'newEventPage' |
2070 | |
2071 | property var date; |
2072 | + property alias allDay: allDayEventCheckbox.checked |
2073 | |
2074 | property var event:null; |
2075 | property var rule :null; |
2076 | - property var model; |
2077 | + property var model:null; |
2078 | |
2079 | property var startDate; |
2080 | property var endDate; |
2081 | @@ -42,56 +43,44 @@ |
2082 | property alias scrollY: flickable.contentY |
2083 | property bool isEdit: false |
2084 | |
2085 | - flickable: null |
2086 | - |
2087 | signal eventAdded(var event); |
2088 | signal eventDeleted(var event); |
2089 | - |
2090 | - onStartDateChanged: { |
2091 | - startDateTimeInput.dateTime = startDate; |
2092 | - |
2093 | - // set time forward to one hour |
2094 | - var time_forward = 3600000; |
2095 | - |
2096 | - if (isEdit && event !== null) { |
2097 | - time_forward = event.endDateTime - event.startDateTime; |
2098 | - } |
2099 | - adjustEndDateToStartDate(time_forward); |
2100 | - } |
2101 | - |
2102 | - onEndDateChanged: { |
2103 | - endDateTimeInput.dateTime = endDate; |
2104 | - } |
2105 | - |
2106 | - head.actions: [ |
2107 | - Action { |
2108 | - text: i18n.tr("Delete"); |
2109 | - objectName: "delete" |
2110 | - iconName: "delete" |
2111 | - visible : isEdit |
2112 | - onTriggered: { |
2113 | - var dialog = PopupUtils.open(Qt.resolvedUrl("DeleteConfirmationDialog.qml"),root,{"event": event}); |
2114 | - dialog.deleteEvent.connect( function(eventId){ |
2115 | - model.removeItem(eventId); |
2116 | - pageStack.pop(); |
2117 | - root.eventDeleted(eventId); |
2118 | - }); |
2119 | - } |
2120 | - }, |
2121 | - Action { |
2122 | - iconName: "ok" |
2123 | - objectName: "save" |
2124 | - text: i18n.tr("Save") |
2125 | - enabled: !!titleEdit.text.trim() |
2126 | - onTriggered: saveToQtPim(); |
2127 | - }] |
2128 | + signal canceled() |
2129 | + |
2130 | Component.onCompleted: { |
2131 | - //If current date is setted by an argument we don't have to change it. |
2132 | - if(typeof(date) === 'undefined'){ |
2133 | + setDate(root.date) |
2134 | + |
2135 | + if (event === undefined) { |
2136 | + return |
2137 | + } else if(event === null){ |
2138 | + isEdit = false; |
2139 | + addEvent(); |
2140 | + } else{ |
2141 | + isEdit = true; |
2142 | + editEvent(event); |
2143 | + } |
2144 | + } |
2145 | + |
2146 | + function cancel() |
2147 | + { |
2148 | + if (pageStack) |
2149 | + pageStack.pop(); |
2150 | + root.canceled() |
2151 | + } |
2152 | + |
2153 | + function updateEventDate(date, allDay) { |
2154 | + root.startDate = undefined |
2155 | + root.endDate = undefined |
2156 | + setDate(date) |
2157 | + root.allDay = allDay |
2158 | + } |
2159 | + |
2160 | + function setDate(date) { |
2161 | + if ((typeof(date) === 'undefined') || (date === null)) { |
2162 | date = new Date(); |
2163 | } |
2164 | |
2165 | - if( typeof(date) == 'undefined' || (date.getHours() == 0 && date.getMinutes() == 0) ) { |
2166 | + if(date.getHours() === 0 && date.getMinutes() === 0) { |
2167 | var newDate = new Date(); |
2168 | date.setHours(newDate.getHours(), newDate.getMinutes()); |
2169 | } |
2170 | @@ -108,15 +97,6 @@ |
2171 | endTimeInput.text = Qt.formatDateTime(endDate, Qt.locale().timeFormat(Locale.ShortFormat)); |
2172 | } |
2173 | |
2174 | - if(event === null){ |
2175 | - isEdit = false; |
2176 | - addEvent(); |
2177 | - } |
2178 | - |
2179 | - else{ |
2180 | - isEdit = true; |
2181 | - editEvent(event); |
2182 | - } |
2183 | } |
2184 | |
2185 | function selectCalendar(collectionId) { |
2186 | @@ -146,14 +126,14 @@ |
2187 | } |
2188 | |
2189 | startDate =new Date(e.startDateTime); |
2190 | - endDate = new Date(e.endDateTime); |
2191 | |
2192 | if(e.displayLabel) { |
2193 | titleEdit.text = e.displayLabel; |
2194 | } |
2195 | - if(e.allDay){ |
2196 | - allDayEventCheckbox.checked =true; |
2197 | - endDate = endDate.addDays(-1); |
2198 | + |
2199 | + if (e.allDay) { |
2200 | + allDayEventCheckbox.checked = true |
2201 | + endDate = new Date(e.endDateTime).addDays(-1); |
2202 | } |
2203 | |
2204 | if(e.location) { |
2205 | @@ -170,8 +150,7 @@ |
2206 | if( e.itemType === Type.Event ) { |
2207 | if(e.attendees){ |
2208 | for( var j = 0 ; j < e.attendees.length ; ++j ) { |
2209 | - contactList.array.push(e.attendees[j]); |
2210 | - contactModel.append(e.attendees[j]); |
2211 | + contactModel.append({"contact": e.attendees[j]}); |
2212 | } |
2213 | } |
2214 | } |
2215 | @@ -202,11 +181,11 @@ |
2216 | } |
2217 | |
2218 | event.allDay = allDayEventCheckbox.checked; |
2219 | - event.startDateTime = startDate; |
2220 | - |
2221 | if (event.allDay){ |
2222 | - event.endDateTime = endDate.addDays(1); |
2223 | + event.startDateTime = new Date(startDate).midnight() |
2224 | + event.endDateTime = new Date(endDate).addDays(1).midnight() |
2225 | } else { |
2226 | + event.startDateTime = startDate; |
2227 | event.endDateTime = endDate; |
2228 | } |
2229 | |
2230 | @@ -215,13 +194,14 @@ |
2231 | event.location = locationEdit.text |
2232 | |
2233 | if( event.itemType === Type.Event ) { |
2234 | - event.attendees = []; // if Edit remove all attendes & add them again if any |
2235 | - var contacts = []; |
2236 | - for(var i=0; i < contactList.array.length ; ++i) { |
2237 | - var contact = contactList.array[i] |
2238 | - contacts.push(contact); |
2239 | + var newContacts = [] |
2240 | + for(var i=0; i < contactModel.count ; ++i) { |
2241 | + var contact = contactModel.get(i).contact |
2242 | + if (contact) { |
2243 | + newContacts.push(internal.attendeeFromData(contact.attendeeId, contact.name, contact.emailAddress)); |
2244 | + } |
2245 | } |
2246 | - event.attendees = contacts; |
2247 | + event.attendees = newContacts; |
2248 | } |
2249 | |
2250 | //Set the Rule object to an event |
2251 | @@ -253,9 +233,9 @@ |
2252 | event.removeDetail(comment); |
2253 | } |
2254 | |
2255 | - model.saveItem(event); |
2256 | - pageStack.pop(); |
2257 | - |
2258 | + model.saveItem(event) |
2259 | + if (pageStack) |
2260 | + pageStack.pop(); |
2261 | root.eventAdded(event); |
2262 | } |
2263 | } |
2264 | @@ -294,10 +274,15 @@ |
2265 | function roundDate(date) { |
2266 | var tempDate = new Date(date) |
2267 | tempDate.setHours(date.getHours(), date.getMinutes(), 0, 0); |
2268 | - if(tempDate.getMinutes() < 30) |
2269 | + var tempMinutes = tempDate.getMinutes() |
2270 | + if (tempMinutes === 0) { |
2271 | + return tempDate |
2272 | + } else if(tempMinutes < 30) { |
2273 | return tempDate.setMinutes(30) |
2274 | - tempDate.setMinutes(0) |
2275 | - return tempDate.setHours(tempDate.getHours() + 1) |
2276 | + } else { |
2277 | + tempDate.setMinutes(0) |
2278 | + return tempDate.setHours(tempDate.getHours() + 1) |
2279 | + } |
2280 | } |
2281 | |
2282 | function adjustEndDateToStartDate(time_forward) { |
2283 | @@ -322,10 +307,65 @@ |
2284 | scrollAnimation.start() |
2285 | } |
2286 | |
2287 | - title: isEdit ? i18n.tr("Edit Event"):i18n.tr("New Event") |
2288 | - |
2289 | - Keys.onEscapePressed: { |
2290 | - pageStack.pop(); |
2291 | + Keys.onEscapePressed: root.cancel() |
2292 | + onStartDateChanged: { |
2293 | + if (!startDate) |
2294 | + return |
2295 | + |
2296 | + startDateTimeInput.dateTime = startDate; |
2297 | + |
2298 | + // set time forward to one hour |
2299 | + var time_forward = 3600000; |
2300 | + |
2301 | + if (isEdit && event !== null) { |
2302 | + time_forward = event.endDateTime - event.startDateTime; |
2303 | + } |
2304 | + adjustEndDateToStartDate(time_forward); |
2305 | + } |
2306 | + |
2307 | + onEndDateChanged: { |
2308 | + if (endDate) |
2309 | + endDateTimeInput.dateTime = endDate; |
2310 | + } |
2311 | + |
2312 | + header: PageHeader { |
2313 | + id: pageHeader |
2314 | + |
2315 | + flickable: null |
2316 | + title: isEdit ? i18n.tr("Edit Event"):i18n.tr("New Event") |
2317 | + leadingActionBar.actions: Action { |
2318 | + id: backAction |
2319 | + |
2320 | + name: "cancel" |
2321 | + text: i18n.tr("Cancel") |
2322 | + iconName: isEdit ? "back" : "down" |
2323 | + onTriggered: root.cancel() |
2324 | + } |
2325 | + |
2326 | + trailingActionBar.actions: [ |
2327 | + Action { |
2328 | + text: i18n.tr("Delete"); |
2329 | + objectName: "delete" |
2330 | + iconName: "delete" |
2331 | + visible : isEdit |
2332 | + onTriggered: { |
2333 | + var dialog = PopupUtils.open(Qt.resolvedUrl("DeleteConfirmationDialog.qml"),root,{"event": event}); |
2334 | + dialog.deleteEvent.connect( function(eventId){ |
2335 | + model.removeItem(eventId); |
2336 | + if (pageStack) |
2337 | + pageStack.pop(); |
2338 | + root.eventDeleted(eventId); |
2339 | + }); |
2340 | + } |
2341 | + }, |
2342 | + Action { |
2343 | + iconName: "ok" |
2344 | + objectName: "save" |
2345 | + text: i18n.tr("Save") |
2346 | + enabled: !!titleEdit.text.trim() |
2347 | + onTriggered: saveToQtPim(); |
2348 | + } |
2349 | + ] |
2350 | } |
2351 | |
2352 | Component{ |
2353 | @@ -376,9 +416,16 @@ |
2354 | flickable.returnToBounds() |
2355 | } |
2356 | |
2357 | - anchors.fill: parent |
2358 | + flickableDirection: Flickable.VerticalFlick |
2359 | + anchors{ |
2360 | + left: parent.left |
2361 | + top: parent.top |
2362 | + topMargin: pageHeader.height |
2363 | + right: parent.right |
2364 | + bottom: keyboardRectangle.top |
2365 | + } |
2366 | contentWidth: width |
2367 | - contentHeight: column.height + units.gu(10) |
2368 | + contentHeight: column.height |
2369 | |
2370 | Column { |
2371 | id: column |
2372 | @@ -411,7 +458,7 @@ |
2373 | } |
2374 | } |
2375 | |
2376 | - ListItem.Standard { |
2377 | + ListItems.Standard { |
2378 | anchors { |
2379 | left: parent.left |
2380 | right: parent.right |
2381 | @@ -426,13 +473,13 @@ |
2382 | } |
2383 | } |
2384 | |
2385 | - ListItem.ThinDivider {} |
2386 | + ListItems.ThinDivider {} |
2387 | |
2388 | Column { |
2389 | width: parent.width |
2390 | spacing: units.gu(1) |
2391 | |
2392 | - ListItem.Header{ |
2393 | + ListItems.Header{ |
2394 | text: i18n.tr("Event Details") |
2395 | } |
2396 | |
2397 | @@ -446,6 +493,7 @@ |
2398 | margins: units.gu(2) |
2399 | } |
2400 | |
2401 | + inputMethodHints: Qt.ImhNoPredictiveText |
2402 | placeholderText: i18n.tr("Event Name") |
2403 | onFocusChanged: { |
2404 | if(titleEdit.focus) { |
2405 | @@ -482,6 +530,7 @@ |
2406 | margins: units.gu(2) |
2407 | } |
2408 | |
2409 | + inputMethodHints: Qt.ImhNoPredictiveText |
2410 | placeholderText: i18n.tr("Location") |
2411 | |
2412 | onFocusChanged: { |
2413 | @@ -496,7 +545,7 @@ |
2414 | width: parent.width |
2415 | spacing: units.gu(1) |
2416 | |
2417 | - ListItem.Header { |
2418 | + ListItems.Header { |
2419 | text: i18n.tr("Calendar") |
2420 | } |
2421 | |
2422 | @@ -521,9 +570,11 @@ |
2423 | width: height |
2424 | height: parent.height - units.gu(2) |
2425 | color: modelData.color |
2426 | - anchors.right: parent.right |
2427 | - anchors.rightMargin: units.gu(2) |
2428 | - anchors.verticalCenter: parent.verticalCenter |
2429 | + anchors { |
2430 | + right: parent.right |
2431 | + rightMargin: units.gu(4) |
2432 | + verticalCenter: parent.verticalCenter |
2433 | + } |
2434 | } |
2435 | } |
2436 | onExpandedChanged: Qt.inputMethod.hide(); |
2437 | @@ -534,14 +585,17 @@ |
2438 | width: parent.width |
2439 | spacing: units.gu(1) |
2440 | |
2441 | - ListItem.Header { |
2442 | + ListItems.Header { |
2443 | text: i18n.tr("Guests") |
2444 | } |
2445 | |
2446 | Button{ |
2447 | + id: addGuestButton |
2448 | + objectName: "addGuestButton" |
2449 | + |
2450 | + property var contactsPopup: null |
2451 | + |
2452 | text: i18n.tr("Add Guest") |
2453 | - objectName: "addGuestButton" |
2454 | - |
2455 | anchors { |
2456 | left: parent.left |
2457 | right: parent.right |
2458 | @@ -549,14 +603,21 @@ |
2459 | } |
2460 | |
2461 | onClicked: { |
2462 | - var popup = PopupUtils.open(Qt.resolvedUrl("ContactChoicePopup.qml"), contactList); |
2463 | - popup.contactSelected.connect( function(contact) { |
2464 | - var t = internal.contactToAttendee(contact); |
2465 | - if( !internal.isContactAlreadyAdded(contact) ) { |
2466 | - contactModel.append(t); |
2467 | - contactList.array.push(t); |
2468 | + if (contactsPopup) |
2469 | + return |
2470 | + |
2471 | + flickable.makeMeVisible(addGuestButton) |
2472 | + contactsPopup = PopupUtils.open(Qt.resolvedUrl("ContactChoicePopup.qml"), addGuestButton); |
2473 | + contactsPopup.contactSelected.connect( function(contact, emailAddress) { |
2474 | + if(!internal.isContactAlreadyAdded(contact, emailAddress) ) { |
2475 | + var t = internal.contactToAttendee(contact, emailAddress); |
2476 | + contactModel.append({"contact": t}); |
2477 | } |
2478 | + |
2479 | }); |
2480 | + contactsPopup.Component.onDestruction.connect( function() { |
2481 | + addGuestButton.contactsPopup = null |
2482 | + }) |
2483 | } |
2484 | } |
2485 | |
2486 | @@ -577,35 +638,40 @@ |
2487 | width: parent.width |
2488 | clip: true |
2489 | |
2490 | - property var array: [] |
2491 | - |
2492 | ListModel{ |
2493 | id: contactModel |
2494 | } |
2495 | |
2496 | Repeater{ |
2497 | model: contactModel |
2498 | - delegate: ListItem.Standard { |
2499 | + delegate: ListItem { |
2500 | objectName: "eventGuest%1".arg(index) |
2501 | - height: units.gu(4) |
2502 | - text: name |
2503 | - removable: true |
2504 | - onItemRemoved: { |
2505 | - contactList.array.splice(index, 1) |
2506 | - contactModel.remove(index) |
2507 | + |
2508 | + ListItemLayout { |
2509 | + title.text: contact.name |
2510 | + subtitle.text: contact.emailAddress |
2511 | + } |
2512 | + |
2513 | + leadingActions: ListItemActions { |
2514 | + actions: Action { |
2515 | + iconName: "delete" |
2516 | + onTriggered: { |
2517 | + contactModel.remove(index) |
2518 | + } |
2519 | + } |
2520 | } |
2521 | } |
2522 | } |
2523 | } |
2524 | } |
2525 | |
2526 | - ListItem.ThinDivider { |
2527 | - visible: event.itemType === Type.Event |
2528 | + ListItems.ThinDivider { |
2529 | + visible: (event != undefined) && (event.itemType === Type.Event) |
2530 | } |
2531 | |
2532 | } |
2533 | |
2534 | - ListItem.Subtitled{ |
2535 | + ListItems.Subtitled{ |
2536 | id:thisHappens |
2537 | objectName :"thisHappens" |
2538 | |
2539 | @@ -615,17 +681,17 @@ |
2540 | |
2541 | showDivider: false |
2542 | progression: true |
2543 | - visible: event.itemType === Type.Event |
2544 | + visible: (event != undefined) && (event.itemType === Type.Event) |
2545 | text: i18n.tr("Repeats") |
2546 | - subText: event.itemType === Type.Event ? rule === null ? Defines.recurrenceLabel[0] : eventUtils.getRecurrenceString(rule) : "" |
2547 | + subText: (event != undefined) && (event.itemType === Type.Event) ? rule === null ? Defines.recurrenceLabel[0] : eventUtils.getRecurrenceString(rule) : "" |
2548 | onClicked: pageStack.push(Qt.resolvedUrl("EventRepetition.qml"),{"eventRoot": root,"isEdit":isEdit}); |
2549 | } |
2550 | |
2551 | - ListItem.ThinDivider { |
2552 | - visible: event.itemType === Type.Event |
2553 | + ListItems.ThinDivider { |
2554 | + visible: (event != undefined) && (event.itemType === Type.Event) |
2555 | } |
2556 | |
2557 | - ListItem.Subtitled{ |
2558 | + ListItems.Subtitled{ |
2559 | id:eventReminder |
2560 | objectName : "eventReminder" |
2561 | |
2562 | @@ -658,22 +724,42 @@ |
2563 | "eventTitle": titleEdit.text}) |
2564 | } |
2565 | |
2566 | - ListItem.ThinDivider {} |
2567 | + ListItems.ThinDivider {} |
2568 | } |
2569 | } |
2570 | + |
2571 | // used to keep the field visible when the keyboard appear or dismiss |
2572 | KeyboardRectangle { |
2573 | - id: keyboard |
2574 | - |
2575 | - onHeightChanged: { |
2576 | - if (flickable.activeItem) { |
2577 | - flickable.makeMeVisible(flickable.activeItem) |
2578 | + id: keyboardRectangle |
2579 | + |
2580 | + anchors { |
2581 | + left: parent.left |
2582 | + right: parent.right |
2583 | + bottom: parent.bottom |
2584 | + } |
2585 | + |
2586 | + Behavior on height { |
2587 | + SequentialAnimation { |
2588 | + PauseAnimation { duration: 200 } |
2589 | + ScriptAction { |
2590 | + script: { |
2591 | + if (addGuestButton.contactsPopup) { |
2592 | + // WORKAROUND: causes the popover to follow the buttom position when keyboard appears |
2593 | + flickable.makeMeVisible(addGuestButton) |
2594 | + addGuestButton.contactsPopup.caller = null |
2595 | + addGuestButton.contactsPopup.caller = addGuestButton |
2596 | + } else { |
2597 | + flickable.makeMeVisible(flickable.activeItem) |
2598 | + } |
2599 | + } |
2600 | + } |
2601 | } |
2602 | } |
2603 | } |
2604 | |
2605 | QtObject { |
2606 | id: internal |
2607 | + |
2608 | property var collectionId; |
2609 | |
2610 | function clearFocus() { |
2611 | @@ -685,22 +771,33 @@ |
2612 | messageEdit.focus = false |
2613 | } |
2614 | |
2615 | - function isContactAlreadyAdded(contact) { |
2616 | - for(var i=0; i < contactList.array.length ; ++i) { |
2617 | - var attendee = contactList.array[i]; |
2618 | - if( attendee.attendeeId === contact.contactId) { |
2619 | - return true; |
2620 | + function isContactAlreadyAdded(contact, emailAddress) { |
2621 | + for(var i=0; i < contactModel.count; i++) { |
2622 | + var attendee = contactModel.get(i).contact; |
2623 | + if (attendee && (emailAddress.length > 0)) { |
2624 | + if (attendee.emailAddress === emailAddress) { |
2625 | + return true; |
2626 | + } |
2627 | + } else { |
2628 | + if (attendee.attendeeId === contact.contactId) { |
2629 | + return true |
2630 | + } |
2631 | } |
2632 | } |
2633 | return false; |
2634 | } |
2635 | |
2636 | - function contactToAttendee(contact) { |
2637 | - var attendee = Qt.createQmlObject("import QtOrganizer 5.0; EventAttendee{}", event, "NewEvent.qml"); |
2638 | - attendee.name = contact.displayLabel.label |
2639 | - attendee.emailAddress = contact.email.emailAddress; |
2640 | - attendee.attendeeId = contact.contactId; |
2641 | + function attendeeFromData(id, name, emailAddress) |
2642 | + { |
2643 | + var attendee = Qt.createQmlObject("import QtOrganizer 5.0; EventAttendee{}", internal, "NewEvent.qml"); |
2644 | + attendee.name = name |
2645 | + attendee.emailAddress = emailAddress |
2646 | + attendee.attendeeId = id |
2647 | return attendee; |
2648 | } |
2649 | + |
2650 | + function contactToAttendee(contact, emailAddress) { |
2651 | + return attendeeFromData(contact.contactId, contact.displayLabel.label, emailAddress) |
2652 | + } |
2653 | } |
2654 | } |
2655 | |
2656 | === added file 'NewEventBottomEdge.qml' |
2657 | --- NewEventBottomEdge.qml 1970-01-01 00:00:00 +0000 |
2658 | +++ NewEventBottomEdge.qml 2016-03-07 15:59:03 +0000 |
2659 | @@ -0,0 +1,115 @@ |
2660 | +/* |
2661 | + * Copyright (C) 2013-2016 Canonical Ltd |
2662 | + * |
2663 | + * This file is part of Ubuntu Calendar App |
2664 | + * |
2665 | + * Ubuntu Calendar App is free software: you can redistribute it and/or modify |
2666 | + * it under the terms of the GNU General Public License version 3 as |
2667 | + * published by the Free Software Foundation. |
2668 | + * |
2669 | + * Ubuntu Calendar App is distributed in the hope that it will be useful, |
2670 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2671 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2672 | + * GNU General Public License for more details. |
2673 | + * |
2674 | + * You should have received a copy of the GNU General Public License |
2675 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2676 | + */ |
2677 | + |
2678 | +import QtQuick 2.4 |
2679 | +import Ubuntu.Components 1.3 |
2680 | + |
2681 | +BottomEdge { |
2682 | + id: bottomEdge |
2683 | + objectName: "bottomEdge" |
2684 | + |
2685 | + property var pageStack: null |
2686 | + property var eventModel: null |
2687 | + property var date: new Date() |
2688 | + |
2689 | + // WORKAROUND: BottomEdge component loads the page async while draging it |
2690 | + // this cause a very bad visual. |
2691 | + // To avoid that we create it as soon as the component is ready and keep |
2692 | + // it invisible until the user start to drag it. |
2693 | + property var _realPage: null |
2694 | + |
2695 | + signal opened() |
2696 | + signal eventCreated(var event) |
2697 | + |
2698 | + function updateNewEventDate(date, allDay) |
2699 | + { |
2700 | + _realPage.updateEventDate(date, allDay) |
2701 | + } |
2702 | + |
2703 | + hint { |
2704 | + visible: bottomEdge.enabled |
2705 | + enabled: visible |
2706 | + action: Action { |
2707 | + objectName: "neweventbutton" |
2708 | + name: "neweventbutton" |
2709 | + |
2710 | + iconName: "new-event" |
2711 | + text: i18n.tr("New Event") |
2712 | + shortcut: "ctrl+n" |
2713 | + enabled: bottomEdge.enabled |
2714 | + onTriggered: bottomEdge.commit() |
2715 | + } |
2716 | + } |
2717 | + |
2718 | + contentComponent: Item { |
2719 | + id: pageContent |
2720 | + |
2721 | + implicitWidth: bottomEdge.width |
2722 | + implicitHeight: bottomEdge.height |
2723 | + children: bottomEdge._realPage |
2724 | + Component.onDestruction: { |
2725 | + if (bottomEdge._realPage) { |
2726 | + bottomEdge._realPage.destroy() |
2727 | + bottomEdge._realPage = null |
2728 | + _realPage = editorPageBottomEdge.createObject(null) |
2729 | + } |
2730 | + } |
2731 | + } |
2732 | + |
2733 | + onCommitStarted: { |
2734 | + bottomEdge.opened() |
2735 | + updateNewEventDate(bottomEdge.date ? bottomEdge.date : new Date(), false) |
2736 | + } |
2737 | + |
2738 | + Component.onCompleted: { |
2739 | + if (eventModel) |
2740 | + _realPage = editorPageBottomEdge.createObject(null) |
2741 | + } |
2742 | + |
2743 | + onEventModelChanged: { |
2744 | + if (eventModel) |
2745 | + _realPage = editorPageBottomEdge.createObject(null) |
2746 | + } |
2747 | + |
2748 | + Component { |
2749 | + id: editorPageBottomEdge |
2750 | + NewEvent { |
2751 | + id: newEventPage |
2752 | + |
2753 | + implicitWidth: bottomEdge.width |
2754 | + implicitHeight: bottomEdge.height |
2755 | + model: bottomEdge.eventModel |
2756 | + date: bottomEdge.date |
2757 | + enabled: bottomEdge.status === BottomEdge.Committed |
2758 | + active: bottomEdge.status === BottomEdge.Committed |
2759 | + visible: (bottomEdge.status !== BottomEdge.Hidden) |
2760 | + onCanceled: bottomEdge.collapse() |
2761 | + onEventAdded: { |
2762 | + bottomEdge.collapse() |
2763 | + bottomEdge.eventCreated(event) |
2764 | + } |
2765 | + } |
2766 | + } |
2767 | + |
2768 | + Component.onDestruction: { |
2769 | + if (bottomEdge._realPage) { |
2770 | + bottomEdge._realPage.destroy() |
2771 | + bottomEdge._realPage = null |
2772 | + } |
2773 | + } |
2774 | +} |
2775 | |
2776 | === added file 'PageWithBottomEdge.qml' |
2777 | --- PageWithBottomEdge.qml 1970-01-01 00:00:00 +0000 |
2778 | +++ PageWithBottomEdge.qml 2016-03-07 15:59:03 +0000 |
2779 | @@ -0,0 +1,44 @@ |
2780 | +/* |
2781 | + * Copyright (C) 2013-2016 Canonical Ltd |
2782 | + * |
2783 | + * This file is part of Ubuntu Calendar App |
2784 | + * |
2785 | + * Ubuntu Calendar App is free software: you can redistribute it and/or modify |
2786 | + * it under the terms of the GNU General Public License version 3 as |
2787 | + * published by the Free Software Foundation. |
2788 | + * |
2789 | + * Ubuntu Calendar App is distributed in the hope that it will be useful, |
2790 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2791 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2792 | + * GNU General Public License for more details. |
2793 | + * |
2794 | + * You should have received a copy of the GNU General Public License |
2795 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2796 | + */ |
2797 | +import QtQuick 2.4 |
2798 | +import Ubuntu.Components 1.3 |
2799 | + |
2800 | +Page { |
2801 | + id: root |
2802 | + |
2803 | + property alias model: bottomEdge.eventModel |
2804 | + property alias createEventAt: bottomEdge.date |
2805 | + property bool bootomEdgeEnabled: bottomEdge.enabled |
2806 | + |
2807 | + signal bottomEdgeCommitStarted() |
2808 | + signal eventCreated(var event) |
2809 | + |
2810 | + function bottomEdgeCommit(date, allDay) |
2811 | + { |
2812 | + bottomEdge.commit() |
2813 | + bottomEdge.updateNewEventDate(date, allDay) |
2814 | + } |
2815 | + |
2816 | + NewEventBottomEdge { |
2817 | + id: bottomEdge |
2818 | + |
2819 | + pageStack: tabs |
2820 | + onOpened: root.bottomEdgeCommitStarted() |
2821 | + onEventCreated: root.eventCreated(event) |
2822 | + } |
2823 | +} |
2824 | |
2825 | === modified file 'PathViewBase.qml' |
2826 | --- PathViewBase.qml 2016-01-25 13:20:32 +0000 |
2827 | +++ PathViewBase.qml 2016-03-07 15:59:03 +0000 |
2828 | @@ -20,8 +20,7 @@ |
2829 | PathView { |
2830 | id: root |
2831 | |
2832 | - model: 3 |
2833 | - snapMode: PathView.SnapOneItem |
2834 | + readonly property alias loopCurrentIndex: intern.loopCurrentIndex |
2835 | |
2836 | signal nextItemHighlighted(); |
2837 | signal previousItemHighlighted(); |
2838 | @@ -29,6 +28,8 @@ |
2839 | signal scrollUp(); |
2840 | signal scrollDown(); |
2841 | |
2842 | + model: 3 |
2843 | + snapMode: PathView.SnapOneItem |
2844 | preferredHighlightBegin: 0.5 |
2845 | preferredHighlightEnd: 0.5 |
2846 | |
2847 | @@ -68,6 +69,11 @@ |
2848 | } |
2849 | } |
2850 | |
2851 | + function scrollToBegginer() |
2852 | + { |
2853 | + intern.loopCurrentIndex = intern.previousIndex = currentIndex = 0 |
2854 | + } |
2855 | + |
2856 | Keys.onLeftPressed:{ |
2857 | root.decrementCurrentIndex(); |
2858 | } |
2859 | @@ -98,15 +104,17 @@ |
2860 | intern.previousIndex = currentIndex |
2861 | |
2862 | if ( diff > 0 ) { |
2863 | - root.nextItemHighlighted(); |
2864 | + intern.loopCurrentIndex++ |
2865 | } |
2866 | else { |
2867 | - root.previousItemHighlighted(); |
2868 | + intern.loopCurrentIndex-- |
2869 | } |
2870 | } |
2871 | |
2872 | QtObject{ |
2873 | id: intern |
2874 | + |
2875 | + property int loopCurrentIndex: 0 |
2876 | property int previousIndex: root.currentIndex |
2877 | } |
2878 | } |
2879 | |
2880 | === modified file 'TimeLineBase.qml' |
2881 | --- TimeLineBase.qml 2016-02-03 08:01:25 +0000 |
2882 | +++ TimeLineBase.qml 2016-03-07 15:59:03 +0000 |
2883 | @@ -1,4 +1,4 @@ |
2884 | -/* |
2885 | + /* |
2886 | * Copyright (C) 2013-2014 Canonical Ltd |
2887 | * |
2888 | * This file is part of Ubuntu Calendar App |
2889 | @@ -20,36 +20,36 @@ |
2890 | import QtOrganizer 5.0 |
2891 | |
2892 | import "dateExt.js" as DateExt |
2893 | +import "calendar_canvas.js" as CanlendarCanvas |
2894 | |
2895 | Item { |
2896 | id: bubbleOverLay |
2897 | |
2898 | property var delegate; |
2899 | property var day; |
2900 | - property int hourHeight: units.gu(8) |
2901 | - property var model; |
2902 | - |
2903 | - Component.onCompleted: { |
2904 | - bubbleOverLay.createEvents(); |
2905 | + |
2906 | + property alias model: modelConnections.target |
2907 | + property var flickable: null |
2908 | + readonly property alias creatingEvent: overlayMouseArea.creatingEvent |
2909 | + readonly property real hourHeight: units.gu(8) |
2910 | + readonly property real minuteHeight: (hourHeight / 60) |
2911 | + |
2912 | + signal pressAndHoldAt(var date) |
2913 | + |
2914 | + function waitForModelChange() |
2915 | + { |
2916 | + intern.waitingForModelChange = true |
2917 | } |
2918 | |
2919 | - MouseArea { |
2920 | - anchors.fill: parent |
2921 | - objectName: "mouseArea" |
2922 | - |
2923 | - onPressAndHold: { |
2924 | - var selectedDate = new Date(day); |
2925 | - var hour = parseInt(mouseY / hourHeight); |
2926 | - selectedDate.setHours(hour) |
2927 | - createOrganizerEvent(selectedDate); |
2928 | - } |
2929 | - |
2930 | - onPressed: { |
2931 | - intern.now = new Date(); |
2932 | - if( intern.now.isSameDay( bubbleOverLay.day ) ) { |
2933 | - bubbleOverLay.showSeparator(); |
2934 | - } |
2935 | - } |
2936 | + function createOrganizerEvent( startDate ) { |
2937 | + var event = Qt.createQmlObject("import QtOrganizer 5.0; Event {}", Qt.application,"TimeLineBase.qml"); |
2938 | + event.collectionId = (model.defaultCollection().collectionId); |
2939 | + var endDate = new Date( startDate.getTime() + 3600000 ); |
2940 | + event.startDateTime = startDate; |
2941 | + event.endDateTime = endDate; |
2942 | + event.displayLabel = i18n.tr("New event"); |
2943 | + event.setDetail(Qt.createQmlObject("import QtOrganizer 5.0; Comment{ comment: 'X-CAL-DEFAULT-EVENT'}", event,"TimeLineBase.qml")); |
2944 | + return event |
2945 | } |
2946 | |
2947 | function getTimeFromYPos(y, day) { |
2948 | @@ -64,32 +64,6 @@ |
2949 | return date; |
2950 | } |
2951 | |
2952 | - function createOrganizerEvent( startDate ) { |
2953 | - var event = Qt.createQmlObject("import QtOrganizer 5.0; Event {}", Qt.application,"TimeLineBase.qml"); |
2954 | - event.collectionId = (model.defaultCollection().collectionId); |
2955 | - var endDate = new Date( startDate.getTime() + 3600000 ); |
2956 | - event.startDateTime = startDate; |
2957 | - event.endDateTime = endDate; |
2958 | - event.displayLabel = i18n.tr("Untitled"); |
2959 | - event.setDetail(Qt.createQmlObject("import QtOrganizer 5.0; Comment{ comment: 'X-CAL-DEFAULT-EVENT'}", event,"TimeLineBase.qml")); |
2960 | - model.saveItem(event); |
2961 | - } |
2962 | - |
2963 | - TimeSeparator { |
2964 | - id: separator |
2965 | - objectName: "separator" |
2966 | - width: bubbleOverLay.width |
2967 | - visible: false |
2968 | - z:10 |
2969 | - } |
2970 | - |
2971 | - QtObject { |
2972 | - id: intern |
2973 | - property var now : new Date(); |
2974 | - property var eventMap; |
2975 | - property var unUsedEvents: new Object(); |
2976 | - } |
2977 | - |
2978 | function showEventDetails(event) { |
2979 | var comment = event.detail(Detail.Comment); |
2980 | if(comment && comment.comment === "X-CAL-DEFAULT-EVENT") { |
2981 | @@ -101,167 +75,279 @@ |
2982 | |
2983 | WorkerScript { |
2984 | id: eventLayoutHelper |
2985 | - source: "EventLayoutHelper.js" |
2986 | |
2987 | + source: "calendar_canvas_worker.js" |
2988 | onMessage: { |
2989 | - layoutEvents(messageObject.schedules,messageObject.maxDepth); |
2990 | - } |
2991 | - } |
2992 | - |
2993 | - function layoutEvents(array, depth) { |
2994 | - for(var i=0; i < array.length ; ++i) { |
2995 | - var schedule = array[i]; |
2996 | - var event = intern.eventMap[schedule.id]; |
2997 | - bubbleOverLay.createEvent(event , schedule.depth, depth +1); |
2998 | - } |
2999 | + // check if anything changed during the process |
3000 | + if (intern.dirty) { |
3001 | + console.debug("Something has changed while work script was running, ignore message") |
3002 | + } else { |
3003 | + var events = messageObject.reply |
3004 | + var dirty = false |
3005 | + for (var i=0; i < events.length; i++) { |
3006 | + var e = intern.eventsById[events[i].eventId] |
3007 | + if (e.eventId != events[i].itemId) { |
3008 | + console.warn("Event does not match id:", i) |
3009 | + dirty = true |
3010 | + } |
3011 | + if (e.startDateTime.getTime() != events[i].eventStartTime) { |
3012 | + console.warn("Event does not match start time") |
3013 | + dirty = true |
3014 | + } |
3015 | + if (e.endDateTime.getTime() != events[i].eventEndTime) { |
3016 | + console.warn("Event does not match end time") |
3017 | + dirty = true |
3018 | + } |
3019 | + |
3020 | + if (dirty) { |
3021 | + console.warn("Mark as dirty") |
3022 | + intern.dirty = true |
3023 | + break |
3024 | + } |
3025 | + |
3026 | + createVisual(events[i]) |
3027 | + } |
3028 | + } |
3029 | + intern.busy = false |
3030 | + intern.eventsById = {} |
3031 | + if (intern.dirty) { |
3032 | + bubbleOverLay.idleCreateEvents() |
3033 | + } |
3034 | + } |
3035 | + } |
3036 | + |
3037 | + function createVisual(eventInfo) |
3038 | + { |
3039 | + var eventBubble; |
3040 | + if (intern.unUsedEvents.length === 0) { |
3041 | + var incubator = delegate.incubateObject(bubbleOverLay) |
3042 | + incubator.forceCompletion() |
3043 | + eventBubble = incubator.object |
3044 | + } else { |
3045 | + eventBubble = getUnusedEventBubble(); |
3046 | + } |
3047 | + |
3048 | + var eventWidth = (bubbleOverLay.width * eventInfo.width) |
3049 | + |
3050 | + eventBubble.anchorDate = bubbleOverLay.day |
3051 | + eventBubble.minuteHeight = bubbleOverLay.minuteHeight |
3052 | + eventBubble.sizeOfRow = eventInfo.width |
3053 | + eventBubble.depthInRow = eventInfo.y |
3054 | + eventBubble.model = bubbleOverLay.model |
3055 | + eventBubble.event = intern.eventsById[eventInfo.eventId] |
3056 | + eventBubble.resize() |
3057 | + eventBubble.visible = true |
3058 | + eventBubble.clicked.connect( bubbleOverLay.showEventDetails ); |
3059 | + } |
3060 | + |
3061 | + function idleCreateEvents() { |
3062 | + createEventsTimer.restart() |
3063 | } |
3064 | |
3065 | function createEvents() { |
3066 | if(!bubbleOverLay || bubbleOverLay == undefined || model === undefined || model === null) { |
3067 | - return; |
3068 | - } |
3069 | - |
3070 | + console.debug("\tabort.") |
3071 | + return; |
3072 | + } |
3073 | + |
3074 | + // check if there is any update in progress |
3075 | + if (intern.busy) { |
3076 | + console.debug("Work script still busy, postpone update") |
3077 | + // mark as dirsty to triggere a new update after the message arrives |
3078 | + intern.dirty = true |
3079 | + return; |
3080 | + } |
3081 | + |
3082 | + intern.busy = true |
3083 | + intern.dirty = false |
3084 | destroyAllChildren(); |
3085 | - |
3086 | - var eventMap = {}; |
3087 | - var allSchs = []; |
3088 | - |
3089 | - var startDate = new Date(day).midnight(); |
3090 | - var endDate = new Date(day).endOfDay(); |
3091 | - var items = model.getItems(startDate,endDate); |
3092 | - for(var i = 0; i < items.length; ++i) { |
3093 | - var event = items[i]; |
3094 | - |
3095 | - if(event.allDay) { |
3096 | - continue; |
3097 | - } |
3098 | - |
3099 | - var schedule = {"startDateTime": event.startDateTime, "endDateTime": event.endDateTime,"id":event.itemId }; |
3100 | - allSchs.push(schedule); |
3101 | - eventMap[event.itemId] = event; |
3102 | - } |
3103 | - |
3104 | - intern.eventMap = eventMap; |
3105 | - eventLayoutHelper.sendMessage(allSchs); |
3106 | - |
3107 | - if( intern.now.isSameDay( bubbleOverLay.day ) ) { |
3108 | + intern.eventsById = {} |
3109 | + |
3110 | + var startDate = day.midnight() |
3111 | + var itemsOfTheDay = model.itemsByTimePeriod(startDate, startDate.endOfDay()) |
3112 | + if (itemsOfTheDay.length === 0) { |
3113 | bubbleOverLay.showSeparator(); |
3114 | - } |
3115 | + intern.busy = false |
3116 | + return |
3117 | + } |
3118 | + |
3119 | + for(var i=0; i < itemsOfTheDay.length; i++) { |
3120 | + var e = itemsOfTheDay[i] |
3121 | + intern.eventsById[e.itemId] = e |
3122 | + } |
3123 | + |
3124 | + var eventInfo = CanlendarCanvas.parseDayEvents(startDate, itemsOfTheDay) |
3125 | + eventLayoutHelper.sendMessage({'events': eventInfo}) |
3126 | + bubbleOverLay.showSeparator(); |
3127 | } |
3128 | |
3129 | function destroyAllChildren() { |
3130 | - for( var i = children.length - 1; i >= 0; --i ) { |
3131 | - if( children[i].objectName === "mouseArea" || |
3132 | - children[i].objectName === "weekdevider") { |
3133 | + separator.visible = false |
3134 | + for(var i=0; i < children.length; i++) { |
3135 | + var child = children[i] |
3136 | + if (!child.isEventBubble) { |
3137 | continue; |
3138 | } |
3139 | - children[i].visible = false; |
3140 | - if( children[i].objectName !== "separator") { |
3141 | - children[i].clicked.disconnect( bubbleOverLay.showEventDetails ); |
3142 | - var key = children[i].objectName; |
3143 | - if (intern.unUsedEvents[key] === "undefined") { |
3144 | - intern.unUsedEvents[key] = children[i]; |
3145 | - } |
3146 | + if (intern.unUsedEvents.indexOf(child) === -1) { |
3147 | + child.event = null |
3148 | + child.visible = false; |
3149 | + child.clicked.disconnect(bubbleOverLay.showEventDetails); |
3150 | + intern.unUsedEvents.push(child) |
3151 | } |
3152 | } |
3153 | } |
3154 | |
3155 | - function isHashEmpty(hash) { |
3156 | - for (var prop in hash) { |
3157 | - if (prop) |
3158 | - return false; |
3159 | - } |
3160 | - return true; |
3161 | - } |
3162 | - |
3163 | - function getAKeyFromHash(hash) { |
3164 | - for (var prop in hash) { |
3165 | - return prop; |
3166 | - } |
3167 | - return "undefined"; |
3168 | - } |
3169 | - |
3170 | function getUnusedEventBubble() { |
3171 | - /* Recycle an item from unUsedEvents, and remove from hash */ |
3172 | - var key = getAKeyFromHash(intern.unUsedEvents); |
3173 | - var unUsedBubble = intern.unUsedEvents[key]; |
3174 | - delete intern.unUsedEvents[key]; |
3175 | - |
3176 | - return unUsedBubble; |
3177 | - } |
3178 | - |
3179 | - function createEvent( event, depth, sizeOfRow ) { |
3180 | - var eventBubble; |
3181 | - if( isHashEmpty(intern.unUsedEvents) ) { |
3182 | - var incubator = delegate.incubateObject(bubbleOverLay); |
3183 | - if (incubator.status !== Component.Ready) { |
3184 | - incubator.onStatusChanged = function(status) { |
3185 | - if (status === Component.Ready) { |
3186 | - incubator.object.objectName = children.length; |
3187 | - assignBubbleProperties(incubator.object, event, depth, sizeOfRow); |
3188 | - } |
3189 | - } |
3190 | - } else { |
3191 | - incubator.object.objectName = children.length; |
3192 | - assignBubbleProperties(incubator.object, event, depth, sizeOfRow); |
3193 | - } |
3194 | - } else { |
3195 | - eventBubble = getUnusedEventBubble(); |
3196 | - assignBubbleProperties(eventBubble, event, depth, sizeOfRow); |
3197 | - } |
3198 | - } |
3199 | - |
3200 | - function assignBubbleProperties(eventBubble, event, depth, sizeOfRow) { |
3201 | - var yPos = 0; |
3202 | - var height = 0; |
3203 | - var hour = 0; |
3204 | - var durationMin = 0; |
3205 | - |
3206 | - // skip it in case of endDateTime == dd-MM-yyyy 12:00 AM |
3207 | - if (event.endDateTime - day == 0) |
3208 | - return; |
3209 | - |
3210 | - if (event.endDateTime.isSameDay(day) && |
3211 | - event.endDateTime.isSameDay(event.startDateTime)) { |
3212 | - hour = event.startDateTime.getHours(); |
3213 | - yPos = (( event.startDateTime.getMinutes() * hourHeight) / 60) + hour * hourHeight |
3214 | - durationMin = (event.endDateTime - event.startDateTime) / Date.msPerMin; |
3215 | - } |
3216 | - if (!event.startDateTime.isSameDay(day) && |
3217 | - event.endDateTime.isSameDay(day)) { |
3218 | - hour = 0; |
3219 | - yPos = 0; |
3220 | - durationMin = event.endDateTime.getHours() * 60; |
3221 | - durationMin += event.endDateTime.getMinutes(); |
3222 | - } |
3223 | - if (event.startDateTime.isSameDay(day) && |
3224 | - !event.endDateTime.isSameDay(day)) { |
3225 | - hour = event.startDateTime.getHours(); |
3226 | - yPos = (( event.startDateTime.getMinutes() * hourHeight) / 60) + hour * hourHeight |
3227 | - durationMin = (24 - event.startDateTime.getHours()) * 60; |
3228 | - } |
3229 | - if (!event.startDateTime.isSameDay(day) && |
3230 | - !event.endDateTime.isSameDay(day)) { |
3231 | - hour = 0; |
3232 | - yPos = 0; |
3233 | - durationMin = 24 * 60; |
3234 | - } |
3235 | - |
3236 | - eventBubble.y = yPos; |
3237 | - height = (durationMin * hourHeight )/ 60; |
3238 | - eventBubble.height = (height > eventBubble.minimumHeight) ? height:eventBubble.minimumHeight ; |
3239 | - |
3240 | - eventBubble.model = bubbleOverLay.model |
3241 | - eventBubble.depthInRow = depth; |
3242 | - eventBubble.sizeOfRow = sizeOfRow; |
3243 | - eventBubble.event = event |
3244 | - eventBubble.visible = true; |
3245 | - eventBubble.clicked.connect( bubbleOverLay.showEventDetails ); |
3246 | + var unusedEvent = null |
3247 | + if (intern.unUsedEvents.length > 0) { |
3248 | + unusedEvent = intern.unUsedEvents[0] |
3249 | + intern.unUsedEvents.splice(0, 1); |
3250 | + } |
3251 | + return unusedEvent |
3252 | } |
3253 | |
3254 | function showSeparator() { |
3255 | - var y = ((intern.now.getMinutes() * hourHeight) / 60) + intern.now.getHours() * hourHeight; |
3256 | - separator.y = y; |
3257 | - separator.visible = true; |
3258 | + intern.now = new Date(); |
3259 | + if (intern.now.isSameDay(bubbleOverLay.day) ) { |
3260 | + var y = ((intern.now.getMinutes() * hourHeight) / 60) + intern.now.getHours() * hourHeight; |
3261 | + separator.y = y; |
3262 | + separator.visible = true; |
3263 | + } else { |
3264 | + separator.visible = false; |
3265 | + } |
3266 | + } |
3267 | + |
3268 | + onDayChanged: bubbleOverLay.idleCreateEvents(); |
3269 | + Component.onCompleted: bubbleOverLay.idleCreateEvents(); |
3270 | + enabled: !intern.busy && !intern.waitingForModelChange |
3271 | + |
3272 | + EventBubble { |
3273 | + id: temporaryEvent |
3274 | + |
3275 | + isEventBubble: false |
3276 | + Drag.active: overlayMouseArea.drag.active |
3277 | + isLiveEditing: overlayMouseArea.creatingEvent |
3278 | + visible: overlayMouseArea.creatingEvent |
3279 | + sizeOfRow: 1.0 |
3280 | + z: 100 |
3281 | + onVisibleChanged: { |
3282 | + if (visible) |
3283 | + y = event ? CanlendarCanvas.minutesSince(bubbleOverLay.day, event.startDateTime) * bubbleOverLay.minuteHeight : 0 |
3284 | + } |
3285 | + } |
3286 | + |
3287 | + Item { |
3288 | + anchors { |
3289 | + topMargin: flickable ? flickable.contentY : 0 |
3290 | + bottomMargin: flickable ? bubbleOverLay.height - flickable.contentY - flickable.height : bubbleOverLay.height |
3291 | + fill: parent |
3292 | + } |
3293 | + |
3294 | + ActivityIndicator { |
3295 | + visible: intern.busy || intern.waitingForModelChange |
3296 | + running: visible |
3297 | + anchors.centerIn: parent |
3298 | + } |
3299 | + |
3300 | + z: 100 |
3301 | + } |
3302 | + |
3303 | + MouseArea { |
3304 | + id: overlayMouseArea |
3305 | + |
3306 | + property bool creatingEvent: false |
3307 | + |
3308 | + anchors.fill: parent |
3309 | + objectName: "mouseArea" |
3310 | + drag { |
3311 | + target: creatingEvent ? temporaryEvent : null |
3312 | + axis: Drag.YAxis |
3313 | + minimumY: 0 |
3314 | + maximumY: height - temporaryEvent.height |
3315 | + } |
3316 | + |
3317 | + |
3318 | + Binding { |
3319 | + target: temporaryEvent |
3320 | + property: "visible" |
3321 | + value: overlayMouseArea.creatingEvent |
3322 | + } |
3323 | + |
3324 | + onPressAndHold: { |
3325 | + var selectedDate = new Date(day); |
3326 | + var pointY = mouse.y - (hourHeight / 2); |
3327 | + selectedDate.setHours(Math.floor(pointY / hourHeight)) |
3328 | + selectedDate.setMinutes(Math.min(pointY % hourHeight, 60)) |
3329 | + var event = createOrganizerEvent(selectedDate) |
3330 | + |
3331 | + Haptics.play() |
3332 | + |
3333 | + temporaryEvent.anchorDate = bubbleOverLay.day |
3334 | + temporaryEvent.minuteHeight = bubbleOverLay.minuteHeight |
3335 | + temporaryEvent.depthInRow = 0 |
3336 | + temporaryEvent.model = bubbleOverLay.model |
3337 | + temporaryEvent.event = event |
3338 | + temporaryEvent.resize() |
3339 | + creatingEvent = true |
3340 | + } |
3341 | + |
3342 | + onReleased: { |
3343 | + if (creatingEvent) { |
3344 | + bubbleOverLay.pressAndHoldAt(temporaryEvent.event.startDateTime) |
3345 | + creatingEvent = false |
3346 | + } |
3347 | + } |
3348 | + |
3349 | + onCanceled: { |
3350 | + if (creatingEvent) { |
3351 | + creatingEvent = false |
3352 | + } |
3353 | + } |
3354 | + } |
3355 | + |
3356 | + TimeSeparator { |
3357 | + id: separator |
3358 | + objectName: "separator" |
3359 | + width: bubbleOverLay.width |
3360 | + visible: false |
3361 | + // make sure that the object is aways visible |
3362 | + z: 1000 |
3363 | + } |
3364 | + |
3365 | + Timer { |
3366 | + id: separtorUpdateTimer |
3367 | + interval: 300000 // every 5 minutes |
3368 | + running: true |
3369 | + repeat: true |
3370 | + onTriggered: showSeparator() |
3371 | + } |
3372 | + |
3373 | + QtObject { |
3374 | + id: intern |
3375 | + |
3376 | + property var now : new Date(); |
3377 | + property var eventsById: ({}) |
3378 | + property var unUsedEvents: [] |
3379 | + property bool busy: false |
3380 | + property bool dirty: false |
3381 | + property bool waitingForModelChange: false |
3382 | + } |
3383 | + |
3384 | + Timer { |
3385 | + id: createEventsTimer |
3386 | + |
3387 | + interval: 300 |
3388 | + running: false |
3389 | + repeat: false |
3390 | + onTriggered: createEvents() |
3391 | + } |
3392 | + |
3393 | + Connections { |
3394 | + id: modelConnections |
3395 | + onModelChanged: { |
3396 | + intern.dirty = true |
3397 | + intern.waitingForModelChange = false |
3398 | + bubbleOverLay.idleCreateEvents() |
3399 | + } |
3400 | } |
3401 | } |
3402 | |
3403 | === modified file 'TimeLineBaseComponent.qml' |
3404 | --- TimeLineBaseComponent.qml 2016-01-29 14:35:14 +0000 |
3405 | +++ TimeLineBaseComponent.qml 2016-03-07 15:59:03 +0000 |
3406 | @@ -29,58 +29,106 @@ |
3407 | |
3408 | property var keyboardEventProvider; |
3409 | |
3410 | + property bool isCurrentItem: false |
3411 | + property bool isActive: false |
3412 | + |
3413 | property date startDay: DateExt.today(); |
3414 | property int weekNumber: startDay.weekNumber(Qt.locale().firstDayOfWeek); |
3415 | - property bool isActive: false |
3416 | property alias contentY: timeLineView.contentY |
3417 | property alias contentInteractive: timeLineView.interactive |
3418 | + property var modelFilter: invalidFilter |
3419 | property var selectedDay; |
3420 | |
3421 | + readonly property real hourItemHeight: units.gu(8) |
3422 | + readonly property int currentHour: timeLineView.contentY > hourItemHeight ? |
3423 | + Math.round(timeLineView.contentY / hourItemHeight) : 1 |
3424 | + readonly property int currentDayOfWeek: timeLineView.contentX > timeLineView.delegateWidth ? |
3425 | + Math.floor(timeLineView.contentX / timeLineView.delegateWidth) : 0 |
3426 | property int type: ViewType.ViewTypeWeek |
3427 | |
3428 | //visible hour |
3429 | property int scrollHour; |
3430 | |
3431 | - property EventListModel mainModel; |
3432 | - |
3433 | signal dateSelected(var date); |
3434 | signal dateHighlighted(var date); |
3435 | - |
3436 | - function scrollToCurrentTime() { |
3437 | - var currentTime = new Date(); |
3438 | - scrollHour = currentTime.getHours(); |
3439 | - |
3440 | - timeLineView.contentY = scrollHour * units.gu(8); |
3441 | - if(timeLineView.contentY >= timeLineView.contentHeight - timeLineView.height) { |
3442 | - timeLineView.contentY = timeLineView.contentHeight - timeLineView.height |
3443 | - } |
3444 | - } |
3445 | - |
3446 | - function scrollTocurrentDate() { |
3447 | - if ( type != ViewType.ViewTypeWeek ){ |
3448 | + signal pressAndHoldAt(var date, bool allDay); |
3449 | + |
3450 | + function timeIsVisible(date) { |
3451 | + |
3452 | + var hour = date.getHours(); |
3453 | + var currentTimeY = (hour * hourItemHeight) |
3454 | + return ((currentTimeY >= timeLineView.contentY) && |
3455 | + (currentTimeY <= (timeLineView.contentY + timeLineView.height))); |
3456 | + } |
3457 | + |
3458 | + function dateIsVisible(date) { |
3459 | + if (date.getFullYear() !== startDay.getFullYear()) { |
3460 | + return false; |
3461 | + } |
3462 | + |
3463 | + if (type != ViewType.ViewTypeWeek) { |
3464 | + return ((date.getMonth() === startDay.getMonth) && |
3465 | + (date.getDate() === startDay.getDate())) |
3466 | + } |
3467 | + |
3468 | + var dateDayOfWeekX = date.getDay() * timeLineView.delegateWidth |
3469 | + return ((dateDayOfWeekX >= timeLineView.contentX) && |
3470 | + (dateDayOfWeekX <= (timeLineView.contentX + timeLineView.width))) |
3471 | + } |
3472 | + |
3473 | + function dateTimeIsVisible(date) { |
3474 | + return dateIsVisible(date) && timeIsVisible(date); |
3475 | + } |
3476 | + |
3477 | + function scrollToTime(date) { |
3478 | + scrollHour = date.getHours(); |
3479 | + |
3480 | + var currentTimeY = (scrollHour * hourItemHeight) |
3481 | + var margin = (timeLineView.height / 2.0) - units.gu(5) |
3482 | + currentTimeY = currentTimeY - margin |
3483 | + timeLineView.contentY = Math.min(timeLineView.contentHeight - timeLineView.height, currentTimeY > 0 ? currentTimeY : 0) |
3484 | + timeLineView.returnToBounds() |
3485 | + } |
3486 | + |
3487 | + function scrollToDate(date) { |
3488 | + if (type != ViewType.ViewTypeWeek) { |
3489 | return; |
3490 | } |
3491 | |
3492 | - var today = DateExt.today(); |
3493 | - var startOfWeek = today.weekStart(Qt.locale().firstDayOfWeek); |
3494 | - var weekDay = today.getDay(); |
3495 | - var diff = weekDay - Qt.locale().firstDayOfWeek |
3496 | - diff = diff < 0 ? 6 : diff |
3497 | - |
3498 | - if( startOfWeek.isSameDay(startDay) && diff > 2) { |
3499 | - timeLineView.contentX = (diff * timeLineView.delegateWidth); |
3500 | - if( timeLineView.contentX > (timeLineView.contentWidth - timeLineView.width) ) { |
3501 | - timeLineView.contentX = timeLineView.contentWidth - timeLineView.width |
3502 | - } |
3503 | + var todayWeekNumber = date.weekNumber(Qt.locale().firstDayOfWeek); |
3504 | + |
3505 | + if (todayWeekNumber === root.weekNumber) { |
3506 | + var startOfWeek = date.weekStart(Qt.locale().firstDayOfWeek); |
3507 | + var weekDay = date.getDay(); |
3508 | + var diff = weekDay - Qt.locale().firstDayOfWeek |
3509 | + diff = diff < 0 ? 0 : diff |
3510 | + |
3511 | + var currentDayY = timeLineView.delegateWidth * diff |
3512 | + var margin = (timeLineView.width - timeLineView.delegateWidth) / 2 |
3513 | + currentDayY = currentDayY - margin |
3514 | + timeLineView.contentX = Math.min(timeLineView.contentWidth - timeLineView.width, currentDayY > 0 ? currentDayY : 0) |
3515 | } else { |
3516 | - //need to check swipe direction |
3517 | - //and change startion position as per direction |
3518 | - if(weekViewPath.swipeDirection() === -1) { |
3519 | - timeLineView.contentX = timeLineView.contentWidth - timeLineView.width |
3520 | - } else { |
3521 | - timeLineView.contentX = 0; |
3522 | - } |
3523 | + timeLineView.contentX = 0 |
3524 | } |
3525 | + |
3526 | + timeLineView.returnToBounds() |
3527 | + } |
3528 | + |
3529 | + function scrollToDateAndTime(date) { |
3530 | + scrollToTime(date) |
3531 | + scrollToDate(date) |
3532 | + } |
3533 | + |
3534 | + function scrollToEnd() |
3535 | + { |
3536 | + timeLineView.contentX = timeLineView.contentWidth - timeLineView.width |
3537 | + timeLineView.returnToBounds() |
3538 | + } |
3539 | + |
3540 | + function scrollToBegin() |
3541 | + { |
3542 | + timeLineView.contentX = 0 |
3543 | + timeLineView.returnToBounds() |
3544 | } |
3545 | |
3546 | Connections{ |
3547 | @@ -110,29 +158,51 @@ |
3548 | } |
3549 | } |
3550 | |
3551 | - Timer{ |
3552 | - interval: 200; running: true; repeat: false |
3553 | - onTriggered: { |
3554 | - mainModel = modelComponent.createObject(); |
3555 | - activityLoader.running = Qt.binding( function (){ return mainModel.isLoading;}); |
3556 | - } |
3557 | - } |
3558 | - |
3559 | - Component { |
3560 | - id: modelComponent |
3561 | - EventListModel { |
3562 | - id: mainModel |
3563 | - startPeriod: startDay.midnight(); |
3564 | - endPeriod: type == ViewType.ViewTypeWeek ? startPeriod.addDays(7).endOfDay(): startPeriod.endOfDay() |
3565 | - filter: eventModel.filter |
3566 | - } |
3567 | - } |
3568 | + onIsActiveChanged: { |
3569 | + if (isActive && (mainModel.filter === invalidFilter)) { |
3570 | + idleRefresh.reset() |
3571 | + } |
3572 | + } |
3573 | + |
3574 | + Timer { |
3575 | + id: idleRefresh |
3576 | + |
3577 | + function reset() |
3578 | + { |
3579 | + mainModel.filter = invalidFilter |
3580 | + restart() |
3581 | + } |
3582 | + |
3583 | + interval: root.isCurrentItem ? 500 : 1000 |
3584 | + repeat: false |
3585 | + onTriggered: { |
3586 | + mainModel.filter = Qt.binding(function() { return root.modelFilter} ) |
3587 | + } |
3588 | + } |
3589 | + |
3590 | + InvalidFilter { |
3591 | + id: invalidFilter |
3592 | + } |
3593 | + |
3594 | + EventListModel { |
3595 | + id: mainModel |
3596 | + |
3597 | + manager:"eds" |
3598 | + startPeriod: startDay.midnight(); |
3599 | + endPeriod: type == ViewType.ViewTypeWeek ? startPeriod.addDays(7).endOfDay(): startPeriod.endOfDay() |
3600 | + filter: invalidFilter |
3601 | + |
3602 | + onStartPeriodChanged: idleRefresh.reset() |
3603 | + onEndPeriodChanged: idleRefresh.reset() |
3604 | + } |
3605 | |
3606 | ActivityIndicator { |
3607 | id: activityLoader |
3608 | + objectName : "activityIndicator" |
3609 | + |
3610 | visible: running |
3611 | - objectName : "activityIndicator" |
3612 | anchors.centerIn: parent |
3613 | + //running: mainModel.isLoading |
3614 | z:2 |
3615 | } |
3616 | |
3617 | @@ -149,11 +219,21 @@ |
3618 | selectedDay: root.selectedDay |
3619 | |
3620 | onDateSelected: { |
3621 | - root.dateSelected(date); |
3622 | + root.dateSelected(date.getFullYear(), |
3623 | + date.getMonth(), |
3624 | + date.getDate(), |
3625 | + root.currentHour, 0, 0) |
3626 | } |
3627 | |
3628 | onDateHighlighted: { |
3629 | - root.dateHighlighted(date); |
3630 | + root.dateHighlighted(date.getFullYear(), |
3631 | + date.getMonth(), |
3632 | + date.getDate(), |
3633 | + root.currentHour, 0, 0) |
3634 | + } |
3635 | + |
3636 | + onAllDayPressAndHold: { |
3637 | + root.pressAndHoldAt(date, true) |
3638 | } |
3639 | } |
3640 | |
3641 | @@ -183,7 +263,7 @@ |
3642 | |
3643 | property int delegateWidth: { |
3644 | if( type == ViewType.ViewTypeWeek ) { |
3645 | - width/3 - units.gu(1) /*partial visible area*/ |
3646 | + width/3 - units.gu(1) // partial visible area |
3647 | } else { |
3648 | width |
3649 | } |
3650 | @@ -198,11 +278,6 @@ |
3651 | } |
3652 | } |
3653 | |
3654 | - onContentWidthChanged: { |
3655 | - scrollToCurrentTime(); |
3656 | - scrollTocurrentDate(); |
3657 | - } |
3658 | - |
3659 | clip: true |
3660 | |
3661 | TimeLineBackground{} |
3662 | @@ -214,7 +289,12 @@ |
3663 | model: type == ViewType.ViewTypeWeek ? 7 : 1 |
3664 | |
3665 | delegate: TimeLineBase { |
3666 | + id: delegate |
3667 | + |
3668 | + objectName: "TimeLineBase_" + root.objectName |
3669 | + |
3670 | property int idx: index |
3671 | + flickable: timeLineView |
3672 | anchors.top: parent.top |
3673 | width: { |
3674 | if( type == ViewType.ViewTypeWeek ) { |
3675 | @@ -228,12 +308,14 @@ |
3676 | day: startDay.addDays(index) |
3677 | model: mainModel |
3678 | |
3679 | - Connections{ |
3680 | - target: mainModel |
3681 | + onPressAndHoldAt: { |
3682 | + root.pressAndHoldAt(date, false) |
3683 | + } |
3684 | |
3685 | - onModelChanged: { |
3686 | - createEvents(); |
3687 | - } |
3688 | + Binding { |
3689 | + target: timeLineView |
3690 | + property: "interactive" |
3691 | + value: !delegate.creatingEvent |
3692 | } |
3693 | |
3694 | DropArea { |
3695 | @@ -251,12 +333,13 @@ |
3696 | event.startDateTime = startDate; |
3697 | event.endDateTime = endDate; |
3698 | |
3699 | - return event; |
3700 | + return event; |
3701 | } |
3702 | |
3703 | onDropped: { |
3704 | var event = dropArea.modifyEventForDrag(drop); |
3705 | - model.saveItem(event); |
3706 | + delegate.waitForModelChange() |
3707 | + delegate.model.saveItem(event); |
3708 | } |
3709 | |
3710 | onPositionChanged: { |
3711 | @@ -299,13 +382,6 @@ |
3712 | anchors.fill: parent |
3713 | } |
3714 | } |
3715 | - |
3716 | - Connections{ |
3717 | - target: mainModel |
3718 | - onStartPeriodChanged:{ |
3719 | - destroyAllChildren(); |
3720 | - } |
3721 | - } |
3722 | } |
3723 | } |
3724 | } |
3725 | @@ -317,8 +393,16 @@ |
3726 | id: comp |
3727 | EventBubble { |
3728 | type: root.type == ViewType.ViewTypeWeek ? narrowType : wideType |
3729 | - flickable: root.isActive ? timeLineView : null |
3730 | + flickable: root.isCurrentItem ? timeLineView : null |
3731 | clip: true |
3732 | + opacity: parent.enabled ? 1.0 : 0.3 |
3733 | + |
3734 | + // send a signal to update application current date |
3735 | + onClicked: root.dateHighlighted(event.startDateTime) |
3736 | + onIsLiveEditingChanged: { |
3737 | + if (isLiveEditing) |
3738 | + root.dateHighlighted(event.startDateTime) |
3739 | + } |
3740 | } |
3741 | } |
3742 | } |
3743 | |
3744 | === modified file 'TimeLineHeader.qml' |
3745 | --- TimeLineHeader.qml 2016-01-29 14:35:14 +0000 |
3746 | +++ TimeLineHeader.qml 2016-03-07 15:59:03 +0000 |
3747 | @@ -33,6 +33,7 @@ |
3748 | |
3749 | signal dateSelected(var date); |
3750 | signal dateHighlighted(var date); |
3751 | + signal allDayPressAndHold(var date); |
3752 | |
3753 | width: parent.width |
3754 | height: units.gu(10) |
3755 | @@ -118,6 +119,9 @@ |
3756 | width: parent.width |
3757 | height: units.gu(5) |
3758 | |
3759 | + onPressAndHold: headerRoot.allDayPressAndHold(date) |
3760 | + |
3761 | + |
3762 | Connections{ |
3763 | target: mainModel |
3764 | onModelChanged : { |
3765 | @@ -184,6 +188,8 @@ |
3766 | height: units.gu(5) |
3767 | model: mainModel |
3768 | |
3769 | + onPressAndHold: headerRoot.allDayPressAndHold(date) |
3770 | + |
3771 | Connections{ |
3772 | target: mainModel |
3773 | onModelChanged : { |
3774 | |
3775 | === modified file 'WeekView.qml' |
3776 | --- WeekView.qml 2016-02-03 08:06:25 +0000 |
3777 | +++ WeekView.qml 2016-03-07 15:59:03 +0000 |
3778 | @@ -22,21 +22,31 @@ |
3779 | import "ViewType.js" as ViewType |
3780 | import "./3rd-party/lunar.js" as Lunar |
3781 | |
3782 | -Page{ |
3783 | +PageWithBottomEdge { |
3784 | id: weekViewPage |
3785 | objectName: "weekViewPage" |
3786 | |
3787 | - property var dayStart: new Date(); |
3788 | - property var firstDay: dayStart.weekStart(Qt.locale().firstDayOfWeek); |
3789 | + property var anchorDate: new Date(); |
3790 | + readonly property var anchorFirstDayOfWeek: anchorDate.weekStart(Qt.locale().firstDayOfWeek) |
3791 | + readonly property var currentDate: weekViewPath.currentItem.item.startDay |
3792 | + readonly property var currentFirstDayOfWeek: currentDate.weekStart(Qt.locale().firstDayOfWeek) |
3793 | + |
3794 | property bool isCurrentPage: false |
3795 | property var selectedDay; |
3796 | + property var highlightedDay; |
3797 | + property bool displayLunarCalendar: false |
3798 | |
3799 | signal dateSelected(var date); |
3800 | - signal dateHighlighted(var date); |
3801 | + signal pressAndHoldAt(var date, bool allDay) |
3802 | + |
3803 | + function delayScrollToDate(scrollDate, scrollTime) { |
3804 | + idleScroll.scrollToTime = scrollTime != undefined ? scrollTime : true |
3805 | + idleScroll.scrollToDate = new Date(scrollDate) |
3806 | + idleScroll.restart() |
3807 | + } |
3808 | |
3809 | Keys.forwardTo: [weekViewPath] |
3810 | - |
3811 | - flickable: null |
3812 | + createEventAt: null |
3813 | |
3814 | Action { |
3815 | id: calendarTodayAction |
3816 | @@ -44,7 +54,60 @@ |
3817 | iconName: "calendar-today" |
3818 | text: i18n.tr("Today") |
3819 | onTriggered: { |
3820 | - dayStart = new Date() |
3821 | + var today = new Date() |
3822 | + delayScrollToDate(today) |
3823 | + anchorDate = today |
3824 | + } |
3825 | + } |
3826 | + |
3827 | + onAnchorDateChanged: { |
3828 | + weekViewPath.scrollToBegginer() |
3829 | + } |
3830 | + |
3831 | + onEventCreated: { |
3832 | + var scrollDate = new Date(event.startDateTime) |
3833 | + var currentWeekNumber = currentDate.weekNumber(Qt.locale().firstDayOfWeek) |
3834 | + var eventWeekNumber = scrollDate.weekNumber(Qt.locale().firstDayOfWeek) |
3835 | + var needScroll = false |
3836 | + |
3837 | + if ((scrollDate.getFullYear() !== currentDate.getFullYear()) || |
3838 | + (currentWeekNumber !== eventWeekNumber)) { |
3839 | + anchorDate = new Date(scrollDate) |
3840 | + needScroll = true |
3841 | + } else { |
3842 | + if (event.allDay) { |
3843 | + needScroll = !weekViewPath.currentItem.item.dateIsVisible(scrollDate) |
3844 | + } else { |
3845 | + needScroll = !weekViewPath.currentItem.item.timeIsVisible(scrollDate) |
3846 | + } |
3847 | + } |
3848 | + |
3849 | + highlightedDay = scrollDate |
3850 | + if (needScroll) { |
3851 | + delayScrollToDate(scrollDate, !event.allDay) |
3852 | + } |
3853 | + } |
3854 | + |
3855 | + Timer { |
3856 | + id: idleScroll |
3857 | + |
3858 | + property var scrollToDate: null |
3859 | + property bool scrollToTime: true |
3860 | + |
3861 | + interval: 200 |
3862 | + repeat:false |
3863 | + onTriggered: { |
3864 | + if (scrollToDate) { |
3865 | + if (scrollToTime) |
3866 | + weekViewPath.currentItem.item.scrollToDateAndTime(scrollToDate); |
3867 | + else |
3868 | + weekViewPath.currentItem.item.scrollToDate(scrollToDate); |
3869 | + } else { |
3870 | + weekViewPath.currentItem.item.scrollToBegin() |
3871 | + } |
3872 | + |
3873 | + scrollToDate = null |
3874 | + scrollToTime = true |
3875 | } |
3876 | } |
3877 | |
3878 | @@ -54,7 +117,6 @@ |
3879 | leadingActionBar.actions: tabs.tabsAction |
3880 | trailingActionBar.actions: [ |
3881 | calendarTodayAction, |
3882 | - commonHeaderActions.newEventAction, |
3883 | commonHeaderActions.showCalendarAction, |
3884 | commonHeaderActions.reloadAction, |
3885 | commonHeaderActions.syncCalendarAction, |
3886 | @@ -62,11 +124,27 @@ |
3887 | ] |
3888 | |
3889 | title: { |
3890 | - // TRANSLATORS: this is a time formatting string, |
3891 | - // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions. |
3892 | - // It's used in the header of the month and week views |
3893 | - var monthName = dayStart.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy")) |
3894 | - return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1) |
3895 | + if(weekViewPage.displayLunarCalendar){ |
3896 | + var lunarDate = Lunar.calendar.solar2lunar(currentDate.getFullYear(), |
3897 | + currentDate.getMonth() + 1, |
3898 | + currentDate.getDate()) |
3899 | + return i18n.tr("%1 %2").arg(lunarDate .IMonthCn).arg(lunarDate.gzYear) |
3900 | + } else { |
3901 | + // TRANSLATORS: this is a time formatting string, |
3902 | + // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions. |
3903 | + // It's used in the header of the month and week views |
3904 | + var currentLastDayOfWeek = currentFirstDayOfWeek.addDays(7) |
3905 | + if (currentLastDayOfWeek.getMonth() !== currentFirstDayOfWeek.getMonth()) { |
3906 | + var firstMonthName = currentFirstDayOfWeek.toLocaleString(Qt.locale(),i18n.tr("MMM")) |
3907 | + var lastMonthName = currentLastDayOfWeek.toLocaleString(Qt.locale(),i18n.tr("MMM")) |
3908 | + return (firstMonthName[0].toUpperCase() + firstMonthName.substr(1, 2) + "/" + |
3909 | + lastMonthName[0].toUpperCase() + lastMonthName.substr(1, 2) + " " + |
3910 | + currentLastDayOfWeek.getFullYear()) |
3911 | + } else { |
3912 | + var monthName = currentDate.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy")) |
3913 | + return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1) |
3914 | + } |
3915 | + } |
3916 | } |
3917 | flickable: null |
3918 | } |
3919 | @@ -80,25 +158,13 @@ |
3920 | topMargin: header.height |
3921 | } |
3922 | |
3923 | + onCurrentIndexChanged: { |
3924 | + weekViewPage.highlightedDay = null |
3925 | + } |
3926 | + |
3927 | //This is used to scroll all view together when currentItem scrolls |
3928 | property var childContentY; |
3929 | |
3930 | - onNextItemHighlighted: { |
3931 | - nextWeek(); |
3932 | - } |
3933 | - |
3934 | - onPreviousItemHighlighted: { |
3935 | - previousWeek(); |
3936 | - } |
3937 | - |
3938 | - function nextWeek() { |
3939 | - dayStart = firstDay.addDays(7); |
3940 | - } |
3941 | - |
3942 | - function previousWeek(){ |
3943 | - dayStart = firstDay.addDays(-7); |
3944 | - } |
3945 | - |
3946 | delegate: Loader { |
3947 | id: timelineLoader |
3948 | width: parent.width |
3949 | @@ -112,39 +178,52 @@ |
3950 | TimeLineBaseComponent { |
3951 | id: timeLineView |
3952 | |
3953 | + startDay: anchorFirstDayOfWeek.addDays((weekViewPath.loopCurrentIndex + weekViewPath.indexType(index)) * 7) |
3954 | + anchors.fill: parent |
3955 | type: ViewType.ViewTypeWeek |
3956 | - anchors.fill: parent |
3957 | - isActive: parent.PathView.isCurrentItem |
3958 | - startDay: firstDay.addDays( weekViewPath.indexType(index) * 7) |
3959 | + isCurrentItem: parent.PathView.isCurrentItem |
3960 | + isActive: !weekViewPath.moving && !weekViewPath.flicking |
3961 | keyboardEventProvider: weekViewPath |
3962 | selectedDay: weekViewPage.selectedDay |
3963 | - |
3964 | - onIsActiveChanged: { |
3965 | - timeLineView.scrollTocurrentDate(); |
3966 | - } |
3967 | + modelFilter: weekViewPage.model ? weekViewPage.model.filter : null |
3968 | |
3969 | onDateSelected: { |
3970 | weekViewPage.dateSelected(date); |
3971 | } |
3972 | |
3973 | onDateHighlighted:{ |
3974 | - weekViewPage.dateHighlighted(date); |
3975 | + weekViewPage.highlightedDay = date |
3976 | + } |
3977 | + |
3978 | + Component.onCompleted: { |
3979 | + var iType = weekViewPath.indexType(index) |
3980 | + if (iType === 0) { |
3981 | + idleScroll.restart() |
3982 | + } else if (iType < 0) { |
3983 | + scrollToEnd() |
3984 | + } |
3985 | + } |
3986 | + |
3987 | + onPressAndHoldAt: { |
3988 | + weekViewPage.pressAndHoldAt(date, allDay) |
3989 | } |
3990 | |
3991 | Connections{ |
3992 | target: calendarTodayAction |
3993 | onTriggered:{ |
3994 | - if( isActive ) |
3995 | - timeLineView.scrollTocurrentDate(); |
3996 | - } |
3997 | + if (isActive) |
3998 | + timeLineView.scrollToDate(new Date()); |
3999 | + } |
4000 | } |
4001 | |
4002 | - Connections{ |
4003 | - target: weekViewPage |
4004 | - onIsCurrentPageChanged:{ |
4005 | - if(weekViewPage.isCurrentPage){ |
4006 | - timeLineView.scrollToCurrentTime(); |
4007 | - timeLineView.scrollTocurrentDate(); |
4008 | + Connections { |
4009 | + target: weekViewPath |
4010 | + onLoopCurrentIndexChanged: { |
4011 | + var iType = weekViewPath.indexType(index) |
4012 | + if (iType < 0) { |
4013 | + scrollToEnd() |
4014 | + } else if (iType > 0) { |
4015 | + scrollToBegin() |
4016 | } |
4017 | } |
4018 | } |
4019 | @@ -164,22 +243,13 @@ |
4020 | value: contentY |
4021 | when: parent.PathView.isCurrentItem |
4022 | } |
4023 | + Binding { |
4024 | + target: weekViewPath |
4025 | + property: "interactive" |
4026 | + value: timeLineView.contentInteractive |
4027 | + } |
4028 | } |
4029 | } |
4030 | } |
4031 | } |
4032 | - |
4033 | - Component.onCompleted: { |
4034 | - pageHeader.title = Qt.binding(function(){ |
4035 | - if(mainView.displayLunarCalendar){ |
4036 | - var lunarDate = Lunar.calendar.solar2lunar(dayStart.getFullYear(), |
4037 | - dayStart.getMonth() + 1, |
4038 | - dayStart.getDate()) |
4039 | - return i18n.tr("%1 %2").arg(lunarDate .IMonthCn).arg(lunarDate.gzYear) |
4040 | - } else { |
4041 | - var monthName = dayStart.toLocaleString(Qt.locale(),i18n.tr("MMMM yyyy")) |
4042 | - return monthName[0].toUpperCase() + monthName.substr(1, monthName.length - 1) |
4043 | - } |
4044 | - }) |
4045 | - } |
4046 | } |
4047 | |
4048 | === modified file 'YearView.qml' |
4049 | --- YearView.qml 2016-02-03 08:06:25 +0000 |
4050 | +++ YearView.qml 2016-03-07 15:59:03 +0000 |
4051 | @@ -18,22 +18,36 @@ |
4052 | |
4053 | import QtQuick 2.4 |
4054 | import Ubuntu.Components 1.3 |
4055 | + |
4056 | import "dateExt.js" as DateExt |
4057 | import "./3rd-party/lunar.js" as Lunar |
4058 | |
4059 | -Page { |
4060 | +PageWithBottomEdge { |
4061 | id: yearViewPage |
4062 | objectName: "yearViewPage" |
4063 | |
4064 | - property int currentYear: DateExt.today().getFullYear(); |
4065 | + property int anchorYear: new Date().getFullYear() |
4066 | + property bool displayLunarCalendar: false |
4067 | + readonly property int currentYear: yearPathView.currentItem.item ? yearPathView.currentItem.item.year : anchorYear |
4068 | + |
4069 | signal monthSelected(var date); |
4070 | |
4071 | + function refreshCurrentYear(year) |
4072 | + { |
4073 | + if (currentYear !== year) { |
4074 | + anchorYear = year; |
4075 | + yearPathView.scrollToBegginer() |
4076 | + } |
4077 | + var yearViewDelegate = yearPathView.currentItem; |
4078 | + if (yearViewDelegate && yearViewDelegate.item) { |
4079 | + yearViewDelegate.item.refresh(); |
4080 | + } |
4081 | + } |
4082 | + |
4083 | + createEventAt: null |
4084 | Keys.forwardTo: [yearPathView] |
4085 | - |
4086 | - function refreshCurrentYear(year) { |
4087 | - currentYear = year; |
4088 | - var yearViewDelegate = yearPathView.currentItem.item; |
4089 | - yearViewDelegate.refresh(); |
4090 | + onAnchorYearChanged: { |
4091 | + yearPathView.scrollToBegginer() |
4092 | } |
4093 | |
4094 | Action { |
4095 | @@ -42,26 +56,50 @@ |
4096 | iconName: "calendar-today" |
4097 | text: i18n.tr("Today") |
4098 | onTriggered: { |
4099 | - currentYear = new Date().getFullYear() |
4100 | + var todayYear = new Date().getFullYear() |
4101 | + yearViewPage.refreshCurrentYear(todayYear) |
4102 | } |
4103 | } |
4104 | |
4105 | header: PageHeader { |
4106 | id: pageHeader |
4107 | - |
4108 | leadingActionBar.actions: tabs.tabsAction |
4109 | trailingActionBar.actions: [ |
4110 | calendarTodayAction, |
4111 | - commonHeaderActions.newEventAction, |
4112 | commonHeaderActions.showCalendarAction, |
4113 | commonHeaderActions.reloadAction, |
4114 | commonHeaderActions.syncCalendarAction, |
4115 | commonHeaderActions.settingsAction |
4116 | ] |
4117 | - title: i18n.tr("Year %1").arg(currentYear) |
4118 | + title: { |
4119 | + if (displayLunarCalendar) { |
4120 | + var lunarDate = Lunar.calendar.solar2lunar(currentYear, 6, 0) |
4121 | + return lunarDate.gzYear +" "+ lunarDate.Animal |
4122 | + } else { |
4123 | + return i18n.tr("Year %1").arg(currentYear) |
4124 | + } |
4125 | + } |
4126 | flickable: null |
4127 | } |
4128 | |
4129 | + ActivityIndicator { |
4130 | + property var startDate: new Date() |
4131 | + |
4132 | + visible: running |
4133 | + running: (yearPathView.currentItem.status !== Loader.Ready) |
4134 | + anchors.centerIn: parent |
4135 | + z:2 |
4136 | + |
4137 | + onRunningChanged: { |
4138 | + if (!running) { |
4139 | + var current = new Date() |
4140 | + console.debug("Elapsed:" + (current.getTime() - startDate.getTime())) |
4141 | + } |
4142 | + } |
4143 | + } |
4144 | + |
4145 | + flickable: null |
4146 | + |
4147 | PathViewBase { |
4148 | id: yearPathView |
4149 | objectName: "yearPathView" |
4150 | @@ -71,46 +109,24 @@ |
4151 | topMargin: header.height |
4152 | } |
4153 | |
4154 | - onNextItemHighlighted: { |
4155 | - currentYear = currentYear + 1; |
4156 | - } |
4157 | - |
4158 | - onPreviousItemHighlighted: { |
4159 | - currentYear = currentYear - 1; |
4160 | - } |
4161 | - |
4162 | delegate: Loader { |
4163 | - width: parent.width |
4164 | - height: parent.height |
4165 | - anchors.top: parent.top |
4166 | - |
4167 | - asynchronous: index !== yearPathView.currentIndex |
4168 | - sourceComponent: delegateComponent |
4169 | - |
4170 | - Component{ |
4171 | - id: delegateComponent |
4172 | - |
4173 | - YearViewDelegate{ |
4174 | - focus: index == yearPathView.currentIndex |
4175 | - |
4176 | - scrollMonth: 0; |
4177 | - isCurrentItem: index == yearPathView.currentIndex |
4178 | - year: (currentYear + yearPathView.indexType(index)) |
4179 | - |
4180 | - anchors.fill: parent |
4181 | + id: delegateLoader |
4182 | + |
4183 | + asynchronous: true |
4184 | + width: PathView.view.width |
4185 | + height: PathView.view.height |
4186 | + |
4187 | + sourceComponent: YearViewDelegate { |
4188 | + visible: delegateLoader.status === Loader.Ready |
4189 | + anchors.fill: parent |
4190 | + scrollMonth: 0; |
4191 | + isCurrentItem: (index === yearPathView.currentIndex) |
4192 | + focus: isCurrentItem |
4193 | + year: (yearViewPage.anchorYear + yearPathView.loopCurrentIndex + yearPathView.indexType(index)) |
4194 | + onMonthSelected: { |
4195 | + yearViewPage.monthSelected(date) |
4196 | } |
4197 | } |
4198 | } |
4199 | } |
4200 | - |
4201 | - Component.onCompleted: { |
4202 | - pageHeader.title = Qt.binding(function(){ |
4203 | - if(mainView.displayLunarCalendar){ |
4204 | - var lunarDate = Lunar.calendar.solar2lunar(currentYear, 6, 0) |
4205 | - return lunarDate.gzYear +" "+ lunarDate.Animal |
4206 | - } else { |
4207 | - return i18n.tr("Year %1").arg(currentYear) |
4208 | - } |
4209 | - }) |
4210 | - } |
4211 | } |
4212 | |
4213 | === modified file 'YearViewDelegate.qml' |
4214 | --- YearViewDelegate.qml 2016-02-03 08:06:25 +0000 |
4215 | +++ YearViewDelegate.qml 2016-03-07 15:59:03 +0000 |
4216 | @@ -1,26 +1,37 @@ |
4217 | import QtQuick 2.4 |
4218 | import Ubuntu.Components 1.3 |
4219 | |
4220 | -GridView{ |
4221 | + GridView{ |
4222 | id: yearView |
4223 | - clip: true |
4224 | |
4225 | property int scrollMonth; |
4226 | property bool isCurrentItem; |
4227 | property int year; |
4228 | - |
4229 | + readonly property var currentDate: new Date() |
4230 | + readonly property int currentYear: currentDate.getFullYear() |
4231 | + readonly property int currentMonth: currentDate.getMonth() |
4232 | readonly property int minCellWidth: units.gu(30) |
4233 | + |
4234 | + signal monthSelected(var date); |
4235 | + |
4236 | + function refresh() { |
4237 | + scrollMonth = 0; |
4238 | + if(year == currentYear) { |
4239 | + scrollMonth = currentMonth |
4240 | + } |
4241 | + yearView.positionViewAtIndex(scrollMonth, GridView.Beginning); |
4242 | + } |
4243 | + |
4244 | + // Does not increase cash buffer if user is scolling |
4245 | + cacheBuffer: parent.PathView.view.flicking || parent.PathView.view.dragging || !isCurrentItem ? 0 : 6 * cellHeight |
4246 | + |
4247 | cellWidth: Math.floor(Math.min.apply(Math, [3, 4].map(function(n) |
4248 | { return ((width / n >= minCellWidth) ? width / n : width / 2) }))) |
4249 | - |
4250 | cellHeight: cellWidth * 1.4 |
4251 | |
4252 | + clip: true |
4253 | model: 12 /* months in a year */ |
4254 | |
4255 | - onYearChanged: { |
4256 | - refresh(); |
4257 | - } |
4258 | - |
4259 | //scroll in case content height changed |
4260 | onHeightChanged: { |
4261 | yearView.positionViewAtIndex(scrollMonth, GridView.Beginning); |
4262 | @@ -30,71 +41,25 @@ |
4263 | yearView.positionViewAtIndex(scrollMonth, GridView.Beginning); |
4264 | } |
4265 | |
4266 | - function refresh() { |
4267 | - scrollMonth = 0; |
4268 | - var today = new Date(); |
4269 | - if(year == today.getFullYear()) { |
4270 | - scrollMonth = today.getMonth(); |
4271 | - } |
4272 | - yearView.positionViewAtIndex(scrollMonth, GridView.Beginning); |
4273 | - } |
4274 | - |
4275 | - Connections{ |
4276 | - target: yearPathView |
4277 | - onScrollUp: { |
4278 | - scrollMonth -= 2; |
4279 | - if(scrollMonth < 0) { |
4280 | - scrollMonth = 0; |
4281 | - } |
4282 | - yearView.positionViewAtIndex(scrollMonth, GridView.Beginning); |
4283 | - } |
4284 | - |
4285 | - onScrollDown: { |
4286 | - scrollMonth += 2; |
4287 | - var visibleMonths = yearView.height / cellHeight; |
4288 | - if( scrollMonth >= (11 - visibleMonths)) { |
4289 | - scrollMonth = (11 - visibleMonths); |
4290 | - } |
4291 | - yearView.positionViewAtIndex(scrollMonth, GridView.Beginning); |
4292 | - } |
4293 | - } |
4294 | - |
4295 | - delegate: Loader { |
4296 | - width: yearView.cellWidth |
4297 | - height: yearView.cellHeight |
4298 | - |
4299 | - sourceComponent: delegateComponent |
4300 | - asynchronous: !yearView.focus |
4301 | - |
4302 | - Component { |
4303 | - id: delegateComponent |
4304 | - |
4305 | - Item { |
4306 | - anchors.fill: parent |
4307 | - anchors.margins: units.gu(0.5) |
4308 | - |
4309 | - MonthComponent { |
4310 | - id: monthComponent |
4311 | - objectName: "monthComponent" + index |
4312 | - showEvents: false |
4313 | - currentMonth: new Date(yearView.year, index, 1, 0, 0, 0, 0) |
4314 | - displayWeekNumber: mainView.displayWeekNumber; |
4315 | - displayLunarCalendar: false; //we disable lunar calendar display in yeaer view due to space |
4316 | - isCurrentItem: yearView.focus |
4317 | - |
4318 | - isYearView: true |
4319 | - anchors.fill: parent |
4320 | - |
4321 | - dayLabelFontSize:"x-small" |
4322 | - dateLabelFontSize: "medium" |
4323 | - monthLabelFontSize: "medium" |
4324 | - yearLabelFontSize: "medium" |
4325 | - |
4326 | - onMonthSelected: { |
4327 | - yearViewPage.monthSelected(date); |
4328 | - } |
4329 | - } |
4330 | - } |
4331 | - } |
4332 | + delegate: MonthComponent { |
4333 | + id: monthComponent |
4334 | + objectName: "monthComponent" + index |
4335 | + |
4336 | + width: yearView.cellWidth - units.gu(1) |
4337 | + height: yearView.cellHeight - units.gu(1) |
4338 | + y: units.gu(0.5) |
4339 | + x: units.gu(0.5) |
4340 | + |
4341 | + currentYear: yearView.year |
4342 | + currentMonth: index |
4343 | + isCurrentItem: yearView.focus |
4344 | + isYearView: true |
4345 | + dayLabelFontSize:"x-small" |
4346 | + dateLabelFontSize: "medium" |
4347 | + monthLabelFontSize: "medium" |
4348 | + yearLabelFontSize: "medium" |
4349 | + onMonthSelected: { |
4350 | + yearView.monthSelected(date); |
4351 | + } |
4352 | } |
4353 | } |
4354 | |
4355 | === modified file 'calendar.qml' |
4356 | --- calendar.qml 2016-02-26 12:57:00 +0000 |
4357 | +++ calendar.qml 2016-03-07 15:59:03 +0000 |
4358 | @@ -20,6 +20,7 @@ |
4359 | import Ubuntu.Components.Popups 1.0 |
4360 | import QtOrganizer 5.0 |
4361 | import Qt.labs.settings 1.0 |
4362 | + |
4363 | import "dateExt.js" as DateExt |
4364 | |
4365 | MainView { |
4366 | @@ -27,6 +28,7 @@ |
4367 | |
4368 | property bool displayWeekNumber: false; |
4369 | property bool displayLunarCalendar: false; |
4370 | + readonly property bool syncInProgress: commonHeaderActions.syncInProgress |
4371 | |
4372 | // Work-around until this branch lands: |
4373 | // https://code.launchpad.net/~tpeeters/ubuntu-ui-toolkit/optIn-tabsDrawer/+merge/212496 |
4374 | @@ -92,7 +94,7 @@ |
4375 | focus: true |
4376 | Keys.forwardTo: [pageStack.currentPage] |
4377 | backgroundColor: "#ffffff" |
4378 | - anchorToKeyboard: true |
4379 | + anchorToKeyboard: false |
4380 | |
4381 | Connections { |
4382 | target: UriHandler |
4383 | @@ -103,8 +105,9 @@ |
4384 | if(commands[0].toLowerCase() === "eventid") { |
4385 | // calendar://eventid=?? |
4386 | if( eventModel ) { |
4387 | + // qtorganizer:eds::<event-id> |
4388 | var eventId = commands[1]; |
4389 | - var prefix = "qtorganizer:eds::system-calendar/"; |
4390 | + var prefix = "qtorganizer:eds::"; |
4391 | if (eventId.indexOf(prefix) < 0) |
4392 | eventId = prefix + eventId; |
4393 | |
4394 | @@ -174,17 +177,37 @@ |
4395 | EventListModel{ |
4396 | id: eventModel |
4397 | |
4398 | - autoUpdate: true |
4399 | + property bool isReady: false |
4400 | + |
4401 | startPeriod: tabs.currentDay |
4402 | endPeriod: tabs.currentDay |
4403 | |
4404 | filter: invalidFilter |
4405 | |
4406 | - function delayedApplyFilter() { |
4407 | - applyFilterTimer.restart(); |
4408 | + onCollectionsChanged: { |
4409 | + if (!isReady) |
4410 | + return |
4411 | + |
4412 | + var collectionIds = enabledColections() |
4413 | + var oldCollections = collectionFilter.ids |
4414 | + var needsUpdate = false |
4415 | + if (collectionIds.length != oldCollections.length) { |
4416 | + needsUpdate = true |
4417 | + } else { |
4418 | + for(var i=oldCollections.length - 1; i >=0 ; i--) { |
4419 | + if (collectionIds.indexOf(oldCollections[i]) === -1) { |
4420 | + needsUpdate = true |
4421 | + break; |
4422 | + } |
4423 | + } |
4424 | + } |
4425 | + |
4426 | + if (needsUpdate) |
4427 | + collectionFilter.ids = collectionIds; |
4428 | } |
4429 | |
4430 | - function applyFilterFinal() { |
4431 | + function enabledColections() |
4432 | + { |
4433 | var collectionIds = []; |
4434 | var collections = eventModel.getCollections(); |
4435 | for(var i=0; i < collections.length ; ++i) { |
4436 | @@ -193,8 +216,18 @@ |
4437 | collectionIds.push(collection.collectionId); |
4438 | } |
4439 | } |
4440 | + return collectionIds |
4441 | + } |
4442 | + |
4443 | + function delayedApplyFilter() { |
4444 | + applyFilterTimer.restart(); |
4445 | + } |
4446 | + |
4447 | + function applyFilterFinal() { |
4448 | + var collectionIds = enabledColections() |
4449 | collectionFilter.ids = collectionIds; |
4450 | - filter = mainFilter |
4451 | + filter = Qt.binding(function() { return mainFilter; }) |
4452 | + isReady = true |
4453 | } |
4454 | |
4455 | function showEventFromId(eventId) { |
4456 | @@ -379,7 +412,7 @@ |
4457 | } |
4458 | } // End of else if (starttime) |
4459 | else if (eventId !== "") { |
4460 | - var prefix = "qtorganizer:eds::system-calendar/"; |
4461 | + var prefix = "qtorganizer:eds::"; |
4462 | if (eventId.indexOf(prefix) < 0) |
4463 | eventId = prefix + eventId; |
4464 | |
4465 | @@ -395,6 +428,11 @@ |
4466 | tabs.selectedTabIndex = settings.defaultViewIndex; |
4467 | } |
4468 | tabs.isReady = true |
4469 | + // WORKAROUND: Due the missing feature on SDK, they can not detect if |
4470 | + // there is a mouse attached to device or not. And this will cause the |
4471 | + // bootom edge component to not work correct on desktop. |
4472 | + // We will consider that a mouse is always attached until it get implement on SDK. |
4473 | + QuickUtils.mouseAttached = true |
4474 | } // End of Component.onCompleted: |
4475 | |
4476 | Keys.onTabPressed: { |
4477 | @@ -530,6 +568,16 @@ |
4478 | id: yearViewComp |
4479 | |
4480 | YearView { |
4481 | + readonly property bool tabSelected: tabs.selectedTabIndex === yearTab.index |
4482 | + |
4483 | + model: eventModel.isReady ? eventModel : null |
4484 | + bootomEdgeEnabled: tabSelected |
4485 | + displayLunarCalendar: mainView.displayLunarCalendar |
4486 | + |
4487 | + onCurrentYearChanged: { |
4488 | + tabs.currentDay = new Date(currentYear, 1, 1) |
4489 | + } |
4490 | + |
4491 | onMonthSelected: { |
4492 | var now = DateExt.today(); |
4493 | if ((date.getMonth() === now.getMonth()) && |
4494 | @@ -540,9 +588,10 @@ |
4495 | } |
4496 | tabs.selectedTabIndex = monthTab.index; |
4497 | } |
4498 | - onActiveChanged: { |
4499 | - if (active) { |
4500 | - refreshCurrentYear(DateExt.today().getFullYear()) |
4501 | + |
4502 | + onTabSelectedChanged: { |
4503 | + if (tabSelected) { |
4504 | + refreshCurrentYear(tabs.currentDay.getFullYear()) |
4505 | } |
4506 | } |
4507 | } |
4508 | @@ -552,13 +601,35 @@ |
4509 | id: monthViewComp |
4510 | |
4511 | MonthView { |
4512 | + readonly property bool tabSelected: tabs.selectedTabIndex === monthTab.index |
4513 | + |
4514 | + model: eventModel.isReady ? eventModel : null |
4515 | + bootomEdgeEnabled: tabSelected |
4516 | + displayLunarCalendar: mainView.displayLunarCalendar |
4517 | + |
4518 | + onCurrentDateChanged: { |
4519 | + tabs.currentDay = currentDate |
4520 | + } |
4521 | + |
4522 | + onHighlightedDateChanged: { |
4523 | + if (highlightedDate) |
4524 | + tabs.currentDay = highlightedDate |
4525 | + else |
4526 | + tabs.currentDay = currentDate |
4527 | + } |
4528 | + |
4529 | onDateSelected: { |
4530 | - tabs.currentDay = date; |
4531 | + tabs.currentDay = date |
4532 | tabs.selectedTabIndex = dayTab.index |
4533 | } |
4534 | - onActiveChanged: { |
4535 | - if (active) |
4536 | - currentMonth = tabs.currentDay.midnight() |
4537 | + |
4538 | + onTabSelectedChanged: { |
4539 | + if (tabSelected) { |
4540 | + anchorDate = new Date(tabs.currentDay.getFullYear(), |
4541 | + tabs.currentDay.getMonth(), |
4542 | + 1, |
4543 | + 0, 0, 0) |
4544 | + } |
4545 | } |
4546 | } |
4547 | } |
4548 | @@ -567,16 +638,45 @@ |
4549 | id: weekViewComp |
4550 | |
4551 | WeekView { |
4552 | - onDayStartChanged: { |
4553 | - tabs.currentDay = dayStart |
4554 | - } |
4555 | + readonly property bool tabSelected: tabs.selectedTab === weekTab |
4556 | + |
4557 | + model: eventModel.isReady ? eventModel : null |
4558 | + bootomEdgeEnabled: tabSelected |
4559 | + displayLunarCalendar: mainView.displayLunarCalendar |
4560 | + |
4561 | + onHighlightedDayChanged: { |
4562 | + if (highlightedDay) |
4563 | + tabs.currentDay = highlightedDay |
4564 | + else |
4565 | + tabs.currentDay = currentFirstDayOfWeek |
4566 | + } |
4567 | + |
4568 | + onCurrentFirstDayOfWeekChanged: { |
4569 | + tabs.currentDay = currentFirstDayOfWeek |
4570 | + } |
4571 | + |
4572 | onDateSelected: { |
4573 | tabs.currentDay = date; |
4574 | tabs.selectedTabIndex = dayTab.index |
4575 | } |
4576 | - onActiveChanged: { |
4577 | - if (active) |
4578 | - dayStart = tabs.currentDay |
4579 | + |
4580 | + onPressAndHoldAt: { |
4581 | + bottomEdgeCommit(date, allDay) |
4582 | + } |
4583 | + |
4584 | + onTabSelectedChanged: { |
4585 | + if (tabSelected) { |
4586 | + // 'tabs.currntDay' can change after set 'anchorDate' to avoid that |
4587 | + // create a copy of the current value |
4588 | + var tabDate = new Date(tabs.currentDay) |
4589 | + if (!anchorDate || |
4590 | + (tabs.currentDay.getFullYear() != anchorDate.getFullYear()) || |
4591 | + (tabs.currentDay.getMonth() != anchorDate.getMonth()) || |
4592 | + (tabs.currentDay.getDate() != anchorDate.getDate())) { |
4593 | + anchorDate = new Date(tabDate) |
4594 | + } |
4595 | + delayScrollToDate(tabDate) |
4596 | + } |
4597 | } |
4598 | } |
4599 | } |
4600 | @@ -585,17 +685,37 @@ |
4601 | id: dayViewComp |
4602 | |
4603 | DayView { |
4604 | - onCurrentDayChanged: { |
4605 | - tabs.currentDay = currentDay; |
4606 | - } |
4607 | + readonly property bool tabSelected: tabs.selectedTabIndex === dayTab.index |
4608 | + |
4609 | + model: eventModel.isReady ? eventModel : null |
4610 | + bootomEdgeEnabled: tabSelected |
4611 | + displayLunarCalendar: mainView.displayLunarCalendar |
4612 | |
4613 | onDateSelected: { |
4614 | tabs.currentDay = date |
4615 | } |
4616 | |
4617 | - onActiveChanged: { |
4618 | - if (active) |
4619 | - currentDay = tabs.currentDay; |
4620 | + onPressAndHoldAt: { |
4621 | + bottomEdgeCommit(date, allDay) |
4622 | + } |
4623 | + |
4624 | + onCurrentDateChanged: { |
4625 | + tabs.currentDay = currentDate |
4626 | + } |
4627 | + |
4628 | + onTabSelectedChanged: { |
4629 | + if (tabSelected) { |
4630 | + // 'tabs.currntDay' can change after set 'anchorDate' to avoid that |
4631 | + // create a copy of the current value |
4632 | + var tabDate = new Date(tabs.currentDay) |
4633 | + if (!anchorDate || |
4634 | + (tabs.currentDay.getFullYear() != anchorDate.getFullYear()) || |
4635 | + (tabs.currentDay.getMonth() != anchorDate.getMonth()) || |
4636 | + (tabs.currentDay.getDate() != anchorDate.getDate())) { |
4637 | + anchorDate = new Date(tabDate) |
4638 | + } |
4639 | + delayScrollToDate(tabDate) |
4640 | + } |
4641 | } |
4642 | } |
4643 | } |
4644 | @@ -604,6 +724,9 @@ |
4645 | id: agendaViewComp |
4646 | |
4647 | AgendaView { |
4648 | + model: eventModel.isReady ? eventModel : null |
4649 | + bootomEdgeEnabled: tabs.selectedTabIndex === agendaTab.index |
4650 | + |
4651 | onDateSelected: { |
4652 | tabs.currentDay = date; |
4653 | tabs.selectedTabIndex = dayTab.index |
4654 | |
4655 | === added file 'calendar_canvas.js' |
4656 | --- calendar_canvas.js 1970-01-01 00:00:00 +0000 |
4657 | +++ calendar_canvas.js 2016-03-07 15:59:03 +0000 |
4658 | @@ -0,0 +1,52 @@ |
4659 | +.pragma library |
4660 | +.import "dateExt.js" as DateExt |
4661 | + |
4662 | +function minutesSince(since, until) |
4663 | +{ |
4664 | + var sinceTime = new Date(since) |
4665 | + sinceTime.setSeconds(0) |
4666 | + var untilTime = new Date(until) |
4667 | + untilTime.setSeconds(0) |
4668 | + |
4669 | + var sinceTimeInSecs = sinceTime.getTime() |
4670 | + var untilTimeInSecs = untilTime.getTime() |
4671 | + |
4672 | + // limit to since day minutes |
4673 | + untilTimeInSecs = Math.min(sinceTime.endOfDay().getTime(), untilTimeInSecs) |
4674 | + |
4675 | + // calculate the time in minutes of this event |
4676 | + var totalSecs = untilTimeInSecs - sinceTimeInSecs |
4677 | + if (totalSecs > 0) |
4678 | + return (totalSecs / 60000) |
4679 | + |
4680 | + return 0 |
4681 | +} |
4682 | + |
4683 | +function parseDayEvents(date, itemsOfTheDay) |
4684 | +{ |
4685 | + var eventsInfo = [] |
4686 | + for(var c=0; c < itemsOfTheDay.length; c++) { |
4687 | + var event = itemsOfTheDay[c] |
4688 | + if (event.allDay) |
4689 | + continue |
4690 | + |
4691 | + var eventStartTimeInMinutes = minutesSince(date, event.startDateTime) |
4692 | + var eventEndTimeInMinutes = minutesSince(date, event.endDateTime) |
4693 | + |
4694 | + // avoid to draw events too small |
4695 | + if ((eventEndTimeInMinutes - eventStartTimeInMinutes) < 20) |
4696 | + eventEndTimeInMinutes = eventStartTimeInMinutes + 20 |
4697 | + |
4698 | + eventsInfo.push({'eventId': event.itemId, |
4699 | + 'eventStartTime': event.startDateTime.getTime(), |
4700 | + 'eventEndTime': event.endDateTime.getTime(), |
4701 | + 'startTime': eventStartTimeInMinutes, |
4702 | + 'endTime': eventEndTimeInMinutes, |
4703 | + 'endTimeInSecs': event.endDateTime.getTime(), |
4704 | + 'y': -1, |
4705 | + 'intersectionCount': 0, |
4706 | + 'width': 1.0}) |
4707 | + } |
4708 | + |
4709 | + return eventsInfo |
4710 | +} |
4711 | |
4712 | === added file 'calendar_canvas_worker.js' |
4713 | --- calendar_canvas_worker.js 1970-01-01 00:00:00 +0000 |
4714 | +++ calendar_canvas_worker.js 2016-03-07 15:59:03 +0000 |
4715 | @@ -0,0 +1,111 @@ |
4716 | +WorkerScript.onMessage = function(message) { |
4717 | + var processedEvents = dayEventsMap(message.events) |
4718 | + WorkerScript.sendMessage({'reply': processedEvents}) |
4719 | +} |
4720 | + |
4721 | +function sortByStartAndSize(eventA, eventB) |
4722 | +{ |
4723 | + var sort = sortEventsByStart(eventA, eventB) |
4724 | + if (sort === 0) |
4725 | + sort = sortEventsBySize(eventA, eventB) |
4726 | + return sort |
4727 | +} |
4728 | + |
4729 | +function sortEventsByStart(eventA, eventB) |
4730 | +{ |
4731 | + if (eventA.startTime < eventB.startTime) |
4732 | + return -1 |
4733 | + else if (eventA.startTime > eventB.startTime) |
4734 | + return 1 |
4735 | + else |
4736 | + return 0 |
4737 | +} |
4738 | + |
4739 | +function sortEventsBySize(eventA, eventB) |
4740 | +{ |
4741 | + var eventAEndTime = eventA.endTimeInSecs |
4742 | + var eventBEndTime = eventB.endTimeInSecs |
4743 | + if (eventAEndTime > eventBEndTime) |
4744 | + return -1 |
4745 | + else if (eventAEndTime < eventBEndTime) |
4746 | + return 1 |
4747 | + return 0 |
4748 | +} |
4749 | + |
4750 | +function yArrayCount(yArray, time) |
4751 | +{ |
4752 | + var maxY = yArray.length - 1 |
4753 | + for(var i=yArray.length - 1; i >= 0; i--) { |
4754 | + if (!yArray[i] || (yArray[i].endTime <= time)) |
4755 | + maxY = i |
4756 | + } |
4757 | +} |
4758 | + |
4759 | +/* |
4760 | + * Find item 'y' position on the intersection list |
4761 | + */ |
4762 | +function findOptimalY(intersections) |
4763 | +{ |
4764 | + if (intersections.length === 0) |
4765 | + return |
4766 | + |
4767 | + var eventWidth = 1.0 / intersections.length |
4768 | + var yArray = new Array(intersections.length) |
4769 | + var maxY = 0 |
4770 | + |
4771 | + for (var i = 0; i < intersections.length; i++) { |
4772 | + var intersection = intersections[i] |
4773 | + for (var y=0; y < yArray.length; y++) { |
4774 | + if (!yArray[y] || (yArray[y].endTime <= intersection.startTime)) { |
4775 | + if (y > maxY) |
4776 | + maxY = y |
4777 | + intersection.y = y |
4778 | + intersection.intersectionCount = intersections.length |
4779 | + yArray[y] = intersection |
4780 | + break |
4781 | + } |
4782 | + } |
4783 | + } |
4784 | + |
4785 | + for (var i = 0; i < intersections.length; i++) { |
4786 | + intersections[i].width = (1.0 / (maxY + 1)) |
4787 | + } |
4788 | + |
4789 | +} |
4790 | + |
4791 | +function dayEventsMap(eventsInfo) |
4792 | +{ |
4793 | + eventsInfo.sort(sortByStartAndSize) |
4794 | + |
4795 | + var events = eventsInfo.slice() |
4796 | + var lines = [] |
4797 | + |
4798 | + while (events.length > 0) { |
4799 | + var aux = {"startTime": 0, "ednTime": 0} |
4800 | + var eventA = events[0] |
4801 | + events.splice(0, 1) |
4802 | + var line = [eventA] |
4803 | + |
4804 | + aux.starTime = eventA.startTime |
4805 | + aux.endTime = eventA.endTime |
4806 | + |
4807 | + var newList = [] |
4808 | + for(var i = 0; i < events.length; i++) { |
4809 | + var eventB = events[i] |
4810 | + if ((aux.startTime < eventB.endTime) && |
4811 | + (eventB.startTime < aux.endTime)) { |
4812 | + if (aux.endTime < eventB.endTime) |
4813 | + aux.endTime = eventB.endTime |
4814 | + line.push(eventB) |
4815 | + } else { |
4816 | + newList.push(eventB) |
4817 | + } |
4818 | + } |
4819 | + |
4820 | + findOptimalY(line) |
4821 | + lines.push(line) |
4822 | + events = newList |
4823 | + } |
4824 | + |
4825 | + return eventsInfo |
4826 | +} |
4827 | |
4828 | === modified file 'click/calendar-helper-apparmor.json' |
4829 | --- click/calendar-helper-apparmor.json 2015-12-16 10:51:01 +0000 |
4830 | +++ click/calendar-helper-apparmor.json 2016-03-07 15:59:03 +0000 |
4831 | @@ -3,5 +3,5 @@ |
4832 | "policy_groups": [ |
4833 | "push-notification-client" |
4834 | ], |
4835 | - "policy_version": 1.2 |
4836 | + "policy_version": 1.3 |
4837 | } |
4838 | |
4839 | === modified file 'click/calendar.apparmor' |
4840 | --- click/calendar.apparmor 2015-12-16 10:51:01 +0000 |
4841 | +++ click/calendar.apparmor 2016-03-07 15:59:03 +0000 |
4842 | @@ -6,5 +6,5 @@ |
4843 | "accounts", |
4844 | "push-notification-client" |
4845 | ], |
4846 | - "policy_version": 1.2 |
4847 | + "policy_version": 1.3 |
4848 | } |
4849 | |
4850 | === modified file 'click/manifest.json.in' |
4851 | --- click/manifest.json.in 2015-12-16 10:51:01 +0000 |
4852 | +++ click/manifest.json.in 2016-03-07 15:59:03 +0000 |
4853 | @@ -1,7 +1,7 @@ |
4854 | { |
4855 | "architecture": "all", |
4856 | "description": "A calendar for Ubuntu which syncs with online accounts", |
4857 | - "framework": "ubuntu-sdk-14.10-qml", |
4858 | + "framework": "ubuntu-sdk-15.04.4", |
4859 | "hooks": { |
4860 | "calendar": { |
4861 | "account-application": "@PROJECT_NAME@_@APP_NAME@.application", |
4862 | |
4863 | === modified file 'dateExt.js' |
4864 | --- dateExt.js 2015-11-27 01:34:46 +0000 |
4865 | +++ dateExt.js 2016-03-07 15:59:03 +0000 |
4866 | @@ -60,11 +60,23 @@ |
4867 | } |
4868 | |
4869 | Date.prototype.addDays = function(days) { |
4870 | + if (days === 0) |
4871 | + return this |
4872 | + |
4873 | var date = new Date(this) |
4874 | date.setDate(date.getDate() + days); |
4875 | return date |
4876 | } |
4877 | |
4878 | +Date.prototype.addMinutes = function(minutes) { |
4879 | + if (minutes === 0) |
4880 | + return this |
4881 | + |
4882 | + var date = new Date(this) |
4883 | + date.setMinutes(date.getMinutes() + minutes); |
4884 | + return date |
4885 | +} |
4886 | + |
4887 | Date.prototype.addMonths = function(months) { |
4888 | var date = new Date(this) |
4889 | date.setMonth(date.getMonth() + months) |
4890 | @@ -87,8 +99,9 @@ |
4891 | } |
4892 | |
4893 | Date.prototype.weekNumber = function(weekStartDay) { |
4894 | - var date = this.weekStart(weekStartDay).addDays(3) // Thursday midnight |
4895 | - var onejan = new Date(this.getFullYear(), 0, 3); |
4896 | + var date = new Date(this) |
4897 | + date = date.weekStart(weekStartDay).addDays(3) // Thursday midnight |
4898 | + var onejan = new Date(date.getFullYear(), 0, 3); |
4899 | return Math.ceil((((date - onejan) / 86400000) + onejan.getDay()+1)/7); |
4900 | } |
4901 | |
4902 | |
4903 | === modified file 'po/com.ubuntu.calendar.pot' |
4904 | --- po/com.ubuntu.calendar.pot 2016-03-01 11:07:01 +0000 |
4905 | +++ po/com.ubuntu.calendar.pot 2016-03-07 15:59:03 +0000 |
4906 | @@ -8,7 +8,7 @@ |
4907 | msgstr "" |
4908 | "Project-Id-Version: \n" |
4909 | "Report-Msgid-Bugs-To: \n" |
4910 | -"POT-Creation-Date: 2016-03-01 16:35+0530\n" |
4911 | +"POT-Creation-Date: 2016-03-02 13:04-0300\n" |
4912 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
4913 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
4914 | "Language-Team: LANGUAGE <LL@li.org>\n" |
4915 | @@ -18,221 +18,277 @@ |
4916 | "Content-Transfer-Encoding: 8bit\n" |
4917 | "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" |
4918 | |
4919 | -#: ../AgendaView.qml:52 ../DayView.qml:41 ../MonthView.qml:40 |
4920 | -#: ../WeekView.qml:45 ../YearView.qml:43 |
4921 | +#: ../AgendaView.qml:53 ../DayView.qml:68 ../MonthView.qml:48 |
4922 | +#: ../WeekView.qml:52 ../YearView.qml:55 ../build/install/AgendaView.qml:53 |
4923 | +#: ../build/install/DayView.qml:43 ../build/install/MonthView.qml:47 |
4924 | +#: ../build/install/WeekView.qml:45 ../build/install/YearView.qml:49 |
4925 | +#: ../build/pkg/AgendaView.qml:53 ../build/pkg/DayView.qml:68 |
4926 | +#: ../build/pkg/MonthView.qml:48 ../build/pkg/WeekView.qml:52 |
4927 | +#: ../build/pkg/YearView.qml:55 |
4928 | msgid "Today" |
4929 | msgstr "" |
4930 | |
4931 | -#: ../AgendaView.qml:62 ../calendar.qml:288 ../calendar.qml:509 |
4932 | +#: ../AgendaView.qml:63 ../build/install/AgendaView.qml:63 |
4933 | +#: ../build/install/calendar.qml:287 ../build/install/calendar.qml:503 |
4934 | +#: ../build/pkg/AgendaView.qml:63 ../build/pkg/calendar.qml:288 |
4935 | +#: ../build/pkg/calendar.qml:504 ../calendar.qml:288 ../calendar.qml:504 |
4936 | msgid "Agenda" |
4937 | msgstr "" |
4938 | |
4939 | -#: ../AgendaView.qml:101 |
4940 | +#: ../AgendaView.qml:101 ../build/install/AgendaView.qml:101 |
4941 | +#: ../build/pkg/AgendaView.qml:101 |
4942 | msgid "No upcoming events" |
4943 | msgstr "" |
4944 | |
4945 | -#: ../AgendaView.qml:104 |
4946 | +#: ../AgendaView.qml:104 ../build/install/AgendaView.qml:104 |
4947 | +#: ../build/pkg/AgendaView.qml:104 |
4948 | msgid "You have no calendars enabled" |
4949 | msgstr "" |
4950 | |
4951 | -#: ../AgendaView.qml:114 |
4952 | +#: ../AgendaView.qml:114 ../build/install/AgendaView.qml:114 |
4953 | +#: ../build/pkg/AgendaView.qml:114 |
4954 | msgid "Enable calendars" |
4955 | msgstr "" |
4956 | |
4957 | #. TRANSLATORS: the first argument (%1) refers to a start time for an event, |
4958 | #. while the second one (%2) refers to the end time |
4959 | -#: ../AgendaView.qml:177 ../EventBubble.qml:133 |
4960 | +#: ../AgendaView.qml:177 ../EventBubble.qml:97 |
4961 | +#: ../build/install/AgendaView.qml:177 ../build/install/EventBubble.qml:134 |
4962 | +#: ../build/pkg/AgendaView.qml:177 ../build/pkg/EventBubble.qml:97 |
4963 | #, qt-format |
4964 | msgid "%1 - %2" |
4965 | msgstr "" |
4966 | |
4967 | -#: ../AgendaView.qml:183 |
4968 | -#, qt-format |
4969 | -msgid "%1 %2 %3 %4 %5" |
4970 | +#: ../AllDayEventComponent.qml:86 ../TimeLineBase.qml:50 |
4971 | +#: ../build/install/AllDayEventComponent.qml:108 |
4972 | +#: ../build/install/TimeLineBase.qml:131 |
4973 | +#: ../build/pkg/AllDayEventComponent.qml:86 ../build/pkg/TimeLineBase.qml:50 |
4974 | +msgid "New event" |
4975 | msgstr "" |
4976 | |
4977 | #. TRANSLATORS: the first parameter refers to the number of all-day events |
4978 | #. on a given day. "Ev." is short form for "Events". |
4979 | #. Please keep the translation of "Ev." to 3 characters only, as the week view |
4980 | #. where it's shown has limited space |
4981 | -#: ../AllDayEventComponent.qml:123 |
4982 | +#: ../AllDayEventComponent.qml:158 |
4983 | +#: ../build/install/AllDayEventComponent.qml:156 |
4984 | +#: ../build/pkg/AllDayEventComponent.qml:158 |
4985 | #, qt-format |
4986 | msgid "%1 ev." |
4987 | msgstr "" |
4988 | |
4989 | #. TRANSLATORS: the argument refers to the number of all day events |
4990 | -#: ../AllDayEventComponent.qml:127 |
4991 | +#: ../AllDayEventComponent.qml:162 |
4992 | +#: ../build/install/AllDayEventComponent.qml:160 |
4993 | +#: ../build/pkg/AllDayEventComponent.qml:162 |
4994 | #, qt-format |
4995 | msgid "%1 all day event" |
4996 | msgid_plural "%1 all day events" |
4997 | msgstr[0] "" |
4998 | msgstr[1] "" |
4999 | |
5000 | -#: ../CalendarChoicePopup.qml:34 ../EventActions.qml:63 |
PASSED: Continuous integration, rev:617 91.189. 93.70:8080/ job/ubuntu- calendar- app-ci/ 1113/ 91.189. 93.70:8080/ job/generic- mediumtests- utopic/ 2438 91.189. 93.70:8080/ job/generic- mediumtests- utopic/ 2438/artifact/ work/output/ *zip*/output. zip 91.189. 93.70:8080/ job/ubuntu- calendar- app-utopic- amd64-ci/ 584 91.189. 93.70:8080/ job/ubuntu- calendar- app-vivid- amd64-ci/ 110
http://
Executed test runs:
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: 91.189. 93.70:8080/ job/ubuntu- calendar- app-ci/ 1113/rebuild
http://