Merge lp:~pkunal-parmar/ubuntu-calendar-app/TimeLineView into lp:ubuntu-calendar-app

Proposed by Kunal Parmar
Status: Merged
Approved by: Olivier Tilloy
Approved revision: 41
Merged at revision: 30
Proposed branch: lp:~pkunal-parmar/ubuntu-calendar-app/TimeLineView
Merge into: lp:ubuntu-calendar-app
Diff against target: 509 lines (+352/-60)
7 files modified
.bzrignore.moved (+0/-6)
DiaryViewDelegate.qml (+3/-1)
EventView.qml (+23/-9)
TimeLineView.qml (+259/-0)
calendar.qml (+9/-1)
dateExt.js (+6/-0)
po/ubuntu-calendar-app.pot (+52/-43)
To merge this branch: bzr merge lp:~pkunal-parmar/ubuntu-calendar-app/TimeLineView
Reviewer Review Type Date Requested Status
Olivier Tilloy (community) Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Ubuntu Calendar Developers Pending
Review via email: mp+161809@code.launchpad.net

Commit message

TimeLine view added

Description of the change

TimeLine view added

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

Somes typos:

146 + id: scrolllView

 should be scrollView (I’d even suggest to rename to something more explicit, e.g. "timeline", but I’ll leave it up to you

368 + id: seperator

 should be spelled "separator"

review: Needs Fixing
Revision history for this message
Olivier Tilloy (osomon) wrote :

44 + delegate: Item{

Replace with a Loader item (http://qt-project.org/doc/qt-5.0/qtquick/qml-qtquick2-loader.html) to avoid the whole subdelegate complexity: just set the 'source' property of the loader to either "DiaryView.qml" or "TimeLineView.qml".

review: Needs Fixing
Revision history for this message
Olivier Tilloy (osomon) wrote :

As a bonus for using a Loader, you won’t need to destroy the subdelegates yourself, this is automatically handled by QML, and you can replace the 'timeLineViewEnable' boolean property by a string property that’s an alias to the loader’s 'source' property.

Revision history for this message
Olivier Tilloy (osomon) wrote :

The TimeLineView component has a number of properties and signals in common with the DiaryView component: they should inherit from a common base component.

review: Needs Fixing
Revision history for this message
Olivier Tilloy (osomon) wrote :

269 + z:0

314 + z:1

 those can be removed: by default, children are stacked in the order they appear in the hierarchy

review: Needs Fixing
Revision history for this message
Olivier Tilloy (osomon) wrote :

If I read correctly the code of the eventLineColumn, there can be only one event per hour. And even if there’s only one (or no) event, there are 24 invisible delegates sitting there, eating up memory. Or am I misunderstanding the code?

I think this is typically a case where we want dynamic object creation and deletion, not a repeater inside a column. This will give us more flexibility, allowing to have as many (or as few) events as needed, positioned exactly where they need to be.

review: Needs Fixing
23. By Kunal Parmar

Review comment addressed

24. By Kunal Parmar

typo fixed

Revision history for this message
Kunal Parmar (pkunal-parmar) wrote :

> If I read correctly the code of the eventLineColumn, there can be only one
> event per hour. And even if there’s only one (or no) event, there are 24
> invisible delegates sitting there, eating up memory. Or am I misunderstanding
> the code?
>
> I think this is typically a case where we want dynamic object creation and
> deletion, not a repeater inside a column. This will give us more flexibility,
> allowing to have as many (or as few) events as needed, positioned exactly
> where they need to be.

I addressed almost all your concern except following one.

"The TimeLineView component has a number of properties and signals in common with the DiaryView component: they should inherit from a common base component."

This I will address in another review request.

25. By Kunal Parmar

unnecessary print statement removed

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

minor changes

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

19 - onClicked: {
20 - if (expanded)
21 + onClicked: {
22 + if (expanded) {
23 compress()
24 - else
25 + }
26 + else {
27 expand()
28 + }

This change is unrelated to the functionality introduced by this MR, and it introduces trailing whitespaces. Please revert.

review: Needs Fixing
Revision history for this message
Olivier Tilloy (osomon) wrote :

55 + property bool timeLineViewEnable : false

You can get rid of this extraneous property, and instead expose eventViewDelegate.source as an alias at the top level of EventView. Thus, instead of flipping the value of a boolean property, you’d simply set the source component directly. That’s less code, easier to read, and more flexible in case we want to add other types of views in the future.

85 + function loadSubDelegate() {
86 + source = eventView.timeLineViewEnable ? "TimeLineView.qml" : "DiaryView.qml"
87 + }
88 +
89 + Component.onCompleted: {
90 + loadSubDelegate();
91 + }

The above becomes useless if you get rid of timeLineViewEnable.

111 + onTimeLineViewEnableChanged :{
112 + loadSubDelegate();
113 + }

Same goes for the above.

review: Needs Fixing
Revision history for this message
Olivier Tilloy (osomon) wrote :

115 + onExpandedChanged:{
116 + item.expanded = eventView.expanded;
117 + }

To achieve the same result in a more declarative way, use the Binding item:

  Binding {
    target: eventViewDelegate.item
    property: "expanded"
    value: eventView.expanded
  }

review: Needs Fixing
Revision history for this message
Olivier Tilloy (osomon) wrote :

> "The TimeLineView component has a number of properties and signals in common
> with the DiaryView component: they should inherit from a common base
> component."
>
> This I will address in another review request.

Not sure why this would benefit from being addressed in a separate branch, but OK.

Revision history for this message
Olivier Tilloy (osomon) wrote :

322 + function createSeperator(hour) {
323 + var sepatator = separatorComponent.createObject(bubbleOverLay);

There’s still a typo there. Actually there’s two of them now :)

  s/createSeperator/createSeparator/g
  s/sepatator/separator/g

Not that it breaks the functionality, but fixing them would make the code easier to read (and ultimately, to maintain).

review: Needs Fixing
Revision history for this message
Olivier Tilloy (osomon) wrote :

138 + property bool expanded: false
139 + property bool expanding: false
140 + property bool compressing: false

As a side note, the above code (and all associated animations) could be largely simplified by using QML states (see http://qt-project.org/doc/qt-5.0/qtquick/qtquick-statesanimations-states.html).
This can be addressed in a separate branch though.

Revision history for this message
Olivier Tilloy (osomon) wrote :

301 + function destroyAllChilds() {

should be destroyAllChildren

review: Needs Fixing
Revision history for this message
Olivier Tilloy (osomon) wrote :

301 + function destroyAllChilds() {
302 + for(var i=0 ; i < children.length ; ++i ) {
303 + children[i].destroy(100);
304 + }
305 + }

Is there a good reason for specifying a delay of 100 msec before each child is destroyed?

Also, this loop is incorrect, as children are destroyed their length decreases, but the iterator keeps increasing, and will eventually become invalid. You need to do something like this:

  while (children.length > 0) {
    children[0].destroy()
  }

review: Needs Fixing
27. By Kunal Parmar

Merge from Trunk

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Kunal Parmar (pkunal-parmar) wrote :

> 55 + property bool timeLineViewEnable : false
>
> You can get rid of this extraneous property, and instead expose
> eventViewDelegate.source as an alias at the top level of EventView. Thus,
> instead of flipping the value of a boolean property, you’d simply set the
> source component directly. That’s less code, easier to read, and more flexible
> in case we want to add other types of views in the future.
>
> 85 + function loadSubDelegate() {
> 86 + source = eventView.timeLineViewEnable ? "TimeLineView.qml" :
> "DiaryView.qml"
> 87 + }
> 88 +
> 89 + Component.onCompleted: {
> 90 + loadSubDelegate();
> 91 + }
>
> The above becomes useless if you get rid of timeLineViewEnable.
>
> 111 + onTimeLineViewEnableChanged :{
> 112 + loadSubDelegate();
> 113 + }
>
> Same goes for the above.

Some how I am not able to expose eventViewDelegate.source property under EventView scope, it gives me error "Invalid alias reference. Unable to find id "eventViewDelegate".

I instead created new property under EventView "property string eventViewType", and binded it to eventViewDelegate "source: eventView.eventViewType"

28. By Kunal Parmar

changes in DiaryView.qml reverted

29. By Kunal Parmar

Unnecessary property timeLineViewEnable removed

30. By Kunal Parmar

Binding element used, instead of updating expanded value manually using signal

31. By Kunal Parmar

Typo fixed

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Kunal Parmar (pkunal-parmar) wrote :

> 138 + property bool expanded: false
> 139 + property bool expanding: false
> 140 + property bool compressing: false
>
> As a side note, the above code (and all associated animations) could be
> largely simplified by using QML states (see http://qt-
> project.org/doc/qt-5.0/qtquick/qtquick-statesanimations-states.html).
> This can be addressed in a separate branch though.

I will address this, when I will work on creating common base for DiaryView and TimeLineView

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Kunal Parmar (pkunal-parmar) wrote :

> 301 + function destroyAllChilds() {
> 302 + for(var i=0 ; i < children.length ; ++i ) {
> 303 + children[i].destroy(100);
> 304 + }
> 305 + }
>
> Is there a good reason for specifying a delay of 100 msec before each child is
> destroyed?

no, i guess i was just trying to understand how it works with time and without it
>
> Also, this loop is incorrect, as children are destroyed their length
> decreases, but the iterator keeps increasing, and will eventually become
> invalid. You need to do something like this:
>
> while (children.length > 0) {
> children[0].destroy()
> }

   while (children.length > 0) {
     children[0].destroy()
   }

This will create a infinite loop, as we are not removing object from children array/list. we are just destroying object so length of array will not change. so existing loop works fine, but then array length will keep increasing so to reset length i am now resetting array with following statement.

children = [];

32. By Kunal Parmar

destroyAllChilds ->destroyAllChildren and resetting children array

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

> > Also, this loop is incorrect, as children are destroyed their length
> > decreases, but the iterator keeps increasing, and will eventually become
> > invalid. You need to do something like this:
> >
> > while (children.length > 0) {
> > children[0].destroy()
> > }
>
> while (children.length > 0) {
> children[0].destroy()
> }
>
> This will create a infinite loop, as we are not removing object from children
> array/list. we are just destroying object so length of array will not change.
> so existing loop works fine, but then array length will keep increasing so to
> reset length i am now resetting array with following statement.
>
> children = [];

Good point, I should have tested my suggestion first.
However, I don’t think you should reset the value of children to [], that’s likely to break automatic memory management, since we don’t have a guarantee of when the children are actually removed from the list. I think the following would work (destroying the children in reverse order, thus ensuring the iterator never becomes invalid):

    function destroyAllChildren() {
        for(var i = children.length; i >= 0; --i) {
            children[i].destroy();
        }
    }

review: Needs Fixing
Revision history for this message
Olivier Tilloy (osomon) wrote :

> Some how I am not able to expose eventViewDelegate.source property under
> EventView scope, it gives me error "Invalid alias reference. Unable to find id
> "eventViewDelegate".
>
> I instead created new property under EventView "property string
> eventViewType", and binded it to eventViewDelegate "source:
> eventView.eventViewType"

Your solution works. For reference, what I was suggesting would look like this:

Revision history for this message
Olivier Tilloy (osomon) wrote :

Scrap the above comment (which by the way was posted incomplete), my suggestion could not work because the Loader is a delegate, so it can be instantiated several times. Your solution works nicely.

Revision history for this message
Olivier Tilloy (osomon) wrote :

34 + property bool timeLineViewEnable : false

this property is unused now, it should be removed

review: Needs Fixing
Revision history for this message
Olivier Tilloy (osomon) wrote :

75 + onDayStartChanged: {
76 + if (status == Loader.Ready) {
77 + item.dayStart = dayStart;
78 + }
79 + }

To make this more declarative, you can replace the above block (and remove the assignment of item.dayStart in onStatusChanged) by:

    Binding {
        target: item
        property: "dayStart"
        value: eventViewDelegate.dayStart
    }

(no need to check the status, as item will be null if the status is ≠ Loader.Ready).

review: Needs Fixing
Revision history for this message
Olivier Tilloy (osomon) wrote :

> > > Also, this loop is incorrect, as children are destroyed their length
> > > decreases, but the iterator keeps increasing, and will eventually become
> > > invalid. You need to do something like this:
> > >
> > > while (children.length > 0) {
> > > children[0].destroy()
> > > }
> >
> > while (children.length > 0) {
> > children[0].destroy()
> > }
> >
> > This will create a infinite loop, as we are not removing object from
> children
> > array/list. we are just destroying object so length of array will not
> change.
> > so existing loop works fine, but then array length will keep increasing so
> to
> > reset length i am now resetting array with following statement.
> >
> > children = [];
>
> Good point, I should have tested my suggestion first.
> However, I don’t think you should reset the value of children to [], that’s
> likely to break automatic memory management, since we don’t have a guarantee
> of when the children are actually removed from the list. I think the following
> would work (destroying the children in reverse order, thus ensuring the
> iterator never becomes invalid):
>
> function destroyAllChildren() {
> for(var i = children.length; i >= 0; --i) {
> children[i].destroy();
> }
> }

Hmmm, that’s embarassing, I’m struggling with indexes today… Make that:

    function destroyAllChildren() {
        for(var i = children.length - 1; i >= 0; --i) {
            children[i].destroy();
        }
    }

33. By Kunal Parmar

redundant property timeLineViewEnable removed

34. By Kunal Parmar

Binding used instead of manual assignment

35. By Kunal Parmar

destroyAllChildren loop reversed

Revision history for this message
Kunal Parmar (pkunal-parmar) wrote :

addressed the latest comments

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

232 + // FIXME: how to represent
233 + text: index+":00"

This should be localized, probably something like this (not sure it would work for all possible regional settings, but should be better than nothing):

    text: i18n.tr("%1:00").arg(index)

What exactly was the FIXME about?

review: Needs Fixing
Revision history for this message
Olivier Tilloy (osomon) wrote :

208 + Rectangle{
209 + id: background; anchors.fill: parent
210 + }

There’s no color set for this background rectangle, is it actually useful?

Revision history for this message
Olivier Tilloy (osomon) wrote :

239 + height:units.gu(0.1)

Here (and in other places where using units.gu(0.1), you probably want to use units.dp(1) instead: this will basically ensure the line is one pixel high.

review: Needs Fixing
Revision history for this message
David Planella (dpm) wrote :

Al 20/05/13 12:12, En/na Olivier Tilloy ha escrit:
> Review: Needs Fixing
>
> 232 + // FIXME: how to represent
> 233 + text: index+":00"
>
> This should be localized, probably something like this (not sure it would work for all possible regional settings, but should be better than nothing):
>

Numbers, as in the "00" after the colon, can automatically be localized
too. See the numberToLocaleString() function the Clock team uses:

https://code.launchpad.net/~nik90/ubuntu-clock-app/replace-number-translations/+merge/164582

Revision history for this message
Kunal Parmar (pkunal-parmar) wrote :

> 208 + Rectangle{
> 209 + id: background; anchors.fill: parent
> 210 + }
>
> There’s no color set for this background rectangle, is it actually useful?

This sets white background, as Rectangle's default color is White, I did not mentioned it explicitly. But I will change it to be more clear.

36. By Kunal Parmar

units.gu(0.1) -> units.dp(1)

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Kunal Parmar (pkunal-parmar) wrote :

> 232 + // FIXME: how to represent
> 233 + text: index+":00"
>
> This should be localized, probably something like this (not sure it would work
> for all possible regional settings, but should be better than nothing):
>
> text: i18n.tr("%1:00").arg(index)
>
> What exactly was the FIXME about?

As David suggested, I tried to use code from Clock source code. I am using following code now. Does it looks ok ?

My implementation is only hourToLocaleString() this function. Other are copied from Clock.

    function numberToLocaleString(num) {
        return Number(num).toLocaleString(Qt.locale(), "f", 0)
    }

    function zeroleft ( number, zeroCount ) {
        var resstring = numberToLocaleString(number.toString());
        var numchart = zeroCount;
        var numberlength = resstring.length;
        for (var i = numberlength; i < numchart; i++) {
            resstring = numberToLocaleString("0") + resstring;
        }
        return resstring;
    }

    function hourToLocaleString(hour) {
        return zeroleft(hour,2)+":"+ zeroleft(0,2);
    }

37. By Kunal Parmar

Hour label localization issue addressed

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

> > 208 + Rectangle{
> > 209 + id: background; anchors.fill: parent
> > 210 + }
> >
> > There’s no color set for this background rectangle, is it actually useful?
>
> This sets white background, as Rectangle's default color is White, I did not
> mentioned it explicitly. But I will change it to be more clear.

Please do, to set it to "white" explicitly. The fact that it is white by default might change in the future.

Revision history for this message
Olivier Tilloy (osomon) wrote :

> As David suggested, I tried to use code from Clock source code. I am using
> following code now. Does it looks ok ?
>
> My implementation is only hourToLocaleString() this function. Other are copied
> from Clock.
>
> function numberToLocaleString(num) {
> return Number(num).toLocaleString(Qt.locale(), "f", 0)
> }
>
> function zeroleft ( number, zeroCount ) {
> var resstring = numberToLocaleString(number.toString());
> var numchart = zeroCount;
> var numberlength = resstring.length;
> for (var i = numberlength; i < numchart; i++) {
> resstring = numberToLocaleString("0") + resstring;
> }
> return resstring;
> }
>
> function hourToLocaleString(hour) {
> return zeroleft(hour,2)+":"+ zeroleft(0,2);
> }

If that is the way the Clock app does it, then it’s wrong. The zeroleft function is not i18n friendly, and there’s a much shorter solution:

  Label {
    id: timeLabel
    text: new Date(0, 0, 0, index).toLocaleTimeString(Qt.locale(), "HH:mm")
  }

review: Needs Fixing
Revision history for this message
David Planella (dpm) wrote :

Ah, I hadn't picked up on the fact that the zeroleft function was used, as that part was not internationalized in the Clock app. I was pointing at the other parts in which they internationalized numbers.

Olivier's solution looks most sensible to me. My experience on i18n on code is mostly on Python or C apps based on gettext and GTK, so I'm still figuring out what the equivalents in the QML/JavaScript world are.

Revision history for this message
Olivier Tilloy (osomon) wrote :

> > […]
>
> If that is the way the Clock app does it, then it’s wrong. The zeroleft
> function is not i18n friendly, and there’s a much shorter solution:
>
> Label {
> id: timeLabel
> text: new Date(0, 0, 0, index).toLocaleTimeString(Qt.locale(), "HH:mm")
> }

And even better, the format string should be localized, with a comment for translators:

  Label {
    id: timeLabel
    // TRANSLATORS: this is a time formatting string, see http://qt-project.org/doc/qt-5.0/qtqml/qml-qtquick2-date.html#details for valid expressions
    text: new Date(0, 0, 0, index).toLocaleTimeString(Qt.locale(), i18n.tr("HH:mm"))
  }

38. By Kunal Parmar

Localization issue fixed for hour label

39. By Kunal Parmar

Merge from trunck code

Revision history for this message
Kunal Parmar (pkunal-parmar) wrote :

> > > […]
> >
> > If that is the way the Clock app does it, then it’s wrong. The zeroleft
> > function is not i18n friendly, and there’s a much shorter solution:
> >
> > Label {
> > id: timeLabel
> > text: new Date(0, 0, 0, index).toLocaleTimeString(Qt.locale(), "HH:mm")
> > }
>
> And even better, the format string should be localized, with a comment for
> translators:
>
> Label {
> id: timeLabel
> // TRANSLATORS: this is a time formatting string, see http://qt-
> project.org/doc/qt-5.0/qtqml/qml-qtquick2-date.html#details for valid
> expressions
> text: new Date(0, 0, 0, index).toLocaleTimeString(Qt.locale(),
> i18n.tr("HH:mm"))
> }

Ok, I used above code as it is. my experience in localization is almost zero, so thanks for helping me :)

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

Looks good. Two last details:

 - You haven’t addressed my comment about setting the background color to "white" explicitly.

 - Since there are new strings, you need to regenerate the translation template. To do that, just invoke the following command in your branch, and commit the modified .pot file:

    xgettext --from-code=UTF-8 -o po/ubuntu-calendar-app.pot -D . --c++ --qt --add-comments=TRANSLATORS --keyword=tr --package-name=ubuntu-calendar-app -s *.qml

review: Needs Fixing
40. By Kunal Parmar

setting white as background color

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

regenerating the translation template

Revision history for this message
Kunal Parmar (pkunal-parmar) wrote :

> Looks good. Two last details:
>
> - You haven’t addressed my comment about setting the background color to
> "white" explicitly.
>
> - Since there are new strings, you need to regenerate the translation
> template. To do that, just invoke the following command in your branch, and
> commit the modified .pot file:
>
> xgettext --from-code=UTF-8 -o po/ubuntu-calendar-app.pot -D . --c++ --qt
> --add-comments=TRANSLATORS --keyword=tr --package-name=ubuntu-calendar-app -s
> *.qml

ahh, I thought I made changes to set background, but then forgotten about it. Did it now. and also updated the pot file.

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

Looks good now. Let’s merge!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== removed file '.bzrignore.moved'
2--- .bzrignore.moved 2013-04-11 19:47:11 +0000
3+++ .bzrignore.moved 1970-01-01 00:00:00 +0000
4@@ -1,6 +0,0 @@
5-calendar.qmlproject.user
6-
7-debian/files
8-debian/ubuntu-calendar-app/
9-debian/*.debhelper.log
10-debian/*.substvars
11
12=== modified file 'DiaryViewDelegate.qml'
13--- DiaryViewDelegate.qml 2013-04-13 04:43:04 +0000
14+++ DiaryViewDelegate.qml 2013-05-23 14:11:30 +0000
15@@ -38,7 +38,9 @@
16 var now = new Date;
17 var lastEvent = eventModel.get(index-1);
18
19- if( endTime >= now && (lastEvent === undefined || lastEvent.endTime < now ) ) {
20+ if( endTime >= now
21+ && (lastEvent === undefined || lastEvent.endTime < now )
22+ && endTime.isSameDay(now) ) {
23 collapse(false);
24 bgColor = "#fffdaa";
25 seperator.visible = true;
26
27=== modified file 'EventView.qml'
28--- EventView.qml 2013-03-29 23:12:40 +0000
29+++ EventView.qml 2013-05-23 14:11:30 +0000
30@@ -6,6 +6,7 @@
31 id: eventView
32
33 property var currentDayStart: (new Date()).midnight()
34+ property string eventViewType: "DiaryView.qml";
35
36 signal incrementCurrentDay
37 signal decrementCurrentDay
38@@ -58,23 +59,36 @@
39
40 model: 3
41
42- delegate: DiaryView {
43- id: diaryView
44+ delegate: Loader {
45+ id: eventViewDelegate
46
47 width: eventView.width
48 height: eventView.height
49+ source: eventView.eventViewType
50
51- dayStart: {
52+ property var dayStart: {
53 if (index == intern.currentIndex) return intern.currentDayStart
54 var previousIndex = intern.currentIndex > 0 ? intern.currentIndex - 1 : 2
55- if (index == previousIndex) return intern.currentDayStart.addDays(-1)
56+ if (index === previousIndex) return intern.currentDayStart.addDays(-1)
57 return intern.currentDayStart.addDays(1)
58 }
59
60- expanded: eventView.expanded
61-
62- onExpand: eventView.expand()
63- onCompress: eventView.compress()
64- onNewEvent: eventView.newEvent()
65+ onLoaded: {
66+ item.expand.connect(eventView.expand);
67+ item.compress.connect(eventView.compress);
68+ item.newEvent.connect(eventView.newEvent);
69+ }
70+
71+ Binding {
72+ target: item
73+ property: "dayStart"
74+ value: eventViewDelegate.dayStart
75+ }
76+
77+ Binding {
78+ target: item
79+ property: "expanded"
80+ value: eventView.expanded
81+ }
82 }
83 }
84
85=== added file 'TimeLineView.qml'
86--- TimeLineView.qml 1970-01-01 00:00:00 +0000
87+++ TimeLineView.qml 2013-05-23 14:11:30 +0000
88@@ -0,0 +1,259 @@
89+import QtQuick 2.0
90+import Ubuntu.Components 0.1
91+
92+import "dateExt.js" as DateExt
93+import "dataService.js" as DataService
94+
95+
96+Flickable{
97+ id: timeLineView
98+
99+ property var dayStart : new Date();
100+
101+ property bool expanded: false
102+ property bool expanding: false
103+ property bool compressing: false
104+
105+ signal expand()
106+ signal compress()
107+ signal newEvent()
108+
109+ function scroll() {
110+ //scroll to first event or current hour
111+ var hour = intern.now.getHours();
112+ if(eventListModel.count > 0) {
113+ hour = eventListModel.get(0).startTime.getHours();
114+ }
115+
116+ timeLineView.contentY = hour * units.gu(10);
117+
118+ if(timeLineView.contentY >= timeLineView.contentHeight - timeLineView.height) {
119+ timeLineView.contentY = timeLineView.contentHeight - timeLineView.height
120+ }
121+ }
122+
123+ function createEventMap() {
124+ var eventMap = {};
125+ for(var i = 0 ; i < eventListModel.count ; ++i) {
126+ var event = eventListModel.get(i);
127+ eventMap[event.startTime.getHours()] = event
128+ }
129+ return eventMap;
130+ }
131+
132+ function createEvents() {
133+ intern.eventMap = createEventMap();
134+
135+ bubbleOverLay.destroyAllChildren();
136+
137+ for( var i=0; i < 24; ++i ) {
138+ var event = intern.eventMap[i];
139+ if( event ) {
140+ bubbleOverLay.createEvent(event,i);
141+ } else if( i === intern.now.getHours()
142+ && intern.now.isSameDay( timeLineView.dayStart )) {
143+ bubbleOverLay.createSeparator(i);
144+ }
145+ }
146+
147+ scroll();
148+ }
149+
150+ function showEventDetails(hour) {
151+ var event = intern.eventMap[hour];
152+ pageStack.push(Qt.resolvedUrl("EventDetails.qml"),{"event":event});
153+ }
154+
155+ onContentYChanged: {
156+ // console.log(expanded, expanding, compressing, dragging, flicking, moving, contentY)
157+ if (expanding || compressing || !dragging) return
158+
159+ if (expanded) {
160+ if (contentY < -units.gu(0.5)) {
161+ compressing = true
162+ expanding = false
163+ }
164+ }
165+ else {
166+ if (contentY < -units.gu(0.5)) {
167+ expanding = true
168+ compressing = false
169+ }
170+ }
171+ }
172+
173+ onDraggingChanged: {
174+ if (dragging) return
175+
176+ if (expanding) {
177+ expanding = false
178+ expand()
179+ }
180+ else if (compressing) {
181+ compressing = false
182+ compress()
183+ }
184+ }
185+
186+ clip: true
187+
188+ contentHeight: timeLineColumn.height + units.gu(3)
189+ contentWidth: width
190+
191+ QtObject {
192+ id: intern
193+ property var eventMap;
194+ property var now : new Date();
195+ property var hourHeight : units.gu(10)
196+ }
197+
198+ EventListModel {
199+ id: eventListModel
200+ termStart: timeLineView.dayStart
201+ termLength: Date.msPerDay
202+
203+ onReload: {
204+ createEvents();
205+ }
206+ }
207+
208+ Rectangle{
209+ id: background; anchors.fill: parent
210+ color: "white"
211+ }
212+
213+ //Time line view
214+ Column{
215+ id: timeLineColumn
216+ anchors.top: parent.top
217+ anchors.topMargin: units.gu(3)
218+ width: parent.width
219+
220+ Repeater{
221+ model: 24 // hour in a day
222+
223+ delegate: Item {
224+ id: delegate
225+ width: parent.width
226+ height: intern.hourHeight
227+
228+ Row {
229+ width: parent.width
230+ y: -timeLabel.height/2
231+ Label{
232+ id: timeLabel
233+ // TRANSLATORS: this is a time formatting string,
234+ // see http://qt-project.org/doc/qt-5.0/qtqml/qml-qtquick2-date.html#details for valid expressions
235+ text: new Date(0, 0, 0, index).toLocaleTimeString(Qt.locale(), i18n.tr("HH:mm"))
236+ color:"gray"
237+ anchors.top: parent.top
238+ }
239+ Rectangle{
240+ width: parent.width -timeLabel.width
241+ height:units.dp(1)
242+ color:"gray"
243+ anchors.verticalCenter: parent.verticalCenter
244+ }
245+ }
246+
247+ Rectangle{
248+ width: parent.width - units.gu(5)
249+ height:units.dp(1)
250+ color:"gray"
251+ anchors.verticalCenter: parent.verticalCenter
252+ anchors.horizontalCenter: parent.horizontalCenter
253+ }
254+ }
255+ }
256+ }
257+
258+ Item {
259+ id: bubbleOverLay
260+
261+ width: timeLineColumn.width
262+ height: timeLineColumn.height
263+ anchors.top: parent.top
264+ anchors.topMargin: units.gu(3)
265+
266+ function destroyAllChildren() {
267+ for( var i = children.length - 1; i >= 0; --i ) {
268+ children[i].destroy();
269+ }
270+ }
271+
272+ function createEvent( event ,hour) {
273+ var eventBubble = infoBubbleComponent.createObject(bubbleOverLay);
274+ eventBubble.title = event.title;
275+ eventBubble.location = "test";
276+ eventBubble.hour = hour;
277+
278+ var yPos = (( event.startTime.getMinutes() * intern.hourHeight) / 60) + hour * intern.hourHeight
279+ eventBubble.y = yPos;
280+
281+ var durationMin = (event.endTime.getHours() - event.startTime.getHours()) * 60;
282+ durationMin += (event.endTime.getMinutes() - event.startTime.getMinutes());
283+ var height = (durationMin * intern.hourHeight )/ 60;
284+ eventBubble.height = height;
285+ }
286+
287+ function createSeparator(hour) {
288+ var separator = separatorComponent.createObject(bubbleOverLay);
289+ var yPos = ((intern.now.getMinutes() * intern.hourHeight) / 60) + hour * intern.hourHeight
290+ separator.visible = true;
291+ separator.y = yPos;
292+ separator.x = (parent.width - separator.width)/2
293+ }
294+ }
295+
296+ Component{
297+ id: infoBubbleComponent
298+ Rectangle{
299+ id: infoBubble
300+
301+ property string title;
302+ property string location;
303+ property int hour;
304+
305+ color:'#fffdaa';
306+ width: timeLineView.width - units.gu(8)
307+ x: units.gu(5)
308+
309+ border.color: "#f4d690"
310+
311+ Column{
312+ id: column
313+ anchors {
314+ left: parent.left
315+ right: parent.right
316+ top: parent.top
317+
318+ leftMargin: units.gu(1)
319+ rightMargin: units.gu(1)
320+ topMargin: units.gu(1)
321+ }
322+ spacing: units.gu(1)
323+ Label{text:infoBubble.title;fontSize:"medium";color:"black"}
324+ Label{text:infoBubble.location; fontSize:"small"; color:"black"}
325+ }
326+
327+ MouseArea{
328+ anchors.fill: parent
329+ onClicked: {
330+ timeLineView.showEventDetails(hour);
331+ }
332+ }
333+ }
334+ }
335+
336+ Component {
337+ id: separatorComponent
338+ Rectangle {
339+ id: separator
340+ height: units.gu(0.5)
341+ width: timeLineView.width - units.gu(2)
342+ color: "#c94212"
343+ visible: false
344+ }
345+ }
346+}
347+
348
349=== modified file 'calendar.qml'
350--- calendar.qml 2013-05-22 16:00:01 +0000
351+++ calendar.qml 2013-05-23 14:11:30 +0000
352@@ -39,7 +39,15 @@
353 Action {
354 iconSource: Qt.resolvedUrl("avatar.png")
355 text: i18n.tr("Timeline")
356- onTriggered:; // FIXME
357+ onTriggered: {
358+ if( eventView.eventViewType === "DiaryView.qml") {
359+ eventView.eventViewType = "TimeLineView.qml";
360+ text = i18n.tr("Diary")
361+ } else {
362+ eventView.eventViewType = "DiaryView.qml";
363+ text = i18n.tr("Timeline")
364+ }
365+ }
366 }
367 }
368
369
370=== modified file 'dateExt.js'
371--- dateExt.js 2013-03-19 18:00:35 +0000
372+++ dateExt.js 2013-05-23 14:11:30 +0000
373@@ -79,3 +79,9 @@
374 Date.prototype.weeksInMonth = function(weekday) {
375 return Date.weeksInMonth(this.getFullYear(), this.getMonth(), weekday)
376 }
377+
378+Date.prototype.isSameDay = function ( otherDay ) {
379+ return ( this.getDate() === otherDay.getDate()
380+ && this.getMonth() === otherDay.getMonth()
381+ && this.getFullYear() === otherDay.getFullYear() );
382+}
383
384=== modified file 'po/ubuntu-calendar-app.pot'
385--- po/ubuntu-calendar-app.pot 2013-05-15 09:23:44 +0000
386+++ po/ubuntu-calendar-app.pot 2013-05-23 14:11:30 +0000
387@@ -1,14 +1,14 @@
388-# Ubuntu Calendar app translation.
389-# Copyright (C) Canonical Ltd. 2013.
390-# This file is distributed under the same license as the ubuntu-calendar-app package.
391+# SOME DESCRIPTIVE TITLE.
392+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
393+# This file is distributed under the same license as the PACKAGE package.
394 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
395 #
396 #, fuzzy
397 msgid ""
398 msgstr ""
399-"Project-Id-Version: PACKAGE VERSION\n"
400+"Project-Id-Version: ubuntu-calendar-app\n"
401 "Report-Msgid-Bugs-To: \n"
402-"POT-Creation-Date: 2013-05-15 10:19+0200\n"
403+"POT-Creation-Date: 2013-05-23 23:02+0900\n"
404 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
405 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
406 "Language-Team: LANGUAGE <LL@li.org>\n"
407@@ -17,55 +17,64 @@
408 "Content-Type: text/plain; charset=CHARSET\n"
409 "Content-Transfer-Encoding: 8bit\n"
410
411-#: /home/dpm/dev/coreapps/ubuntu-calendar-app/calendar.qml:29
412-msgid "To-do"
413-msgstr ""
414-
415-#: /home/dpm/dev/coreapps/ubuntu-calendar-app/calendar.qml:34
416-msgid "New Event"
417-msgstr ""
418-
419-#: /home/dpm/dev/coreapps/ubuntu-calendar-app/calendar.qml:39
420-msgid "Timeline"
421-msgstr ""
422-
423-#: /home/dpm/dev/coreapps/ubuntu-calendar-app/DiaryView.qml:54
424+#: DiaryView.qml:54
425 msgid "(+) New Event"
426 msgstr ""
427
428-#: /home/dpm/dev/coreapps/ubuntu-calendar-app/EventDetails.qml:69
429+#: NewEvent.qml:104
430+msgid "Add Location"
431+msgstr ""
432+
433+#: NewEvent.qml:25
434+msgid "Add event name"
435+msgstr ""
436+
437+#: EventDetails.qml:69
438 msgid "Add invite"
439 msgstr ""
440
441-#: /home/dpm/dev/coreapps/ubuntu-calendar-app/EventDetails.qml:75
442+#: NewEvent.qml:20
443+msgid "Create event"
444+msgstr ""
445+
446+#: calendar.qml:45
447+msgid "Diary"
448+msgstr ""
449+
450+#: EventDetails.qml:75
451 msgid "Edit"
452 msgstr ""
453
454-#: /home/dpm/dev/coreapps/ubuntu-calendar-app/EventDetails.qml:150
455-#: /home/dpm/dev/coreapps/ubuntu-calendar-app/NewEvent.qml:112
456+#. TRANSLATORS: this is a time formatting string,
457+#. see http://qt-project.org/doc/qt-5.0/qtqml/qml-qtquick2-date.html#details for valid expressions
458+#: TimeLineView.qml:147
459+msgid "HH:mm"
460+msgstr ""
461+
462+#: NewEvent.qml:117
463+msgid "Invite People"
464+msgstr ""
465+
466+#: NewEvent.qml:99
467+msgid "Location"
468+msgstr ""
469+
470+#: calendar.qml:36
471+msgid "New Event"
472+msgstr ""
473+
474+#: EventDetails.qml:150 NewEvent.qml:112
475 msgid "People"
476 msgstr ""
477
478-#: /home/dpm/dev/coreapps/ubuntu-calendar-app/NewEvent.qml:20
479-msgid "Create event"
480-msgstr ""
481-
482-#: /home/dpm/dev/coreapps/ubuntu-calendar-app/NewEvent.qml:25
483-msgid "Add event name"
484-msgstr ""
485-
486-#: /home/dpm/dev/coreapps/ubuntu-calendar-app/NewEvent.qml:99
487-msgid "Location"
488-msgstr ""
489-
490-#: /home/dpm/dev/coreapps/ubuntu-calendar-app/NewEvent.qml:104
491-msgid "Add Location"
492-msgstr ""
493-
494-#: /home/dpm/dev/coreapps/ubuntu-calendar-app/NewEvent.qml:117
495-msgid "Invite People"
496-msgstr ""
497-
498-#: /home/dpm/dev/coreapps/ubuntu-calendar-app/NewEvent.qml:128
499+#: NewEvent.qml:128
500 msgid "Save"
501 msgstr ""
502+
503+#: calendar.qml:41 calendar.qml:48
504+msgid "Timeline"
505+msgstr ""
506+
507+#: calendar.qml:31
508+msgid "To-do"
509+msgstr ""

Subscribers

People subscribed via source and target branches

to status/vote changes: