Merge lp:~macslow/unity/phablet-notification-renderer into lp:unity/phablet
- phablet-notification-renderer
- Merge into phablet
Status: | Merged |
---|---|
Approved by: | Mirco Müller |
Approved revision: | no longer in the source branch. |
Merged at revision: | 675 |
Proposed branch: | lp:~macslow/unity/phablet-notification-renderer |
Merge into: | lp:unity/phablet |
Diff against target: |
645 lines (+591/-0) 7 files modified
CMakeLists.txt (+1/-0) Notifications/Notification.qml (+153/-0) Notifications/Notifications.qml (+77/-0) Notifications/timings.js (+23/-0) debian/qml-phone-shell.install (+1/-0) tests/qmltests/CMakeLists.txt (+1/-0) tests/qmltests/Notifications/tst_Notifications.qml (+335/-0) |
To merge this branch: | bzr merge lp:~macslow/unity/phablet-notification-renderer |
Related bugs: | |
Related blueprints: |
Backend for Notifications NG
(Undefined)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Michał Sawicz | Approve | ||
Michael Zanetti (community) | Approve | ||
Review via email: mp+155512@code.launchpad.net |
Commit message
First basic NotificationRen
Description of the change
First basic NotificationRen
Michał Sawicz (saviq) wrote : | # |
The Columns might be overkill, but you need to wrap the Labels in an Item anyway for the Row to not try and position them next to each other.
Mirco Müller (macslow) wrote : | # |
Ah, ok. I'll fix all those issues.
kevin gunn (kgunn72) wrote : | # |
wrt >> I'd rename Bubble to Notification
I worry a little that we are overloading "notification"
e.g. if back end is also called notification it might get confusing
am i being a nut? ultimately i'll defer to Saviq/Mirco
Michał Sawicz (saviq) wrote : | # |
Sure, that's a valid concern. QML Notification component would end up as a QQmlNotification in C++ world, and anyway namespacing is helping here - it's going to be Unity::
What's more, we'll never use (title case) Notification as exposed from the QML Unity.Notifications plugin, at least not in the shell. That object will only be created C++ side.
So, all in all I don't foresee naming conflicts, but to avoid confusion, I could agree to a different name ;). I'd like to avoid "Bubble" nevertheless ;D
Mirco Müller (macslow) wrote : | # |
All mentioned suggestions were addressed, with two variations:
* I left the explicit sizes for icon and secondary-icon in place as a "safty-net", for those cases when rogue applications pass in too small or too large icons, thus the layout of the notification does not break away from the design-guideline.
* With the way anchors are used in summayLabel and bodyLabel now, there's no danger of them being accidentally placed next to each other.
Good to merge with trunk?
Michał Sawicz (saviq) wrote : | # |
Yeah, explicit sizes of icons / assets is correct, of course.
Michał Sawicz (saviq) wrote : | # |
* please order the properties on the component in a more structured way (at least public properties first, and secondaryIcon source just after iconSource)
* components should not have their dimensions predefined, unless they depend (as in this case the height) on their child objects
width: parent.width
height: childrenRect.height + units.gu (1)
* don't expose private properties on root objects of components - in this case I don't see the need for that separate property, you can simply use the condition itself
property bool snapDecision: type == "Notifications.
* components should not define their overall animations - and you're not changing the opacity of it anyway - you're going to that in the transitions
Behavior on opacity {NumberAnimation{}}
* I would prefer using a Row for the icons and summary+body, all the margin: units.gu(1) make it hard to read, and it would handle the invisible secondaryIcon automatically
* explicitly check for source !== undefined && source != "", you could also check for status == Image.Ready [1], but that might result in some movement while the image is being loaded
visible: source
* set sourceSize [2] on Images that come from outside to reduce memory
* this is not correct, use bodyLabel.y + bodyLabel.height - height of items does not include anchor margins - and if you use a Row above, you will just anchor to the row's bottom anyway
top: (icon.height >= summaryLabel.height + bodyLabel.height) ? icon.bottom : bodyLabel.bottom
* why this??
parent: notification
* set visibility instead
topMargin: snapDecision ? units.gu (1) : 0
* there's a binding loop - should go away when you move to using a Row, too
Notificatio
* don't use space between function name and parenthesis [units.gu(1), not units.gu (1)]
* use camelCase instead of underscores (action_label)
* no need to prefix action's values with action_
* Notifications should not have its own queue or the displayNotification method, it should be a ListView{}, not a Showable{
* Notifications - see above about predefined dimensions
anchors.fill: parent
* don't add Notifications to Shell, yet, since there's no model to supply to it
* the tests are not really tests - there's no way they would fail, is there?
[1] http://
[2] http://
Michał Sawicz (saviq) wrote : | # |
=========
48 + color: Qt.rgba(0, 0, 0, 0.85)
with that, shouldn't you be able to just drop the Item?
=========
54 + top: parent.top
unnecessary
=========
56 + height: childrenRect.height + contentColumn.
since spacing / margins are units.gu(1) everywhere, let's just use contentColumn.
=========
81 + width: (body == "") ? units.gu(2) : units.gu(6)
82 + height: (body == "") ? units.gu(2) : units.gu(6)
we need to be able to collapse items that _do_ have body text, no? actually, it's the secondaryIcon that should stay on screen in collapsed state, even if there is a primary icon, no? IMO it's a perfect place to use States and Transitions [1]
expanded => collapsed transition as follows:
SequentialAnimation {
ParallelAnimation {
icon shrinks
icon fades out
labelColumn
labelColumn fades out
}
PropertyAction {
icon becomes invisible
bodyLabel becomes invisible
}
}
collapsed => expanded transition as follows:
SequentialAnimation {
PropertyAction {
icon becomes visible
bodyLabel becomes visible
}
ParallelAnimation {
icon grows
icon fades in
labelColumn
labelColumn fades in
}
}
you also need to bind labelColumn.clip to Transition.running [2] of the two above.
=========
107 + height: childrenRect.height
this shouldn't be needed in Positioners - they get sized by their children implicitly
=========
113 + width: parent.width - x
this should only be like that in labelColumn, the Labels themselves should anchor to parent.left/right
=========
124 + visible: body != ""
as mentioned above, use States and Transitions for that
=========
141 + visible: type == "Notifications.
145 + model: actions
reference by object - notification.type, notification.
=========
74 + width: parent.width
142 + width: childrenRect.width
you should anchor the Rows to parent.left/right instead
=========
because of the quality issues, let's drop sourceSize for now (sorry)
=========
46 + width: parent.width
191 + anchors.fill: parent
these need to go away (component dimensions should generally be handled in their parents, unless they depend on content dimensions):
=========
225 +
rogue newline
=========
237 +<<<<<<< TREE
238 +=======
239 +add_qml_
240 +add_qml_
241 +>>>>>>> MERGE-SOURCE
conflict (DashPreview shouldn't be touched)
=========
add some controls next to the view so that you can manually drive the test (see other tests in qmluitests for examples)
=========
[1] http://
[2] http://
Mirco Müller (macslow) wrote : | # |
> 81 + width: (body == "") ? units.gu(2) : units.gu(6)
> 82 + height: (body == "") ? units.gu(2) : units.gu(6)
> we need to be able to collapse items that _do_ have body text, no? actually,
> it's the secondaryIcon that should stay on screen in collapsed state, even if
> there is a primary icon, no?
No, this here is meant for the special "icon+summary" layout case Design wants. This is different from a collapsing snap-decision, which is only meant to be used on large screens.
> 141 + visible: type == "Notifications.
> 145 + model: actions
> reference by object - notification.type, notification.
That does not work. How can I make the object known? Do I need to change the way data is passed in the test?
> 46 + width: parent.width
> 191 + anchors.fill: parent
> these need to go away (component dimensions should generally be handled in
> their parents, unless they depend on content dimensions):
I could get rid of "anchors.fill: parent", but I have to keep "width: parent.width". That way the width of the ListView holding the notifications follows any embedding parent-item. Otherwise there's no way to tell the ListView what horizontal space it may occupy.
Michał Sawicz (saviq) wrote : | # |
W dniu 09.04.2013 17:07, Mirco Müller pisze:
>> 81 + width: (body == "") ? units.gu(2) : units.gu(6)
>> > 82 + height: (body == "") ? units.gu(2) : units.gu(6)
>> > we need to be able to collapse items that _do_ have body text, no? actually,
>> > it's the secondaryIcon that should stay on screen in collapsed state, even if
>> > there is a primary icon, no?
> No, this here is meant for the special "icon+summary" layout case Design wants. This is different from a collapsing snap-decision, which is only meant to be used on large screens.
Right, but since the icon is that small, do we not want to get rid of
the UbuntuShape for it? We could easily switch the source of the icons
instead of the dimensions of them? Is the lack of a body the only
determinant of whether it's a icon+summary layout? Does that mean body
is required for the generic case? It doesn't make sense for the icon to
be an avatar, for example, when it's just 2x2 GUs?
>> > 141 + visible: type == "Notifications.
>> > 145 + model: actions
>> > reference by object - notification.type, notification.
> That does not work. How can I make the object known? Do I need to change the way data is passed in the test?
Hum? The top level UbuntuShape has «id: notification» and type and
actions properties, can you not access those for some reason?
>> > 46 + width: parent.width
>> > 191 + anchors.fill: parent
>> > these need to go away (component dimensions should generally be handled in
>> > their parents, unless they depend on content dimensions):
> I could get rid of "anchors.fill: parent", but I have to keep "width: parent.width". That way the width of the ListView holding the notifications follows any embedding parent-item. Otherwise there's no way to tell the ListView what horizontal space it may occupy.
That's the «width: parent.width» in Notification.
Notifications.
dimensions (again, unless they depend on their content). I think the
rule of thumb is: top-level component in a file should not have its
dimensions defined, unless they depend on the contents.
Case in point: it's Shell.qml that will decide on the dimensions of
Notifications {}, it's Notifications.qml that will decide on the
dimensions of Notification {} - it's delegate. And you should use
anchors there, too.
Cheers,
--
Michał Sawicz <email address hidden>
Canonical Services Ltd.
Michał Sawicz (saviq) wrote : | # |
Hey, make sure to merge trunk, soon, there's conflicts.
Michael Zanetti (mzanetti) wrote : | # |
if possible remove wait()s from the tests. They take ~8 secs now while I think they could run in < 0.5 secs. (Makes a huge difference in Jenkins where all 300 tests we have are summed up)
Michael Zanetti (mzanetti) wrote : | # |
I'd like to see tests for the different mouse interactions:
- notifcation is click-through or not (interactive/not interactive?)
- click buttons in notifications
Michael Zanetti (mzanetti) wrote : | # |
604 + wait(100)
really required? I know only because view.count == model.count it doesn't mean that the delegates are created already. I think still wait(0) should be enough. (Fine as long it is set to WIP - just wanted to mention it for completemess)
* Regarding the if() around the signal spys: I think it would be better to add the conditions to the test data. Because if you decide on interactiveArea
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:575
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Michael Zanetti (mzanetti) wrote : | # |
debian/
dh_install: usr/share/
dh_install: usr/share/
dh_install: usr/share/
dh_install: usr/share/
dh_install: usr/share/
dh_install: usr/share/
dh_install: usr/share/
dh_install: usr/share/
dh_install: missing files, aborting
Michael Zanetti (mzanetti) wrote : | # |
I don't think those images should be installed with the shell. They should rather be moved into tests/mocks or the like
Michał Sawicz (saviq) wrote : | # |
Hey, no need to put it as Work In Progress when you go and fix review comments. Leaving it at Needs Review is fine.
Michael Zanetti (mzanetti) wrote : | # |
224 + easing.type: Timings.easing
288 +var easing = "OutQuint";
Interesting... I just thought easings.type is a enum instead of a string. Looking it up in the docs it seems using strings is possible. Easing.OutQuint the preferred one though (I guess because it gives us code completion in QtCreator while strings don't):
<snip>
QML Basic Type: enumeration
An enumeration type consists of a set of named values.
An enumeration value may be specifed as either a string:
Text { horizontalAlign
or as <Element>.<value>:
Text { horizontalAlign
The second form is preferred.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:578
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:578
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michał Sawicz (saviq) wrote : | # |
Tests
=======
627 + wait(0)
Looks unnecessary?
========
643 + interactiveArea
644 + clickThroughSpy
651 + clickThroughSpy
You should do that in a « function cleanup() » instead - it's called after every test case.
========
646 + interactiveArea
647 + verify(
653 + clickThroughSpy
654 + verify(
SignalSpy.wait() already verifies the count is 1.
========
648 + verify(
Should use compare().
========
649 + }
650 + else {
Please keep in one line.
========
638 + compare(
639 + compare(
657 + compare(
658 + compare(
659 + compare(
660 + compare(
The messages could be improved, and also "notification1", "notification2" are not really useful - the tag: from _data() shows which test is being run.
========
662 + if(data.
663 + var buttonCancel = findChild(
664 + var buttonAccept = findChild(
665 +
666 + buttonSpy.target = buttonCancel
667 + buttonSpy.clear()
668 + mouseClick(
669 + buttonSpy.wait()
670 + verify(
671 +
672 + buttonSpy.target = buttonAccept
673 + buttonSpy.clear()
674 + mouseClick(
675 + buttonSpy.wait()
676 + verify(
677 + }
This only tests that Button.clicked signal is working. You should instead create an actionInvoked(
Another run through the code itself
=======
46 + width: parent.width
47 + height: childrenRect.height
Again, top-level components in a file should not have width or height defined. Use implicitHeight and get rid of width altogether - it's determined by the delegate definition in Notifications.qml.
========
57 + visible: type == "Notifications.
MouseAreas are not graphical, use "enabled" instead.
========
58 + onClicked: print("clicked " + actions.get(0).id)
Forward through a...
Michał Sawicz (saviq) wrote : | # |
There's still some questions from https:/
=========
50 + visible: opacity > 0
Is that needed?
=========
57 + visible: type == "Notifications.
Where is type defined?
=========
68 + margins: units.gu(1)
69 + }
70 + height: childrenRect.height + contentColumn.
71 + spacing: units.gu(1)
76 + spacing: units.gu(1)
147 + spacing: units.gu(1)
163 + width: (topRow.width - units.gu(1)) / 2
I mentioned earlier a common spacing could be useful, go with the below instead:
68 + margins: spacing
69 + }
70 + height: childrenRect.height + spacing
71 + spacing: units.gu(1)
76 + spacing: contentColumn.
147 + spacing: contentColumn.
163 + width: (topRow.width - buttonRow.spacing) / 2
=========
81 + height: childrenRect.height
That's unnecessary - default in Positioners.
=========
83 + UbuntuShape {
84 + id: icon
85 +
86 + objectName: "icon"
87 + width: (body == "") ? units.gu(2) : units.gu(6)
88 + height: (body == "") ? units.gu(2) : units.gu(6)
89 + image: Image {
90 + id: avatarIcon
91 +
92 + fillMode: Image.PreserveA
93 + }
94 + }
95 +
96 + Image {
97 + id: secondaryIcon
98 +
99 + objectName: "secondaryIcon"
100 + width: units.gu(2)
101 + height: units.gu(2)
102 + visible: source !== undefined && source != "" && bodyLabel.visible
103 + fillMode: Image.PreserveA
104 + }
I'm still not sure we want a 2x2 GU UbuntuShape, see the comment mentioned at the top of this one.
=========
149 + visible: type == "Notifications.
156 + model: actions
See the comment at the top.
=========
159 + id: button
160 +
Looks unused.
=========
163 + width: (topRow.width - units.gu(1)) / 2
Why topRow? Shouldn't this be buttonRow.width?
=========
166 + onClicked: print("clicked " + id)
Same as before - forward via a actionInvoked(
=========
208 + delegate: Notification {
209 + objectName: "notification" + index
210 + iconSource: model.icon
211 + secondaryIconSo
212 + summary: model.summary
213 + body: model.body
214 + actions: model.actions
215 + }
Use anchors { left: parent.left; right: parent.right } to make the delegates fill Notifications horizontally. Also, "model" here is unnecessary - you can access the roles directly.
=========
309 +add_qml_
326 === added file 'tests/
327 --- tests/qmltests/
Michał Sawicz (saviq) wrote : | # |
Ah one more thing, please reduce the set of test assets to a minimum. You can use those from /graphics instead.
Obviously we need to clean those up, but let's not add more instead.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:579
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:582
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:584
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
Click here to trigger a rebuild:
http://
Mirco Müller (macslow) wrote : | # |
Hm... CI of my branch failed because of a failure with test_hide_
Mirco Müller (macslow) wrote : | # |
qml_phone_
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:588
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:588
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:588
http://
Executed test runs:
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:588
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:589
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:589
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:590
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:590
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:592
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:592
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:593
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:593
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:593
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:594
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:595
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:595
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:595
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:597
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:598
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:598
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michael Zanetti (mzanetti) wrote : | # |
Looks good to me now.
Michał Sawicz (saviq) wrote : | # |
Hey, some last still-remaining tweaks:
=====
77 + spacing: units.gu(1)
Should be contentColumn.
=====
58 + enabled: type == "Notifications.
150 + visible: type == "Notifications.
157 + model: actions
Should be notification.type ==, notification.
=====
162 + width: (topRow.width - buttonRow.spacing) / 2
Why topRow? Shouldn't this be buttonRow.width?
=====
That's it.
Mirco Müller (macslow) wrote : | # |
Did those requested tweaks.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:599
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
PS Jenkins bot (ps-jenkins) : | # |
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2013-04-26 14:54:51 +0000 |
3 | +++ CMakeLists.txt 2013-05-15 15:17:27 +0000 |
4 | @@ -183,6 +183,7 @@ |
5 | Panel |
6 | Launcher |
7 | SideStage |
8 | + Notifications |
9 | ) |
10 | |
11 | install(DIRECTORY ${QML_DIRS} |
12 | |
13 | === added directory 'Notifications' |
14 | === added file 'Notifications/Notification.qml' |
15 | --- Notifications/Notification.qml 1970-01-01 00:00:00 +0000 |
16 | +++ Notifications/Notification.qml 2013-05-15 15:17:27 +0000 |
17 | @@ -0,0 +1,153 @@ |
18 | +/* |
19 | + * Copyright (C) 2013 Canonical, Ltd. |
20 | + * |
21 | + * This program is free software; you can redistribute it and/or modify |
22 | + * it under the terms of the GNU General Public License as published by |
23 | + * the Free Software Foundation; version 3. |
24 | + * |
25 | + * This program is distributed in the hope that it will be useful, |
26 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
27 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
28 | + * GNU General Public License for more details. |
29 | + * |
30 | + * You should have received a copy of the GNU General Public License |
31 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
32 | + */ |
33 | + |
34 | +import QtQuick 2.0 |
35 | +import Ubuntu.Components 0.1 |
36 | + |
37 | +UbuntuShape { |
38 | + id: notification |
39 | + |
40 | + property string type |
41 | + property alias iconSource: avatarIcon.source |
42 | + property alias secondaryIconSource: secondaryIcon.source |
43 | + property alias summary: summaryLabel.text |
44 | + property alias body: bodyLabel.text |
45 | + property var actions |
46 | + |
47 | + signal actionInvoked(string buttonId) |
48 | + |
49 | + implicitHeight: childrenRect.height |
50 | + color: Qt.rgba(0, 0, 0, 0.85) |
51 | + opacity: 0 |
52 | + |
53 | + MouseArea { |
54 | + id: interactiveArea |
55 | + |
56 | + anchors.fill: contentColumn |
57 | + objectName: "interactiveArea" |
58 | + enabled: notification.type == "Notifications.Type.Interactive" |
59 | + onClicked: notification.actionInvoked(actions.get(0).id) |
60 | + } |
61 | + |
62 | + Column { |
63 | + id: contentColumn |
64 | + |
65 | + anchors { |
66 | + left: parent.left |
67 | + right: parent.right |
68 | + top: parent.top |
69 | + margins: spacing |
70 | + } |
71 | + height: childrenRect.height + spacing |
72 | + spacing: units.gu(1) |
73 | + |
74 | + Row { |
75 | + id: topRow |
76 | + |
77 | + spacing: contentColumn.spacing |
78 | + anchors { |
79 | + left: parent.left |
80 | + right: parent.right |
81 | + } |
82 | + |
83 | + UbuntuShape { |
84 | + id: icon |
85 | + |
86 | + objectName: "icon" |
87 | + width: units.gu(6) |
88 | + height: units.gu(6) |
89 | + visible: iconSource !== undefined && iconSource != "" |
90 | + image: Image { |
91 | + id: avatarIcon |
92 | + |
93 | + fillMode: Image.PreserveAspectCrop |
94 | + } |
95 | + } |
96 | + |
97 | + Image { |
98 | + id: secondaryIcon |
99 | + |
100 | + objectName: "secondaryIcon" |
101 | + width: units.gu(2) |
102 | + height: units.gu(2) |
103 | + visible: source !== undefined && source != "" |
104 | + fillMode: Image.PreserveAspectCrop |
105 | + } |
106 | + |
107 | + Column { |
108 | + id: labelColumn |
109 | + width: parent.width - x |
110 | + |
111 | + Label { |
112 | + id: summaryLabel |
113 | + |
114 | + objectName: "summaryLabel" |
115 | + anchors { |
116 | + left: parent.left |
117 | + right: parent.right |
118 | + } |
119 | + fontSize: "medium" |
120 | + font.bold: true |
121 | + color: "#f3f3e7" |
122 | + elide: Text.ElideRight |
123 | + } |
124 | + |
125 | + Label { |
126 | + id: bodyLabel |
127 | + |
128 | + objectName: "bodyLabel" |
129 | + anchors { |
130 | + left: parent.left |
131 | + right: parent.right |
132 | + } |
133 | + visible: body != "" |
134 | + fontSize: "small" |
135 | + color: "#f3f3e7" |
136 | + opacity: 0.6 |
137 | + wrapMode: Text.WordWrap |
138 | + maximumLineCount: 10 |
139 | + elide: Text.ElideRight |
140 | + } |
141 | + } |
142 | + } |
143 | + |
144 | + Row { |
145 | + id: buttonRow |
146 | + |
147 | + objectName: "buttonRow" |
148 | + spacing: contentColumn.spacing |
149 | + layoutDirection: Qt.RightToLeft |
150 | + visible: notification.type == "Notifications.Type.SnapDecision" |
151 | + anchors { |
152 | + left: parent.left |
153 | + right: parent.right |
154 | + } |
155 | + |
156 | + Repeater { |
157 | + model: notification.actions |
158 | + |
159 | + Button { |
160 | + objectName: "button" + index |
161 | + color: Positioner.isFirstItem ? "#d85317" : "#cdcdcb" |
162 | + width: (buttonRow.width - buttonRow.spacing) / 2 |
163 | + height: units.gu(4) |
164 | + text: label |
165 | + onClicked: notification.actionInvoked(id) |
166 | + } |
167 | + } |
168 | + } |
169 | + } |
170 | +} |
171 | |
172 | === added file 'Notifications/Notifications.qml' |
173 | --- Notifications/Notifications.qml 1970-01-01 00:00:00 +0000 |
174 | +++ Notifications/Notifications.qml 2013-05-15 15:17:27 +0000 |
175 | @@ -0,0 +1,77 @@ |
176 | +/* |
177 | + * Copyright (C) 2013 Canonical, Ltd. |
178 | + * |
179 | + * This program is free software; you can redistribute it and/or modify |
180 | + * it under the terms of the GNU General Public License as published by |
181 | + * the Free Software Foundation; version 3. |
182 | + * |
183 | + * This program is distributed in the hope that it will be useful, |
184 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
185 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
186 | + * GNU General Public License for more details. |
187 | + * |
188 | + * You should have received a copy of the GNU General Public License |
189 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
190 | + */ |
191 | + |
192 | +import QtQuick 2.0 |
193 | +import Ubuntu.Components 0.1 |
194 | +import "../Components" |
195 | +import "timings.js" as Timings |
196 | + |
197 | +ListView { |
198 | + id: notificationRenderer |
199 | + |
200 | + objectName: "notificationRenderer" |
201 | + interactive: false |
202 | + |
203 | + spacing: units.gu(.5) |
204 | + delegate: Notification { |
205 | + objectName: "notification" + index |
206 | + anchors { |
207 | + left: parent.left |
208 | + right: parent.right |
209 | + } |
210 | + type: model.type |
211 | + iconSource: model.icon |
212 | + secondaryIconSource: model.secondaryIcon |
213 | + summary: model.summary |
214 | + body: model.body |
215 | + actions: model.actions |
216 | + } |
217 | + |
218 | + populate: Transition { |
219 | + NumberAnimation { |
220 | + property: "opacity" |
221 | + to: 1 |
222 | + duration: Timings.snapBeat |
223 | + easing.type: Timings.easing |
224 | + } |
225 | + } |
226 | + |
227 | + add: Transition { |
228 | + NumberAnimation { |
229 | + property: "opacity" |
230 | + to: 1 |
231 | + duration: Timings.snapBeat |
232 | + easing.type: Timings.easing |
233 | + } |
234 | + } |
235 | + |
236 | + remove: Transition { |
237 | + NumberAnimation { |
238 | + property: "opacity" |
239 | + to: 0 |
240 | + duration: Timings.fastBeat |
241 | + easing.type: Timings.easing |
242 | + } |
243 | + } |
244 | + |
245 | + displaced: Transition { |
246 | + NumberAnimation { |
247 | + properties: "x,y" |
248 | + duration: Timings.fastBeat |
249 | + easing.type: Timings.easing |
250 | + } |
251 | + } |
252 | +} |
253 | |
254 | === added file 'Notifications/timings.js' |
255 | --- Notifications/timings.js 1970-01-01 00:00:00 +0000 |
256 | +++ Notifications/timings.js 2013-05-15 15:17:27 +0000 |
257 | @@ -0,0 +1,23 @@ |
258 | +/* |
259 | + * Copyright (C) 2013 Canonical, Ltd. |
260 | + * |
261 | + * This program is free software; you can redistribute it and/or modify |
262 | + * it under the terms of the GNU General Public License as published by |
263 | + * the Free Software Foundation; version 3. |
264 | + * |
265 | + * This program is distributed in the hope that it will be useful, |
266 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
267 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
268 | + * GNU General Public License for more details. |
269 | + * |
270 | + * You should have received a copy of the GNU General Public License |
271 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
272 | + */ |
273 | + |
274 | +var rhythm = 700; |
275 | +var fastBeat = 0.5 * rhythm; |
276 | +var snapBeat = 0.5 * fastBeat; |
277 | +var slowBeat = rhythm; |
278 | +var sleepyBeat = slowBeat * 2; |
279 | + |
280 | +var easing = Easing.OutQuint; |
281 | |
282 | === modified file 'debian/qml-phone-shell.install' |
283 | --- debian/qml-phone-shell.install 2013-04-23 15:58:17 +0000 |
284 | +++ debian/qml-phone-shell.install 2013-05-15 15:17:27 +0000 |
285 | @@ -7,6 +7,7 @@ |
286 | /usr/share/qml-phone-shell/Greeter/* |
287 | /usr/share/qml-phone-shell/Hud/* |
288 | /usr/share/qml-phone-shell/Launcher/* |
289 | +/usr/share/qml-phone-shell/Notifications/* |
290 | /usr/share/qml-phone-shell/Panel/* |
291 | /usr/share/qml-phone-shell/Shell.qml |
292 | /usr/share/qml-phone-shell/SideStage/* |
293 | |
294 | === modified file 'tests/qmltests/CMakeLists.txt' |
295 | --- tests/qmltests/CMakeLists.txt 2013-05-14 13:42:33 +0000 |
296 | +++ tests/qmltests/CMakeLists.txt 2013-05-15 15:17:27 +0000 |
297 | @@ -48,6 +48,7 @@ |
298 | add_qml_test(Hud Hud) |
299 | add_qml_test(Hud Result) |
300 | add_qml_test(Launcher Launcher) |
301 | +add_qml_test(Notifications Notifications) |
302 | add_qml_test(Panel IndicatorRow) |
303 | add_qml_test(Panel Indicators) |
304 | add_qml_test(Panel MenuContent) |
305 | |
306 | === added directory 'tests/qmltests/Notifications' |
307 | === added file 'tests/qmltests/Notifications/tst_Notifications.qml' |
308 | --- tests/qmltests/Notifications/tst_Notifications.qml 1970-01-01 00:00:00 +0000 |
309 | +++ tests/qmltests/Notifications/tst_Notifications.qml 2013-05-15 15:17:27 +0000 |
310 | @@ -0,0 +1,335 @@ |
311 | +/* |
312 | + * Copyright (C) 2013 Canonical, Ltd. |
313 | + * |
314 | + * This program is free software; you can redistribute it and/or modify |
315 | + * it under the terms of the GNU General Public License as published by |
316 | + * the Free Software Foundation; version 3. |
317 | + * |
318 | + * This program is distributed in the hope that it will be useful, |
319 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
320 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
321 | + * GNU General Public License for more details. |
322 | + * |
323 | + * You should have received a copy of the GNU General Public License |
324 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
325 | + */ |
326 | + |
327 | +import QtQuick 2.0 |
328 | +import QtTest 1.0 |
329 | +import ".." |
330 | +import "../../../Notifications" |
331 | +import Ubuntu.Components 0.1 |
332 | +import Unity.Test 0.1 |
333 | + |
334 | +Row { |
335 | + id: rootRow |
336 | + |
337 | + ListModel { |
338 | + id: mockModel |
339 | + } |
340 | + |
341 | + function addSnapDecisionNotification() { |
342 | + var n = { |
343 | + type: "Notifications.Type.SnapDecision", |
344 | + summary: "Tom Ato", |
345 | + body: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.", |
346 | + icon: "../graphics/avatars/funky.png", |
347 | + secondaryIcon: "../graphics/applicationIcons/facebook.png", |
348 | + actions: [{ id: "ok_id", label: "Ok"}, |
349 | + { id: "cancel_id", label: "Cancel"}] |
350 | + } |
351 | + |
352 | + mockModel.append(n) |
353 | + } |
354 | + |
355 | + function addEphemeralNotification() { |
356 | + var n = { |
357 | + type: "Notifications.Type.Ephemeral", |
358 | + summary: "Cole Raby", |
359 | + body: "I did not expect it to be that late.", |
360 | + icon: "../graphics/avatars/amanda.png", |
361 | + secondaryIcon: "../graphics/applicationIcons/facebook.png", |
362 | + actions: [] |
363 | + } |
364 | + |
365 | + mockModel.append(n) |
366 | + } |
367 | + |
368 | + function addEphemeralIconSummaryNotification() { |
369 | + var n = { |
370 | + type: "Notifications.Type.Ephemeral", |
371 | + summary: "Photo upload completed", |
372 | + body: "", |
373 | + icon: "", |
374 | + secondaryIcon: "../graphics/applicationIcons/facebook.png", |
375 | + actions: [] |
376 | + } |
377 | + |
378 | + mockModel.append(n) |
379 | + } |
380 | + |
381 | + function addInteractiveNotification() { |
382 | + var n = { |
383 | + type: "Notifications.Type.Interactive", |
384 | + summary: "Interactive notification", |
385 | + body: "This is a notification that can be clicked", |
386 | + icon: "../graphics/avatars/anna_olsson.png", |
387 | + secondaryIcon: "", |
388 | + actions: [{ id: "reply_id", label: "Dummy"}], |
389 | + } |
390 | + |
391 | + mockModel.append(n) |
392 | + } |
393 | + |
394 | + function clearNotifications() { |
395 | + mockModel.clear() |
396 | + } |
397 | + |
398 | + function remove1stNotification() { |
399 | + if (mockModel.count > 0) |
400 | + mockModel.remove(0) |
401 | + } |
402 | + |
403 | + Rectangle { |
404 | + id: notificationsRect |
405 | + |
406 | + width: units.gu(40) |
407 | + height: units.gu(71) |
408 | + |
409 | + MouseArea{ |
410 | + id: clickThroughCatcher |
411 | + |
412 | + anchors.fill: parent |
413 | + } |
414 | + |
415 | + Notifications { |
416 | + id: notifications |
417 | + |
418 | + anchors.fill: parent |
419 | + anchors.margins: units.gu(1) |
420 | + model: mockModel |
421 | + } |
422 | + } |
423 | + |
424 | + Rectangle { |
425 | + id: interactiveControls |
426 | + |
427 | + width: units.gu(30) |
428 | + height: units.gu(71) |
429 | + color: "grey" |
430 | + |
431 | + Column { |
432 | + spacing: units.gu(1) |
433 | + anchors.fill: parent |
434 | + anchors.margins: units.gu(1) |
435 | + |
436 | + Button { |
437 | + width: parent.width |
438 | + text: "add a snap-decision" |
439 | + onClicked: addSnapDecisionNotification() |
440 | + } |
441 | + |
442 | + Button { |
443 | + width: parent.width |
444 | + text: "add an ephemeral" |
445 | + onClicked: addEphemeralNotification() |
446 | + } |
447 | + |
448 | + Button { |
449 | + width: parent.width |
450 | + text: "add an icon-summary" |
451 | + onClicked: addEphemeralIconSummaryNotification() |
452 | + } |
453 | + |
454 | + Button { |
455 | + width: parent.width |
456 | + text: "add an interactive" |
457 | + onClicked: addInteractiveNotification() |
458 | + } |
459 | + |
460 | + Button { |
461 | + width: parent.width |
462 | + text: "remove 1st notification" |
463 | + onClicked: remove1stNotification() |
464 | + } |
465 | + |
466 | + Button { |
467 | + width: parent.width |
468 | + text: "clear model" |
469 | + onClicked: clearNotifications() |
470 | + } |
471 | + } |
472 | + } |
473 | + |
474 | + UnityTestCase { |
475 | + id: root |
476 | + name: "NotificationRendererTest" |
477 | + when: windowShown |
478 | + |
479 | + function test_NotificationRenderer_data() { |
480 | + return [ |
481 | + { |
482 | + tag: "Snap Decision with secondary icon", |
483 | + type: "Notifications.Type.SnapDecision", |
484 | + summary: "Tom Ato", |
485 | + body: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.", |
486 | + icon: "../graphics/avatars/funky.png", |
487 | + secondaryIcon: "../graphics/applicationIcons/facebook.png", |
488 | + actions: [{ id: "ok_id", label: "Ok"}, |
489 | + { id: "cancel_id", label: "Cancel"}], |
490 | + summaryVisible: true, |
491 | + bodyVisible: true, |
492 | + interactiveAreaEnabled: false, |
493 | + iconVisible: true, |
494 | + secondaryIconVisible: true, |
495 | + buttonRowVisible: true |
496 | + }, |
497 | + { |
498 | + tag: "Ephemeral notification - icon-summary layout", |
499 | + type: "Notifications.Type.Ephemeral", |
500 | + summary: "Photo upload completed", |
501 | + body: "", |
502 | + icon: "", |
503 | + secondaryIcon: "../graphics/applicationIcons/facebook.png", |
504 | + actions: [], |
505 | + summaryVisible: true, |
506 | + bodyVisible: false, |
507 | + interactiveAreaEnabled: false, |
508 | + iconVisible: false, |
509 | + secondaryIconVisible: true, |
510 | + buttonRowVisible: false |
511 | + }, |
512 | + { |
513 | + tag: "Ephemeral notification - check suppression of secondary icon for icon-summary layout", |
514 | + type: "Notifications.Type.Ephemeral", |
515 | + summary: "New comment successfully published", |
516 | + body: "", |
517 | + icon: "", |
518 | + secondaryIcon: "../graphics/applicationIcons/facebook.png", |
519 | + actions: [], |
520 | + summaryVisible: true, |
521 | + bodyVisible: false, |
522 | + interactiveAreaEnabled: false, |
523 | + iconVisible: false, |
524 | + secondaryIconVisible: true, |
525 | + buttonRowVisible: false |
526 | + }, |
527 | + { |
528 | + tag: "Interactive notification", |
529 | + type: "Notifications.Type.Interactive", |
530 | + summary: "Interactive notification", |
531 | + body: "This is a notification that can be clicked", |
532 | + icon: "../graphics/avatars/amanda.png", |
533 | + secondaryIcon: "", |
534 | + actions: [{ id: "reply_id", label: "Dummy"}], |
535 | + summaryVisible: true, |
536 | + bodyVisible: true, |
537 | + interactiveAreaEnabled: true, |
538 | + iconVisible: true, |
539 | + secondaryIconVisible: false, |
540 | + buttonRowVisible: false |
541 | + }, |
542 | + { |
543 | + tag: "Snap Decision without secondary icon", |
544 | + type: "Notifications.Type.SnapDecision", |
545 | + summary: "Bro Coly", |
546 | + body: "At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.", |
547 | + icon: "../graphics/avatars/anna_olsson.png", |
548 | + secondaryIcon: "", |
549 | + actions: [{ id: "accept_id", label: "Accept"}, |
550 | + { id: "reject_id", label: "Reject"}], |
551 | + summaryVisible: true, |
552 | + bodyVisible: true, |
553 | + interactiveAreaEnabled: false, |
554 | + iconVisible: true, |
555 | + secondaryIconVisible: false, |
556 | + buttonRowVisible: true |
557 | + }, |
558 | + { |
559 | + tag: "Ephemeral notification", |
560 | + type: "Notifications.Type.Ephemeral", |
561 | + summary: "Cole Raby", |
562 | + body: "I did not expect it to be that late.", |
563 | + icon: "../graphics/avatars/funky.png", |
564 | + secondaryIcon: "../graphics/applicationIcons/facebook.png", |
565 | + actions: [], |
566 | + summaryVisible: true, |
567 | + bodyVisible: true, |
568 | + interactiveAreaEnabled: false, |
569 | + iconVisible: true, |
570 | + secondaryIconVisible: true, |
571 | + buttonRowVisible: false |
572 | + } |
573 | + ] |
574 | + } |
575 | + |
576 | + SignalSpy { |
577 | + id: clickThroughSpy |
578 | + |
579 | + target: clickThroughCatcher |
580 | + signalName: "clicked" |
581 | + } |
582 | + |
583 | + SignalSpy { |
584 | + id: actionSpy |
585 | + signalName: "actionInvoked" |
586 | + } |
587 | + |
588 | + function cleanup() { |
589 | + clickThroughSpy.clear() |
590 | + actionSpy.clear() |
591 | + } |
592 | + |
593 | + function test_NotificationRenderer(data) { |
594 | + // populate model with some mock notifications |
595 | + mockModel.append(data) |
596 | + |
597 | + // make sure the model is properly updated before going on |
598 | + tryCompare(notifications, "count", mockModel.count) |
599 | + |
600 | + var notification = findChild(notifications, "notification" + (mockModel.count - 1)) |
601 | + var icon = findChild(notification, "icon") |
602 | + var interactiveArea = findChild(notification, "interactiveArea") |
603 | + var secondaryIcon = findChild(notification, "secondaryIcon") |
604 | + var summaryLabel = findChild(notification, "summaryLabel") |
605 | + var bodyLabel = findChild(notification, "bodyLabel") |
606 | + var buttonRow = findChild(notification, "buttonRow") |
607 | + waitForRendering(buttonRow) |
608 | + |
609 | + compare(icon.visible, data.iconVisible, "avatar-icon visibility is incorrect") |
610 | + compare(interactiveArea.enabled, data.interactiveAreaEnabled, "check for interactive area") |
611 | + |
612 | + if(data.interactiveAreaEnabled) { |
613 | + actionSpy.target = notification |
614 | + mouseClick(notification, notification.width / 2, notification.height / 2) |
615 | + actionSpy.wait() |
616 | + compare(actionSpy.signalArguments[0][0], data.actions[0]["id"], "got wrong id for interactive action") |
617 | + compare(clickThroughSpy.count, 0, "click on interactive notification fell through") |
618 | + } else { |
619 | + mouseClick(notification, notification.width / 2, notification.height / 2) |
620 | + clickThroughSpy.wait() |
621 | + } |
622 | + |
623 | + compare(secondaryIcon.visible, data.secondaryIconVisible, "secondary-icon visibility is incorrect") |
624 | + compare(summaryLabel.visible, data.summaryVisible, "summary-text visibility is incorrect") |
625 | + compare(bodyLabel.visible, data.bodyVisible, "body-text visibility is incorrect") |
626 | + compare(buttonRow.visible, data.buttonRowVisible, "button visibility is incorrect") |
627 | + |
628 | + if(data.buttonRowVisible) { |
629 | + var buttonCancel = findChild(buttonRow, "button1") |
630 | + var buttonAccept = findChild(buttonRow, "button0") |
631 | + |
632 | + waitForRendering(notification) |
633 | + actionSpy.target = notification |
634 | + mouseClick(buttonCancel, buttonCancel.width / 2, buttonCancel.height / 2) |
635 | + actionSpy.wait() |
636 | + compare(actionSpy.signalArguments[0][0], data.actions[1]["id"], "got wrong id for negative action") |
637 | + actionSpy.clear() |
638 | + |
639 | + mouseClick(buttonAccept, buttonAccept.width / 2, buttonAccept.height / 2) |
640 | + actionSpy.wait() |
641 | + compare(actionSpy.signalArguments[0][0], data.actions[0]["id"], "got wrong id positive action") |
642 | + } |
643 | + } |
644 | + } |
645 | +} |
* the change to .bzrignore seems unrelated, please don't submit it spectCrop" for secondaryIcon seems unnecessary, the icon should always be 2x2 GUs visible ? units.gu(11) : units.gu(8)" anchor to icon or secondaryIcon instead visible ? units.gu (25) : units.gu (28)" anchor to parent.right instead get(1). action_ label" you should almost never need Model.get() - use Views or Positioners with Repeaters [2] [3]
* I'd rename Bubble to Notification
* I don't see a reason to separate Bubble.qml into a separate dir - unless really difficult, there should only be a single Notification component that lays itself out according to the properties set
* "property bool snapDecision: actions != null" - this should depend on the type of the notification, not the presence of actions
* the whole bubble is 0.85 opaque, is that per-design? shouldn't just the background be transparent and the text fully opaque?
* please separate the components with newlines
* "visible: secondaryIconSource ? true : false" you can use just "source" here, secondaryIconSource is an alias anyway
* "left: parent.left; leftMargin: units.gu(8)" you should anchor to icon.right instead
* "fillMode: Image.PreserveA
* "leftMargin: secondaryIcon.
* "width: secondaryIcon.
* "font.family: "Ubuntu"" is default
* "opacity: 1.0" is default, and does _not_ mean that the text itself will be fully opaque, because the parent item is not opaque [1]
* "maximumLineCount: 1" is unnecessary when wrapping is disabled
* the above applies to bodyLabel, too, except that bodyLabel should anchor to summaryLabel's left and bottom
* you should elide the body, too, in case it overflows
* "width: units.gu (17.5)" is arbitrary, calculate the width based on parent.width
* "text: actions.
* the Loader is unnecessary, if you use a Repeater, it will not create anything if the model is empty
* use add/move/delete transitions
* in this case I'd rather see the layout to use Positioners [2] extensively for better readability and flexibility, like so:
Column {
Row {
UbuntuShape { id: icon }
Image { id: secondaryIcon}
Column {
Label { id: summary }
Label { id: body }
}
}
Flow {
Repeater { id: buttons }
}
}
[1] http:// qt-project. org/doc/ qt-5.0/ qtquick/ qml-qtquick2- item.html# opacity- prop qt-project. org/doc/ qt-5.0/ qtquick/ qtquick- qmltypereferenc e.html# positioning qt-project. org/doc/ qt-5.0/ qtquick/ qtquick- qmltypereferenc e.html# model-view- types-and- data-storage- and-access
[2] http://
[3] http://