on xenial help sign in
implement onlyPinned property
191c091Michael Zanetti
debian/control+1−1
@@ -27,7 +27,7 @@ Build-Depends: cmake,
27 libqmenumodel-dev (>= 0.2.9),
27 libqmenumodel-dev (>= 0.2.9),
28 libqt5xmlpatterns5-dev,
28 libqt5xmlpatterns5-dev,
29 libsystemsettings-dev,
29 libsystemsettings-dev,
30− libunity-api-dev (>= 7.94),
30+ libunity-api-dev (>= 7.95),
31 libusermetricsoutput1-dev,
31 libusermetricsoutput1-dev,
32 libxcb1-dev,
32 libxcb1-dev,
33 pkg-config,
33 pkg-config,
plugins/Greeter/Unity/Launcher/launchermodelas.cpp+41−13
@@ -30,7 +30,8 @@ using namespace unity::shell::application;
30
30
31LauncherModel::LauncherModel(QObject *parent):
31LauncherModel::LauncherModel(QObject *parent):
32 LauncherModelInterface(parent),
32 LauncherModelInterface(parent),
33− m_accounts(new AccountsServiceDBusAdaptor(this))
33+ m_accounts(new AccountsServiceDBusAdaptor(this)),
34+ m_onlyPinned(true)
34{
35{
35 connect(m_accounts, &AccountsServiceDBusAdaptor::propertiesChanged, this, &LauncherModel::propertiesChanged);
36 connect(m_accounts, &AccountsServiceDBusAdaptor::propertiesChanged, this, &LauncherModel::propertiesChanged);
36 refresh();
37 refresh();
@@ -162,6 +163,20 @@ void LauncherModel::setApplicationManager(unity::shell::application::Application
162 return;
163 return;
163}
164}
164
165
166+bool LauncherModel::onlyPinned() const
167+{
168+ return m_onlyPinned;
169+}
170+
171+void LauncherModel::setOnlyPinned(bool onlyPinned)
172+{
173+ if (m_onlyPinned != onlyPinned) {
174+ m_onlyPinned = onlyPinned;
175+ Q_EMIT onlyPinnedChanged();
176+ refresh();
177+ }
178+}
179+
165int LauncherModel::findApplication(const QString &appId)
180int LauncherModel::findApplication(const QString &appId)
166{
181{
167 for (int i = 0; i < m_list.count(); ++i) {
182 for (int i = 0; i < m_list.count(); ++i) {
@@ -189,13 +204,17 @@ void LauncherModel::refresh()
189 bool found = false;
204 bool found = false;
190 Q_FOREACH(const QVariant &asItem, items) {
205 Q_FOREACH(const QVariant &asItem, items) {
191 if (asItem.toMap().value("id").toString() == item->appId()) {
206 if (asItem.toMap().value("id").toString() == item->appId()) {
207+ // Only keep and update it if we either show unpinned or it is pinned
208+ if (!m_onlyPinned || asItem.toMap().value("pinned").toBool()) {
192− found = true;
209+ found = true;
193− item->setName(asItem.toMap().value("name").toString());
210+ item->setName(asItem.toMap().value("name").toString());
194− item->setIcon(asItem.toMap().value("icon").toString());
211+ item->setIcon(asItem.toMap().value("icon").toString());
195− item->setCount(asItem.toMap().value("count").toInt());
212+ item->setCount(asItem.toMap().value("count").toInt());
196− item->setCountVisible(asItem.toMap().value("countVisible").toBool());
213+ item->setCountVisible(asItem.toMap().value("countVisible").toBool());
197− int idx = m_list.indexOf(item);
214+ int idx = m_list.indexOf(item);
198− Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleName << RoleIcon);
215+ Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleName << RoleIcon);
216+ }
217+ break;
199 }
218 }
200 }
219 }
201 if (!found) {
220 if (!found) {
@@ -211,8 +230,17 @@ void LauncherModel::refresh()
211 }
230 }
212
231
213 // Now walk through list and see if we need to add something
232 // Now walk through list and see if we need to add something
233+ int skipped = 0;
214 for (int asIndex = 0; asIndex < items.count(); ++asIndex) {
234 for (int asIndex = 0; asIndex < items.count(); ++asIndex) {
215 QVariant entry = items.at(asIndex);
235 QVariant entry = items.at(asIndex);
236+
237+ if (m_onlyPinned && !entry.toMap().value("pinned").toBool()) {
238+ // Skipping it as we only show pinned and it is not
239+ skipped++;
240+ continue;
241+ }
242+ int newPosition = asIndex - skipped;
243+
216 int itemIndex = -1;
244 int itemIndex = -1;
217 for (int i = 0; i < m_list.count(); ++i) {
245 for (int i = 0; i < m_list.count(); ++i) {
218 if (m_list.at(i)->appId() == entry.toMap().value("id").toString()) {
246 if (m_list.at(i)->appId() == entry.toMap().value("id").toString()) {
@@ -230,14 +258,14 @@ void LauncherModel::refresh()
230 item->setPinned(true);
258 item->setPinned(true);
231 item->setCount(entry.toMap().value("count").toInt());
259 item->setCount(entry.toMap().value("count").toInt());
232 item->setCountVisible(entry.toMap().value("countVisible").toBool());
260 item->setCountVisible(entry.toMap().value("countVisible").toBool());
233− beginInsertRows(QModelIndex(), asIndex, asIndex);
261+ beginInsertRows(QModelIndex(), newPosition, newPosition);
234− m_list.insert(asIndex, item);
262+ m_list.insert(newPosition, item);
235 endInsertRows();
263 endInsertRows();
236− } else if (itemIndex != asIndex) {
264+ } else if (itemIndex != newPosition) {
237 // The item is already there, but it is in a different place than in the settings.
265 // The item is already there, but it is in a different place than in the settings.
238 // Move it to the addedIndex
266 // Move it to the addedIndex
239− beginMoveRows(QModelIndex(), itemIndex, itemIndex, QModelIndex(), asIndex);
267+ beginMoveRows(QModelIndex(), itemIndex, itemIndex, QModelIndex(), newPosition);
240− m_list.move(itemIndex, asIndex);
268+ m_list.move(itemIndex, newPosition);
241 endMoveRows();
269 endMoveRows();
242 }
270 }
243 }
271 }
plugins/Greeter/Unity/Launcher/launchermodelas.h+4
@@ -54,6 +54,9 @@ public:
54 unity::shell::application::ApplicationManagerInterface* applicationManager() const;
54 unity::shell::application::ApplicationManagerInterface* applicationManager() const;
55 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
55 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
56
56
57+ bool onlyPinned() const override;
58+ void setOnlyPinned(bool onlyPinned) override;
59+
57 int findApplication(const QString &appId);
60 int findApplication(const QString &appId);
58
61
59public Q_SLOTS:
62public Q_SLOTS:
@@ -67,6 +70,7 @@ private:
67 QString m_user;
70 QString m_user;
68 QList<LauncherItem*> m_list;
71 QList<LauncherItem*> m_list;
69 AccountsServiceDBusAdaptor *m_accounts;
72 AccountsServiceDBusAdaptor *m_accounts;
73+ bool m_onlyPinned;
70
74
71 friend class LauncherModelASTest;
75 friend class LauncherModelASTest;
72};
76};
plugins/Unity/Launcher/asadapter.cpp+1
@@ -58,5 +58,6 @@ QVariantMap ASAdapter::itemToVariant(LauncherItem *item) const
58 details.insert("icon", item->icon());
58 details.insert("icon", item->icon());
59 details.insert("count", item->count());
59 details.insert("count", item->count());
60 details.insert("countVisible", item->countVisible());
60 details.insert("countVisible", item->countVisible());
61+ details.insert("pinned", item->pinned());
61 return details;
62 return details;
62}
63}
plugins/Unity/Launcher/launchermodel.cpp+9
@@ -265,6 +265,15 @@ void LauncherModel::setApplicationManager(unity::shell::application::Application
265 }
265 }
266}
266}
267
267
268+bool LauncherModel::onlyPinned() const
269+{
270+ return false;
271+}
272+
273+void LauncherModel::setOnlyPinned(bool onlyPinned) {
274+ Q_UNUSED(onlyPinned);
275+ qWarning() << "This launcher implementation does not support showing only pinned apps";
276+}
268
277
269void LauncherModel::storeAppList()
278void LauncherModel::storeAppList()
270{
279{
plugins/Unity/Launcher/launchermodel.h+3
@@ -55,6 +55,9 @@ public:
55 unity::shell::application::ApplicationManagerInterface* applicationManager() const;
55 unity::shell::application::ApplicationManagerInterface* applicationManager() const;
56 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
56 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
57
57
58+ bool onlyPinned() const override;
59+ void setOnlyPinned(bool onlyPinned) override;
60+
58 int findApplication(const QString &appId);
61 int findApplication(const QString &appId);
59
62
60public Q_SLOTS:
63public Q_SLOTS:
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+10
@@ -209,6 +209,16 @@ void MockLauncherModel::setApplicationManager(unity::shell::application::Applica
209 Q_UNUSED(applicationManager)
209 Q_UNUSED(applicationManager)
210}
210}
211
211
212+bool MockLauncherModel::onlyPinned() const
213+{
214+ return false;
215+}
216+
217+void MockLauncherModel::setOnlyPinned(bool onlyPinned)
218+{
219+ Q_UNUSED(onlyPinned)
220+}
221+
212void MockLauncherModel::emitHint()
222void MockLauncherModel::emitHint()
213{
223{
214 Q_EMIT hint();
224 Q_EMIT hint();
tests/mocks/Unity/Launcher/MockLauncherModel.h+3
@@ -49,6 +49,9 @@ public:
49 unity::shell::application::ApplicationManagerInterface *applicationManager() const;
49 unity::shell::application::ApplicationManagerInterface *applicationManager() const;
50 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager);
50 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager);
51
51
52+ bool onlyPinned() const override;
53+ void setOnlyPinned(bool onlyPinned) override;
54+
52 // For testing
55 // For testing
53 Q_INVOKABLE void emitHint();
56 Q_INVOKABLE void emitHint();
54
57
tests/plugins/Greeter/Unity/Launcher/AccountsServiceDBusAdaptor.cpp+2−3
@@ -31,12 +31,11 @@ AccountsServiceDBusAdaptor::AccountsServiceDBusAdaptor(QObject* parent)
31
31
32QVariant AccountsServiceDBusAdaptor::getUserProperty(const QString &user, const QString &interface, const QString &property)
32QVariant AccountsServiceDBusAdaptor::getUserProperty(const QString &user, const QString &interface, const QString &property)
33{
33{
34− Q_UNUSED(user)
35 Q_UNUSED(interface)
34 Q_UNUSED(interface)
36 Q_UNUSED(property) // We only fake one property here (LauncherItems)
35 Q_UNUSED(property) // We only fake one property here (LauncherItems)
37
36
38 QDBusMessage msg;
37 QDBusMessage msg;
39− QVariant v = QVariant::fromValue(properties);
38+ QVariant v = QVariant::fromValue(mockProperties.value(user));
40 QDBusVariant dv(v);
39 QDBusVariant dv(v);
41 QVariant packed = QVariant::fromValue(dv);
40 QVariant packed = QVariant::fromValue(dv);
42 msg << packed;
41 msg << packed;
@@ -47,6 +46,6 @@ QVariant AccountsServiceDBusAdaptor::getUserProperty(const QString &user, const
47void AccountsServiceDBusAdaptor::simulatePropertyChange(const QString &user, const QString &property, const QVariant &value)
46void AccountsServiceDBusAdaptor::simulatePropertyChange(const QString &user, const QString &property, const QVariant &value)
48{
47{
49 Q_ASSERT(property == "LauncherItems");
48 Q_ASSERT(property == "LauncherItems");
50− properties = value.value<QList<QVariantMap>>();
49+ mockProperties[user] = value.value<QList<QVariantMap>>();
51 Q_EMIT propertiesChanged(user, "com.canonical.unity.AccountsService", QStringList() << property);
50 Q_EMIT propertiesChanged(user, "com.canonical.unity.AccountsService", QStringList() << property);
52}
51}
tests/plugins/Greeter/Unity/Launcher/AccountsServiceDBusAdaptor.h+2−3
@@ -26,7 +26,7 @@
26#include <QHash>
26#include <QHash>
27#include <QDBusArgument>
27#include <QDBusArgument>
28
28
29−extern QList<QVariantMap> properties;
29+extern QHash<QString, QList<QVariantMap>> mockProperties;
30
30
31class AccountsServiceDBusAdaptor: public QObject
31class AccountsServiceDBusAdaptor: public QObject
32{
32{
@@ -38,10 +38,9 @@ public:
38 Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &interface, const QString &property);
38 Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &interface, const QString &property);
39 template <typename T>
39 template <typename T>
40 inline T getUserProperty(const QString &user, const QString &interface, const QString &property) {
40 inline T getUserProperty(const QString &user, const QString &interface, const QString &property) {
41− Q_UNUSED(user)
42 Q_UNUSED(interface)
41 Q_UNUSED(interface)
43 Q_ASSERT(property == "LauncherItems");
42 Q_ASSERT(property == "LauncherItems");
44− T ret = properties;
43+ T ret = mockProperties[user];
45 return ret;
44 return ret;
46 }
45 }
47
46
tests/plugins/Greeter/Unity/Launcher/launchermodelastest.cpp+55−22
@@ -27,7 +27,7 @@
27
27
28#include <QtTest>
28#include <QtTest>
29
29
30−QList<QVariantMap> properties;
30+QHash<QString, QList<QVariantMap>> mockProperties;
31
31
32class LauncherModelASTest : public QObject
32class LauncherModelASTest : public QObject
33{
33{
@@ -47,22 +47,30 @@ private Q_SLOTS:
47 item.insert("icon", "fake.svg");
47 item.insert("icon", "fake.svg");
48 item.insert("count", 0);
48 item.insert("count", 0);
49 item.insert("countVisible", false);
49 item.insert("countVisible", false);
50+ item.insert("pinned", true);
50 list.append(item);
51 list.append(item);
51 item["id"] = "appId2";
52 item["id"] = "appId2";
52 item["name"] = "Item 2";
53 item["name"] = "Item 2";
53 list.append(item);
54 list.append(item);
54− properties = list;
55+ mockProperties["user1"] = list;
55 }
56 }
56
57
57− bool isInSync(LauncherModel *model) {
58+ bool isInSync(LauncherModel *model, const QList<QVariantMap> &properties) {
58− // Verify after startup the items are in there
59+ QList<QVariantMap> list;
60+ // Strip unpinned if onlyPinned is set
61+ Q_FOREACH (const QVariantMap &map, properties) {
62+ if (!model->onlyPinned() || map.value("pinned").toBool()) {
63+ list << map;
64+ }
65+ }
66+
59− bool inSync = model->rowCount() == properties.count();
67+ bool inSync = model->rowCount() == list.count();
60− for (int i = 0; i < model->rowCount(); i++) {
68+ for (int i = 0; inSync && i < model->rowCount(); i++) {
61− inSync &= model->get(i)->appId() == properties.at(i).value("id").toString();
69+ inSync &= model->get(i)->appId() == list.at(i).value("id").toString();
62− inSync &= model->get(i)->name() == properties.at(i).value("name").toString();
70+ inSync &= model->get(i)->name() == list.at(i).value("name").toString();
63− inSync &= model->get(i)->icon() == properties.at(i).value("icon").toString();
71+ inSync &= model->get(i)->icon() == list.at(i).value("icon").toString();
64− inSync &= model->get(i)->count() == properties.at(i).value("count").toInt();
72+ inSync &= model->get(i)->count() == list.at(i).value("count").toInt();
65− inSync &= model->get(i)->countVisible() == properties.at(i).value("countVisible").toBool();
73+ inSync &= model->get(i)->countVisible() == list.at(i).value("countVisible").toBool();
66 }
74 }
67 return inSync;
75 return inSync;
68 }
76 }
@@ -74,9 +82,9 @@ private Q_SLOTS:
74 // We didn't set a user yet. model should be empty
82 // We didn't set a user yet. model should be empty
75 QCOMPARE(model->rowCount(), 0);
83 QCOMPARE(model->rowCount(), 0);
76
84
77− model->setUser("dummy");
85+ model->setUser("user1");
78
86
79− QCOMPARE(isInSync(model), true);
87+ QCOMPARE(isInSync(model, mockProperties["user1"]), true);
80 model->deleteLater();
88 model->deleteLater();
81 }
89 }
82
90
@@ -85,8 +93,8 @@ private Q_SLOTS:
85 QTest::addColumn<QString>("changedUser");
93 QTest::addColumn<QString>("changedUser");
86 QTest::addColumn<bool>("inSync");
94 QTest::addColumn<bool>("inSync");
87
95
88− QTest::newRow("this user changed") << "dummy" << "dummy" << true;
96+ QTest::newRow("this user changed") << "user1" << "user1" << true;
89− QTest::newRow("other user changed") << "dummy" << "fred" << false;
97+ QTest::newRow("other user changed") << "user1" << "user2" << false;
90 }
98 }
91
99
92 void testASChangedUpdatesModel() {
100 void testASChangedUpdatesModel() {
@@ -97,42 +105,67 @@ private Q_SLOTS:
97 LauncherModel* model = new LauncherModel(this);
105 LauncherModel* model = new LauncherModel(this);
98 model->setUser(modelUser);
106 model->setUser(modelUser);
99
107
100− int oldCount = properties.count();
108+ int oldCount = mockProperties[modelUser].count();
101 QCOMPARE(model->rowCount(), oldCount);
109 QCOMPARE(model->rowCount(), oldCount);
102
110
103− QList<QVariantMap> newList = properties;
111+ QList<QVariantMap> newList;
104 QVariantMap newEntry;
112 QVariantMap newEntry;
105 newEntry.insert("id", "newappId");
113 newEntry.insert("id", "newappId");
106 newEntry.insert("name", "New app");
114 newEntry.insert("name", "New app");
107 newEntry.insert("icon", "some-icon.svg");
115 newEntry.insert("icon", "some-icon.svg");
108 newEntry.insert("count", 0);
116 newEntry.insert("count", 0);
109 newEntry.insert("countVisible", false);
117 newEntry.insert("countVisible", false);
118+ newEntry.insert("pinned", true);
110 newList.append(newEntry);
119 newList.append(newEntry);
111 model->m_accounts->simulatePropertyChange(changedUser, "LauncherItems", QVariant::fromValue(newList));
120 model->m_accounts->simulatePropertyChange(changedUser, "LauncherItems", QVariant::fromValue(newList));
112
121
113− QCOMPARE(isInSync(model), inSync);
122+ QCOMPARE(isInSync(model, mockProperties[modelUser]), true);
123+ QCOMPARE(isInSync(model, mockProperties[changedUser]), inSync);
114
124
115 model->deleteLater();
125 model->deleteLater();
116 }
126 }
117
127
118 void testUpdateCount() {
128 void testUpdateCount() {
119 LauncherModel* model = new LauncherModel(this);
129 LauncherModel* model = new LauncherModel(this);
120− model->setUser("dummy");
130+ model->setUser("user1");
121
131
122 QCOMPARE(model->get(0)->countVisible(), false);
132 QCOMPARE(model->get(0)->countVisible(), false);
123 QCOMPARE(model->get(0)->count(), 0);
133 QCOMPARE(model->get(0)->count(), 0);
124
134
125− QList<QVariantMap> newList = properties;
135+ QList<QVariantMap> newList = mockProperties["user1"];
126 QVariantMap entry = newList.at(0);
136 QVariantMap entry = newList.at(0);
127 entry["countVisible"] = true;
137 entry["countVisible"] = true;
128 entry["count"] = 55;
138 entry["count"] = 55;
129 newList[0] = entry;
139 newList[0] = entry;
130− model->m_accounts->simulatePropertyChange("dummy", "LauncherItems", QVariant::fromValue(newList));
140+ model->m_accounts->simulatePropertyChange("user1", "LauncherItems", QVariant::fromValue(newList));
131
141
132− QCOMPARE(isInSync(model), true);
142+ QCOMPARE(isInSync(model, mockProperties["user1"]), true);
133
143
134 model->deleteLater();
144 model->deleteLater();
135 }
145 }
146+
147+ void testOnlyPinned() {
148+ LauncherModel *model = new LauncherModel(this);
149+ model->setUser("user1");
150+ model->setOnlyPinned(true);
151+
152+ QCOMPARE(isInSync(model, mockProperties["user1"]), true);
153+
154+ // Let's unpin one item
155+ QList<QVariantMap> newList = mockProperties["user1"];
156+ QVariantMap entry = newList.at(0);
157+ entry["pinned"] = false;
158+ newList[0] = entry;
159+ model->m_accounts->simulatePropertyChange("user1", "LauncherItems", QVariant::fromValue(newList));
160+ QCOMPARE(isInSync(model, mockProperties["user1"]), true);
161+
162+ // Now toggle onlyPinned and make sure the model keeps up
163+ model->setOnlyPinned(false);
164+ QCOMPARE(isInSync(model, mockProperties["user1"]), true);
165+
166+ model->setOnlyPinned(true);
167+ QCOMPARE(isInSync(model, mockProperties["user1"]), true);
168+ }
136};
169};
137
170
138QTEST_GUILESS_MAIN(LauncherModelASTest)
171QTEST_GUILESS_MAIN(LauncherModelASTest)