on xenial help sign in
rebase on top of lp:~macslow/unity8/use-set-progress-api
commitef55e4d4ee23e72ab9963ea7a97c14bd59f8c6ad authorLukáš Tinkl <lukas.tinkl@canonical.com> dateThu 23 Jul 2015, 16:13 (11 years ago) parentd5c26b569a45c9
37 files changed+637−67
debian/control+3−3 plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1 plugins/Greeter/Unity/Launcher/launcheritem.cpp+14 plugins/Greeter/Unity/Launcher/launcheritem.h+4 plugins/Greeter/Unity/Launcher/launchermodelas.cpp+26−14 plugins/Greeter/Unity/Launcher/launchermodelas.h+1 plugins/Unity/Launcher/CMakeLists.txt+1−1 plugins/Unity/Launcher/asadapter.cpp+1 plugins/Unity/Launcher/dbusinterface.cpp+39−13 plugins/Unity/Launcher/dbusinterface.h+2 plugins/Unity/Launcher/launcheritem.cpp+23 plugins/Unity/Launcher/launcheritem.h+3 plugins/Unity/Launcher/launchermodel.cpp+32 plugins/Unity/Launcher/launchermodel.h+2 qml/Launcher/LauncherDelegate.qml+101−15 qml/Launcher/LauncherPanel.qml+71 qml/Launcher/graphics/progressbar-fill@30.png qml/Launcher/graphics/progressbar-fill@30.sci−5 qml/Launcher/graphics/progressbar-trough@30.png qml/Launcher/graphics/progressbar-trough@30.sci−5 tests/mocks/Unity/Launcher/CMakeLists.txt+1−1 tests/mocks/Unity/Launcher/MockLauncherItem.cpp+19 tests/mocks/Unity/Launcher/MockLauncherItem.h+3 tests/mocks/Unity/Launcher/MockLauncherModel.cpp+50 tests/mocks/Unity/Launcher/MockLauncherModel.h+4 tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1 tests/plugins/Greeter/Unity/Launcher/launchermodelastest.cpp+3 tests/plugins/Unity/Launcher/CMakeLists.txt+1−1 tests/plugins/Unity/Launcher/launchermodeltest.cpp+23−7 tests/qmltests/Launcher/tst_Launcher.qml+181 tests/scripts/README+9 tests/scripts/alert-launcher-icon.sh+3 tests/scripts/get-progress.sh+3 tests/scripts/list-launcher-icons.sh+3 tests/scripts/set-count-visible.sh+3 tests/scripts/set-count.sh+3 tests/scripts/set-progress.sh+3
debian/control+3−3
@@ -30,7 +30,7 @@ Build-Depends: cmake,
3030 libqt5xmlpatterns5-dev,
3131 libsystemsettings-dev,
3232 libudev-dev,
33− libunity-api-dev (>= 7.97),
33+ libunity-api-dev (>= 7.98),
3434 libusermetricsoutput1-dev,
3535 libxcb1-dev,
3636 pkg-config,
@@ -101,7 +101,7 @@ Depends: gsettings-desktop-schemas,
101101 qtdeclarative5-ubuntu-telephony0.1,
102102 qtdeclarative5-ubuntu-web-plugin,
103103 ubuntu-system-settings,
104− unity-launcher-impl-4,
104+ unity-launcher-impl-7,
105105 unity8-common (= ${source:Version}),
106106 unity8-private (= ${binary:Version}),
107107 unity8-private | unity-launcher-impl,
@@ -191,7 +191,7 @@ Depends: accountsservice-ubuntu-schemas (>= 0.0.3),
191191 ${misc:Depends},
192192 ${shlibs:Depends},
193193Provides: unity-launcher-impl,
194− unity-launcher-impl-4,
194+ unity-launcher-impl-7,
195195Description: Unity 8 private libs
196196 The Unity 8 shell is the primary user interface for Ubuntu devices.
197197 .
plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
22pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
33pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
44
plugins/Greeter/Unity/Launcher/launcheritem.cpp+14
@@ -31,6 +31,7 @@ LauncherItem::LauncherItem(const QString &appId, const QString &name, const QStr
3131 m_count(0),
3232 m_countVisible(false),
3333 m_focused(false),
34+ m_alerting(false),
3435 m_quickList(new QuickListModel(this))
3536{
3637 QuickListEntry nameAction;
@@ -165,6 +166,19 @@ void LauncherItem::setFocused(bool focused)
165166 }
166167}
167168
169+bool LauncherItem::alerting() const
170+{
171+ return m_alerting;
172+}
173+
174+void LauncherItem::setAlerting(bool alerting)
175+{
176+ if (m_alerting != alerting) {
177+ m_alerting = alerting;
178+ Q_EMIT alertingChanged(alerting);
179+ }
180+}
181+
168182unity::shell::launcher::QuickListModelInterface *LauncherItem::quickList() const
169183{
170184 return m_quickList;
plugins/Greeter/Unity/Launcher/launcheritem.h+4
@@ -41,6 +41,7 @@ public:
4141 int count() const override;
4242 bool countVisible() const override;
4343 bool focused() const override;
44+ bool alerting() const override;
4445
4546 unity::shell::launcher::QuickListModelInterface *quickList() const override;
4647
@@ -54,6 +55,8 @@ private:
5455 void setCount(int count);
5556 void setCountVisible(bool countVisible);
5657 void setFocused(bool focused);
58+ void setAlerting(bool alerting);
59+
5760
5861private:
5962 QString m_appId;
@@ -66,6 +69,7 @@ private:
6669 int m_count;
6770 bool m_countVisible;
6871 bool m_focused;
72+ bool m_alerting;
6973 QuickListModel *m_quickList;
7074
7175 friend class LauncherModel;
plugins/Greeter/Unity/Launcher/launchermodelas.cpp+26−14
@@ -74,6 +74,13 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
7474 return QVariant();
7575}
7676
77+void LauncherModel::setAlerting(const QString &appId, bool alerting)
78+{
79+ Q_UNUSED(appId)
80+ Q_UNUSED(alerting)
81+ qWarning() << "This is a read only implementation. Cannot set alert-state of items.";
82+}
83+
7784unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) const
7885{
7986 if (index < 0 || index >= m_list.count()) {
@@ -202,17 +209,20 @@ void LauncherModel::refresh()
202209 Q_FOREACH (LauncherItem* item, m_list) {
203210 bool found = false;
204211 Q_FOREACH(const QVariant &asItem, items) {
205− if (asItem.toMap().value("id").toString() == item->appId()) {
212+ QVariantMap cachedMap = asItem.toMap();
213+ if (cachedMap.value("id").toString() == item->appId()) {
206214 // Only keep and update it if we either show unpinned or it is pinned
207− if (!m_onlyPinned || asItem.toMap().value("pinned").toBool()) {
215+ if (!m_onlyPinned || cachedMap.value("pinned").toBool()) {
208216 found = true;
209− item->setName(asItem.toMap().value("name").toString());
210− item->setIcon(asItem.toMap().value("icon").toString());
211− item->setCount(asItem.toMap().value("count").toInt());
212− item->setCountVisible(asItem.toMap().value("countVisible").toBool());
213− item->setRunning(asItem.toMap().value("running").toBool());
217+ item->setName(cachedMap.value("name").toString());
218+ item->setIcon(cachedMap.value("icon").toString());
219+ item->setCount(cachedMap.value("count").toInt());
220+ item->setCountVisible(cachedMap.value("countVisible").toBool());
221+ item->setProgress(cachedMap.value("progress").toInt());
222+ item->setRunning(cachedMap.value("running").toBool());
223+
214224 int idx = m_list.indexOf(item);
215− Q_EMIT dataChanged(index(idx), index(idx), {RoleName, RoleIcon, RoleCount, RoleCountVisible, RoleRunning});
225+ Q_EMIT dataChanged(index(idx), index(idx), {RoleName, RoleIcon, RoleCount, RoleCountVisible, RoleRunning, RoleProgress});
216226 }
217227 break;
218228 }
@@ -250,15 +260,17 @@ void LauncherModel::refresh()
250260 }
251261
252262 if (itemIndex == -1) {
263+ QVariantMap cachedMap = entry.toMap();
253264 // Need to add it. Just add it into the addedIndex to keep same ordering as the list in AS.
254− LauncherItem *item = new LauncherItem(entry.toMap().value("id").toString(),
255− entry.toMap().value("name").toString(),
256− entry.toMap().value("icon").toString(),
265+ LauncherItem *item = new LauncherItem(cachedMap.value("id").toString(),
266+ cachedMap.value("name").toString(),
267+ cachedMap.value("icon").toString(),
257268 this);
258269 item->setPinned(true);
259− item->setCount(entry.toMap().value("count").toInt());
260− item->setCountVisible(entry.toMap().value("countVisible").toBool());
261− item->setRunning(entry.toMap().value("running").toBool());
270+ item->setCount(cachedMap.value("count").toInt());
271+ item->setCountVisible(cachedMap.value("countVisible").toBool());
272+ item->setProgress(cachedMap.value("progress").toInt());
273+ item->setRunning(cachedMap.value("running").toBool());
262274 beginInsertRows(QModelIndex(), newPosition, newPosition);
263275 m_list.insert(newPosition, item);
264276 endInsertRows();
plugins/Greeter/Unity/Launcher/launchermodelas.h+1
@@ -41,6 +41,7 @@ public:
4141
4242 QVariant data(const QModelIndex &index, int role) const override;
4343
44+ Q_INVOKABLE void setAlerting(const QString &appId, bool alerting) override;
4445 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const override;
4546 Q_INVOKABLE void move(int oldIndex, int newIndex) override;
4647 Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
plugins/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
22pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
33pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
44
plugins/Unity/Launcher/asadapter.cpp+1
@@ -57,5 +57,6 @@ QVariantMap ASAdapter::itemToVariant(LauncherItem *item) const
5757 details.insert("countVisible", item->countVisible());
5858 details.insert("pinned", item->pinned());
5959 details.insert("running", item->running());
60+ details.insert("progress", item->progress());
6061 return details;
6162}
plugins/Unity/Launcher/dbusinterface.cpp+39−13
@@ -62,6 +62,8 @@ QString DBusInterface::introspect(const QString &path) const
6262 "<interface name=\"com.canonical.Unity.Launcher.Item\">"
6363 "<property name=\"count\" type=\"i\" access=\"readwrite\" />"
6464 "<property name=\"countVisible\" type=\"b\" access=\"readwrite\" />"
65+ "<property name=\"progress\" type=\"i\" access=\"readwrite\" />"
66+ "<method name=\"Alert\" />"
6567 "</interface>";
6668 return nodeiface;
6769}
@@ -122,6 +124,20 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
122124 return false;
123125 }
124126
127+ /* Break down the path to just the app id */
128+ bool validpath = true;
129+ QString pathtemp = message.path();
130+ if (!pathtemp.startsWith("/com/canonical/Unity/Launcher/")) {
131+ validpath = false;
132+ }
133+ pathtemp.remove("/com/canonical/Unity/Launcher/");
134+ if (pathtemp.indexOf('/') >= 0) {
135+ validpath = false;
136+ }
137+
138+ /* Find ourselves an appid */
139+ QString appid = decodeAppId(pathtemp);
140+
125141 // First handle methods of the Launcher interface
126142 if (message.interface() == "com.canonical.Unity.Launcher") {
127143 if (message.member() == "Refresh") {
@@ -129,6 +145,13 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
129145 Q_EMIT refreshCalled();
130146 return connection.send(reply);
131147 }
148+ } else if (message.interface() == "com.canonical.Unity.Launcher.Item") {
149+ // Handle methods of the Launcher-Item interface
150+ if (message.member() == "Alert" && validpath) {
151+ QDBusMessage reply = message.createReply();
152+ Q_EMIT alertCalled(appid);
153+ return connection.send(reply);
154+ }
132155 }
133156
134157 // Now handle dynamic properties (for launcher emblems)
@@ -140,50 +163,53 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
140163 return false;
141164 }
142165
143− /* Break down the path to just the app id */
144− QString pathtemp = message.path();
145− if (!pathtemp.startsWith("/com/canonical/Unity/Launcher/")) {
146− return false;
147− }
148− pathtemp.remove("/com/canonical/Unity/Launcher/");
149− if (pathtemp.indexOf('/') >= 0) {
166+ if (!validpath) {
150167 return false;
151168 }
152169
153− /* Find ourselves an appid */
154− QString appid = decodeAppId(pathtemp);
155170 int index = m_launcherModel->findApplication(appid);
156171 LauncherItem *item = static_cast<LauncherItem*>(m_launcherModel->get(index));
157172
158173 QVariantList retval;
159174 if (message.member() == "Get") {
175+ QString cachedString = message.arguments()[1].toString();
160176 if (!item) {
161177 return false;
162178 }
163− if (message.arguments()[1].toString() == "count") {
179+ if (cachedString == "count") {
164180 retval.append(QVariant::fromValue(QDBusVariant(item->count())));
165− } else if (message.arguments()[1].toString() == "countVisible") {
181+ } else if (cachedString == "countVisible") {
166182 retval.append(QVariant::fromValue(QDBusVariant(item->countVisible())));
183+ } else if (cachedString == "progress") {
184+ retval.append(QVariant::fromValue(QDBusVariant(item->progress())));
167185 }
168186 } else if (message.member() == "Set") {
169− if (message.arguments()[1].toString() == "count") {
187+ QString cachedString = message.arguments()[1].toString();
188+ if (cachedString == "count") {
170189 int newCount = message.arguments()[2].value<QDBusVariant>().variant().toInt();
171190 if (!item || newCount != item->count()) {
172191 Q_EMIT countChanged(appid, newCount);
173192 notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "count", QVariant(newCount));
174193 }
175− } else if (message.arguments()[1].toString() == "countVisible") {
194+ } else if (cachedString == "countVisible") {
176195 bool newVisible = message.arguments()[2].value<QDBusVariant>().variant().toBool();
177196 if (!item || newVisible != item->countVisible()) {
178197 Q_EMIT countVisibleChanged(appid, newVisible);
179198 notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "countVisible", newVisible);
180199 }
200+ } else if (cachedString == "progress") {
201+ int newProgress = message.arguments()[2].value<QDBusVariant>().variant().toInt();
202+ if (!item || newProgress != item->progress()) {
203+ Q_EMIT progressChanged(appid, newProgress);
204+ notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "progress", QVariant(newProgress));
205+ }
181206 }
182207 } else if (message.member() == "GetAll") {
183208 if (item) {
184209 QVariantMap all;
185210 all.insert("count", item->count());
186211 all.insert("countVisible", item->countVisible());
212+ all.insert("progress", item->progress());
187213 retval.append(all);
188214 }
189215 } else {
plugins/Unity/Launcher/dbusinterface.h+2
@@ -36,7 +36,9 @@ public:
3636Q_SIGNALS:
3737 void countChanged(const QString &appId, int count);
3838 void countVisibleChanged(const QString &appId, bool countVisible);
39+ void progressChanged(const QString &appId, int progress);
3940 void refreshCalled();
41+ void alertCalled(const QString &appId);
4042
4143private:
4244 static QString decodeAppId(const QString& path);
plugins/Unity/Launcher/launcheritem.cpp+23
@@ -34,6 +34,7 @@ LauncherItem::LauncherItem(const QString &appId, const QString &name, const QStr
3434 m_count(0),
3535 m_countVisible(false),
3636 m_focused(false),
37+ m_alerting(false),
3738 m_quickList(new QuickListModel(this))
3839{
3940 QuickListEntry nameAction;
@@ -160,6 +161,9 @@ void LauncherItem::setCount(int count)
160161 if (m_count != count) {
161162 m_count = count;
162163 Q_EMIT countChanged(count);
164+ if (m_countVisible) {
165+ setAlerting(true);
166+ }
163167 }
164168}
165169
@@ -173,6 +177,9 @@ void LauncherItem::setCountVisible(bool countVisible)
173177 if (m_countVisible != countVisible) {
174178 m_countVisible = countVisible;
175179 Q_EMIT countVisibleChanged(countVisible);
180+ if (countVisible) {
181+ setAlerting(true);
182+ }
176183 }
177184}
178185
@@ -185,10 +192,26 @@ void LauncherItem::setFocused(bool focused)
185192{
186193 if (m_focused != focused) {
187194 m_focused = focused;
195+ if (focused) {
196+ setAlerting(false);
197+ }
188198 Q_EMIT focusedChanged(focused);
189199 }
190200}
191201
202+bool LauncherItem::alerting() const
203+{
204+ return m_alerting;
205+}
206+
207+void LauncherItem::setAlerting(bool alerting)
208+{
209+ if (m_alerting != alerting) {
210+ m_alerting = alerting;
211+ Q_EMIT alertingChanged(alerting);
212+ }
213+}
214+
192215unity::shell::launcher::QuickListModelInterface *LauncherItem::quickList() const
193216{
194217 return m_quickList;
plugins/Unity/Launcher/launcheritem.h+3
@@ -44,6 +44,7 @@ public:
4444 int count() const override;
4545 bool countVisible() const override;
4646 bool focused() const override;
47+ bool alerting() const override;
4748
4849 unity::shell::launcher::QuickListModelInterface *quickList() const override;
4950
@@ -57,6 +58,7 @@ private:
5758 void setCount(int count);
5859 void setCountVisible(bool countVisible);
5960 void setFocused(bool focused);
61+ void setAlerting(bool alerting);
6062
6163private:
6264 QString m_appId;
@@ -69,6 +71,7 @@ private:
6971 int m_count;
7072 bool m_countVisible;
7173 bool m_focused;
74+ bool m_alerting;
7275 QuickListModel *m_quickList;
7376 QuickListEntry m_quitAction;
7477
plugins/Unity/Launcher/launchermodel.cpp+32
@@ -40,7 +40,9 @@ LauncherModel::LauncherModel(QObject *parent):
4040{
4141 connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged);
4242 connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged);
43+ connect(m_dbusIface, &DBusInterface::progressChanged, this, &LauncherModel::progressChanged);
4344 connect(m_dbusIface, &DBusInterface::refreshCalled, this, &LauncherModel::refresh);
45+ connect(m_dbusIface, &DBusInterface::alertCalled, this, &LauncherModel::alert);
4446
4547 connect(m_settings, &GSettings::changed, this, &LauncherModel::refresh);
4648
@@ -82,6 +84,8 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
8284 return item->progress();
8385 case RoleFocused:
8486 return item->focused();
87+ case RoleAlerting:
88+ return item->alerting();
8589 case RoleRunning:
8690 return item->running();
8791 default:
@@ -92,6 +96,18 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
9296 return QVariant();
9397}
9498
99+void LauncherModel::setAlerting(const QString &appId, bool alerting) {
100+ int index = findApplication(appId);
101+ if (index >= 0) {
102+ QModelIndex modelIndex = this->index(index);
103+ LauncherItem *item = m_list.at(index);
104+ if (!item->focused()) {
105+ item->setAlerting(alerting);
106+ Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RoleAlerting);
107+ }
108+ }
109+}
110+
95111unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) const
96112{
97113 if (index < 0 || index >= m_list.count()) {
@@ -342,6 +358,9 @@ void LauncherModel::countChanged(const QString &appId, int count)
342358 if (idx >= 0) {
343359 LauncherItem *item = m_list.at(idx);
344360 item->setCount(count);
361+ if (item->countVisible()) {
362+ setAlerting(item->appId(), true);
363+ }
345364 m_asAdapter->syncItems(m_list);
346365 Q_EMIT dataChanged(index(idx), index(idx), {RoleCount});
347366 }
@@ -353,6 +372,9 @@ void LauncherModel::countVisibleChanged(const QString &appId, int countVisible)
353372 if (idx >= 0) {
354373 LauncherItem *item = m_list.at(idx);
355374 item->setCountVisible(countVisible);
375+ if (countVisible) {
376+ setAlerting(item->appId(), true);
377+ }
356378 Q_EMIT dataChanged(index(idx), index(idx), {RoleCountVisible});
357379
358380 // If countVisible goes to false, and the item is neither pinned nor recent we can drop it
@@ -462,6 +484,16 @@ void LauncherModel::refresh()
462484 m_asAdapter->syncItems(m_list);
463485}
464486
487+void LauncherModel::alert(const QString &appId)
488+{
489+ int idx = findApplication(appId);
490+ if (idx >= 0) {
491+ LauncherItem *item = m_list.at(idx);
492+ setAlerting(item->appId(), true);
493+ Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleAlerting);
494+ }
495+}
496+
465497void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
466498{
467499 Q_UNUSED(parent);
plugins/Unity/Launcher/launchermodel.h+2
@@ -45,6 +45,7 @@ public:
4545
4646 QVariant data(const QModelIndex &index, int role) const override;
4747
48+ Q_INVOKABLE void setAlerting(const QString &appId, bool alerting) override;
4849 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const override;
4950 Q_INVOKABLE void move(int oldIndex, int newIndex) override;
5051 Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
@@ -63,6 +64,7 @@ public:
6364public Q_SLOTS:
6465 void requestRemove(const QString &appId) override;
6566 Q_INVOKABLE void refresh();
67+ Q_INVOKABLE void alert(const QString &appId);
6668
6769private:
6870 void storeAppList();
qml/Launcher/LauncherDelegate.qml+101−15
@@ -15,7 +15,7 @@
1515 */
1616
1717import QtQuick 2.0
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
1919
2020Item {
2121 id: root
@@ -27,6 +27,8 @@ Item {
2727 property bool itemFocused: false
2828 property real maxAngle: 0
2929 property bool inverted: false
30+ property bool alerting: false
31+ readonly property alias wiggling: wiggleAnim.running
3032
3133 readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * itemHeight
3234 readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * itemHeight
@@ -39,6 +41,82 @@ Item {
3941 property real offset: 0
4042 property real itemOpacity: 1
4143 property real brightness: 0
44+ property double maxWiggleAngle: 5.0
45+
46+ QtObject {
47+ id: priv
48+
49+ readonly property int wiggleDuration: UbuntuAnimation.SnapDuration
50+ property real wiggleAngle: 0
51+ }
52+
53+ SequentialAnimation {
54+ id: wiggleAnim
55+
56+ running: alerting
57+ loops: 1
58+ alwaysRunToEnd: true
59+
60+ NumberAnimation {
61+ target: priv
62+ property: "wiggleAngle"
63+ from: 0
64+ to: maxWiggleAngle
65+ duration: priv.wiggleDuration
66+ easing.type: Easing.InQuad
67+ }
68+
69+ NumberAnimation {
70+ target: priv
71+ property: "wiggleAngle"
72+ from: maxWiggleAngle
73+ to: -maxWiggleAngle
74+ duration: priv.wiggleDuration
75+ easing.type: Easing.InOutQuad
76+ }
77+
78+ NumberAnimation {
79+ target: priv
80+ property: "wiggleAngle"
81+ from: -maxWiggleAngle
82+ to: maxWiggleAngle
83+ duration: priv.wiggleDuration
84+ easing.type: Easing.InOutQuad
85+ }
86+
87+ NumberAnimation {
88+ target: priv
89+ property: "wiggleAngle"
90+ from: maxWiggleAngle
91+ to: -maxWiggleAngle
92+ duration: priv.wiggleDuration
93+ easing.type: Easing.InOutQuad
94+ }
95+
96+ NumberAnimation {
97+ target: priv
98+ property: "wiggleAngle"
99+ from: -maxWiggleAngle
100+ to: maxWiggleAngle
101+ duration: priv.wiggleDuration
102+ easing.type: Easing.InOutQuad
103+ }
104+
105+ NumberAnimation {
106+ target: priv
107+ property: "wiggleAngle"
108+ from: maxWiggleAngle
109+ to: 0
110+ duration: priv.wiggleDuration
111+ easing.type: Easing.OutQuad
112+ }
113+
114+ UbuntuNumberAnimation {
115+ target: root
116+ property: "alerting"
117+ to: 0
118+ }
119+ }
42120
43121 Item {
44122 id: iconItem
@@ -75,7 +153,7 @@ Item {
75153 objectName: "countEmblem"
76154 anchors {
77155 right: parent.right
78− top: parent.top
156+ bottom: parent.bottom
79157 margins: units.dp(3)
80158 }
81159 width: Math.min(root.itemWidth, Math.max(units.gu(2), countLabel.implicitWidth + units.gu(1)))
@@ -100,23 +178,21 @@ Item {
100178 }
101179 }
102180
103− BorderImage {
181+ UbuntuShape {
104182 id: progressOverlay
105183 objectName: "progressOverlay"
184+
106185 anchors {
107186 left: iconItem.left
108187 right: iconItem.right
109− bottom: iconItem.bottom
110− leftMargin: units.gu(1)
111− rightMargin: units.gu(1)
112− bottomMargin: units.gu(1)
188+ verticalCenter: parent.verticalCenter
189+ leftMargin: units.gu(1.5)
190+ rightMargin: units.gu(1.5)
113191 }
114− height: units.gu(1.5)
192+ height: units.gu(1)
115193 visible: root.progress > -1
116− source: "graphics/progressbar-trough.sci"
117−
118− // For fill calculation we need to remove the 2 units of border defined in .sci file
119− property int adjustedWidth: width - units.gu(2)
194+ color: UbuntuColors.darkGrey
195+ borderSource: "none"
120196
121197 Item {
122198 anchors {
@@ -124,20 +200,22 @@ Item {
124200 top: parent.top
125201 bottom: parent.bottom
126202 }
127− width: Math.min(100, root.progress) / 100 * parent.adjustedWidth + units.gu(1)
203+ width: Math.min(100, root.progress) / 100 * parent.width
128204 clip: true
129205
130− BorderImage {
206+ UbuntuShape {
131207 anchors {
132208 left: parent.left
133209 top: parent.top
134210 bottom: parent.bottom
135211 }
212+ color: "white"
213+ borderSource: "none"
136214 width: progressOverlay.width
137− source: "graphics/progressbar-fill.sci"
138215 }
139216 }
140217 }
218+
141219 Image {
142220 objectName: "focusedHighlight"
143221 anchors {
@@ -167,6 +245,14 @@ Item {
167245 }
168246
169247 transform: [
248+ // The rotation about the icon's center/z-axis for the wiggle
249+ // needs to happen here too, because there's no other way to
250+ // align the wiggle with the icon-folding otherwise
251+ Rotation {
252+ axis { x: 0; y: 0; z: 1 }
253+ origin { x: iconItem.width / 2; y: iconItem.height / 2; z: 0 }
254+ angle: priv.wiggleAngle
255+ },
170256 // Rotating 3 times at top/bottom because that increases the perspective.
171257 // This is a hack, but as QML does not support real 3D coordinates
172258 // getting a higher perspective can only be done by a hack. This is the most
qml/Launcher/LauncherPanel.qml+71
@@ -86,6 +86,7 @@ Rectangle {
8686 height: parent.height - dashItem.height - parent.spacing*2
8787
8888 Item {
89+ id: launcherListViewItem
8990 anchors.fill: parent
9091 clip: true
9192
@@ -109,12 +110,21 @@ Rectangle {
109110 preferredHighlightBegin: (height - itemHeight) / 2
110111 preferredHighlightEnd: (height + itemHeight) / 2
111112
113+ // for the single peeking icon, when alert-state is set on delegate
114+ property int peekingIndex: -1
115+
112116 // The size of the area the ListView is extended to make sure items are not
113117 // destroyed when dragging them outside the list. This needs to be at least
114118 // itemHeight to prevent folded items from disappearing and DragArea limits
115119 // need to be smaller than this size to avoid breakage.
116120 property int extensionSize: 0
117121
122+ // These properties are needed to facilitate implicitly unfolding the
123+ // launcher to the peeking icon, if it is not already fully flat
124+ // and in view for the user
125+ property real moveToIndexYFrom
126+ property real moveToIndexYTo
127+
118128 // Setting extensionSize after the list has been populated because it has
119129 // the potential to mess up with the intial positioning in combination
120130 // with snapping to the center of the list. This catches all the cases
@@ -128,6 +138,14 @@ Rectangle {
128138 flick(0, clickFlickSpeed)
129139 }
130140
141+ UbuntuNumberAnimation {
142+ id: moveToIndexAnimation
143+ target: launcherListView
144+ property: "contentY"
145+ from: launcherListView.moveToIndexYFrom
146+ to: launcherListView.moveToIndexYTo
147+ }
148+
131149 // The height of the area where icons start getting folded
132150 property int foldingStartHeight: units.gu(6.5)
133151 // The height of the area where the items reach the final folding angle
@@ -180,10 +198,63 @@ Rectangle {
180198 progress: model.progress
181199 itemFocused: model.focused
182200 inverted: root.inverted
201+ alerting: model.alerting
183202 z: -Math.abs(offset)
184203 maxAngle: 55
185204 property bool dragging: false
186205
206+ SequentialAnimation {
207+ id: peekingAnimation
208+
209+ // revealing
210+ PropertyAction { target: root; property: "visible"; value: (launcher.visibleWidth === 0) ? 1 : 0 }
211+ PropertyAction { target: launcherListViewItem; property: "clip"; value: 0 }
212+
213+ UbuntuNumberAnimation {
214+ target: launcherDelegate
215+ alwaysRunToEnd: true
216+ loops: 1
217+ properties: "x"
218+ to: (units.gu(.5) + launcherListView.width * .5) * (root.inverted ? -1 : 1)
219+ duration: UbuntuAnimation.BriskDuration
220+ }
221+
222+ // hiding
223+ UbuntuNumberAnimation {
224+ target: launcherDelegate
225+ alwaysRunToEnd: true
226+ loops: 1
227+ properties: "x"
228+ to: 0
229+ duration: UbuntuAnimation.BriskDuration
230+ }
231+
232+ PropertyAction { target: launcherListViewItem; property: "clip"; value: 1 }
233+ PropertyAction { target: root; property: "visible"; value: (launcher.visibleWidth === 0) ? 0 : 1 }
234+ }
235+
236+ onAlertingChanged: {
237+ if(alerting) {
238+ if (!dragging && (launcherListView.peekingIndex === -1 || launcher.visibleWidth > 0)) {
239+ launcherListView.moveToIndexYFrom = launcherListView.contentY
240+ launcherListView.positionViewAtIndex(index, ListView.Center)
241+ launcherListView.moveToIndexYTo = launcherListView.contentY
242+ moveToIndexAnimation.start()
243+ if (!dragging && launcher.state !== "visible") {
244+ peekingAnimation.start()
245+ }
246+ }
247+
248+ if (launcherListView.peekingIndex === -1) {
249+ launcherListView.peekingIndex = index
250+ }
251+ } else {
252+ if (launcherListView.peekingIndex === index) {
253+ launcherListView.peekingIndex = -1
254+ }
255+ }
256+ }
257+
187258 ThinDivider {
188259 id: dropIndicator
189260 objectName: "dropIndicator"
qml/Launcher/graphics/progressbar-fill@30.pngdeleted

Binary file not shown.

qml/Launcher/graphics/progressbar-fill@30.scideleted−5
@@ -1,5 +0,0 @@
1−border.left: 36
2−border.top: 16
3−border.bottom: 28
4−border.right: 36
5−source: progressbar-fill@30.png
qml/Launcher/graphics/progressbar-trough@30.pngdeleted

Binary file not shown.

qml/Launcher/graphics/progressbar-trough@30.scideleted−5
@@ -1,5 +0,0 @@
1−border.left: 36
2−border.top: 16
3−border.bottom: 26
4−border.right: 36
5−source: progressbar-trough@30.png
tests/mocks/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
22
33include_directories(
44 ${CMAKE_CURRENT_SOURCE_DIR}
tests/mocks/Unity/Launcher/MockLauncherItem.cpp+19
@@ -37,6 +37,7 @@ MockLauncherItem::MockLauncherItem(const QString &appId, const QString& desktopF
3737 m_count(0),
3838 m_countVisible(false),
3939 m_focused(false),
40+ m_alerting(false),
4041 m_quickList(new MockQuickListModel(this))
4142{
4243}
@@ -132,6 +133,9 @@ void MockLauncherItem::setCount(int count)
132133 {
133134 m_count = count;
134135 Q_EMIT countChanged(count);
136+ if (m_countVisible) {
137+ setAlerting(true);
138+ }
135139 }
136140}
137141
@@ -145,6 +149,9 @@ void MockLauncherItem::setCountVisible(bool countVisible)
145149 if (m_countVisible != countVisible) {
146150 m_countVisible = countVisible;
147151 Q_EMIT countVisibleChanged(countVisible);
152+ if (countVisible) {
153+ setAlerting(true);
154+ }
148155 }
149156}
150157
@@ -162,6 +169,18 @@ void MockLauncherItem::setFocused(bool focused)
162169 }
163170}
164171
172+bool MockLauncherItem::alerting() const
173+{
174+ return m_alerting;
175+}
176+
177+void MockLauncherItem::setAlerting(bool alerting)
178+{
179+ if (m_alerting != alerting) {
180+ m_alerting = alerting;
181+ Q_EMIT alertingChanged(alerting);
182+ }
183+}
165184
166185unity::shell::launcher::QuickListModelInterface *MockLauncherItem::quickList() const
167186{
tests/mocks/Unity/Launcher/MockLauncherItem.h+3
@@ -45,6 +45,7 @@ public:
4545 int count() const override;
4646 bool countVisible() const override;
4747 bool focused() const override;
48+ bool alerting() const override;
4849
4950 unity::shell::launcher::QuickListModelInterface *quickList() const override;
5051
@@ -56,6 +57,7 @@ private:
5657 void setCount(int count);
5758 void setCountVisible(bool countVisible);
5859 void setFocused(bool focused);
60+ void setAlerting(bool alerting);
5961
6062 QString m_appId;
6163 QString m_desktopFile;
@@ -68,6 +70,7 @@ private:
6870 int m_count;
6971 bool m_countVisible;
7072 bool m_focused;
73+ bool m_alerting;
7174 MockQuickListModel *m_quickList;
7275
7376 friend class MockLauncherModel;
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+50
@@ -34,6 +34,7 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
3434 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
3535 item->setProgress(50);
3636 item->setCountVisible(true);
37+ item->setAlerting(false);
3738 m_list.append(item);
3839 item = new MockLauncherItem("music-app", "/usr/share/applications/music-app.desktop", "Music", "soundcloud", this);
3940 m_list.append(item);
@@ -44,25 +45,30 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
4445 item->setCount(1);
4546 item->setCountVisible(true);
4647 item->setRunning(true);
48+ item->setAlerting(false);
4749 m_list.append(item);
4850 item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
4951 item->setCount(12);
5052 item->setCountVisible(true);
53+ item->setAlerting(false);
5154 item->setPinned(true);
5255 m_list.append(item);
5356 item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
5457 item->setCount(123);
5558 item->setCountVisible(true);
59+ item->setAlerting(false);
5660 m_list.append(item);
5761 item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
5862 item->setCount(1234567890);
5963 item->setCountVisible(true);
64+ item->setAlerting(false);
6065 item->setPinned(true);
6166 m_list.append(item);
6267 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
6368 item->setProgress(50);
6469 item->setCount(5);
6570 item->setCountVisible(true);
71+ item->setAlerting(false);
6672 item->setFocused(true);
6773 item->setPinned(true);
6874 m_list.append(item);
@@ -106,6 +112,8 @@ QVariant MockLauncherModel::data(const QModelIndex& index, int role) const
106112 return item->countVisible();
107113 case RoleFocused:
108114 return item->focused();
115+ case RoleAlerting:
116+ return item->alerting();
109117 }
110118
111119 return QVariant();
@@ -120,6 +128,18 @@ unity::shell::launcher::LauncherItemInterface *MockLauncherModel::get(int index)
120128 return m_list.at(index);
121129}
122130
131+void MockLauncherModel::setAlerting(const QString &appId, bool alerting) {
132+ int index = findApp(appId);
133+ if (index >= 0) {
134+ QModelIndex modelIndex = this->index(index);
135+ MockLauncherItem *item = m_list.at(index);
136+ if (!item->focused()) {
137+ item->setAlerting(alerting);
138+ Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RoleAlerting);
139+ }
140+ }
141+}
142+
123143void MockLauncherModel::move(int oldIndex, int newIndex)
124144{
125145 // Make sure its not moved outside the lists
@@ -198,6 +218,16 @@ int MockLauncherModel::findApp(const QString &appId)
198218 return -1;
199219}
200220
221+void MockLauncherModel::setProgress(const QString &appId, int progress)
222+{
223+ int index = findApp(appId);
224+ if (index >= 0) {
225+ m_list.at(index)->setProgress(progress);
226+ QModelIndex modelIndex = this->index(index);
227+ Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RoleProgress);
228+ }
229+}
230+
201231void MockLauncherModel::setUser(const QString &username)
202232{
203233 Q_UNUSED(username)
@@ -229,6 +259,26 @@ void MockLauncherModel::emitHint()
229259 Q_EMIT hint();
230260}
231261
262+void MockLauncherModel::setCount(const QString &appId, int count)
263+{
264+ int index = findApp(appId);
265+ if (index >= 0) {
266+ m_list.at(index)->setCount(count);
267+ QModelIndex modelIndex = this->index(index);
268+ Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RoleCount);
269+ }
270+}
271+
272+void MockLauncherModel::setCountVisible(const QString &appId, bool countVisible)
273+{
274+ int index = findApp(appId);
275+ if (index >= 0) {
276+ m_list.at(index)->setCountVisible(countVisible);
277+ QModelIndex modelIndex = this->index(index);
278+ Q_EMIT dataChanged(modelIndex, modelIndex);
279+ }
280+}
281+
232282unity::shell::application::ApplicationManagerInterface *MockLauncherModel::applicationManager() const
233283{
234284 return nullptr;
tests/mocks/Unity/Launcher/MockLauncherModel.h+4
@@ -38,6 +38,7 @@ public:
3838
3939 QVariant data(const QModelIndex& index, int role) const override;
4040
41+ Q_INVOKABLE void setAlerting(const QString &appId, bool alerting) override;
4142 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface *get(int index) const override;
4243 Q_INVOKABLE void move(int oldIndex, int newIndex) override;
4344 Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
@@ -54,6 +55,9 @@ public:
5455
5556 // For testing
5657 Q_INVOKABLE void emitHint();
58+ Q_INVOKABLE void setProgress(const QString &appId, int progress);
59+ Q_INVOKABLE void setCount(const QString &appId, int count);
60+ Q_INVOKABLE void setCountVisible(const QString &appId, bool countVisible);
5761
5862Q_SIGNALS:
5963 void quickListTriggered(const QString &appId, int index);
tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
22pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
33
44include_directories(
tests/plugins/Greeter/Unity/Launcher/launchermodelastest.cpp+3
@@ -43,6 +43,7 @@ private Q_SLOTS:
4343 item.insert("name", "Item 1");
4444 item.insert("icon", "fake.svg");
4545 item.insert("count", 0);
46+ item.insert("progress", 0);
4647 item.insert("countVisible", false);
4748 item.insert("pinned", true);
4849 list.append(item);
@@ -67,6 +68,7 @@ private Q_SLOTS:
6768 inSync &= model->get(i)->name() == list.at(i).value("name").toString();
6869 inSync &= model->get(i)->icon() == list.at(i).value("icon").toString();
6970 inSync &= model->get(i)->count() == list.at(i).value("count").toInt();
71+ inSync &= model->get(i)->progress() == list.at(i).value("progress").toInt();
7072 inSync &= model->get(i)->countVisible() == list.at(i).value("countVisible").toBool();
7173 }
7274 return inSync;
@@ -111,6 +113,7 @@ private Q_SLOTS:
111113 newEntry.insert("name", "New app");
112114 newEntry.insert("icon", "some-icon.svg");
113115 newEntry.insert("count", 0);
116+ newEntry.insert("progress", 42);
114117 newEntry.insert("countVisible", false);
115118 newEntry.insert("pinned", true);
116119 newList.append(newEntry);
tests/plugins/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,5 +1,5 @@
11pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
33
44include_directories(
55 ${CMAKE_CURRENT_SOURCE_DIR}
tests/plugins/Unity/Launcher/launchermodeltest.cpp+23−7
@@ -409,11 +409,18 @@ private Q_SLOTS:
409409 }
410410
411411 void testCountEmblems() {
412+ QSignalSpy spy(launcherModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)));
413+
412414 // Call GetAll on abs-icon
413415 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "org.freedesktop.DBus.Properties");
414416 QDBusReply<QVariantMap> reply = interface.call("GetAll");
415417 QVariantMap map = reply.value();
416418
419+ // Check that the alerting-status is still false, and the item on the upper side of the API
420+ int index = launcherModel->findApplication("abs-icon");
421+ QCOMPARE(index >= 0, true);
422+ QVERIFY(launcherModel->get(index)->alerting() == false);
423+
417424 // Make sure GetAll returns a map with count and countVisible props
418425 QCOMPARE(map.contains("count"), true);
419426 QCOMPARE(map.contains("countVisible"), true);
@@ -434,13 +441,8 @@ private Q_SLOTS:
434441 QCOMPARE(map.value("count").toInt(), 55);
435442 QCOMPARE(map.value("countVisible").toBool(), true);
436443
437− // Now the item on the upper side of the API
438− int index = launcherModel->findApplication("abs-icon");
439− QCOMPARE(index >= 0, true);
440−
441− // And make sure values have changed there as well
442− QCOMPARE(launcherModel->get(index)->countVisible(), true);
443− QCOMPARE(launcherModel->get(index)->count(), 55);
444+ // Finally check, that the change to "count" implicitly also set the alerting-state to true
445+ QVERIFY(launcherModel->get(index)->alerting() == true);
444446 }
445447
446448 void testCountEmblemAddsRemovesItem_data() {
@@ -500,6 +502,20 @@ private Q_SLOTS:
500502 QCOMPARE(index == -1, !isRunning && !isPinned && !startWhenVisible);
501503 }
502504
505+ void testAlert() {
506+ // Check that the alerting-status is still false
507+ int index = launcherModel->findApplication("abs-icon");
508+ QCOMPARE(index >= 0, true);
509+ QVERIFY(launcherModel->get(index)->alerting() == false);
510+
511+ // Call Alert() on "abs-icon"
512+ QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "com.canonical.Unity.Launcher.Item");
513+ interface.call("Alert");
514+
515+ // Check that the alerting-status is now true
516+ QVERIFY(launcherModel->get(index)->alerting() == true);
517+ }
518+
503519 void testRefreshAfterDeletedDesktopFiles_data() {
504520 QTest::addColumn<bool>("deleted");
505521 QTest::newRow("have .desktop files") << false;
tests/qmltests/Launcher/tst_Launcher.qml+181
@@ -93,6 +93,57 @@ Item {
9393 onClicked: launcherLoader.item.inverted = !launcherLoader.item.inverted
9494 Layout.fillWidth: true
9595 }
96+
97+ Row {
98+ spacing: units.gu(1)
99+
100+ Button {
101+ text: "35% bar"
102+ onClicked: LauncherModel.setProgress(LauncherModel.get(parseInt(appIdEntryBar.displayText)).appId, 35)
103+ Layout.fillWidth: true
104+ }
105+
106+ TextArea {
107+ id: appIdEntryBar
108+ anchors.verticalCenter: parent.verticalCenter
109+ width: units.gu(4)
110+ height: units.gu(4)
111+ autoSize: true
112+ text: "2"
113+ maximumLineCount: 1
114+ }
115+
116+ Button {
117+ text: "no bar"
118+ onClicked: LauncherModel.setProgress(LauncherModel.get(parseInt(appIdEntryBar.displayText)).appId, -1)
119+ Layout.fillWidth: true
120+ }
121+ }
122+
123+ Row {
124+ spacing: units.gu(1)
125+
126+ Button {
127+ text: "set alert"
128+ onClicked: LauncherModel.setAlerting(LauncherModel.get(parseInt(appIdEntryAlert.displayText)).appId, true)
129+ }
130+
131+ TextArea {
132+ id: appIdEntryAlert
133+ anchors.verticalCenter: parent.verticalCenter
134+ width: units.gu(5)
135+ height: units.gu(4)
136+ autoSize: true
137+ text: "2"
138+ maximumLineCount: 1
139+ Layout.fillWidth: true
140+ }
141+
142+ Button {
143+ text: "unset alert"
144+ onClicked: LauncherModel.setAlerting(LauncherModel.get(parseInt(appIdEntryAlert.displayText)).appId, false)
145+ }
146+ }
96147 }
97148
98149 SignalSpy {
@@ -169,6 +220,16 @@ Item {
169220 tryCompare(panel, "x", -panel.width, 1000);
170221 }
171222
223+ function waitForWiggleToStart(appIcon) {
224+ verify(appIcon != undefined)
225+ tryCompare(appIcon, "wiggling", true, 1000, "wiggle-anim should not be in stopped state")
226+ }
227+
228+ function waitForWiggleToStop(appIcon) {
229+ verify(appIcon != undefined)
230+ tryCompare(appIcon, "wiggling", false, 1000, "wiggle-anim should not be in running state")
231+ }
232+
172233 function positionLauncherListAtBeginning() {
173234 var listView = testCase.findChild(launcherLoader.item, "launcherListView");
174235 listView.contentY = -listView.topMargin;
@@ -658,5 +719,125 @@ Item {
658719 tryCompare(launcher, "state", "", 1000, "Launcher didn't hide after moving mouse away from it");
659720 waitUntilLauncherDisappears();
660721 }
722+
723+ function test_progressChangeViaModel() {
724+ dragLauncherIntoView();
725+ var item = findChild(launcher, "launcherDelegate0")
726+ verify(item != undefined)
727+ LauncherModel.setProgress(LauncherModel.get(0).appId, -1)
728+ compare(findChild(item, "progressOverlay").visible, false)
729+ LauncherModel.setProgress(LauncherModel.get(0).appId, 20)
730+ compare(findChild(item, "progressOverlay").visible, true)
731+ LauncherModel.setProgress(LauncherModel.get(0).appId, 0)
732+ }
733+
734+ function test_alertPeekingIcon() {
735+ var listView = findChild(launcher, "launcherListView")
736+ verify(listView != undefined)
737+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
738+ tryCompare(listView, "peekingIndex", 5, 1000, "Wrong appId set as peeking-index")
739+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
740+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
741+ }
742+
743+ function test_alertHidingIcon() {
744+ var listView = findChild(launcher, "launcherListView")
745+ verify(listView != undefined)
746+ var appIcon6 = findChild(launcher, "launcherDelegate6")
747+ verify(appIcon6 != undefined)
748+ LauncherModel.setAlerting(LauncherModel.get(6).appId, true)
749+ waitForWiggleToStart(appIcon6)
750+ LauncherModel.setAlerting(LauncherModel.get(6).appId, false)
751+ waitForWiggleToStop(appIcon6)
752+ tryCompare(appIcon6, "x", 0, 1000, "x-value of appId #6 should not be non-zero")
753+ waitForRendering(listView)
754+ }
755+
756+ function test_alertIgnoreFocusedApp() {
757+ LauncherModel.setAlerting(LauncherModel.get(0).appId, true)
758+ compare(LauncherModel.get(0).alerting, false, "Focused app should not have the alert-state set")
759+ }
760+
761+ function test_alertOnlyOnePeekingIcon() {
762+ var listView = findChild(launcher, "launcherListView")
763+ verify(listView != undefined)
764+ LauncherModel.setAlerting(LauncherModel.get(3).appId, true)
765+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
766+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
767+ tryCompare(listView, "peekingIndex", 3, 1000, "Wrong appId set as peeking-index")
768+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
769+ LauncherModel.setAlerting(LauncherModel.get(3).appId, false)
770+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
771+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
772+ waitForRendering(listView)
773+ }
774+
775+ function test_alertMultipleApps() {
776+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
777+ LauncherModel.setAlerting(LauncherModel.get(3).appId, true)
778+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
779+ LauncherModel.setAlerting(LauncherModel.get(7).appId, true)
780+ compare(LauncherModel.get(1).alerting, true, "Alert-state of appId #1 should not be false")
781+ compare(LauncherModel.get(3).alerting, true, "Alert-state of appId #3 should not be false")
782+ compare(LauncherModel.get(5).alerting, true, "Alert-state of appId #5 should not be false")
783+ compare(LauncherModel.get(7).alerting, true, "Alert-state of appId #7 should not be false")
784+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
785+ LauncherModel.setAlerting(LauncherModel.get(3).appId, false)
786+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
787+ LauncherModel.setAlerting(LauncherModel.get(7).appId, false)
788+ compare(LauncherModel.get(1).alerting, false, "Alert-state of appId #1 should not be true")
789+ compare(LauncherModel.get(3).alerting, false, "Alert-state of appId #1 should not be true")
790+ compare(LauncherModel.get(5).alerting, false, "Alert-state of appId #1 should not be true")
791+ compare(LauncherModel.get(7).alerting, false, "Alert-state of appId #1 should not be true")
792+ }
793+
794+ function test_alertMoveIconIntoView() {
795+ dragLauncherIntoView();
796+ var appIcon1 = findChild(launcher, "launcherDelegate1");
797+ var appIcon7 = findChild(launcher, "launcherDelegate7");
798+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
799+ tryCompare(appIcon1, "angle", 0, 1000, "angle of appId #1 should not be non-zero")
800+ waitForWiggleToStart(appIcon1)
801+ LauncherModel.setAlerting(LauncherModel.get(7).appId, true)
802+ tryCompare(appIcon7, "angle", 0, 1000, "angle of appId #7 should not be non-zero")
803+ waitForWiggleToStart(appIcon7)
804+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
805+ waitForWiggleToStop(appIcon1)
806+ LauncherModel.setAlerting(LauncherModel.get(7).appId, false)
807+ waitForWiggleToStop(appIcon7)
808+ }
809+
810+ function test_alertWigglePeekDrag() {
811+ var appIcon5 = findChild(launcher, "launcherDelegate5");
812+ var listView = findChild(launcher, "launcherListView")
813+ verify(listView != undefined)
814+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
815+ tryCompare(listView, "peekingIndex", 5, 1000, "Wrong appId set as peeking-index")
816+ waitForWiggleToStart(appIcon5)
817+ tryCompare(appIcon5, "wiggling", true, 1000, "appId #6 should not be still")
818+ dragLauncherIntoView();
819+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
820+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
821+ waitForWiggleToStop(appIcon5)
822+ tryCompare(appIcon5, "wiggling", false, 1000, "appId #1 should not be wiggling")
823+ }
824+
825+ function test_alertViaCountAndCountVisible() {
826+ dragLauncherIntoView();
827+ var appIcon1 = findChild(launcher, "launcherDelegate1")
828+ var oldCount = LauncherModel.get(1).count
829+ LauncherModel.setCount(LauncherModel.get(1).appId, 42)
830+ tryCompare(appIcon1, "wiggling", false, 1000, "appId #1 should be still")
831+ LauncherModel.setCountVisible(LauncherModel.get(1).appId, 1)
832+ tryCompare(appIcon1, "wiggling", true, 1000, "appId #1 should not be still")
833+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
834+ waitForWiggleToStop(appIcon1)
835+ LauncherModel.setCount(LauncherModel.get(1).appId, 4711)
836+ tryCompare(appIcon1, "wiggling", true, 1000, "appId #1 should not be still")
837+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
838+ waitForWiggleToStop(appIcon1)
839+ LauncherModel.setCountVisible(LauncherModel.get(1).appId, 0)
840+ LauncherModel.setCount(LauncherModel.get(1).appId, oldCount)
841+ }
661842 }
662843}
tests/scripts/READMEnew file+9
@@ -0,0 +1,9 @@
1+This directory holds a few handy shell-scripts to simplify runtime-testing of DBus-APIs (requires qdbus-qt5 to be installed).
2+
3+launcher-icon related:
4+ * list-launcher-icons.sh - get all icons of unity8-launcher
5+ * alert-launcher-icon.sh - trigger alert/wiggle of unfocused launcher-icon
6+ * get-progress.sh - get progress-value of a launcher-icon
7+ * set-progress.sh - set progress-value of a launcher-icon
8+ * set-count.sh - set the count of a launcher-icon
9+ * set-count-visible.sh - set the count-visible flag of a launcher-icon
tests/scripts/alert-launcher-icon.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 com.canonical.Unity.Launcher.Item.Alert
tests/scripts/get-progress.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Get com.canonical.Unity.Launcher.Item progress
tests/scripts/list-launcher-icons.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity | grep Launcher | cut -f6 -d/
tests/scripts/set-count-visible.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Set com.canonical.Unity.Launcher.Item countVisible $2
tests/scripts/set-count.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Set com.canonical.Unity.Launcher.Item count $2
tests/scripts/set-progress.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Set com.canonical.Unity.Launcher.Item progress $2