on xenial help sign in
Add pull-to-refresh to app drawer
2a12bafRatchanan Srirattanamet
plugins/Unity/Launcher/CMakeLists.txt+1−1
@@ -50,6 +50,6 @@ target_link_libraries(UnityLauncher-qml
50 ${UAL_LIBRARIES}
50 ${UAL_LIBRARIES}
51 )
51 )
52
52
53−qt5_use_modules(UnityLauncher-qml DBus Qml Gui)
53+qt5_use_modules(UnityLauncher-qml DBus Qml Gui Concurrent)
54
54
55add_unity8_plugin(Unity.Launcher 0.1 Unity/Launcher TARGETS UnityLauncher-qml)
55add_unity8_plugin(Unity.Launcher 0.1 Unity/Launcher TARGETS UnityLauncher-qml)
plugins/Unity/Launcher/appdrawermodel.cpp+83−16
@@ -1,5 +1,6 @@
1/*
1/*
2 * Copyright (C) 2016 Canonical, Ltd.
2 * Copyright (C) 2016 Canonical, Ltd.
3+ * Copyright (C) 2020 UBports Foundation.
3 *
4 *
4 * This program is free software; you can redistribute it and/or modify
5 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * it under the terms of the GNU General Public License as published by
@@ -20,28 +21,31 @@
20
21
21#include <QDebug>
22#include <QDebug>
22#include <QDateTime>
23#include <QDateTime>
24+#include <QtConcurrentRun>
25+
26+static std::shared_ptr<LauncherItem> makeSharedLauncherItem(
27+ const QString &appId, const QString &name, const QString &icon, QObject * parent)
28+{
29+ return std::shared_ptr<LauncherItem>(
30+ new LauncherItem(appId, name, icon, parent),
31+ [] (LauncherItem *item) { item->deleteLater(); });
32+}
23
33
24AppDrawerModel::AppDrawerModel(QObject *parent):
34AppDrawerModel::AppDrawerModel(QObject *parent):
25 AppDrawerModelInterface(parent),
35 AppDrawerModelInterface(parent),
26 m_ual(new UalWrapper(this)),
36 m_ual(new UalWrapper(this)),
27− m_xdgWatcher(new XdgWatcher(this))
37+ m_xdgWatcher(new XdgWatcher(this)),
38+ m_refreshing(false)
28{
39{
29− Q_FOREACH (const QString &appId, UalWrapper::installedApps()) {
40+ connect(&m_refreshFutureWatcher, &QFutureWatcher<ItemList>::finished,
30− UalWrapper::AppInfo info = UalWrapper::getApplicationInfo(appId);
41+ this, &AppDrawerModel::onRefreshFinished);
31− if (!info.valid) {
32− qWarning() << "Failed to get app info for app" << appId;
33− continue;
34− }
35− LauncherItem* item = new LauncherItem(appId, info.name, info.icon, this);
36− item->setKeywords(info.keywords);
37− item->setPopularity(info.popularity);
38− m_list.append(item);
39− }
40
42
41 // keep this a queued connection as it's coming from another thread.
43 // keep this a queued connection as it's coming from another thread.
42 connect(m_xdgWatcher, &XdgWatcher::appAdded, this, &AppDrawerModel::appAdded, Qt::QueuedConnection);
44 connect(m_xdgWatcher, &XdgWatcher::appAdded, this, &AppDrawerModel::appAdded, Qt::QueuedConnection);
43 connect(m_xdgWatcher, &XdgWatcher::appRemoved, this, &AppDrawerModel::appRemoved, Qt::QueuedConnection);
45 connect(m_xdgWatcher, &XdgWatcher::appRemoved, this, &AppDrawerModel::appRemoved, Qt::QueuedConnection);
44 connect(m_xdgWatcher, &XdgWatcher::appInfoChanged, this, &AppDrawerModel::appInfoChanged, Qt::QueuedConnection);
46 connect(m_xdgWatcher, &XdgWatcher::appInfoChanged, this, &AppDrawerModel::appInfoChanged, Qt::QueuedConnection);
47+
48+ refresh();
45}
49}
46
50
47int AppDrawerModel::rowCount(const QModelIndex &parent) const
51int AppDrawerModel::rowCount(const QModelIndex &parent) const
@@ -70,6 +74,10 @@ QVariant AppDrawerModel::data(const QModelIndex &index, int role) const
70
74
71void AppDrawerModel::appAdded(const QString &appId)
75void AppDrawerModel::appAdded(const QString &appId)
72{
76{
77+ if (m_refreshing)
78+ // Will be replaced by the refresh result anyway.
79+ return;
80+
73 UalWrapper::AppInfo info = UalWrapper::getApplicationInfo(appId);
81 UalWrapper::AppInfo info = UalWrapper::getApplicationInfo(appId);
74 if (!info.valid) {
82 if (!info.valid) {
75 qWarning() << "App added signal received but failed to get app info for app" << appId;
83 qWarning() << "App added signal received but failed to get app info for app" << appId;
@@ -77,15 +85,19 @@ void AppDrawerModel::appAdded(const QString &appId)
77 }
85 }
78
86
79 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
87 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
80− LauncherItem* item = new LauncherItem(appId, info.name, info.icon, this);
88+ auto item = makeSharedLauncherItem(appId, info.name, info.icon, /* parent */ nullptr);
81 item->setKeywords(info.keywords);
89 item->setKeywords(info.keywords);
82 item->setPopularity(info.popularity);
90 item->setPopularity(info.popularity);
83− m_list.append(item);
91+ m_list.append(std::move(item));
84 endInsertRows();
92 endInsertRows();
85}
93}
86
94
87void AppDrawerModel::appRemoved(const QString &appId)
95void AppDrawerModel::appRemoved(const QString &appId)
88{
96{
97+ if (m_refreshing)
98+ // Will be replaced by the refresh result anyway.
99+ return;
100+
89 int idx = -1;
101 int idx = -1;
90 for (int i = 0; i < m_list.count(); i++) {
102 for (int i = 0; i < m_list.count(); i++) {
91 if (m_list.at(i)->appId() == appId) {
103 if (m_list.at(i)->appId() == appId) {
@@ -98,13 +110,17 @@ void AppDrawerModel::appRemoved(const QString &appId)
98 return;
110 return;
99 }
111 }
100 beginRemoveRows(QModelIndex(), idx, idx);
112 beginRemoveRows(QModelIndex(), idx, idx);
101− m_list.takeAt(idx)->deleteLater();
113+ m_list.removeAt(idx);
102 endRemoveRows();
114 endRemoveRows();
103}
115}
104
116
105void AppDrawerModel::appInfoChanged(const QString &appId)
117void AppDrawerModel::appInfoChanged(const QString &appId)
106{
118{
119+ if (m_refreshing)
120+ // Will be replaced by the refresh result anyway.
121+ return;
122+
107− LauncherItem *item = nullptr;
123+ std::shared_ptr<LauncherItem> item;
108 int idx = -1;
124 int idx = -1;
109
125
110 for(int i = 0; i < m_list.count(); i++) {
126 for(int i = 0; i < m_list.count(); i++) {
@@ -123,3 +139,54 @@ void AppDrawerModel::appInfoChanged(const QString &appId)
123 item->setPopularity(info.popularity);
139 item->setPopularity(info.popularity);
124 Q_EMIT dataChanged(index(idx), index(idx), {AppDrawerModelInterface::RoleUsage});
140 Q_EMIT dataChanged(index(idx), index(idx), {AppDrawerModelInterface::RoleUsage});
125}
141}
142+
143+bool AppDrawerModel::refreshing() {
144+ return m_refreshing;
145+}
146+
147+void AppDrawerModel::refresh() {
148+ if (m_refreshing)
149+ return;
150+
151+ m_refreshFutureWatcher.setFuture(QtConcurrent::run([](QThread *thread) {
152+ ItemList list;
153+
154+ Q_FOREACH (const QString &appId, UalWrapper::installedApps()) {
155+ UalWrapper::AppInfo info = UalWrapper::getApplicationInfo(appId);
156+ if (!info.valid) {
157+ qWarning() << "Failed to get app info for app" << appId;
158+ continue;
159+ }
160+ // We don't pass parent in because this may run after the model is destroyed.
161+ // (And, in fact, we can't, because the model is in a diferent thread.)
162+ auto item = makeSharedLauncherItem(appId, info.name, info.icon, /* parent */ nullptr);
163+ item->setKeywords(info.keywords);
164+ item->setPopularity(info.popularity);
165+ item->moveToThread(thread);
166+ list.append(std::move(item));
167+ }
168+
169+ return list;
170+ }, this->thread()));
171+
172+ m_refreshing = true;
173+ Q_EMIT refreshingChanged();
174+}
175+
176+void AppDrawerModel::onRefreshFinished() {
177+ if (m_refreshFutureWatcher.isCanceled())
178+ // This is the result of setting canceled future below.
179+ return;
180+
181+ beginResetModel();
182+
183+ m_list = m_refreshFutureWatcher.result();
184+ // Remove the future & its result, so that future modifications won't
185+ // create a copy.
186+ m_refreshFutureWatcher.setFuture(QFuture<ItemList>());
187+
188+ endResetModel();
189+
190+ m_refreshing = false;
191+ Q_EMIT refreshingChanged();
192+}
plugins/Unity/Launcher/appdrawermodel.h+23−2
@@ -1,5 +1,6 @@
1/*
1/*
2 * Copyright (C) 2016 Canonical, Ltd.
2 * Copyright (C) 2016 Canonical, Ltd.
3+ * Copyright (C) 2020 UBports Foundation.
3 *
4 *
4 * This program is free software; you can redistribute it and/or modify
5 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * it under the terms of the GNU General Public License as published by
@@ -14,7 +15,8 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16 */
16
17
17−
18+#include <memory>
19+#include <QFutureWatcher>
18#include <unity/shell/launcher/AppDrawerModelInterface.h>
20#include <unity/shell/launcher/AppDrawerModelInterface.h>
19
21
20#include "launcheritem.h"
22#include "launcheritem.h"
@@ -25,19 +27,38 @@ class XdgWatcher;
25class AppDrawerModel: public AppDrawerModelInterface
27class AppDrawerModel: public AppDrawerModelInterface
26{
28{
27 Q_OBJECT
29 Q_OBJECT
30+ // TODO: Add this to AppDrawerModelInterface in unity-api.
31+ // Or, better yet, remove AppDrawerModelInterface from unity-api.
32+ Q_PROPERTY(bool refreshing READ refreshing NOTIFY refreshingChanged)
28public:
33public:
29 AppDrawerModel(QObject* parent = nullptr);
34 AppDrawerModel(QObject* parent = nullptr);
30
35
31 int rowCount(const QModelIndex &parent) const override;
36 int rowCount(const QModelIndex &parent) const override;
32 QVariant data(const QModelIndex &index, int role) const override;
37 QVariant data(const QModelIndex &index, int role) const override;
33
38
39+ // TODO: add these to AppDrawerModelInterface too.
40+ bool refreshing();
41+ Q_INVOKABLE void refresh();
42+
43+Q_SIGNALS:
44+ void refreshingChanged();
45+
34private Q_SLOTS:
46private Q_SLOTS:
35 void appAdded(const QString &appId);
47 void appAdded(const QString &appId);
36 void appRemoved(const QString &appId);
48 void appRemoved(const QString &appId);
37 void appInfoChanged(const QString &appId);
49 void appInfoChanged(const QString &appId);
38
50
51+ void onRefreshFinished();
52+
39private:
53private:
54+ // Using shared_ptr is unavoidable in order to share the refresh result
55+ // from the worker thread safely without a memory leak, in case the model
56+ // is destructed before the worker finishes.
40− QList<LauncherItem*> m_list;
57+ typedef QList<std::shared_ptr<LauncherItem>> ItemList;
58+
59+ ItemList m_list;
41 UalWrapper *m_ual;
60 UalWrapper *m_ual;
42 XdgWatcher *m_xdgWatcher;
61 XdgWatcher *m_xdgWatcher;
62+ QFutureWatcher<ItemList> m_refreshFutureWatcher;
63+ bool m_refreshing;
43};
64};
plugins/Unity/Launcher/launcheritem.cpp−1
@@ -37,7 +37,6 @@ LauncherItem::LauncherItem(const QString &appId, const QString &name, const QStr
37 m_alerting(false),
37 m_alerting(false),
38 m_quickList(new QuickListModel(this))
38 m_quickList(new QuickListModel(this))
39{
39{
40− Q_ASSERT(parent != nullptr);
41 QuickListEntry nameAction;
40 QuickListEntry nameAction;
42 nameAction.setActionId(QStringLiteral("launch_item"));
41 nameAction.setActionId(QStringLiteral("launch_item"));
43 nameAction.setText(m_name);
42 nameAction.setText(m_name);
qml/Launcher/Drawer.qml+6
@@ -1,5 +1,6 @@
1/*
1/*
2 * Copyright (C) 2016 Canonical, Ltd.
2 * Copyright (C) 2016 Canonical, Ltd.
3+ * Copyright (C) 2020 UBports Foundation.
3 *
4 *
4 * This program is free software; you can redistribute it and/or modify
5 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * it under the terms of the GNU General Public License as published by
@@ -284,6 +285,11 @@ FocusScope {
284 unFocusInput();
285 unFocusInput();
285 }
286 }
286 }
287 }
288+
289+ refreshing: appDrawerModel.refreshing
290+ onRefresh: {
291+ appDrawerModel.refresh();
292+ }
287 }
293 }
288 }
294 }
289
295
qml/Launcher/DrawerGridView.qml+31
@@ -1,5 +1,6 @@
1/*
1/*
2 * Copyright (C) 2016 Canonical, Ltd.
2 * Copyright (C) 2016 Canonical, Ltd.
3+ * Copyright (C) 2020 UBports Foundation.
3 *
4 *
4 * This program is free software; you can redistribute it and/or modify
5 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * it under the terms of the GNU General Public License as published by
@@ -15,6 +16,7 @@
15 */
16 */
16
17
17import QtQuick 2.4
18import QtQuick 2.4
19+import Ubuntu.Components 1.3
18import "../Components"
20import "../Components"
19
21
20FocusScope {
22FocusScope {
@@ -35,6 +37,9 @@ FocusScope {
35 readonly property int columns: width / delegateWidth
37 readonly property int columns: width / delegateWidth
36 readonly property int rows: Math.ceil(gridView.model.count / root.columns)
38 readonly property int rows: Math.ceil(gridView.model.count / root.columns)
37
39
40+ property alias refreshing: pullToRefresh.refreshing
41+ signal refresh();
42+
38 GridView {
43 GridView {
39 id: gridView
44 id: gridView
40 anchors.fill: parent
45 anchors.fill: parent
@@ -47,6 +52,32 @@ FocusScope {
47
52
48 cellWidth: root.delegateWidth + spacing
53 cellWidth: root.delegateWidth + spacing
49 cellHeight: root.delegateHeight
54 cellHeight: root.delegateHeight
55+
56+ PullToRefresh {
57+ id: pullToRefresh
58+ parent: gridView
59+ target: gridView
60+
61+ readonly property real contentY: gridView.contentY - gridView.originY
62+ y: -contentY - units.gu(5)
63+
64+ readonly property color pullLabelColor: "white"
65+ style: PullToRefreshScopeStyle {
66+ activationThreshold: Math.min(units.gu(14), gridView.height / 5)
67+ }
68+
69+ onRefresh: root.refresh();
70+ }
71+ }
72+
73+ ProgressBar {
74+ anchors {
75+ left: parent.left
76+ right: parent.right
77+ bottom: parent.bottom
78+ }
79+ visible: refreshing
80+ indeterminate: true
50 }
81 }
51
82
52 function getFirstAppId() {
83 function getFirstAppId() {
qml/Launcher/PullToRefreshScopeStyle.qmlnew file+83
@@ -0,0 +1,83 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import Ubuntu.Components.Styles 1.3
20+
21+/**
22+ * TODO: Once the SDK version of PullToRefreshStyle doesn't have bug 1375799
23+ * (https://launchpad.net/bugs/1375799) anymore, we should switch to using a
24+ * a subclass of the Ambiance version with a hidden ActivityIndicator.
25+ */
26+PullToRefreshStyle {
27+ releaseToRefresh: styledItem.target.originY - styledItem.target.contentY > activationThreshold
28+
29+ Connections {
30+ property bool willRefresh: false
31+
32+ target: styledItem.target
33+ onDraggingChanged: {
34+ if (!styledItem.target.dragging && releaseToRefresh) {
35+ willRefresh = true
36+ }
37+ }
38+ onContentYChanged: {
39+ if (styledItem.target.originY - styledItem.target.contentY == 0 && willRefresh) {
40+ styledItem.refresh()
41+ willRefresh = false
42+ }
43+ }
44+ }
45+
46+ Label {
47+ id: pullLabel
48+ anchors.horizontalCenter: parent.horizontalCenter
49+ horizontalAlignment: Text.AlignHCenter
50+ verticalAlignment: Text.AlignVCenter
51+ color: styledItem.pullLabelColor
52+ states: [
53+ State {
54+ name: "pulling"
55+ when: styledItem.target.dragging && !releaseToRefresh
56+ PropertyChanges { target: pullLabel; text: i18n.tr("Pull to refresh…") }
57+ },
58+ State {
59+ name: "releasable"
60+ when: styledItem.target.dragging && releaseToRefresh
61+ PropertyChanges { target: pullLabel; text: i18n.tr("Release to refresh…") }
62+ }
63+ ]
64+ transitions: Transition {
65+ SequentialAnimation {
66+ UbuntuNumberAnimation {
67+ target: pullLabel
68+ property: "opacity"
69+ to: 0.0
70+ }
71+ PropertyAction {
72+ target: pullLabel
73+ property: "text"
74+ }
75+ UbuntuNumberAnimation {
76+ target: pullLabel
77+ property: "opacity"
78+ to: 1.0
79+ }
80+ }
81+ }
82+ }
83+}
tests/mocks/Unity/Launcher/MockAppDrawerModel.cpp+25−1
@@ -1,5 +1,6 @@
1/*
1/*
2 * Copyright (C) 2016 Canonical, Ltd.
2 * Copyright (C) 2016 Canonical, Ltd.
3+ * Copyright (C) 2020 UBports Foundation.
3 *
4 *
4 * This program is free software; you can redistribute it and/or modify
5 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * it under the terms of the GNU General Public License as published by
@@ -18,9 +19,11 @@
18
19
19#include <QDebug>
20#include <QDebug>
20#include <QDateTime>
21#include <QDateTime>
22+#include <QTimer>
21
23
22MockAppDrawerModel::MockAppDrawerModel(QObject *parent):
24MockAppDrawerModel::MockAppDrawerModel(QObject *parent):
23− AppDrawerModelInterface(parent)
25+ AppDrawerModelInterface(parent),
26+ m_refresing(false)
24{
27{
25 MockLauncherItem *item = new MockLauncherItem("dialer-app", "/usr/share/applications/dialer-app.desktop", "Dialer", "dialer-app", this);
28 MockLauncherItem *item = new MockLauncherItem("dialer-app", "/usr/share/applications/dialer-app.desktop", "Dialer", "dialer-app", this);
26 m_list.append(item);
29 m_list.append(item);
@@ -72,3 +75,24 @@ QVariant MockAppDrawerModel::data(const QModelIndex &index, int role) const
72
75
73 return QVariant();
76 return QVariant();
74}
77}
78+
79+bool MockAppDrawerModel::refreshing() {
80+ return m_refresing;
81+}
82+
83+void MockAppDrawerModel::refresh() {
84+ if (m_refresing)
85+ return;
86+
87+ m_refresing = true;
88+ Q_EMIT refreshingChanged();
89+
90+ QTimer::singleShot(/* msec */ 1000, this, [this] {
91+ beginResetModel();
92+ // Pretended that the data has changed;
93+ endResetModel();
94+
95+ m_refresing = false;
96+ Q_EMIT refreshingChanged();
97+ });
98+}
tests/mocks/Unity/Launcher/MockAppDrawerModel.h+11
@@ -1,5 +1,6 @@
1/*
1/*
2 * Copyright (C) 2016 Canonical, Ltd.
2 * Copyright (C) 2016 Canonical, Ltd.
3+ * Copyright (C) 2020 UBports Foundation.
3 *
4 *
4 * This program is free software; you can redistribute it and/or modify
5 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * it under the terms of the GNU General Public License as published by
@@ -21,12 +22,22 @@
21class MockAppDrawerModel: public AppDrawerModelInterface
22class MockAppDrawerModel: public AppDrawerModelInterface
22{
23{
23 Q_OBJECT
24 Q_OBJECT
25+ // TODO: Add this to AppDrawerModelInterface in unity-api.
26+ // Or, better yet, remove AppDrawerModelInterface from unity-api.
27+ Q_PROPERTY(bool refreshing READ refreshing NOTIFY refreshingChanged)
24public:
28public:
25 MockAppDrawerModel(QObject* parent = nullptr);
29 MockAppDrawerModel(QObject* parent = nullptr);
26
30
27 int rowCount(const QModelIndex &parent) const override;
31 int rowCount(const QModelIndex &parent) const override;
28 QVariant data(const QModelIndex &index, int role) const override;
32 QVariant data(const QModelIndex &index, int role) const override;
29
33
34+ bool refreshing();
35+ Q_INVOKABLE void refresh();
36+
37+Q_SIGNALS:
38+ void refreshingChanged();
39+
30private:
40private:
31 QList<MockLauncherItem*> m_list;
41 QList<MockLauncherItem*> m_list;
42+ bool m_refresing;
32};
43};
tests/plugins/Unity/Launcher/CMakeLists.txt+1−1
@@ -81,7 +81,7 @@ target_link_libraries(appdrawermodeltestExec
81 ${UAL_LIBRARIES}
81 ${UAL_LIBRARIES}
82 )
82 )
83add_dependencies(appdrawermodeltestExec mock-server)
83add_dependencies(appdrawermodeltestExec mock-server)
84−qt5_use_modules(appdrawermodeltestExec Test Core DBus Xml Gui Qml)
84+qt5_use_modules(appdrawermodeltestExec Test Core DBus Xml Gui Qml Concurrent)
85install(TARGETS appdrawermodeltestExec
85install(TARGETS appdrawermodeltestExec
86 DESTINATION "${SHELL_PRIVATE_LIBDIR}/tests/plugins/Unity/Launcher"
86 DESTINATION "${SHELL_PRIVATE_LIBDIR}/tests/plugins/Unity/Launcher"
87 )
87 )
tests/plugins/Unity/Launcher/appdrawermodeltest.cpp+12
@@ -32,6 +32,8 @@ private Q_SLOTS:
32 void initTestCase() {
32 void initTestCase() {
33 UalWrapper::s_list << QStringLiteral("app1") << QStringLiteral("app2");
33 UalWrapper::s_list << QStringLiteral("app1") << QStringLiteral("app2");
34 appDrawerModel = new AppDrawerModel(this);
34 appDrawerModel = new AppDrawerModel(this);
35+ QTRY_VERIFY(!appDrawerModel->refreshing());
36+
35 QCOMPARE(appDrawerModel->rowCount(QModelIndex()), 2);
37 QCOMPARE(appDrawerModel->rowCount(QModelIndex()), 2);
36 }
38 }
37
39
@@ -50,6 +52,16 @@ private Q_SLOTS:
50
52
51 QCOMPARE(appDrawerModel->rowCount(QModelIndex()), 2);
53 QCOMPARE(appDrawerModel->rowCount(QModelIndex()), 2);
52 }
54 }
55+
56+ void testRefresh() {
57+ QSignalSpy refreshingSpy(appDrawerModel, &AppDrawerModel::refreshingChanged);
58+
59+ appDrawerModel->refresh();
60+ QTRY_VERIFY(!appDrawerModel->refreshing());
61+
62+ QCOMPARE(refreshingSpy.count(), 2);
63+ QCOMPARE(appDrawerModel->rowCount(QModelIndex()), 2);
64+ }
53};
65};
54
66
55QTEST_GUILESS_MAIN(AppDrawerModelTest)
67QTEST_GUILESS_MAIN(AppDrawerModelTest)