on xenial help sign in
Add a hinting animation to indicate changes in the backend Fixes: 1376707

Approved by: Daniel d'Andrada

commitbebdf63cd59a3d273545ce7ebcb644549b888f6d authorMichael Zanetti <michael.zanetti@canonical.com> dateTue 4 Nov 2014, 12:54 (12 years ago) parentb99f83615d7b45
12 files changed+87−10
debian/control+1−1 plugins/Unity/Launcher/CMakeLists.txt+1−1 plugins/Unity/Launcher/launchermodel.cpp+15−4 qml/Launcher/Launcher.qml+24−2 tests/mocks/Unity/Launcher/CMakeLists.txt+1−1 tests/mocks/Unity/Launcher/MockLauncherModel.cpp+5 tests/mocks/Unity/Launcher/MockLauncherModel.h+3 tests/plugins/Unity/Launcher/CMakeLists.txt+1−1 tests/plugins/Unity/Launcher/gsettings.cpp+1 tests/plugins/Unity/Launcher/gsettings.h+1 tests/plugins/Unity/Launcher/launchermodeltest.cpp+12 tests/qmltests/Launcher/tst_Launcher.qml+22
debian/control+1−1
@@ -25,7 +25,7 @@ Build-Depends: cmake,
2525 libpulse-dev,
2626 libqmenumodel-dev (>= 0.2.8),
2727 libqt5xmlpatterns5-dev,
28− libunity-api-dev (>= 7.92),
28+ libunity-api-dev (>= 7.93),
2929 libusermetricsoutput1-dev,
3030 libxcb1-dev,
3131 pkg-config,
plugins/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,5 +1,5 @@
11include(FindPkgConfig)
2−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=4)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=5)
33pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
44pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
55
plugins/Unity/Launcher/launchermodel.cpp+15−4
@@ -281,9 +281,11 @@ void LauncherModel::unpin(const QString &appId)
281281 }
282282
283283 if (m_appManager->findApplication(appId)) {
284− m_list.at(index)->setPinned(false);
285− QModelIndex modelIndex = this->index(index);
286− Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RolePinned);
284+ if (m_list.at(index)->pinned()) {
285+ m_list.at(index)->setPinned(false);
286+ QModelIndex modelIndex = this->index(index);
287+ Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RolePinned);
288+ }
287289 } else {
288290 beginRemoveRows(QModelIndex(), index, index);
289291 m_list.takeAt(index)->deleteLater();
@@ -347,6 +349,7 @@ void LauncherModel::countVisibleChanged(const QString &appId, int countVisible)
347349 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
348350 m_list.append(item);
349351 endInsertRows();
352+ Q_EMIT hint();
350353 }
351354 }
352355}
@@ -375,6 +378,8 @@ void LauncherModel::refresh()
375378 unpin(item->appId());
376379 }
377380
381+ bool changed = toBeRemoved.count() > 0;
382+
378383 // This brings the Launcher into sync with the settings backend again. There's an issue though:
379384 // If we can't find a .desktop file for an entry we need to skip it. That makes our settingsIndex
380385 // go out of sync with the actual index of items. So let's also use an addedIndex which reflects
@@ -409,18 +414,24 @@ void LauncherModel::refresh()
409414 beginInsertRows(QModelIndex(), addedIndex, addedIndex);
410415 m_list.insert(addedIndex, item);
411416 endInsertRows();
417+ changed = true;
412418 } else if (itemIndex != addedIndex) {
413− // The item is already there, but it is in a different place then in the settings.
419+ // The item is already there, but it is in a different place than in the settings.
414420 // Move it to the addedIndex
415421 beginMoveRows(QModelIndex(), itemIndex, itemIndex, QModelIndex(), addedIndex);
416422 m_list.move(itemIndex, addedIndex);
417423 endMoveRows();
424+ changed = true;
418425 }
419426
420427 // Just like settingsIndex, this will increase with every item, except the ones we
421428 // skipped with the "continue" call above.
422429 addedIndex++;
423430 }
431+
432+ if (changed) {
433+ Q_EMIT hint();
434+ }
424435}
425436
426437void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
qml/Launcher/Launcher.qml+24−2
@@ -65,13 +65,22 @@ Item {
6565
6666 function tease() {
6767 if (available && !dragArea.dragging) {
68+ teaseTimer.mode = "teasing"
69+ teaseTimer.start();
70+ }
71+ }
72+
73+ function hint() {
74+ if (available && root.state == "") {
75+ teaseTimer.mode = "hinting"
6876 teaseTimer.start();
6977 }
7078 }
7179
7280 Timer {
7381 id: teaseTimer
74− interval: 200
82+ interval: mode == "teasing" ? 200 : 300
83+ property string mode: "teasing"
7584 }
7685
7786 Timer {
@@ -104,6 +113,11 @@ Item {
104113 }
105114 }
106115
116+ Connections {
117+ target: LauncherModel
118+ onHint: hint();
119+ }
120+
107121 SequentialAnimation {
108122 id: fadeOutAnimation
109123 ScriptAction {
@@ -274,11 +288,19 @@ Item {
274288 },
275289 State {
276290 name: "teasing"
277− when: teaseTimer.running
291+ when: teaseTimer.running && teaseTimer.mode == "teasing"
278292 PropertyChanges {
279293 target: panel
280294 x: -root.panelWidth + units.gu(2)
281295 }
296+ },
297+ State {
298+ name: "hinting"
299+ when: teaseTimer.running && teaseTimer.mode == "hinting"
300+ PropertyChanges {
301+ target: panel
302+ x: 0
303+ }
282304 }
283305 ]
284306}
tests/mocks/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=4)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=5)
22
33include_directories(
44 ${CMAKE_CURRENT_SOURCE_DIR}
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+5
@@ -209,6 +209,11 @@ void MockLauncherModel::setApplicationManager(unity::shell::application::Applica
209209 Q_UNUSED(applicationManager)
210210}
211211
212+void MockLauncherModel::emitHint()
213+{
214+ Q_EMIT hint();
215+}
216+
212217unity::shell::application::ApplicationManagerInterface *MockLauncherModel::applicationManager() const
213218{
214219 return nullptr;
tests/mocks/Unity/Launcher/MockLauncherModel.h+3
@@ -49,6 +49,9 @@ public:
4949 unity::shell::application::ApplicationManagerInterface *applicationManager() const;
5050 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager);
5151
52+ // For testing
53+ Q_INVOKABLE void emitHint();
54+
5255Q_SIGNALS:
5356 void quickListTriggered(const QString &appId, int index);
5457
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=4)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=5)
33pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
44
55include_directories(
tests/plugins/Unity/Launcher/gsettings.cpp+1
@@ -39,6 +39,7 @@ void GSettings::setStoredApplications(const QStringList &storedApplications)
3939
4040void GSettings::simulateDConfChanged(const QStringList &storedApplications)
4141{
42+ m_entries = storedApplications;
4243 setStoredApplications(storedApplications);
4344 Q_EMIT changed();
4445}
tests/plugins/Unity/Launcher/gsettings.h+1
@@ -34,6 +34,7 @@ public:
3434 QStringList storedApplications() const;
3535 void setStoredApplications(const QStringList &storedApplications);
3636
37+ // Only for testing
3738 void simulateDConfChanged(const QStringList &storedApplications);
3839
3940Q_SIGNALS:
tests/plugins/Unity/Launcher/launchermodeltest.cpp+12
@@ -187,12 +187,20 @@ private Q_SLOTS:
187187 QCOMPARE(spy.count() > 0, true);
188188 QCOMPARE(spy.at(0).at(2).value<QVector<int>>().first(), (int)LauncherModelInterface::RolePinned);
189189
190+ // App should be pinned now
190191 spy.clear();
191192 launcherModel->requestRemove(launcherModel->get(0)->appId());
192193 QCOMPARE(launcherModel->get(0)->pinned(), false);
193194 QCOMPARE(launcherModel->get(1)->pinned(), false);
194195 QCOMPARE(spy.count(), 1);
195196 QCOMPARE(spy.at(0).at(2).value<QVector<int>>().first(), (int)LauncherModelInterface::RolePinned);
197+
198+ // Now that the app is unpinned, nothing should change and the signal should not be emitted
199+ spy.clear();
200+ launcherModel->requestRemove(launcherModel->get(0)->appId());
201+ QCOMPARE(launcherModel->get(0)->pinned(), false);
202+ QCOMPARE(launcherModel->get(1)->pinned(), false);
203+ QCOMPARE(spy.count(), 0);
196204 }
197205
198206 void testRemove_data() {
@@ -424,6 +432,7 @@ private Q_SLOTS:
424432
425433 void testSettings() {
426434 GSettings *settings = launcherModel->m_settings;
435+ QSignalSpy spy(launcherModel, SIGNAL(hint()));
427436
428437 // Nothing pinned at startup
429438 QCOMPARE(settings->storedApplications().count(), 0);
@@ -431,6 +440,7 @@ private Q_SLOTS:
431440 // pin both apps
432441 launcherModel->pin("abs-icon");
433442 launcherModel->pin("no-icon");
443+ QCOMPARE(spy.count(), 0);
434444
435445 // Now settings should have 2 apps
436446 QCOMPARE(settings->storedApplications().count(), 2);
@@ -445,6 +455,7 @@ private Q_SLOTS:
445455 // Now remove 1 app through the backend, make sure one is still there
446456 settings->simulateDConfChanged(QStringList() << "abs-icon");
447457 QCOMPARE(settings->storedApplications().count(), 1);
458+ QCOMPARE(spy.count(), 1);
448459
449460 // Check if it disappeared from the frontend too
450461 QCOMPARE(launcherModel->rowCount(), 1);
@@ -454,6 +465,7 @@ private Q_SLOTS:
454465 QCOMPARE(launcherModel->rowCount(), 2);
455466 QCOMPARE(launcherModel->get(0)->appId(), QString("no-icon"));
456467 QCOMPARE(launcherModel->get(1)->appId(), QString("abs-icon"));
468+ QCOMPARE(spy.count(), 2);
457469 }
458470};
459471
tests/qmltests/Launcher/tst_Launcher.qml+22
@@ -17,6 +17,7 @@
1717import QtQuick 2.0
1818import QtTest 1.0
1919import Unity.Test 0.1 as UT
20+import Ubuntu.Components 1.1
2021import ".."
2122import "../../../qml/Launcher"
2223import Unity.Launcher 0.1
@@ -73,6 +74,12 @@ Item {
7374 }
7475 }
7576
77+ Button {
78+ anchors { bottom: parent.bottom; right: parent.right; margins: units.gu(1) }
79+ text: "emit hinting signal"
80+ onClicked: LauncherModel.emitHint()
81+ }
82+
7683 SignalSpy {
7784 id: signalSpy
7885 target: LauncherModel
@@ -228,6 +235,21 @@ Item {
228235 launcher.available = true;
229236 }
230237
238+ function test_hintLauncherOnChange() {
239+ var launcherPanel = findChild(launcher, "launcherPanel")
240+ // Make sure we start hidden
241+ tryCompare(launcherPanel, "x", -launcher.panelWidth)
242+ // reset our measurement property
243+ launcher.maxPanelX = -launcher.panelWidth
244+ // change it
245+ LauncherModel.move(0, 1)
246+ LauncherModel.emitHint();
247+
248+ // make sure it opened fully and hides again without delay
249+ tryCompare(launcher, "maxPanelX", 0)
250+ tryCompare(launcherPanel, "x", -launcher.panelWidth, 1000)
251+ }
252+
231253 function test_countEmblems() {
232254 dragLauncherIntoView();
233255 var launcherListView = findChild(launcher, "launcherListView");