Merge lp:~macslow/unity8/launcher-icon-wobble into lp:unity8

Proposed by Mirco Müller
Status: Merged
Approved by: Michael Zanetti
Approved revision: 1840
Merged at revision: 1887
Proposed branch: lp:~macslow/unity8/launcher-icon-wobble
Merge into: lp:unity8
Diff against target: 1177 lines (+539/-43)
27 files modified
debian/control (+3/-3)
plugins/Greeter/Unity/Launcher/CMakeLists.txt (+1/-1)
plugins/Greeter/Unity/Launcher/launcheritem.cpp (+14/-0)
plugins/Greeter/Unity/Launcher/launcheritem.h (+4/-0)
plugins/Greeter/Unity/Launcher/launchermodelas.cpp (+7/-0)
plugins/Greeter/Unity/Launcher/launchermodelas.h (+1/-0)
plugins/Unity/Launcher/CMakeLists.txt (+1/-1)
plugins/Unity/Launcher/dbusinterface.cpp (+38/-20)
plugins/Unity/Launcher/dbusinterface.h (+1/-0)
plugins/Unity/Launcher/launcheritem.cpp (+23/-0)
plugins/Unity/Launcher/launcheritem.h (+3/-0)
plugins/Unity/Launcher/launchermodel.cpp (+32/-2)
plugins/Unity/Launcher/launchermodel.h (+3/-1)
qml/Launcher/LauncherDelegate.qml (+87/-1)
qml/Launcher/LauncherPanel.qml (+75/-4)
tests/mocks/Unity/Launcher/CMakeLists.txt (+1/-1)
tests/mocks/Unity/Launcher/MockLauncherItem.cpp (+19/-0)
tests/mocks/Unity/Launcher/MockLauncherItem.h (+3/-0)
tests/mocks/Unity/Launcher/MockLauncherModel.cpp (+40/-0)
tests/mocks/Unity/Launcher/MockLauncherModel.h (+3/-0)
tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt (+1/-1)
tests/plugins/Unity/Launcher/CMakeLists.txt (+1/-1)
tests/plugins/Unity/Launcher/launchermodeltest.cpp (+23/-7)
tests/qmltests/Launcher/tst_Launcher.qml (+144/-0)
tests/scripts/README (+5/-0)
tests/scripts/alert-launcher-icon.sh (+3/-0)
tests/scripts/list-launcher-icons.sh (+3/-0)
To merge this branch: bzr merge lp:~macslow/unity8/launcher-icon-wobble
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Michael Zanetti (community) Approve
Albert Astals Cid (community) tags clean & merges fine Abstain
Review via email: mp+262355@code.launchpad.net

Commit message

Implemented alert/wiggle feature for launcher-icons.

Description of the change

Implemented alert/wiggle feature for launcher-icons. See http://macslow.org/wiggle-try-9.mp4 for a brief narrated screencast of this branch in action. The wiggle-animation working on the device: https://www.youtube.com/watch?v=KyinOKLge3Y. And the peeking-animation working on the device: https://www.youtube.com/watch?v=6o4byK3I_S4

* Are there any related MPs required for this MP to build/function as expected? Please list.
Yes.

The branch lp:~macslow/unity-api/launcher-icon-wobble needs to be merged to lp:lp:unity-api first before this can work properly.

* Did you perform an exploratory manual test run of your code change and any related functionality?
Yes.

* Did you make sure that your branch does not contain spurious tags?
Yes.

* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
Not applicable.

* If you changed the UI, has there been a design review?
Yes.

To post a comment you must log in.
Revision history for this message
Lukáš Tinkl (lukas-kde) wrote :

Quick code review, see the inline comments (there are quite some conflicts btw)

Revision history for this message
Mirco Müller (macslow) wrote :

> Quick code review, see the inline comments (there are quite some conflicts
> btw)

I'm on it.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

Reading through the animation (line 327 - 343 in the diff), it seems it has a jump from +wiggleAngle to -wiggleAngle. It goes quite fast in the animation so it doesn't look obviously wrong, but looking closely one can see the jump. Is this intended?

review: Needs Information
Revision history for this message
Michael Zanetti (mzanetti) wrote :

99 +#include <iostream>
129 + std::cout << "LauncherItem::setAlerting()" << std::endl;

We probably don't need this.

=========

526 + panel.visible = true

Two issues here: panel is not within the scope of this QML context (file). Afaict you want to operator on "root" here anyways. But, more importantly, you cannot use an assignent here. This breaks any binding that is acting on "panel.visible" and thus break the optimization of making the launcher invisible when it's outside of the screen. You can see that by running

QSG_VISUALIZE=overdraw make tryLauncher

As soon as you wobble an item once, the launcher never goes away again, wasting graphics resources.

=======

minor thing: in tryLauncher, can you make the TextField Layout.fillWidth: true, otherwise it's really hard to type in an index > 9.

====

can you add a test that first make an item wiggle & peek, then dragLauncherIntoView() and then verify that it's wiggle & !peek. then hide the launcher and make sure it doesn't peek back.

=======

Doesn't work on index 0 and index 9. Probably because of the "focused" arrow that's painted on those?

Think it's worth adding a test that tries to wiggle all the items one after another, just to make sure we catch all combinations like these?

review: Needs Fixing
Revision history for this message
Michael Zanetti (mzanetti) wrote :

> Doesn't work on index 0 and index 9. Probably because of the "focused" arrow
> that's painted on those?

I just realize this is intended because the focused app never should wiggle, so ignore this...

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

You have bad tags

review: Needs Fixing
Revision history for this message
Mirco Müller (macslow) wrote :

> You have bad tags

Argl... before I merged my branch with trunk, I checked for stale tags and did not find any. I wonder where these came from... cleaning up now.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

The question about the jump in the animation is still open.

===

Also it feels a bit strange that only one icon will wiggle until you revealed the launcher. is that intentional?

review: Needs Information
Revision history for this message
Michael Zanetti (mzanetti) wrote :

Oh, also, shouldn't this automatically wiggle when a count emblem changes? Mind hooking it up to that? I think it's just a matter of calling setAlerting(appId, true) in the right places of LauncherModel::countVisibleChanged()

review: Needs Fixing
Revision history for this message
Mirco Müller (macslow) wrote :

> The question about the jump in the animation is still open.

What "jump" do you refer to here? In which context?

> Also it feels a bit strange that only one icon will wiggle until you revealed
> the launcher. is that intentional?

Yes, for the moment. Along the way Design changed some requirements and disambiguated specs upon seeing the implementation in action in presented screencasts. It was agreed to merge what we currently have - to not defer this any longer - and implement the new peeking-policy in a second step. With this new peeking-policy the "wiggle-lock" (requiring a launcher-reveal to unlock) will be loosened up considerably.

Revision history for this message
Mirco Müller (macslow) wrote :

> Oh, also, shouldn't this automatically wiggle when a count emblem changes?
> Mind hooking it up to that? I think it's just a matter of calling
> setAlerting(appId, true) in the right places of
> LauncherModel::countVisibleChanged()

Sure that's a easy change. Did you verify this with Design? If not I can do that too.

Revision history for this message
Michael Zanetti (mzanetti) wrote :

> > The question about the jump in the animation is still open.
>
> What "jump" do you refer to here? In which context?
>

Reading through the animation (line 327 - 343 in the diff), it seems it has a jump from +wiggleAngle to -wiggleAngle. It goes quite fast in the animation so it doesn't look obviously wrong, but looking closely one can see the jump. Is this intended?

Revision history for this message
Michael Zanetti (mzanetti) wrote :

You also need to increase the version dependency in debian/control to depend on the updated libunity-api-dev package

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

ok. looks good now.

 * Did you perform an exploratory manual test run of the code change and any related functionality?

yes

 * Did CI run pass? If not, please explain why.

no, unity-api dep bumped

 * Did you make sure that the branch does not contain spurious tags?

yes

review: Approve
Revision history for this message
Michael Zanetti (mzanetti) wrote :

Hmm, seems to not quite work yet.

Testing this in the silo I couldn't get the icon to wobble on incoming notifications.

I did a little digging on why, see inline comments.

review: Needs Fixing
Revision history for this message
Mirco Müller (macslow) wrote :

> Hmm, seems to not quite work yet.
>
> Testing this in the silo I couldn't get the icon to wobble on incoming
> notifications.
>
> I did a little digging on why, see inline comments.

All those are addressed now in r1812.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

fails tests now :/

review: Needs Fixing
Revision history for this message
Mirco Müller (macslow) wrote :

The reason CI failed is because "libunity-api-dev (>= 7.98)" can't be satisfied yet, as lp:~macslow/unity-api/launcher-icon-wobble is not yet merged to lp:unity-api.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

Still not working. Icons don't wobble when a notification comes in. I've removed it from the silo.

review: Needs Fixing
Revision history for this message
Mirco Müller (macslow) wrote :

Looking into it.

Revision history for this message
Albert Astals Cid (aacid) :
review: Abstain (tags clean & merges fine)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

Text conflict in debian/control
1 conflicts encountered.

Revision history for this message
Mirco Müller (macslow) wrote :

> Text conflict in debian/control
> 1 conflicts encountered.

Fixed with r1816.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

still not working :(

* the wiggle animation only works once
* the peeking doesn't work at all (even breaks dragging the launcher)
* You're reaching out of context again from within the LauncherDelegate.qml... the whole peaking code looks much more complicated as it seems is required.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Mirco Müller (macslow) wrote :

Addressed all but the peeking-animation not working at runtime. Still digging into that.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Mirco Müller (macslow) wrote :

wiggle-animation working on the device: https://www.youtube.com/watch?v=KyinOKLge3Y

Revision history for this message
Mirco Müller (macslow) wrote :

peeking-animation working on the device: https://www.youtube.com/watch?v=6o4byK3I_S4

Revision history for this message
Michael Zanetti (mzanetti) :
review: Needs Fixing
Revision history for this message
Mirco Müller (macslow) wrote :

Almost everything is addressed in r1825... remaining bits in the works.

Revision history for this message
Mirco Müller (macslow) wrote :

Everything from the MP-comments addressed in r1828. Still need to update the launcher-icon related qmltests.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) :
review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1831. By Mirco Müller

Removed newly added progress-bar assets to make room for reverting initially deleted ones in r1780.

1832. By Mirco Müller

Reverted deletion of progress-bar assets in r1780.

Revision history for this message
Mirco Müller (macslow) wrote :

Addressed all issues but the DBus alerting-flag vs DBus alerting-method one.

1833. By Mirco Müller

Fixed suggestions from MP-comments.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1834. By Mirco Müller

Changed DBus-interface for launcher-item from alert-flag to alert-method upon request in MP-comment.

1835. By Mirco Müller

Added some handy shell-scripts for easier runtime-debugging of DBus-APIs on the device.

1836. By Mirco Müller

Fixed launcher-model unit-test.

1837. By Mirco Müller

Added unit-test for new DBus-API Alert() of launcher-model.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

I really was about to approve but because Jenkins doesn't run the tests on this branch I decided to run them manually and it seems there's one that reliably fails in "make xvfbtestLauncher". It does pass in "make testLauncher" tho, so I suspect a missing waitForRendering() somewhere...

FAIL! : qmltestrunner::Launcher::test_alertMoveIconIntoView() property x
   Actual (): -64
   Expected (): 0
   Loc: [/home/micha/Develop/reviews/launcher-icon-wobble/tests/qmltests/Launcher/tst_Launcher.qml(177)]

Apart from that it seems to work fine now and I'm happy with the code.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1838. By Mirco Müller

Merged with trunk.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

Text conflict in qml/Launcher/LauncherPanel.qml
1 conflicts encountered.

review: Needs Fixing
1839. By Mirco Müller

Merged with trunk and fixed conflicts.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1840. By Mirco Müller

Fixed failures of xvfbtestLauncher run.

Revision history for this message
Albert Astals Cid (aacid) :
review: Abstain (tags clean & merges fine)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

 * Did you perform an exploratory manual test run of the code change and any related functionality?

yes

 * Did CI run pass? If not, please explain why.

no, would require unity-api, but tests verified manually.

 * Did you make sure that the branch does not contain spurious tags?

yes

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1841. By Mirco Müller

Merged mzanetti's branch fixing issues for launcher-icons of apps not pinned or running.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2015-07-09 20:22:36 +0000
3+++ debian/control 2015-07-25 11:34:57 +0000
4@@ -30,7 +30,7 @@
5 libqt5xmlpatterns5-dev,
6 libsystemsettings-dev,
7 libudev-dev,
8- libunity-api-dev (>= 7.97),
9+ libunity-api-dev (>= 7.98),
10 libusermetricsoutput1-dev,
11 libxcb1-dev,
12 pkg-config,
13@@ -101,7 +101,7 @@
14 qtdeclarative5-ubuntu-telephony0.1,
15 qtdeclarative5-ubuntu-web-plugin,
16 ubuntu-system-settings,
17- unity-launcher-impl-4,
18+ unity-launcher-impl-7,
19 unity8-common (= ${source:Version}),
20 unity8-private (= ${binary:Version}),
21 unity8-private | unity-launcher-impl,
22@@ -191,7 +191,7 @@
23 ${misc:Depends},
24 ${shlibs:Depends},
25 Provides: unity-launcher-impl,
26- unity-launcher-impl-4,
27+ unity-launcher-impl-7,
28 Description: Unity 8 private libs
29 The Unity 8 shell is the primary user interface for Ubuntu devices.
30 .
31
32=== modified file 'plugins/Greeter/Unity/Launcher/CMakeLists.txt'
33--- plugins/Greeter/Unity/Launcher/CMakeLists.txt 2015-04-24 09:53:37 +0000
34+++ plugins/Greeter/Unity/Launcher/CMakeLists.txt 2015-07-25 11:34:57 +0000
35@@ -1,4 +1,4 @@
36-pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
37+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
38 pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
39 pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
40
41
42=== modified file 'plugins/Greeter/Unity/Launcher/launcheritem.cpp'
43--- plugins/Greeter/Unity/Launcher/launcheritem.cpp 2015-02-11 14:02:24 +0000
44+++ plugins/Greeter/Unity/Launcher/launcheritem.cpp 2015-07-25 11:34:57 +0000
45@@ -31,6 +31,7 @@
46 m_count(0),
47 m_countVisible(false),
48 m_focused(false),
49+ m_alerting(false),
50 m_quickList(new QuickListModel(this))
51 {
52 QuickListEntry nameAction;
53@@ -165,6 +166,19 @@
54 }
55 }
56
57+bool LauncherItem::alerting() const
58+{
59+ return m_alerting;
60+}
61+
62+void LauncherItem::setAlerting(bool alerting)
63+{
64+ if (m_alerting != alerting) {
65+ m_alerting = alerting;
66+ Q_EMIT alertingChanged(alerting);
67+ }
68+}
69+
70 unity::shell::launcher::QuickListModelInterface *LauncherItem::quickList() const
71 {
72 return m_quickList;
73
74=== modified file 'plugins/Greeter/Unity/Launcher/launcheritem.h'
75--- plugins/Greeter/Unity/Launcher/launcheritem.h 2015-02-11 14:02:24 +0000
76+++ plugins/Greeter/Unity/Launcher/launcheritem.h 2015-07-25 11:34:57 +0000
77@@ -41,6 +41,7 @@
78 int count() const override;
79 bool countVisible() const override;
80 bool focused() const override;
81+ bool alerting() const override;
82
83 unity::shell::launcher::QuickListModelInterface *quickList() const override;
84
85@@ -54,6 +55,8 @@
86 void setCount(int count);
87 void setCountVisible(bool countVisible);
88 void setFocused(bool focused);
89+ void setAlerting(bool alerting);
90+
91
92 private:
93 QString m_appId;
94@@ -66,6 +69,7 @@
95 int m_count;
96 bool m_countVisible;
97 bool m_focused;
98+ bool m_alerting;
99 QuickListModel *m_quickList;
100
101 friend class LauncherModel;
102
103=== modified file 'plugins/Greeter/Unity/Launcher/launchermodelas.cpp'
104--- plugins/Greeter/Unity/Launcher/launchermodelas.cpp 2015-02-11 14:02:24 +0000
105+++ plugins/Greeter/Unity/Launcher/launchermodelas.cpp 2015-07-25 11:34:57 +0000
106@@ -72,6 +72,13 @@
107 return QVariant();
108 }
109
110+void LauncherModel::setAlerting(const QString &appId, bool alerting)
111+{
112+ Q_UNUSED(appId)
113+ Q_UNUSED(alerting)
114+ qWarning() << "This is a read only implementation. Cannot set alert-state of items.";
115+}
116+
117 unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) const
118 {
119 if (index < 0 || index >= m_list.count()) {
120
121=== modified file 'plugins/Greeter/Unity/Launcher/launchermodelas.h'
122--- plugins/Greeter/Unity/Launcher/launchermodelas.h 2015-04-30 09:31:51 +0000
123+++ plugins/Greeter/Unity/Launcher/launchermodelas.h 2015-07-25 11:34:57 +0000
124@@ -41,6 +41,7 @@
125
126 QVariant data(const QModelIndex &index, int role) const override;
127
128+ Q_INVOKABLE void setAlerting(const QString &appId, bool alerting) override;
129 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const override;
130 Q_INVOKABLE void move(int oldIndex, int newIndex) override;
131 Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
132
133=== modified file 'plugins/Unity/Launcher/CMakeLists.txt'
134--- plugins/Unity/Launcher/CMakeLists.txt 2015-04-24 09:53:37 +0000
135+++ plugins/Unity/Launcher/CMakeLists.txt 2015-07-25 11:34:57 +0000
136@@ -1,4 +1,4 @@
137-pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
138+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
139 pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
140 pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
141
142
143=== modified file 'plugins/Unity/Launcher/dbusinterface.cpp'
144--- plugins/Unity/Launcher/dbusinterface.cpp 2015-06-11 23:45:12 +0000
145+++ plugins/Unity/Launcher/dbusinterface.cpp 2015-07-25 11:34:57 +0000
146@@ -62,6 +62,7 @@
147 "<interface name=\"com.canonical.Unity.Launcher.Item\">"
148 "<property name=\"count\" type=\"i\" access=\"readwrite\" />"
149 "<property name=\"countVisible\" type=\"b\" access=\"readwrite\" />"
150+ "<method name=\"Alert\" />"
151 "</interface>";
152 return nodeiface;
153 }
154@@ -122,36 +123,53 @@
155 return false;
156 }
157
158- // First handle methods of the Launcher interface
159- if (message.interface() == "com.canonical.Unity.Launcher") {
160- if (message.member() == "Refresh") {
161- QDBusMessage reply = message.createReply();
162- Q_EMIT refreshCalled();
163- return connection.send(reply);
164- }
165- }
166-
167- // Now handle dynamic properties (for launcher emblems)
168- if (message.interface() != "org.freedesktop.DBus.Properties") {
169- return false;
170- }
171-
172- if (message.member() != "GetAll" && message.arguments()[0].toString() != "com.canonical.Unity.Launcher.Item") {
173- return false;
174- }
175-
176 /* Break down the path to just the app id */
177+ bool validpath = true;
178 QString pathtemp = message.path();
179 if (!pathtemp.startsWith("/com/canonical/Unity/Launcher/")) {
180- return false;
181+ validpath = false;
182 }
183 pathtemp.remove("/com/canonical/Unity/Launcher/");
184 if (pathtemp.indexOf('/') >= 0) {
185- return false;
186+ validpath = false;
187 }
188
189 /* Find ourselves an appid */
190 QString appid = decodeAppId(pathtemp);
191+
192+ // First handle methods of the Launcher interface
193+ if (message.interface() == "com.canonical.Unity.Launcher") {
194+ if (message.member() == "Refresh") {
195+ QDBusMessage reply = message.createReply();
196+ Q_EMIT refreshCalled();
197+ return connection.send(reply);
198+ }
199+ } else if (message.interface() == "com.canonical.Unity.Launcher.Item") {
200+ // Handle methods of the Launcher-Item interface
201+ if (message.member() == "Alert" && validpath) {
202+ QDBusMessage reply = message.createReply();
203+ Q_EMIT alertCalled(appid);
204+ return connection.send(reply);
205+ }
206+ }
207+
208+ // Now handle dynamic properties (for launcher emblems)
209+ if (message.interface() != "org.freedesktop.DBus.Properties") {
210+ return false;
211+ }
212+
213+ if (message.member() == "Get" && (message.arguments().count() != 2 || message.arguments()[0].toString() != "com.canonical.Unity.Launcher.Item")) {
214+ return false;
215+ }
216+
217+ if (message.member() == "Set" && (message.arguments().count() != 3 || message.arguments()[0].toString() != "com.canonical.Unity.Launcher.Item")) {
218+ return false;
219+ }
220+
221+ if (!validpath) {
222+ return false;
223+ }
224+
225 int index = m_launcherModel->findApplication(appid);
226 LauncherItem *item = static_cast<LauncherItem*>(m_launcherModel->get(index));
227
228
229=== modified file 'plugins/Unity/Launcher/dbusinterface.h'
230--- plugins/Unity/Launcher/dbusinterface.h 2015-06-11 23:45:12 +0000
231+++ plugins/Unity/Launcher/dbusinterface.h 2015-07-25 11:34:57 +0000
232@@ -37,6 +37,7 @@
233 void countChanged(const QString &appId, int count);
234 void countVisibleChanged(const QString &appId, bool countVisible);
235 void refreshCalled();
236+ void alertCalled(const QString &appId);
237
238 private:
239 static QString decodeAppId(const QString& path);
240
241=== modified file 'plugins/Unity/Launcher/launcheritem.cpp'
242--- plugins/Unity/Launcher/launcheritem.cpp 2014-11-19 17:43:09 +0000
243+++ plugins/Unity/Launcher/launcheritem.cpp 2015-07-25 11:34:57 +0000
244@@ -34,6 +34,7 @@
245 m_count(0),
246 m_countVisible(false),
247 m_focused(false),
248+ m_alerting(false),
249 m_quickList(new QuickListModel(this))
250 {
251 QuickListEntry nameAction;
252@@ -150,6 +151,9 @@
253 if (m_count != count) {
254 m_count = count;
255 Q_EMIT countChanged(count);
256+ if (m_countVisible) {
257+ setAlerting(true);
258+ }
259 }
260 }
261
262@@ -163,6 +167,9 @@
263 if (m_countVisible != countVisible) {
264 m_countVisible = countVisible;
265 Q_EMIT countVisibleChanged(countVisible);
266+ if (countVisible) {
267+ setAlerting(true);
268+ }
269 }
270 }
271
272@@ -175,10 +182,26 @@
273 {
274 if (m_focused != focused) {
275 m_focused = focused;
276+ if (focused) {
277+ setAlerting(false);
278+ }
279 Q_EMIT focusedChanged(focused);
280 }
281 }
282
283+bool LauncherItem::alerting() const
284+{
285+ return m_alerting;
286+}
287+
288+void LauncherItem::setAlerting(bool alerting)
289+{
290+ if (m_alerting != alerting) {
291+ m_alerting = alerting;
292+ Q_EMIT alertingChanged(alerting);
293+ }
294+}
295+
296 unity::shell::launcher::QuickListModelInterface *LauncherItem::quickList() const
297 {
298 return m_quickList;
299
300=== modified file 'plugins/Unity/Launcher/launcheritem.h'
301--- plugins/Unity/Launcher/launcheritem.h 2014-09-02 18:22:37 +0000
302+++ plugins/Unity/Launcher/launcheritem.h 2015-07-25 11:34:57 +0000
303@@ -44,6 +44,7 @@
304 int count() const override;
305 bool countVisible() const override;
306 bool focused() const override;
307+ bool alerting() const override;
308
309 unity::shell::launcher::QuickListModelInterface *quickList() const override;
310
311@@ -57,6 +58,7 @@
312 void setCount(int count);
313 void setCountVisible(bool countVisible);
314 void setFocused(bool focused);
315+ void setAlerting(bool alerting);
316
317 private:
318 QString m_appId;
319@@ -69,6 +71,7 @@
320 int m_count;
321 bool m_countVisible;
322 bool m_focused;
323+ bool m_alerting;
324 QuickListModel *m_quickList;
325
326 friend class LauncherModel;
327
328=== modified file 'plugins/Unity/Launcher/launchermodel.cpp'
329--- plugins/Unity/Launcher/launchermodel.cpp 2015-03-30 17:36:42 +0000
330+++ plugins/Unity/Launcher/launchermodel.cpp 2015-07-25 11:34:57 +0000
331@@ -41,6 +41,7 @@
332 connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged);
333 connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged);
334 connect(m_dbusIface, &DBusInterface::refreshCalled, this, &LauncherModel::refresh);
335+ connect(m_dbusIface, &DBusInterface::alertCalled, this, &LauncherModel::alert);
336
337 connect(m_settings, &GSettings::changed, this, &LauncherModel::refresh);
338
339@@ -82,11 +83,25 @@
340 return item->progress();
341 case RoleFocused:
342 return item->focused();
343+ case RoleAlerting:
344+ return item->alerting();
345 }
346
347 return QVariant();
348 }
349
350+void LauncherModel::setAlerting(const QString &appId, bool alerting) {
351+ int index = findApplication(appId);
352+ if (index >= 0) {
353+ QModelIndex modelIndex = this->index(index);
354+ LauncherItem *item = m_list.at(index);
355+ if (!item->focused()) {
356+ item->setAlerting(alerting);
357+ Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RoleAlerting);
358+ }
359+ }
360+}
361+
362 unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) const
363 {
364 if (index < 0 || index >= m_list.count()) {
365@@ -334,17 +349,23 @@
366 if (idx >= 0) {
367 LauncherItem *item = m_list.at(idx);
368 item->setCount(count);
369+ if (item->countVisible()) {
370+ setAlerting(item->appId(), true);
371+ }
372 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
373 m_asAdapter->syncItems(m_list);
374 }
375 }
376
377-void LauncherModel::countVisibleChanged(const QString &appId, int countVisible)
378+void LauncherModel::countVisibleChanged(const QString &appId, bool countVisible)
379 {
380 int idx = findApplication(appId);
381 if (idx >= 0) {
382 LauncherItem *item = m_list.at(idx);
383 item->setCountVisible(countVisible);
384+ if (countVisible) {
385+ setAlerting(item->appId(), true);
386+ }
387 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCountVisible);
388
389 // If countVisible goes to false, and the item is neither pinned nor recent we can drop it
390@@ -364,7 +385,6 @@
391 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
392 m_list.append(item);
393 endInsertRows();
394- Q_EMIT hint();
395 }
396 }
397 m_asAdapter->syncItems(m_list);
398@@ -453,6 +473,16 @@
399 m_asAdapter->syncItems(m_list);
400 }
401
402+void LauncherModel::alert(const QString &appId)
403+{
404+ int idx = findApplication(appId);
405+ if (idx >= 0) {
406+ LauncherItem *item = m_list.at(idx);
407+ setAlerting(item->appId(), true);
408+ Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleAlerting);
409+ }
410+}
411+
412 void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
413 {
414 Q_UNUSED(parent);
415
416=== modified file 'plugins/Unity/Launcher/launchermodel.h'
417--- plugins/Unity/Launcher/launchermodel.h 2015-04-30 09:31:51 +0000
418+++ plugins/Unity/Launcher/launchermodel.h 2015-07-25 11:34:57 +0000
419@@ -45,6 +45,7 @@
420
421 QVariant data(const QModelIndex &index, int role) const override;
422
423+ Q_INVOKABLE void setAlerting(const QString &appId, bool alerting) override;
424 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const override;
425 Q_INVOKABLE void move(int oldIndex, int newIndex) override;
426 Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
427@@ -63,6 +64,7 @@
428 public Q_SLOTS:
429 void requestRemove(const QString &appId) override;
430 Q_INVOKABLE void refresh();
431+ Q_INVOKABLE void alert(const QString &appId);
432
433 private:
434 void storeAppList();
435@@ -71,7 +73,7 @@
436
437 private Q_SLOTS:
438 void countChanged(const QString &appId, int count);
439- void countVisibleChanged(const QString &appId, int count);
440+ void countVisibleChanged(const QString &appId, bool count);
441 void progressChanged(const QString &appId, int progress);
442
443 void applicationAdded(const QModelIndex &parent, int row);
444
445=== modified file 'qml/Launcher/LauncherDelegate.qml'
446--- qml/Launcher/LauncherDelegate.qml 2014-10-14 12:28:07 +0000
447+++ qml/Launcher/LauncherDelegate.qml 2015-07-25 11:34:57 +0000
448@@ -15,7 +15,7 @@
449 */
450
451 import QtQuick 2.0
452-import Ubuntu.Components 0.1
453+import Ubuntu.Components 1.1
454
455 Item {
456 id: root
457@@ -27,6 +27,8 @@
458 property bool itemFocused: false
459 property real maxAngle: 0
460 property bool inverted: false
461+ property bool alerting: false
462+ readonly property alias wiggling: wiggleAnim.running
463
464 readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * itemHeight
465 readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * itemHeight
466@@ -39,6 +41,82 @@
467 property real offset: 0
468 property real itemOpacity: 1
469 property real brightness: 0
470+ property double maxWiggleAngle: 5.0
471+
472+ QtObject {
473+ id: priv
474+
475+ readonly property int wiggleDuration: UbuntuAnimation.SnapDuration
476+ property real wiggleAngle: 0
477+ }
478+
479+ SequentialAnimation {
480+ id: wiggleAnim
481+
482+ running: alerting
483+ loops: 1
484+ alwaysRunToEnd: true
485+
486+ NumberAnimation {
487+ target: priv
488+ property: "wiggleAngle"
489+ from: 0
490+ to: maxWiggleAngle
491+ duration: priv.wiggleDuration
492+ easing.type: Easing.InQuad
493+ }
494+
495+ NumberAnimation {
496+ target: priv
497+ property: "wiggleAngle"
498+ from: maxWiggleAngle
499+ to: -maxWiggleAngle
500+ duration: priv.wiggleDuration
501+ easing.type: Easing.InOutQuad
502+ }
503+
504+ NumberAnimation {
505+ target: priv
506+ property: "wiggleAngle"
507+ from: -maxWiggleAngle
508+ to: maxWiggleAngle
509+ duration: priv.wiggleDuration
510+ easing.type: Easing.InOutQuad
511+ }
512+
513+ NumberAnimation {
514+ target: priv
515+ property: "wiggleAngle"
516+ from: maxWiggleAngle
517+ to: -maxWiggleAngle
518+ duration: priv.wiggleDuration
519+ easing.type: Easing.InOutQuad
520+ }
521+
522+ NumberAnimation {
523+ target: priv
524+ property: "wiggleAngle"
525+ from: -maxWiggleAngle
526+ to: maxWiggleAngle
527+ duration: priv.wiggleDuration
528+ easing.type: Easing.InOutQuad
529+ }
530+
531+ NumberAnimation {
532+ target: priv
533+ property: "wiggleAngle"
534+ from: maxWiggleAngle
535+ to: 0
536+ duration: priv.wiggleDuration
537+ easing.type: Easing.OutQuad
538+ }
539+
540+ UbuntuNumberAnimation {
541+ target: root
542+ property: "alerting"
543+ to: 0
544+ }
545+ }
546
547 Item {
548 id: iconItem
549@@ -167,6 +245,14 @@
550 }
551
552 transform: [
553+ // The rotation about the icon's center/z-axis for the wiggle
554+ // needs to happen here too, because there's no other way to
555+ // align the wiggle with the icon-folding otherwise
556+ Rotation {
557+ axis { x: 0; y: 0; z: 1 }
558+ origin { x: iconItem.width / 2; y: iconItem.height / 2; z: 0 }
559+ angle: priv.wiggleAngle
560+ },
561 // Rotating 3 times at top/bottom because that increases the perspective.
562 // This is a hack, but as QML does not support real 3D coordinates
563 // getting a higher perspective can only be done by a hack. This is the most
564
565=== modified file 'qml/Launcher/LauncherPanel.qml'
566--- qml/Launcher/LauncherPanel.qml 2015-07-01 11:39:49 +0000
567+++ qml/Launcher/LauncherPanel.qml 2015-07-25 11:34:57 +0000
568@@ -86,6 +86,7 @@
569 height: parent.height - dashItem.height - parent.spacing*2
570
571 Item {
572+ id: launcherListViewItem
573 anchors.fill: parent
574 clip: true
575
576@@ -109,6 +110,9 @@
577 preferredHighlightBegin: (height - itemHeight) / 2
578 preferredHighlightEnd: (height + itemHeight) / 2
579
580+ // for the single peeking icon, when alert-state is set on delegate
581+ property int peekingIndex: -1
582+
583 // The size of the area the ListView is extended to make sure items are not
584 // destroyed when dragging them outside the list. This needs to be at least
585 // itemHeight to prevent folded items from disappearing and DragArea limits
586@@ -159,6 +163,17 @@
587 to: launcherListView.contentHeight - launcherListView.height + launcherListView.originY - launcherListView.topMargin
588 }
589
590+ UbuntuNumberAnimation {
591+ id: moveAnimation
592+ target: launcherListView
593+ property: "contentY"
594+ function moveTo(contentY) {
595+ from = launcherListView.contentY;
596+ to = contentY;
597+ start();
598+ }
599+ }
600+
601 displaced: Transition {
602 NumberAnimation { properties: "x,y"; duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
603 }
604@@ -180,10 +195,67 @@
605 progress: model.progress
606 itemFocused: model.focused
607 inverted: root.inverted
608+ alerting: model.alerting
609 z: -Math.abs(offset)
610 maxAngle: 55
611 property bool dragging: false
612
613+ SequentialAnimation {
614+ id: peekingAnimation
615+
616+ // revealing
617+ PropertyAction { target: root; property: "visible"; value: (launcher.visibleWidth === 0) ? 1 : 0 }
618+ PropertyAction { target: launcherListViewItem; property: "clip"; value: 0 }
619+
620+ UbuntuNumberAnimation {
621+ target: launcherDelegate
622+ alwaysRunToEnd: true
623+ loops: 1
624+ properties: "x"
625+ to: (units.gu(.5) + launcherListView.width * .5) * (root.inverted ? -1 : 1)
626+ duration: UbuntuAnimation.BriskDuration
627+ }
628+
629+ // hiding
630+ UbuntuNumberAnimation {
631+ target: launcherDelegate
632+ alwaysRunToEnd: true
633+ loops: 1
634+ properties: "x"
635+ to: 0
636+ duration: UbuntuAnimation.BriskDuration
637+ }
638+
639+ PropertyAction { target: launcherListViewItem; property: "clip"; value: 1 }
640+ PropertyAction { target: root; property: "visible"; value: (launcher.visibleWidth === 0) ? 0 : 1 }
641+ }
642+
643+ onAlertingChanged: {
644+ if(alerting) {
645+ if (!dragging && (launcherListView.peekingIndex === -1 || launcher.visibleWidth > 0)) {
646+ var itemPosition = index * launcherListView.itemHeight;
647+ var height = launcherListView.height - launcherListView.topMargin - launcherListView.bottomMargin
648+ var distanceToEnd = index == 0 || index == launcherListView.count - 1 ? 0 : launcherListView.itemHeight
649+ if (itemPosition + launcherListView.itemHeight + distanceToEnd > launcherListView.contentY + launcherListView.topMargin + height) {
650+ moveAnimation.moveTo(itemPosition + launcherListView.itemHeight - launcherListView.topMargin - height + distanceToEnd);
651+ } else if (itemPosition - distanceToEnd < launcherListView.contentY + launcherListView.topMargin) {
652+ moveAnimation.moveTo(itemPosition - distanceToEnd - launcherListView.topMargin);
653+ }
654+ if (!dragging && launcher.state !== "visible") {
655+ peekingAnimation.start()
656+ }
657+ }
658+
659+ if (launcherListView.peekingIndex === -1) {
660+ launcherListView.peekingIndex = index
661+ }
662+ } else {
663+ if (launcherListView.peekingIndex === index) {
664+ launcherListView.peekingIndex = -1
665+ }
666+ }
667+ }
668+
669 ThinDivider {
670 id: dropIndicator
671 objectName: "dropIndicator"
672@@ -521,7 +593,7 @@
673 Image {
674 anchors {
675 left: parent.left
676- leftMargin: quickList.item ? (quickList.item.width - units.gu(1)) / 2 - width / 2 : 0
677+ leftMargin: quickList.item !== undefined ? (quickList.item.width - units.gu(1)) / 2 - width / 2 : 0
678 verticalCenter: parent.verticalCenter
679 verticalCenterOffset: (parent.height / 2 + units.dp(3)) * (quickList.offset > 0 ? 1 : -1) * (root.inverted ? 1 : -1)
680 }
681@@ -564,10 +636,9 @@
682
683 // internal
684 property int itemCenter: item ? root.mapFromItem(quickList.item).y + (item.height / 2) : units.gu(1)
685- property int offset: item ? itemCenter + (item.height/2) + height + units.gu(1) > parent.height ?
686+ property int offset: item !== undefined ? (itemCenter + (item.height/2) + height + units.gu(1) > parent.height ?
687 -(item.height/2) - height - units.gu(.5) :
688- (item.height/2) + units.gu(.5) :
689- 0
690+ (item.height/2) + units.gu(.5)) : 0
691
692 Column {
693 id: quickListColumn
694
695=== modified file 'tests/mocks/Unity/Launcher/CMakeLists.txt'
696--- tests/mocks/Unity/Launcher/CMakeLists.txt 2015-04-13 09:33:28 +0000
697+++ tests/mocks/Unity/Launcher/CMakeLists.txt 2015-07-25 11:34:57 +0000
698@@ -1,4 +1,4 @@
699-pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
700+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
701
702 include_directories(
703 ${CMAKE_CURRENT_SOURCE_DIR}
704
705=== modified file 'tests/mocks/Unity/Launcher/MockLauncherItem.cpp'
706--- tests/mocks/Unity/Launcher/MockLauncherItem.cpp 2014-09-03 13:30:52 +0000
707+++ tests/mocks/Unity/Launcher/MockLauncherItem.cpp 2015-07-25 11:34:57 +0000
708@@ -37,6 +37,7 @@
709 m_count(0),
710 m_countVisible(false),
711 m_focused(false),
712+ m_alerting(false),
713 m_quickList(new MockQuickListModel(this))
714 {
715
716@@ -129,6 +130,9 @@
717 {
718 m_count = count;
719 Q_EMIT countChanged(count);
720+ if (m_countVisible) {
721+ setAlerting(true);
722+ }
723 }
724 }
725
726@@ -142,6 +146,9 @@
727 if (m_countVisible != countVisible) {
728 m_countVisible = countVisible;
729 Q_EMIT countVisibleChanged(countVisible);
730+ if (countVisible) {
731+ setAlerting(true);
732+ }
733 }
734 }
735
736@@ -159,6 +166,18 @@
737 }
738 }
739
740+bool MockLauncherItem::alerting() const
741+{
742+ return m_alerting;
743+}
744+
745+void MockLauncherItem::setAlerting(bool alerting)
746+{
747+ if (m_alerting != alerting) {
748+ m_alerting = alerting;
749+ Q_EMIT alertingChanged(alerting);
750+ }
751+}
752
753 unity::shell::launcher::QuickListModelInterface *MockLauncherItem::quickList() const
754 {
755
756=== modified file 'tests/mocks/Unity/Launcher/MockLauncherItem.h'
757--- tests/mocks/Unity/Launcher/MockLauncherItem.h 2015-04-30 09:31:51 +0000
758+++ tests/mocks/Unity/Launcher/MockLauncherItem.h 2015-07-25 11:34:57 +0000
759@@ -44,6 +44,7 @@
760 int count() const override;
761 bool countVisible() const override;
762 bool focused() const override;
763+ bool alerting() const override;
764
765 unity::shell::launcher::QuickListModelInterface *quickList() const override;
766
767@@ -55,6 +56,7 @@
768 void setCount(int count);
769 void setCountVisible(bool countVisible);
770 void setFocused(bool focused);
771+ void setAlerting(bool alerting);
772
773 QString m_appId;
774 QString m_desktopFile;
775@@ -67,6 +69,7 @@
776 int m_count;
777 bool m_countVisible;
778 bool m_focused;
779+ bool m_alerting;
780 MockQuickListModel *m_quickList;
781
782 friend class MockLauncherModel;
783
784=== modified file 'tests/mocks/Unity/Launcher/MockLauncherModel.cpp'
785--- tests/mocks/Unity/Launcher/MockLauncherModel.cpp 2015-03-06 04:44:11 +0000
786+++ tests/mocks/Unity/Launcher/MockLauncherModel.cpp 2015-07-25 11:34:57 +0000
787@@ -34,6 +34,7 @@
788 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
789 item->setProgress(50);
790 item->setCountVisible(true);
791+ item->setAlerting(false);
792 m_list.append(item);
793 item = new MockLauncherItem("music-app", "/usr/share/applications/music-app.desktop", "Music", "soundcloud", this);
794 m_list.append(item);
795@@ -43,25 +44,30 @@
796 item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
797 item->setCount(1);
798 item->setCountVisible(true);
799+ item->setAlerting(false);
800 m_list.append(item);
801 item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
802 item->setCount(12);
803 item->setCountVisible(true);
804+ item->setAlerting(false);
805 item->setPinned(true);
806 m_list.append(item);
807 item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
808 item->setCount(123);
809 item->setCountVisible(true);
810+ item->setAlerting(false);
811 m_list.append(item);
812 item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
813 item->setCount(1234567890);
814 item->setCountVisible(true);
815+ item->setAlerting(false);
816 item->setPinned(true);
817 m_list.append(item);
818 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
819 item->setProgress(50);
820 item->setCount(5);
821 item->setCountVisible(true);
822+ item->setAlerting(false);
823 item->setFocused(true);
824 item->setPinned(true);
825 m_list.append(item);
826@@ -105,6 +111,8 @@
827 return item->countVisible();
828 case RoleFocused:
829 return item->focused();
830+ case RoleAlerting:
831+ return item->alerting();
832 }
833
834 return QVariant();
835@@ -119,6 +127,18 @@
836 return m_list.at(index);
837 }
838
839+void MockLauncherModel::setAlerting(const QString &appId, bool alerting) {
840+ int index = findApp(appId);
841+ if (index >= 0) {
842+ QModelIndex modelIndex = this->index(index);
843+ MockLauncherItem *item = m_list.at(index);
844+ if (!item->focused()) {
845+ item->setAlerting(alerting);
846+ Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RoleAlerting);
847+ }
848+ }
849+}
850+
851 void MockLauncherModel::move(int oldIndex, int newIndex)
852 {
853 // Make sure its not moved outside the lists
854@@ -226,6 +246,26 @@
855 Q_EMIT hint();
856 }
857
858+void MockLauncherModel::setCount(const QString &appId, int count)
859+{
860+ int index = findApp(appId);
861+ if (index >= 0) {
862+ m_list.at(index)->setCount(count);
863+ QModelIndex modelIndex = this->index(index);
864+ Q_EMIT dataChanged(modelIndex, modelIndex);
865+ }
866+}
867+
868+void MockLauncherModel::setCountVisible(const QString &appId, bool countVisible)
869+{
870+ int index = findApp(appId);
871+ if (index >= 0) {
872+ m_list.at(index)->setCountVisible(countVisible);
873+ QModelIndex modelIndex = this->index(index);
874+ Q_EMIT dataChanged(modelIndex, modelIndex);
875+ }
876+}
877+
878 unity::shell::application::ApplicationManagerInterface *MockLauncherModel::applicationManager() const
879 {
880 return nullptr;
881
882=== modified file 'tests/mocks/Unity/Launcher/MockLauncherModel.h'
883--- tests/mocks/Unity/Launcher/MockLauncherModel.h 2015-04-30 09:31:51 +0000
884+++ tests/mocks/Unity/Launcher/MockLauncherModel.h 2015-07-25 11:34:57 +0000
885@@ -38,6 +38,7 @@
886
887 QVariant data(const QModelIndex& index, int role) const override;
888
889+ Q_INVOKABLE void setAlerting(const QString &appId, bool alerting) override;
890 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface *get(int index) const override;
891 Q_INVOKABLE void move(int oldIndex, int newIndex) override;
892 Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
893@@ -54,6 +55,8 @@
894
895 // For testing
896 Q_INVOKABLE void emitHint();
897+ Q_INVOKABLE void setCount(const QString &appId, int count);
898+ Q_INVOKABLE void setCountVisible(const QString &appId, bool countVisible);
899
900 Q_SIGNALS:
901 void quickListTriggered(const QString &appId, int index);
902
903=== modified file 'tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt'
904--- tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt 2015-06-17 12:14:27 +0000
905+++ tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt 2015-07-25 11:34:57 +0000
906@@ -1,4 +1,4 @@
907-pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
908+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
909 pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
910
911 include_directories(
912
913=== modified file 'tests/plugins/Unity/Launcher/CMakeLists.txt'
914--- tests/plugins/Unity/Launcher/CMakeLists.txt 2015-06-17 12:14:27 +0000
915+++ tests/plugins/Unity/Launcher/CMakeLists.txt 2015-07-25 11:34:57 +0000
916@@ -1,5 +1,5 @@
917 pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
918-pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
919+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
920
921 include_directories(
922 ${CMAKE_CURRENT_SOURCE_DIR}
923
924=== modified file 'tests/plugins/Unity/Launcher/launchermodeltest.cpp'
925--- tests/plugins/Unity/Launcher/launchermodeltest.cpp 2015-06-12 16:07:43 +0000
926+++ tests/plugins/Unity/Launcher/launchermodeltest.cpp 2015-07-25 11:34:57 +0000
927@@ -368,11 +368,18 @@
928 }
929
930 void testCountEmblems() {
931+ QSignalSpy spy(launcherModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)));
932+
933 // Call GetAll on abs-icon
934 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "org.freedesktop.DBus.Properties");
935 QDBusReply<QVariantMap> reply = interface.call("GetAll");
936 QVariantMap map = reply.value();
937
938+ // Check that the alerting-status is still false, and the item on the upper side of the API
939+ int index = launcherModel->findApplication("abs-icon");
940+ QCOMPARE(index >= 0, true);
941+ QVERIFY(launcherModel->get(index)->alerting() == false);
942+
943 // Make sure GetAll returns a map with count and countVisible props
944 QCOMPARE(map.contains("count"), true);
945 QCOMPARE(map.contains("countVisible"), true);
946@@ -393,13 +400,8 @@
947 QCOMPARE(map.value("count").toInt(), 55);
948 QCOMPARE(map.value("countVisible").toBool(), true);
949
950- // Now the item on the upper side of the API
951- int index = launcherModel->findApplication("abs-icon");
952- QCOMPARE(index >= 0, true);
953-
954- // And make sure values have changed there as well
955- QCOMPARE(launcherModel->get(index)->countVisible(), true);
956- QCOMPARE(launcherModel->get(index)->count(), 55);
957+ // Finally check, that the change to "count" implicitly also set the alerting-state to true
958+ QVERIFY(launcherModel->get(index)->alerting() == true);
959 }
960
961 void testCountEmblemAddsRemovesItem_data() {
962@@ -459,6 +461,20 @@
963 QCOMPARE(index == -1, !isRunning && !isPinned && !startWhenVisible);
964 }
965
966+ void testAlert() {
967+ // Check that the alerting-status is still false
968+ int index = launcherModel->findApplication("abs-icon");
969+ QCOMPARE(index >= 0, true);
970+ QVERIFY(launcherModel->get(index)->alerting() == false);
971+
972+ // Call Alert() on "abs-icon"
973+ QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "com.canonical.Unity.Launcher.Item");
974+ interface.call("Alert");
975+
976+ // Check that the alerting-status is now true
977+ QVERIFY(launcherModel->get(index)->alerting() == true);
978+ }
979+
980 void testRefreshAfterDeletedDesktopFiles_data() {
981 QTest::addColumn<bool>("deleted");
982 QTest::newRow("have .desktop files") << false;
983
984=== modified file 'tests/qmltests/Launcher/tst_Launcher.qml'
985--- tests/qmltests/Launcher/tst_Launcher.qml 2015-04-24 09:53:37 +0000
986+++ tests/qmltests/Launcher/tst_Launcher.qml 2015-07-25 11:34:57 +0000
987@@ -93,6 +93,31 @@
988 onClicked: launcherLoader.item.inverted = !launcherLoader.item.inverted
989 Layout.fillWidth: true
990 }
991+
992+ Row {
993+ spacing: units.gu(1)
994+
995+ Button {
996+ text: "set alert"
997+ onClicked: LauncherModel.setAlerting(LauncherModel.get(parseInt(appIdEntry.displayText)).appId, true)
998+ }
999+
1000+ TextArea {
1001+ id: appIdEntry
1002+ anchors.verticalCenter: parent.verticalCenter
1003+ width: units.gu(5)
1004+ height: units.gu(4)
1005+ autoSize: true
1006+ text: "2"
1007+ maximumLineCount: 1
1008+ Layout.fillWidth: true
1009+ }
1010+
1011+ Button {
1012+ text: "unset alert"
1013+ onClicked: LauncherModel.setAlerting(LauncherModel.get(parseInt(appIdEntry.displayText)).appId, false)
1014+ }
1015+ }
1016 }
1017
1018 SignalSpy {
1019@@ -169,6 +194,16 @@
1020 tryCompare(panel, "x", -panel.width, 1000);
1021 }
1022
1023+ function waitForWiggleToStart(appIcon) {
1024+ verify(appIcon != undefined)
1025+ tryCompare(appIcon, "wiggling", true, 1000, "wiggle-anim should not be in stopped state")
1026+ }
1027+
1028+ function waitForWiggleToStop(appIcon) {
1029+ verify(appIcon != undefined)
1030+ tryCompare(appIcon, "wiggling", false, 1000, "wiggle-anim should not be in running state")
1031+ }
1032+
1033 function positionLauncherListAtBeginning() {
1034 var listView = testCase.findChild(launcherLoader.item, "launcherListView");
1035 listView.contentY = -listView.topMargin;
1036@@ -636,5 +671,114 @@
1037 tryCompare(launcher, "state", "", 1000, "Launcher didn't hide after moving mouse away from it");
1038 waitUntilLauncherDisappears();
1039 }
1040+
1041+ function test_alertPeekingIcon() {
1042+ var listView = findChild(launcher, "launcherListView")
1043+ verify(listView != undefined)
1044+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
1045+ tryCompare(listView, "peekingIndex", 5, 1000, "Wrong appId set as peeking-index")
1046+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
1047+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
1048+ }
1049+
1050+ function test_alertHidingIcon() {
1051+ var listView = findChild(launcher, "launcherListView")
1052+ verify(listView != undefined)
1053+ var appIcon6 = findChild(launcher, "launcherDelegate6")
1054+ verify(appIcon6 != undefined)
1055+ LauncherModel.setAlerting(LauncherModel.get(6).appId, true)
1056+ waitForWiggleToStart(appIcon6)
1057+ LauncherModel.setAlerting(LauncherModel.get(6).appId, false)
1058+ waitForWiggleToStop(appIcon6)
1059+ tryCompare(appIcon6, "x", 0, 1000, "x-value of appId #6 should not be non-zero")
1060+ waitForRendering(listView)
1061+ }
1062+
1063+ function test_alertIgnoreFocusedApp() {
1064+ LauncherModel.setAlerting(LauncherModel.get(0).appId, true)
1065+ compare(LauncherModel.get(0).alerting, false, "Focused app should not have the alert-state set")
1066+ }
1067+
1068+ function test_alertOnlyOnePeekingIcon() {
1069+ var listView = findChild(launcher, "launcherListView")
1070+ verify(listView != undefined)
1071+ LauncherModel.setAlerting(LauncherModel.get(3).appId, true)
1072+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
1073+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
1074+ tryCompare(listView, "peekingIndex", 3, 1000, "Wrong appId set as peeking-index")
1075+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
1076+ LauncherModel.setAlerting(LauncherModel.get(3).appId, false)
1077+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
1078+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
1079+ waitForRendering(listView)
1080+ }
1081+
1082+ function test_alertMultipleApps() {
1083+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
1084+ LauncherModel.setAlerting(LauncherModel.get(3).appId, true)
1085+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
1086+ LauncherModel.setAlerting(LauncherModel.get(7).appId, true)
1087+ compare(LauncherModel.get(1).alerting, true, "Alert-state of appId #1 should not be false")
1088+ compare(LauncherModel.get(3).alerting, true, "Alert-state of appId #3 should not be false")
1089+ compare(LauncherModel.get(5).alerting, true, "Alert-state of appId #5 should not be false")
1090+ compare(LauncherModel.get(7).alerting, true, "Alert-state of appId #7 should not be false")
1091+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
1092+ LauncherModel.setAlerting(LauncherModel.get(3).appId, false)
1093+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
1094+ LauncherModel.setAlerting(LauncherModel.get(7).appId, false)
1095+ compare(LauncherModel.get(1).alerting, false, "Alert-state of appId #1 should not be true")
1096+ compare(LauncherModel.get(3).alerting, false, "Alert-state of appId #1 should not be true")
1097+ compare(LauncherModel.get(5).alerting, false, "Alert-state of appId #1 should not be true")
1098+ compare(LauncherModel.get(7).alerting, false, "Alert-state of appId #1 should not be true")
1099+ }
1100+
1101+ function test_alertMoveIconIntoView() {
1102+ dragLauncherIntoView();
1103+ var appIcon1 = findChild(launcher, "launcherDelegate1");
1104+ var appIcon7 = findChild(launcher, "launcherDelegate7");
1105+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
1106+ tryCompare(appIcon1, "angle", 0, 1000, "angle of appId #1 should not be non-zero")
1107+ waitForWiggleToStart(appIcon1)
1108+ LauncherModel.setAlerting(LauncherModel.get(7).appId, true)
1109+ tryCompare(appIcon7, "angle", 0, 1000, "angle of appId #7 should not be non-zero")
1110+ waitForWiggleToStart(appIcon7)
1111+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
1112+ waitForWiggleToStop(appIcon1)
1113+ LauncherModel.setAlerting(LauncherModel.get(7).appId, false)
1114+ waitForWiggleToStop(appIcon7)
1115+ }
1116+
1117+ function test_alertWigglePeekDrag() {
1118+ var appIcon5 = findChild(launcher, "launcherDelegate5");
1119+ var listView = findChild(launcher, "launcherListView")
1120+ verify(listView != undefined)
1121+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
1122+ tryCompare(listView, "peekingIndex", 5, 1000, "Wrong appId set as peeking-index")
1123+ waitForWiggleToStart(appIcon5)
1124+ tryCompare(appIcon5, "wiggling", true, 1000, "appId #6 should not be still")
1125+ dragLauncherIntoView();
1126+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
1127+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
1128+ waitForWiggleToStop(appIcon5)
1129+ tryCompare(appIcon5, "wiggling", false, 1000, "appId #1 should not be wiggling")
1130+ }
1131+
1132+ function test_alertViaCountAndCountVisible() {
1133+ dragLauncherIntoView();
1134+ var appIcon1 = findChild(launcher, "launcherDelegate1")
1135+ var oldCount = LauncherModel.get(1).count
1136+ LauncherModel.setCount(LauncherModel.get(1).appId, 42)
1137+ tryCompare(appIcon1, "wiggling", false, 1000, "appId #1 should be still")
1138+ LauncherModel.setCountVisible(LauncherModel.get(1).appId, 1)
1139+ tryCompare(appIcon1, "wiggling", true, 1000, "appId #1 should not be still")
1140+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
1141+ waitForWiggleToStop(appIcon1)
1142+ LauncherModel.setCount(LauncherModel.get(1).appId, 4711)
1143+ tryCompare(appIcon1, "wiggling", true, 1000, "appId #1 should not be still")
1144+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
1145+ waitForWiggleToStop(appIcon1)
1146+ LauncherModel.setCountVisible(LauncherModel.get(1).appId, 0)
1147+ LauncherModel.setCount(LauncherModel.get(1).appId, oldCount)
1148+ }
1149 }
1150 }
1151
1152=== added directory 'tests/scripts'
1153=== added file 'tests/scripts/README'
1154--- tests/scripts/README 1970-01-01 00:00:00 +0000
1155+++ tests/scripts/README 2015-07-25 11:34:57 +0000
1156@@ -0,0 +1,5 @@
1157+This directory holds a few handy shell-scripts to simplify runtime-testing of DBus-APIs (requires qdbus-qt5 to be installed).
1158+
1159+launcher-icon related:
1160+ * list-launcher-icons.sh - get all icons of unity8-launcher
1161+ * alert-launcher-icon.sh - trigger alert/wiggle of unfocused launcher-icon
1162
1163=== added file 'tests/scripts/alert-launcher-icon.sh'
1164--- tests/scripts/alert-launcher-icon.sh 1970-01-01 00:00:00 +0000
1165+++ tests/scripts/alert-launcher-icon.sh 2015-07-25 11:34:57 +0000
1166@@ -0,0 +1,3 @@
1167+#!/bin/sh
1168+
1169+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 com.canonical.Unity.Launcher.Item.Alert
1170
1171=== added file 'tests/scripts/list-launcher-icons.sh'
1172--- tests/scripts/list-launcher-icons.sh 1970-01-01 00:00:00 +0000
1173+++ tests/scripts/list-launcher-icons.sh 2015-07-25 11:34:57 +0000
1174@@ -0,0 +1,3 @@
1175+#!/bin/sh
1176+
1177+qdbus com.canonical.Unity | grep Launcher | cut -f6 -d/

Subscribers

People subscribed via source and target branches