Merge lp:~stolowski/unity-scopes-shell/favorite-scopes into lp:unity-scopes-shell
- favorite-scopes
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Paweł Stołowski |
Approved revision: | 148 |
Merged at revision: | 130 |
Proposed branch: | lp:~stolowski/unity-scopes-shell/favorite-scopes |
Merge into: | lp:unity-scopes-shell |
Prerequisite: | lp:~aacid/unity-scopes-shell/resetMeansCountChanged |
Diff against target: |
879 lines (+453/-49) 19 files modified
debian/control (+3/-1) src/Unity/CMakeLists.txt (+1/-1) src/Unity/overviewcategories.cpp (+10/-5) src/Unity/overviewcategories.h (+3/-2) src/Unity/overviewresults.cpp (+45/-3) src/Unity/overviewscope.cpp (+24/-3) src/Unity/overviewscope.h (+2/-0) src/Unity/scope.cpp (+8/-4) src/Unity/scope.h (+4/-0) src/Unity/scopes.cpp (+138/-29) src/Unity/scopes.h (+9/-1) tests/CMakeLists.txt (+2/-0) tests/departmentstest.cpp (+4/-0) tests/favoritestest.cpp (+168/-0) tests/overviewtest.cpp (+4/-0) tests/previewtest.cpp (+3/-0) tests/resultstest.cpp (+3/-0) tests/settingsendtoendtest.cpp (+4/-0) tests/test-utils.h (+18/-0) |
To merge this branch: | bzr merge lp:~stolowski/unity-scopes-shell/favorite-scopes |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Needs Fixing | |
Albert Astals Cid (community) | Approve | ||
Michał Sawicz | Pending | ||
Review via email:
|
This proposal supersedes a proposal from 2014-08-14.
Commit message
Support scope favouriting.
Description of the change
Support scope favouriting.
Note: the only way to test it atm is to update gsettings key, e.g.
gsettings set com.canonical.
The change should be reflected in the overview ("Favorites") and in the Dash (cards of available scopes).
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Pete Woods (pete-woods) wrote : Posted in a previous version of this proposal | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:140
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Paweł Stołowski (stolowski) wrote : Posted in a previous version of this proposal | # |
> A quick question before reading in detail. Do the tests mess around with the
> your real settings for the favourite scopes?
No, the tests use a gsettings memory backend and the intention of that is to use a temporary value for the duration of the tests.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal | # |
Looking good, few minor comments.
All in all I feel like this required too much effort... Especially how you have to maintain two models :/
We should consider:
a) making the Scopes model a model canned queries, with dash requesting Scope objects as needed
b) using the same model for overview favorites and Scopes
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:144
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Paweł Stołowski (stolowski) wrote : Posted in a previous version of this proposal | # |
Ok, pushed a bunch of fixes and added comments inline.
As discussed on IRC, (a) and (b) suggested above are correct observations, but they require more refactoring in the existing code, so it's fot later.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:145
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal | # |
I seem to be losing the scope too early when I unfavorite it, causing the transition to be quite jarring :/
We'll need to defer destroying them for a while, or maybe move ownership to QML. Or stop supplying scopes in the model and relying on getScope() instead.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:146
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal | # |
+1, thanks!
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Albert Astals Cid (aacid) wrote : | # |
Michał approved the other branch, this one adds r147 that looks good
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:147
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 149. By Paweł Stołowski
-
Fix test failure.
Preview Diff
1 | === modified file 'debian/control' | |||
2 | --- debian/control 2014-08-14 15:31:11 +0000 | |||
3 | +++ debian/control 2014-08-25 17:04:42 +0000 | |||
4 | @@ -4,7 +4,7 @@ | |||
5 | 4 | Build-Depends: cmake, | 4 | Build-Depends: cmake, |
6 | 5 | debhelper (>= 9), | 5 | debhelper (>= 9), |
7 | 6 | libunity-api-dev (>= 7.88), | 6 | libunity-api-dev (>= 7.88), |
9 | 7 | libunity-scopes-dev (>= 0.6.2~), | 7 | libunity-scopes-dev (>= 0.6.3~), |
10 | 8 | libgsettings-qt-dev (>= 0.1), | 8 | libgsettings-qt-dev (>= 0.1), |
11 | 9 | libqtdbustest1-dev (>= 0.2), | 9 | libqtdbustest1-dev (>= 0.2), |
12 | 10 | libqtdbusmock1-dev (>= 0.2), | 10 | libqtdbusmock1-dev (>= 0.2), |
13 | @@ -15,6 +15,7 @@ | |||
14 | 15 | qtdeclarative5-dev, | 15 | qtdeclarative5-dev, |
15 | 16 | qtdeclarative5-dev-tools, | 16 | qtdeclarative5-dev-tools, |
16 | 17 | qtdeclarative5-qtquick2-plugin, | 17 | qtdeclarative5-qtquick2-plugin, |
17 | 18 | unity-schemas (>= 7.3.1), | ||
18 | 18 | Standards-Version: 3.9.5 | 19 | Standards-Version: 3.9.5 |
19 | 19 | Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> | 20 | Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
20 | 20 | Homepage: https://launchpad.net/unity-scopes-shell | 21 | Homepage: https://launchpad.net/unity-scopes-shell |
21 | @@ -30,6 +31,7 @@ | |||
22 | 30 | Pre-Depends: ${misc:Pre-Depends} | 31 | Pre-Depends: ${misc:Pre-Depends} |
23 | 31 | Depends: ${misc:Depends}, | 32 | Depends: ${misc:Depends}, |
24 | 32 | ${shlibs:Depends}, | 33 | ${shlibs:Depends}, |
25 | 34 | unity-schemas (>= 7.3.1), | ||
26 | 33 | Provides: unity-scopes-impl, | 35 | Provides: unity-scopes-impl, |
27 | 34 | unity-scopes-impl-0, | 36 | unity-scopes-impl-0, |
28 | 35 | unity-scopes-impl-1, | 37 | unity-scopes-impl-1, |
29 | 36 | 38 | ||
30 | === modified file 'src/Unity/CMakeLists.txt' | |||
31 | --- src/Unity/CMakeLists.txt 2014-08-14 15:31:11 +0000 | |||
32 | +++ src/Unity/CMakeLists.txt 2014-08-25 17:04:42 +0000 | |||
33 | @@ -3,7 +3,7 @@ | |||
34 | 3 | 3 | ||
35 | 4 | # Dependencies | 4 | # Dependencies |
36 | 5 | pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=4) | 5 | pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=4) |
38 | 6 | pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=0.6.2) | 6 | pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=0.6.3) |
39 | 7 | pkg_check_modules(GSETTINGSQT REQUIRED gsettings-qt) | 7 | pkg_check_modules(GSETTINGSQT REQUIRED gsettings-qt) |
40 | 8 | pkg_check_modules(UBUNTU_LOCATION_SERVICE REQUIRED ubuntu-location-service) | 8 | pkg_check_modules(UBUNTU_LOCATION_SERVICE REQUIRED ubuntu-location-service) |
41 | 9 | 9 | ||
42 | 10 | 10 | ||
43 | === modified file 'src/Unity/overviewcategories.cpp' | |||
44 | --- src/Unity/overviewcategories.cpp 2014-07-22 13:49:50 +0000 | |||
45 | +++ src/Unity/overviewcategories.cpp 2014-08-25 17:04:42 +0000 | |||
46 | @@ -53,7 +53,7 @@ | |||
47 | 53 | , m_isSurfacing(true) | 53 | , m_isSurfacing(true) |
48 | 54 | { | 54 | { |
49 | 55 | m_allScopes.reset(new OverviewResultsModel(this)); | 55 | m_allScopes.reset(new OverviewResultsModel(this)); |
51 | 56 | m_favouriteScopes.reset(new OverviewResultsModel(this)); | 56 | m_favoriteScopes.reset(new OverviewResultsModel(this)); |
52 | 57 | 57 | ||
53 | 58 | m_surfaceCategories.append(QSharedPointer<ScopesCategoryData>(new ScopesCategoryData("favorites", CATEGORY_JSON))); | 58 | m_surfaceCategories.append(QSharedPointer<ScopesCategoryData>(new ScopesCategoryData("favorites", CATEGORY_JSON))); |
54 | 59 | m_surfaceCategories.append(QSharedPointer<ScopesCategoryData>(new ScopesCategoryData("all", CATEGORY_JSON))); | 59 | m_surfaceCategories.append(QSharedPointer<ScopesCategoryData>(new ScopesCategoryData("all", CATEGORY_JSON))); |
55 | @@ -84,10 +84,10 @@ | |||
56 | 84 | QModelIndex changedIndex(index(1)); | 84 | QModelIndex changedIndex(index(1)); |
57 | 85 | dataChanged(changedIndex, changedIndex, roles); | 85 | dataChanged(changedIndex, changedIndex, roles); |
58 | 86 | } | 86 | } |
61 | 87 | 87 | ||
62 | 88 | void OverviewCategories::setFavouriteScopes(const QList<unity::scopes::ScopeMetadata::SPtr>& scopes) | 88 | void OverviewCategories::setFavoriteScopes(const QList<unity::scopes::ScopeMetadata::SPtr>& scopes) |
63 | 89 | { | 89 | { |
65 | 90 | m_favouriteScopes->setResults(scopes); | 90 | m_favoriteScopes->setResults(scopes); |
66 | 91 | 91 | ||
67 | 92 | if (!m_isSurfacing) return; | 92 | if (!m_isSurfacing) return; |
68 | 93 | 93 | ||
69 | @@ -98,6 +98,11 @@ | |||
70 | 98 | dataChanged(changedIndex, changedIndex, roles); | 98 | dataChanged(changedIndex, changedIndex, roles); |
71 | 99 | } | 99 | } |
72 | 100 | 100 | ||
73 | 101 | void OverviewCategories::updateFavoriteScopes(const QList<unity::scopes::ScopeMetadata::SPtr>& scopes) | ||
74 | 102 | { | ||
75 | 103 | m_favoriteScopes->setResults(scopes); | ||
76 | 104 | } | ||
77 | 105 | |||
78 | 101 | int OverviewCategories::rowCount(const QModelIndex& parent) const | 106 | int OverviewCategories::rowCount(const QModelIndex& parent) const |
79 | 102 | { | 107 | { |
80 | 103 | if (m_isSurfacing) { | 108 | if (m_isSurfacing) { |
81 | @@ -115,7 +120,7 @@ | |||
82 | 115 | } | 120 | } |
83 | 116 | 121 | ||
84 | 117 | ScopesCategoryData* catData = m_surfaceCategories.at(index.row()).data(); | 122 | ScopesCategoryData* catData = m_surfaceCategories.at(index.row()).data(); |
86 | 118 | OverviewResultsModel* results = index.row() == 0 ? m_favouriteScopes.data() : m_allScopes.data(); | 123 | OverviewResultsModel* results = index.row() == 0 ? m_favoriteScopes.data() : m_allScopes.data(); |
87 | 119 | 124 | ||
88 | 120 | switch (role) { | 125 | switch (role) { |
89 | 121 | case RoleCategoryId: | 126 | case RoleCategoryId: |
90 | 122 | 127 | ||
91 | === modified file 'src/Unity/overviewcategories.h' | |||
92 | --- src/Unity/overviewcategories.h 2014-07-18 09:21:24 +0000 | |||
93 | +++ src/Unity/overviewcategories.h 2014-08-25 17:04:42 +0000 | |||
94 | @@ -45,14 +45,15 @@ | |||
95 | 45 | int rowCount(const QModelIndex& parent = QModelIndex()) const override; | 45 | int rowCount(const QModelIndex& parent = QModelIndex()) const override; |
96 | 46 | 46 | ||
97 | 47 | void setAllScopes(const QList<unity::scopes::ScopeMetadata::SPtr>& scopes); | 47 | void setAllScopes(const QList<unity::scopes::ScopeMetadata::SPtr>& scopes); |
99 | 48 | void setFavouriteScopes(const QList<unity::scopes::ScopeMetadata::SPtr>& scopes); | 48 | void setFavoriteScopes(const QList<unity::scopes::ScopeMetadata::SPtr>& scopes); |
100 | 49 | void updateFavoriteScopes(const QList<unity::scopes::ScopeMetadata::SPtr>& scopes); | ||
101 | 49 | 50 | ||
102 | 50 | private: | 51 | private: |
103 | 51 | bool m_isSurfacing; | 52 | bool m_isSurfacing; |
104 | 52 | 53 | ||
105 | 53 | QList<QSharedPointer<ScopesCategoryData>> m_surfaceCategories; | 54 | QList<QSharedPointer<ScopesCategoryData>> m_surfaceCategories; |
106 | 54 | QScopedPointer<OverviewResultsModel> m_allScopes; | 55 | QScopedPointer<OverviewResultsModel> m_allScopes; |
108 | 55 | QScopedPointer<OverviewResultsModel> m_favouriteScopes; | 56 | QScopedPointer<OverviewResultsModel> m_favoriteScopes; |
109 | 56 | }; | 57 | }; |
110 | 57 | 58 | ||
111 | 58 | } // namespace scopes_ng | 59 | } // namespace scopes_ng |
112 | 59 | 60 | ||
113 | === modified file 'src/Unity/overviewresults.cpp' | |||
114 | --- src/Unity/overviewresults.cpp 2014-08-25 17:04:42 +0000 | |||
115 | +++ src/Unity/overviewresults.cpp 2014-08-25 17:04:42 +0000 | |||
116 | @@ -25,6 +25,7 @@ | |||
117 | 25 | #include "utils.h" | 25 | #include "utils.h" |
118 | 26 | 26 | ||
119 | 27 | #include <unity/scopes/Result.h> | 27 | #include <unity/scopes/Result.h> |
120 | 28 | #include <QSet> | ||
121 | 28 | 29 | ||
122 | 29 | namespace scopes_ng { | 30 | namespace scopes_ng { |
123 | 30 | 31 | ||
124 | @@ -68,9 +69,50 @@ | |||
125 | 68 | 69 | ||
126 | 69 | void OverviewResultsModel::setResults(const QList<unity::scopes::ScopeMetadata::SPtr>& results) | 70 | void OverviewResultsModel::setResults(const QList<unity::scopes::ScopeMetadata::SPtr>& results) |
127 | 70 | { | 71 | { |
131 | 71 | beginResetModel(); | 72 | if (m_results.empty()) { |
132 | 72 | m_results = results; | 73 | beginResetModel(); |
133 | 73 | endResetModel(); | 74 | m_results = results; |
134 | 75 | endResetModel(); | ||
135 | 76 | Q_EMIT countChanged(); | ||
136 | 77 | return; | ||
137 | 78 | } | ||
138 | 79 | |||
139 | 80 | QSet<QString> newResult; | ||
140 | 81 | for (auto const res: results) { | ||
141 | 82 | newResult.insert(QString::fromStdString(res->scope_id())); | ||
142 | 83 | } | ||
143 | 84 | |||
144 | 85 | // itearate over old results, remove rows that are not present in new results | ||
145 | 86 | int row = 0; | ||
146 | 87 | for (auto it = m_results.begin(); it != m_results.end();) | ||
147 | 88 | { | ||
148 | 89 | if (!newResult.contains(QString::fromStdString((*it)->scope_id()))) { | ||
149 | 90 | beginRemoveRows(QModelIndex(), row, row); | ||
150 | 91 | it = m_results.erase(it); | ||
151 | 92 | endRemoveRows(); | ||
152 | 93 | } else { | ||
153 | 94 | ++it; | ||
154 | 95 | ++row; | ||
155 | 96 | } | ||
156 | 97 | } | ||
157 | 98 | |||
158 | 99 | QSet<QString> oldResult; | ||
159 | 100 | for (auto const res: m_results) { | ||
160 | 101 | oldResult.insert(QString::fromStdString(res->scope_id())); | ||
161 | 102 | } | ||
162 | 103 | |||
163 | 104 | // iterate over new results, insert rows if not in previous model | ||
164 | 105 | row = 0; | ||
165 | 106 | for (auto const newRes: results) | ||
166 | 107 | { | ||
167 | 108 | if (!oldResult.contains(QString::fromStdString(newRes->scope_id()))) | ||
168 | 109 | { | ||
169 | 110 | beginInsertRows(QModelIndex(), row, row); | ||
170 | 111 | m_results.insert(row, newRes); | ||
171 | 112 | endInsertRows(); | ||
172 | 113 | } | ||
173 | 114 | ++row; | ||
174 | 115 | } | ||
175 | 74 | Q_EMIT countChanged(); | 116 | Q_EMIT countChanged(); |
176 | 75 | } | 117 | } |
177 | 76 | 118 | ||
178 | 77 | 119 | ||
179 | === modified file 'src/Unity/overviewscope.cpp' | |||
180 | --- src/Unity/overviewscope.cpp 2014-08-01 11:43:58 +0000 | |||
181 | +++ src/Unity/overviewscope.cpp 2014-08-25 17:04:42 +0000 | |||
182 | @@ -66,11 +66,11 @@ | |||
183 | 66 | } | 66 | } |
184 | 67 | 67 | ||
185 | 68 | QMap<QString, scopes::ScopeMetadata::SPtr> allMetadata = m_scopesInstance->getAllMetadata(); | 68 | QMap<QString, scopes::ScopeMetadata::SPtr> allMetadata = m_scopesInstance->getAllMetadata(); |
187 | 69 | QList<scopes::ScopeMetadata::SPtr> favourites; | 69 | QList<scopes::ScopeMetadata::SPtr> favorites; |
188 | 70 | Q_FOREACH(QString id, m_scopesInstance->getFavoriteIds()) { | 70 | Q_FOREACH(QString id, m_scopesInstance->getFavoriteIds()) { |
189 | 71 | auto it = allMetadata.find(id); | 71 | auto it = allMetadata.find(id); |
190 | 72 | if (it != allMetadata.end()) { | 72 | if (it != allMetadata.end()) { |
192 | 73 | favourites.append(it.value()); | 73 | favorites.append(it.value()); |
193 | 74 | } | 74 | } |
194 | 75 | } | 75 | } |
195 | 76 | 76 | ||
196 | @@ -88,7 +88,7 @@ | |||
197 | 88 | 88 | ||
198 | 89 | // FIXME: filter invisible scopes? | 89 | // FIXME: filter invisible scopes? |
199 | 90 | categories->setAllScopes(allScopes); | 90 | categories->setAllScopes(allScopes); |
201 | 91 | categories->setFavouriteScopes(favourites); | 91 | categories->setFavoriteScopes(favorites); |
202 | 92 | } | 92 | } |
203 | 93 | 93 | ||
204 | 94 | QString OverviewScope::id() const | 94 | QString OverviewScope::id() const |
205 | @@ -106,6 +106,27 @@ | |||
206 | 106 | } | 106 | } |
207 | 107 | } | 107 | } |
208 | 108 | 108 | ||
209 | 109 | void OverviewScope::updateFavorites(const QStringList& favorites) | ||
210 | 110 | { | ||
211 | 111 | QList<scopes::ScopeMetadata::SPtr> favs; | ||
212 | 112 | auto allMetadata = m_scopesInstance->getAllMetadata(); | ||
213 | 113 | for (auto const id: favorites) | ||
214 | 114 | { | ||
215 | 115 | auto it = allMetadata.find(id); | ||
216 | 116 | if (it != allMetadata.end()) { | ||
217 | 117 | favs.append(it.value()); | ||
218 | 118 | } | ||
219 | 119 | } | ||
220 | 120 | |||
221 | 121 | OverviewCategories* categories = qobject_cast<OverviewCategories*>(m_categories.data()); | ||
222 | 122 | if (!categories) { | ||
223 | 123 | qWarning("Unable to cast m_categories to OverviewCategories"); | ||
224 | 124 | return; | ||
225 | 125 | } | ||
226 | 126 | |||
227 | 127 | categories->updateFavoriteScopes(favs); | ||
228 | 128 | } | ||
229 | 129 | |||
230 | 109 | void OverviewScope::dispatchSearch() | 130 | void OverviewScope::dispatchSearch() |
231 | 110 | { | 131 | { |
232 | 111 | OverviewCategories* categories = qobject_cast<OverviewCategories*>(m_categories.data()); | 132 | OverviewCategories* categories = qobject_cast<OverviewCategories*>(m_categories.data()); |
233 | 112 | 133 | ||
234 | === modified file 'src/Unity/overviewscope.h' | |||
235 | --- src/Unity/overviewscope.h 2014-08-01 11:43:58 +0000 | |||
236 | +++ src/Unity/overviewscope.h 2014-08-25 17:04:42 +0000 | |||
237 | @@ -38,6 +38,8 @@ | |||
238 | 38 | 38 | ||
239 | 39 | void dispatchSearch() override; | 39 | void dispatchSearch() override; |
240 | 40 | 40 | ||
241 | 41 | void updateFavorites(const QStringList& favorites); | ||
242 | 42 | |||
243 | 41 | unity::scopes::ScopeProxy proxy_for_result(unity::scopes::Result::SPtr const& result) const override; | 43 | unity::scopes::ScopeProxy proxy_for_result(unity::scopes::Result::SPtr const& result) const override; |
244 | 42 | 44 | ||
245 | 43 | private Q_SLOTS: | 45 | private Q_SLOTS: |
246 | 44 | 46 | ||
247 | === modified file 'src/Unity/scope.cpp' | |||
248 | --- src/Unity/scope.cpp 2014-08-18 18:41:23 +0000 | |||
249 | +++ src/Unity/scope.cpp 2014-08-25 17:04:42 +0000 | |||
250 | @@ -74,6 +74,7 @@ | |||
251 | 74 | , m_delayedClear(false) | 74 | , m_delayedClear(false) |
252 | 75 | , m_hasNavigation(false) | 75 | , m_hasNavigation(false) |
253 | 76 | , m_hasAltNavigation(false) | 76 | , m_hasAltNavigation(false) |
254 | 77 | , m_favorite(false) | ||
255 | 77 | , m_searchController(new CollectionController) | 78 | , m_searchController(new CollectionController) |
256 | 78 | , m_activationController(new CollectionController) | 79 | , m_activationController(new CollectionController) |
257 | 79 | , m_status(Status::Okay) | 80 | , m_status(Status::Okay) |
258 | @@ -753,7 +754,7 @@ | |||
259 | 753 | 754 | ||
260 | 754 | bool Scope::favorite() const | 755 | bool Scope::favorite() const |
261 | 755 | { | 756 | { |
263 | 756 | return true; | 757 | return m_favorite; |
264 | 757 | } | 758 | } |
265 | 758 | 759 | ||
266 | 759 | QString Scope::shortcut() const | 760 | QString Scope::shortcut() const |
267 | @@ -993,9 +994,12 @@ | |||
268 | 993 | 994 | ||
269 | 994 | void Scope::setFavorite(const bool value) | 995 | void Scope::setFavorite(const bool value) |
270 | 995 | { | 996 | { |
274 | 996 | Q_UNUSED(value); | 997 | if (value != m_favorite) |
275 | 997 | 998 | { | |
276 | 998 | qWarning("Unimplemented: %s", __func__); | 999 | m_favorite = value; |
277 | 1000 | Q_EMIT favoriteChanged(value); | ||
278 | 1001 | m_scopesInstance->setFavorite(id(), value); | ||
279 | 1002 | } | ||
280 | 999 | } | 1003 | } |
281 | 1000 | 1004 | ||
282 | 1001 | void Scope::activate(QVariant const& result_var) | 1005 | void Scope::activate(QVariant const& result_var) |
283 | 1002 | 1006 | ||
284 | === modified file 'src/Unity/scope.h' | |||
285 | --- src/Unity/scope.h 2014-08-13 08:36:17 +0000 | |||
286 | +++ src/Unity/scope.h 2014-08-25 17:04:42 +0000 | |||
287 | @@ -111,6 +111,8 @@ | |||
288 | 111 | 111 | ||
289 | 112 | virtual bool event(QEvent* ev) override; | 112 | virtual bool event(QEvent* ev) override; |
290 | 113 | 113 | ||
291 | 114 | Q_PROPERTY(bool favorite READ favorite WRITE setFavorite NOTIFY favoriteChanged) | ||
292 | 115 | |||
293 | 114 | /* getters */ | 116 | /* getters */ |
294 | 115 | QString id() const override; | 117 | QString id() const override; |
295 | 116 | QString name() const override; | 118 | QString name() const override; |
296 | @@ -162,6 +164,7 @@ | |||
297 | 162 | 164 | ||
298 | 163 | Q_SIGNALS: | 165 | Q_SIGNALS: |
299 | 164 | void resultsDirtyChanged(); | 166 | void resultsDirtyChanged(); |
300 | 167 | void favoriteChanged(bool); | ||
301 | 165 | 168 | ||
302 | 166 | private Q_SLOTS: | 169 | private Q_SLOTS: |
303 | 167 | void flushUpdates(); | 170 | void flushUpdates(); |
304 | @@ -207,6 +210,7 @@ | |||
305 | 207 | bool m_delayedClear; | 210 | bool m_delayedClear; |
306 | 208 | bool m_hasNavigation; | 211 | bool m_hasNavigation; |
307 | 209 | bool m_hasAltNavigation; | 212 | bool m_hasAltNavigation; |
308 | 213 | bool m_favorite; | ||
309 | 210 | 214 | ||
310 | 211 | std::unique_ptr<CollectionController> m_searchController; | 215 | std::unique_ptr<CollectionController> m_searchController; |
311 | 212 | std::unique_ptr<CollectionController> m_activationController; | 216 | std::unique_ptr<CollectionController> m_activationController; |
312 | 213 | 217 | ||
313 | === modified file 'src/Unity/scopes.cpp' | |||
314 | --- src/Unity/scopes.cpp 2014-08-01 13:12:54 +0000 | |||
315 | +++ src/Unity/scopes.cpp 2014-08-25 17:04:42 +0000 | |||
316 | @@ -82,6 +82,7 @@ | |||
317 | 82 | } | 82 | } |
318 | 83 | 83 | ||
319 | 84 | int Scopes::LIST_DELAY = -1; | 84 | int Scopes::LIST_DELAY = -1; |
320 | 85 | const int Scopes::SCOPE_DELETE_DELAY = 3; | ||
321 | 85 | 86 | ||
322 | 86 | class Scopes::Priv : public QObject { | 87 | class Scopes::Priv : public QObject { |
323 | 87 | Q_OBJECT | 88 | Q_OBJECT |
324 | @@ -111,6 +112,12 @@ | |||
325 | 111 | 112 | ||
326 | 112 | QDBusConnection::sessionBus().connect(QString(), QString("/com/canonical/unity/scopes"), QString("com.canonical.unity.scopes"), QString("InvalidateResults"), this, SLOT(invalidateScopeResults(QString))); | 113 | QDBusConnection::sessionBus().connect(QString(), QString("/com/canonical/unity/scopes"), QString("com.canonical.unity.scopes"), QString("InvalidateResults"), this, SLOT(invalidateScopeResults(QString))); |
327 | 113 | 114 | ||
328 | 115 | m_dashSettings = QGSettings::isSchemaInstalled("com.canonical.Unity.Dash") ? new QGSettings("com.canonical.Unity.Dash", QByteArray(), this) : nullptr; | ||
329 | 116 | if (m_dashSettings) | ||
330 | 117 | { | ||
331 | 118 | QObject::connect(m_dashSettings, &QGSettings::changed, this, &Scopes::dashSettingsChanged); | ||
332 | 119 | } | ||
333 | 120 | |||
334 | 114 | m_overviewScope = new OverviewScope(this); | 121 | m_overviewScope = new OverviewScope(this); |
335 | 115 | m_locationService.reset(new UbuntuLocationService()); | 122 | m_locationService.reset(new UbuntuLocationService()); |
336 | 116 | } | 123 | } |
337 | @@ -159,31 +166,8 @@ | |||
338 | 159 | std::bind(&Scopes::Priv::safeInvalidateScopeResults, | 166 | std::bind(&Scopes::Priv::safeInvalidateScopeResults, |
339 | 160 | m_priv.get(), SCOPES_SCOPE_ID)))); | 167 | m_priv.get(), SCOPES_SCOPE_ID)))); |
340 | 161 | 168 | ||
341 | 162 | // FIXME: use a dconf setting for this | ||
342 | 163 | QByteArray enabledScopes = qgetenv("UNITY_SCOPES_LIST"); | ||
343 | 164 | |||
344 | 165 | beginResetModel(); | 169 | beginResetModel(); |
345 | 166 | 170 | ||
346 | 167 | if (!enabledScopes.isNull()) { | ||
347 | 168 | QList<QByteArray> scopeList = enabledScopes.split(';'); | ||
348 | 169 | for (int i = 0; i < scopeList.size(); i++) { | ||
349 | 170 | std::string scope_name(scopeList[i].constData()); | ||
350 | 171 | auto it = scopes.find(scope_name); | ||
351 | 172 | if (it != scopes.end()) { | ||
352 | 173 | auto scope = new Scope(this); | ||
353 | 174 | scope->setScopeData(it->second); | ||
354 | 175 | m_scopes.append(scope); | ||
355 | 176 | } | ||
356 | 177 | } | ||
357 | 178 | } else { | ||
358 | 179 | // add all the scopes | ||
359 | 180 | for (auto it = scopes.begin(); it != scopes.end(); ++it) { | ||
360 | 181 | auto scope = new Scope(this); | ||
361 | 182 | scope->setScopeData(it->second); | ||
362 | 183 | m_scopes.append(scope); | ||
363 | 184 | } | ||
364 | 185 | } | ||
365 | 186 | |||
366 | 187 | // HACK! deal with the overview scope | 171 | // HACK! deal with the overview scope |
367 | 188 | { | 172 | { |
368 | 189 | auto it = scopes.find(SCOPES_SCOPE_ID); | 173 | auto it = scopes.find(SCOPES_SCOPE_ID); |
369 | @@ -199,6 +183,7 @@ | |||
370 | 199 | m_cachedMetadata[QString::fromStdString(it->first)] = std::make_shared<unity::scopes::ScopeMetadata>(it->second); | 183 | m_cachedMetadata[QString::fromStdString(it->first)] = std::make_shared<unity::scopes::ScopeMetadata>(it->second); |
371 | 200 | } | 184 | } |
372 | 201 | 185 | ||
373 | 186 | processFavoriteScopes(); | ||
374 | 202 | endResetModel(); | 187 | endResetModel(); |
375 | 203 | 188 | ||
376 | 204 | m_loaded = true; | 189 | m_loaded = true; |
377 | @@ -210,6 +195,104 @@ | |||
378 | 210 | m_listThread = nullptr; | 195 | m_listThread = nullptr; |
379 | 211 | } | 196 | } |
380 | 212 | 197 | ||
381 | 198 | void Scopes::processFavoriteScopes() | ||
382 | 199 | { | ||
383 | 200 | // | ||
384 | 201 | // read the favoriteScopes array value from gsettings. | ||
385 | 202 | // process it and turn its values into scope ids. | ||
386 | 203 | // create new Scope objects or remove existing according to the list of favorities. | ||
387 | 204 | // notify about scopes model changes accordingly. | ||
388 | 205 | if (m_dashSettings) { | ||
389 | 206 | QStringList newFavorites; | ||
390 | 207 | QSet<QString> favScopesLut; | ||
391 | 208 | for (auto const& fv: m_dashSettings->get("favoriteScopes").toList()) | ||
392 | 209 | { | ||
393 | 210 | try | ||
394 | 211 | { | ||
395 | 212 | auto const query = unity::scopes::CannedQuery::from_uri(fv.toString().toStdString()); | ||
396 | 213 | const QString id = QString::fromStdString(query.scope_id()); | ||
397 | 214 | newFavorites.push_back(id); | ||
398 | 215 | favScopesLut.insert(id); | ||
399 | 216 | } | ||
400 | 217 | catch (const InvalidArgumentException &e) | ||
401 | 218 | { | ||
402 | 219 | qWarning() << "Invalid canned query '" << fv.toString() << "'" << QString::fromStdString(e.what()); | ||
403 | 220 | } | ||
404 | 221 | } | ||
405 | 222 | |||
406 | 223 | // this prevents further processing if we get called back when calling scope->setFavorite() below | ||
407 | 224 | if (m_favoriteScopes == newFavorites) | ||
408 | 225 | return; | ||
409 | 226 | |||
410 | 227 | m_favoriteScopes = newFavorites; | ||
411 | 228 | |||
412 | 229 | QSet<QString> oldScopes; | ||
413 | 230 | int row = 0; | ||
414 | 231 | // remove un-favorited scopes | ||
415 | 232 | for (auto it = m_scopes.begin(); it != m_scopes.end();) | ||
416 | 233 | { | ||
417 | 234 | if (!favScopesLut.contains((*it)->id())) | ||
418 | 235 | { | ||
419 | 236 | beginRemoveRows(QModelIndex(), row, row); | ||
420 | 237 | (*it)->setFavorite(false); | ||
421 | 238 | // | ||
422 | 239 | // we need to delay actual deletion of Scope object so that shell can animate it | ||
423 | 240 | QTimer::singleShot(1000 * SCOPE_DELETE_DELAY, (*it), SLOT(deleteLater)); | ||
424 | 241 | it = m_scopes.erase(it); | ||
425 | 242 | endRemoveRows(); | ||
426 | 243 | } | ||
427 | 244 | else | ||
428 | 245 | { | ||
429 | 246 | oldScopes.insert((*it)->id()); | ||
430 | 247 | ++it; | ||
431 | 248 | ++row; | ||
432 | 249 | } | ||
433 | 250 | } | ||
434 | 251 | |||
435 | 252 | // add new favorites | ||
436 | 253 | row = 0; | ||
437 | 254 | for (auto favIt = m_favoriteScopes.begin(); favIt != m_favoriteScopes.end(); ) | ||
438 | 255 | { | ||
439 | 256 | auto const fav = *favIt; | ||
440 | 257 | if (!oldScopes.contains(fav)) | ||
441 | 258 | { | ||
442 | 259 | auto it = m_cachedMetadata.find(fav); | ||
443 | 260 | if (it != m_cachedMetadata.end()) | ||
444 | 261 | { | ||
445 | 262 | auto scope = new Scope(this); | ||
446 | 263 | scope->setScopeData(*(it.value())); | ||
447 | 264 | scope->setFavorite(true); | ||
448 | 265 | beginInsertRows(QModelIndex(), row, row); | ||
449 | 266 | m_scopes.insert(row, scope); | ||
450 | 267 | endInsertRows(); | ||
451 | 268 | } | ||
452 | 269 | else | ||
453 | 270 | { | ||
454 | 271 | qWarning() << "No such scope:" << fav; | ||
455 | 272 | favIt = m_favoriteScopes.erase(favIt); | ||
456 | 273 | continue; | ||
457 | 274 | } | ||
458 | 275 | } | ||
459 | 276 | ++row; | ||
460 | 277 | ++favIt; | ||
461 | 278 | } | ||
462 | 279 | } | ||
463 | 280 | } | ||
464 | 281 | |||
465 | 282 | void Scopes::dashSettingsChanged(QString const& key) | ||
466 | 283 | { | ||
467 | 284 | if (key != "favoriteScopes") { | ||
468 | 285 | return; | ||
469 | 286 | } | ||
470 | 287 | |||
471 | 288 | processFavoriteScopes(); | ||
472 | 289 | |||
473 | 290 | if (m_overviewScope) | ||
474 | 291 | { | ||
475 | 292 | m_overviewScope->updateFavorites(m_favoriteScopes); | ||
476 | 293 | } | ||
477 | 294 | } | ||
478 | 295 | |||
479 | 213 | void Scopes::refreshFinished() | 296 | void Scopes::refreshFinished() |
480 | 214 | { | 297 | { |
481 | 215 | ScopeListWorker* thread = qobject_cast<ScopeListWorker*>(sender()); | 298 | ScopeListWorker* thread = qobject_cast<ScopeListWorker*>(sender()); |
482 | @@ -288,13 +371,39 @@ | |||
483 | 288 | 371 | ||
484 | 289 | QStringList Scopes::getFavoriteIds() const | 372 | QStringList Scopes::getFavoriteIds() const |
485 | 290 | { | 373 | { |
490 | 291 | QStringList ids; | 374 | return m_favoriteScopes; |
491 | 292 | 375 | } | |
492 | 293 | Q_FOREACH(Scope* scope, m_scopes) { | 376 | |
493 | 294 | ids << scope->id(); | 377 | void Scopes::setFavorite(QString const& scopeId, bool value) |
494 | 378 | { | ||
495 | 379 | if (m_dashSettings) | ||
496 | 380 | { | ||
497 | 381 | QStringList cannedQueries; | ||
498 | 382 | bool changed = false; | ||
499 | 383 | |||
500 | 384 | for (auto const& fav: m_favoriteScopes) | ||
501 | 385 | { | ||
502 | 386 | if (value == false && fav == scopeId) { | ||
503 | 387 | changed = true; | ||
504 | 388 | continue; // skip it | ||
505 | 389 | } | ||
506 | 390 | // TODO: use CannedQuery::to_uri() when we really support them | ||
507 | 391 | const QString query = "scope://" + fav; | ||
508 | 392 | cannedQueries.push_back(query); | ||
509 | 393 | } | ||
510 | 394 | |||
511 | 395 | if (value && !m_favoriteScopes.contains(scopeId)) { | ||
512 | 396 | const QString query = "scope://" + scopeId; | ||
513 | 397 | cannedQueries.push_back(query); | ||
514 | 398 | changed = true; | ||
515 | 399 | } | ||
516 | 400 | |||
517 | 401 | if (changed) { | ||
518 | 402 | // update gsettings entry | ||
519 | 403 | // note: this will trigger notification, so that new favorites are processed by processFavoriteScopes | ||
520 | 404 | m_dashSettings->set("favoriteScopes", QVariant(cannedQueries)); | ||
521 | 405 | } | ||
522 | 295 | } | 406 | } |
523 | 296 | |||
524 | 297 | return ids; | ||
525 | 298 | } | 407 | } |
526 | 299 | 408 | ||
527 | 300 | QMap<QString, unity::scopes::ScopeMetadata::SPtr> Scopes::getAllMetadata() const | 409 | QMap<QString, unity::scopes::ScopeMetadata::SPtr> Scopes::getAllMetadata() const |
528 | 301 | 410 | ||
529 | === modified file 'src/Unity/scopes.h' | |||
530 | --- src/Unity/scopes.h 2014-08-01 13:12:54 +0000 | |||
531 | +++ src/Unity/scopes.h 2014-08-25 17:04:42 +0000 | |||
532 | @@ -29,6 +29,7 @@ | |||
533 | 29 | #include <QThread> | 29 | #include <QThread> |
534 | 30 | #include <QStringList> | 30 | #include <QStringList> |
535 | 31 | #include <QSharedPointer> | 31 | #include <QSharedPointer> |
536 | 32 | #include <QGSettings> | ||
537 | 32 | 33 | ||
538 | 33 | #include <unity/scopes/Runtime.h> | 34 | #include <unity/scopes/Runtime.h> |
539 | 34 | #include <unity/scopes/Registry.h> | 35 | #include <unity/scopes/Registry.h> |
540 | @@ -40,6 +41,7 @@ | |||
541 | 40 | { | 41 | { |
542 | 41 | 42 | ||
543 | 42 | class Scope; | 43 | class Scope; |
544 | 44 | class OverviewScope; | ||
545 | 43 | 45 | ||
546 | 44 | class Q_DECL_EXPORT Scopes : public unity::shell::scopes::ScopesInterface | 46 | class Q_DECL_EXPORT Scopes : public unity::shell::scopes::ScopesInterface |
547 | 45 | { | 47 | { |
548 | @@ -58,6 +60,7 @@ | |||
549 | 58 | unity::scopes::ScopeMetadata::SPtr getCachedMetadata(QString const& scopeId) const; | 60 | unity::scopes::ScopeMetadata::SPtr getCachedMetadata(QString const& scopeId) const; |
550 | 59 | QMap<QString, unity::scopes::ScopeMetadata::SPtr> getAllMetadata() const; | 61 | QMap<QString, unity::scopes::ScopeMetadata::SPtr> getAllMetadata() const; |
551 | 60 | QStringList getFavoriteIds() const; | 62 | QStringList getFavoriteIds() const; |
552 | 63 | void setFavorite(QString const& scopeId, bool value); | ||
553 | 61 | 64 | ||
554 | 62 | void refreshScopeMetadata(); | 65 | void refreshScopeMetadata(); |
555 | 63 | 66 | ||
556 | @@ -71,6 +74,8 @@ | |||
557 | 71 | void metadataRefreshed(); | 74 | void metadataRefreshed(); |
558 | 72 | 75 | ||
559 | 73 | private Q_SLOTS: | 76 | private Q_SLOTS: |
560 | 77 | void dashSettingsChanged(QString const &key); | ||
561 | 78 | void processFavoriteScopes(); | ||
562 | 74 | void populateScopes(); | 79 | void populateScopes(); |
563 | 75 | void discoveryFinished(); | 80 | void discoveryFinished(); |
564 | 76 | void refreshFinished(); | 81 | void refreshFinished(); |
565 | @@ -78,11 +83,14 @@ | |||
566 | 78 | 83 | ||
567 | 79 | private: | 84 | private: |
568 | 80 | static int LIST_DELAY; | 85 | static int LIST_DELAY; |
569 | 86 | static const int SCOPE_DELETE_DELAY; | ||
570 | 81 | class Priv; | 87 | class Priv; |
571 | 82 | 88 | ||
572 | 83 | QList<Scope*> m_scopes; | 89 | QList<Scope*> m_scopes; |
573 | 90 | QStringList m_favoriteScopes; | ||
574 | 91 | QGSettings* m_dashSettings; | ||
575 | 84 | QMap<QString, unity::scopes::ScopeMetadata::SPtr> m_cachedMetadata; | 92 | QMap<QString, unity::scopes::ScopeMetadata::SPtr> m_cachedMetadata; |
577 | 85 | Scope* m_overviewScope; | 93 | OverviewScope* m_overviewScope; |
578 | 86 | QThread* m_listThread; | 94 | QThread* m_listThread; |
579 | 87 | bool m_loaded; | 95 | bool m_loaded; |
580 | 88 | 96 | ||
581 | 89 | 97 | ||
582 | === modified file 'tests/CMakeLists.txt' | |||
583 | --- tests/CMakeLists.txt 2014-08-13 17:16:21 +0000 | |||
584 | +++ tests/CMakeLists.txt 2014-08-25 17:04:42 +0000 | |||
585 | @@ -36,6 +36,7 @@ | |||
586 | 36 | add_executable(${_test}Exec | 36 | add_executable(${_test}Exec |
587 | 37 | ${_test}.cpp | 37 | ${_test}.cpp |
588 | 38 | ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/PreviewModelInterface.h | 38 | ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/PreviewModelInterface.h |
589 | 39 | ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ScopeInterface.h | ||
590 | 39 | ) | 40 | ) |
591 | 40 | qt5_use_modules(${_test}Exec Test Core Qml DBus) | 41 | qt5_use_modules(${_test}Exec Test Core Qml DBus) |
592 | 41 | set_tests_properties(test${CLASSNAME}${_test} | 42 | set_tests_properties(test${CLASSNAME}${_test} |
593 | @@ -57,6 +58,7 @@ | |||
594 | 57 | 58 | ||
595 | 58 | run_tests( | 59 | run_tests( |
596 | 59 | departmentstest | 60 | departmentstest |
597 | 61 | favoritestest | ||
598 | 60 | locationtest | 62 | locationtest |
599 | 61 | overviewtest | 63 | overviewtest |
600 | 62 | previewtest | 64 | previewtest |
601 | 63 | 65 | ||
602 | === modified file 'tests/departmentstest.cpp' | |||
603 | --- tests/departmentstest.cpp 2014-08-04 15:06:55 +0000 | |||
604 | +++ tests/departmentstest.cpp 2014-08-25 17:04:42 +0000 | |||
605 | @@ -64,6 +64,10 @@ | |||
606 | 64 | 64 | ||
607 | 65 | void init() | 65 | void init() |
608 | 66 | { | 66 | { |
609 | 67 | QStringList favs; | ||
610 | 68 | favs << "scope://mock-scope-departments" << "scope://mock-scope-double-nav"; | ||
611 | 69 | setFavouriteScopes(favs); | ||
612 | 70 | |||
613 | 67 | m_scopes.reset(new Scopes(nullptr)); | 71 | m_scopes.reset(new Scopes(nullptr)); |
614 | 68 | // no scopes on startup | 72 | // no scopes on startup |
615 | 69 | QCOMPARE(m_scopes->rowCount(), 0); | 73 | QCOMPARE(m_scopes->rowCount(), 0); |
616 | 70 | 74 | ||
617 | === added file 'tests/favoritestest.cpp' | |||
618 | --- tests/favoritestest.cpp 1970-01-01 00:00:00 +0000 | |||
619 | +++ tests/favoritestest.cpp 2014-08-25 17:04:42 +0000 | |||
620 | @@ -0,0 +1,168 @@ | |||
621 | 1 | /* | ||
622 | 2 | * Copyright (C) 2014 Canonical, Ltd. | ||
623 | 3 | * | ||
624 | 4 | * This program is free software; you can redistribute it and/or modify | ||
625 | 5 | * it under the terms of the GNU General Public License as published by | ||
626 | 6 | * the Free Software Foundation; version 3. | ||
627 | 7 | * | ||
628 | 8 | * This program is distributed in the hope that it will be useful, | ||
629 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
630 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
631 | 11 | * GNU General Public License for more details. | ||
632 | 12 | * | ||
633 | 13 | * You should have received a copy of the GNU General Public License | ||
634 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
635 | 15 | * | ||
636 | 16 | * Authors: | ||
637 | 17 | * Pawel Stolowski <pawel.stolowski@canonical.com> | ||
638 | 18 | */ | ||
639 | 19 | |||
640 | 20 | #include <QObject> | ||
641 | 21 | #include <QTest> | ||
642 | 22 | #include <QScopedPointer> | ||
643 | 23 | #include <QSignalSpy> | ||
644 | 24 | |||
645 | 25 | #include <scopes.h> | ||
646 | 26 | #include <scope.h> | ||
647 | 27 | #include <overviewresults.h> | ||
648 | 28 | #include <unity/shell/scopes/ScopeInterface.h> | ||
649 | 29 | |||
650 | 30 | #include "registry-spawner.h" | ||
651 | 31 | #include "test-utils.h" | ||
652 | 32 | |||
653 | 33 | using namespace scopes_ng; | ||
654 | 34 | using namespace unity::shell::scopes; | ||
655 | 35 | |||
656 | 36 | class FavoritesTest: public QObject | ||
657 | 37 | { | ||
658 | 38 | Q_OBJECT | ||
659 | 39 | private: | ||
660 | 40 | QScopedPointer<Scopes> m_scopes; | ||
661 | 41 | Scope* m_overviewScope; | ||
662 | 42 | QScopedPointer<RegistrySpawner> m_registry; | ||
663 | 43 | |||
664 | 44 | private Q_SLOTS: | ||
665 | 45 | void initTestCase() | ||
666 | 46 | { | ||
667 | 47 | m_registry.reset(new RegistrySpawner); | ||
668 | 48 | } | ||
669 | 49 | |||
670 | 50 | void cleanupTestCase() | ||
671 | 51 | { | ||
672 | 52 | m_registry.reset(); | ||
673 | 53 | } | ||
674 | 54 | |||
675 | 55 | void init() | ||
676 | 56 | { | ||
677 | 57 | setFavouriteScopes(QStringList()); | ||
678 | 58 | |||
679 | 59 | m_scopes.reset(new Scopes(nullptr)); | ||
680 | 60 | |||
681 | 61 | // no scopes on startup | ||
682 | 62 | QCOMPARE(m_scopes->rowCount(), 0); | ||
683 | 63 | QCOMPARE(m_scopes->loaded(), false); | ||
684 | 64 | QSignalSpy spy(m_scopes.data(), SIGNAL(loadedChanged())); | ||
685 | 65 | |||
686 | 66 | // wait till the registry spawns | ||
687 | 67 | QVERIFY(spy.wait()); | ||
688 | 68 | QCOMPARE(m_scopes->loaded(), true); | ||
689 | 69 | |||
690 | 70 | // no scopes after startup, since favorites empty | ||
691 | 71 | QCOMPARE(m_scopes->rowCount(), 0); | ||
692 | 72 | |||
693 | 73 | // get overview scope proxy | ||
694 | 74 | m_overviewScope = qobject_cast<scopes_ng::Scope*>(m_scopes->overviewScope()); | ||
695 | 75 | QVERIFY(m_overviewScope!= nullptr); | ||
696 | 76 | m_overviewScope->setActive(true); | ||
697 | 77 | } | ||
698 | 78 | |||
699 | 79 | void testFavoritePropertyUpdates() | ||
700 | 80 | { | ||
701 | 81 | QStringList favs; | ||
702 | 82 | favs << "scope://mock-scope-departments"; | ||
703 | 83 | setFavouriteScopes(favs); | ||
704 | 84 | |||
705 | 85 | // should have one scope now | ||
706 | 86 | QTRY_COMPARE(m_scopes->rowCount(), 1); | ||
707 | 87 | auto scope1 = qobject_cast<scopes_ng::Scope*>(m_scopes->getScope(QString("mock-scope-departments"))); | ||
708 | 88 | QVERIFY(scope1 != nullptr); | ||
709 | 89 | QCOMPARE(scope1->favorite(), true); | ||
710 | 90 | |||
711 | 91 | favs << "scope://mock-scope-double-nav"; | ||
712 | 92 | setFavouriteScopes(favs); | ||
713 | 93 | |||
714 | 94 | // should have two scopes now | ||
715 | 95 | QTRY_COMPARE(m_scopes->rowCount(), 2); | ||
716 | 96 | auto scope2 = qobject_cast<scopes_ng::Scope*>(m_scopes->getScope(QString("mock-scope-double-nav"))); | ||
717 | 97 | QVERIFY(scope2 != nullptr); | ||
718 | 98 | QCOMPARE(scope2->favorite(), true); | ||
719 | 99 | |||
720 | 100 | // unfavorite 1st scope | ||
721 | 101 | QSignalSpy spy(scope1, SIGNAL(favoriteChanged(bool))); | ||
722 | 102 | scope1->setFavorite(false); | ||
723 | 103 | QTRY_COMPARE(spy.count(), 1); | ||
724 | 104 | QTRY_COMPARE(m_scopes->rowCount(), 1); | ||
725 | 105 | QVERIFY(m_scopes->getScopeById("mock-scope-departments") == nullptr); | ||
726 | 106 | QCOMPARE(m_scopes->data(m_scopes->index(0), Scopes::RoleId), QVariant(QString("mock-scope-double-nav"))); | ||
727 | 107 | |||
728 | 108 | // favorite a scope | ||
729 | 109 | auto overviewScope = m_scopes->overviewScope(); | ||
730 | 110 | QVERIFY(overviewScope != nullptr); | ||
731 | 111 | connect(overviewScope, &unity::shell::scopes::ScopeInterface::openScope, [](unity::shell::scopes::ScopeInterface* scope) { | ||
732 | 112 | QCOMPARE(scope->favorite(), false); | ||
733 | 113 | scope->setFavorite(true); | ||
734 | 114 | }); | ||
735 | 115 | |||
736 | 116 | QVERIFY(m_scopes->getScopeById("mock-scope") == nullptr); | ||
737 | 117 | overviewScope->performQuery("scope://mock-scope"); | ||
738 | 118 | QTRY_VERIFY(m_scopes->getScopeById("mock-scope") != nullptr); | ||
739 | 119 | } | ||
740 | 120 | |||
741 | 121 | void testFavoritesOverviewUpdates() | ||
742 | 122 | { | ||
743 | 123 | auto categories = m_overviewScope->categories(); | ||
744 | 124 | QVERIFY(categories->rowCount() > 0); | ||
745 | 125 | QCOMPARE(categories->data(categories->index(0), Categories::Roles::RoleCategoryId), QVariant(QString("favorites"))); | ||
746 | 126 | |||
747 | 127 | QVariant results_var = categories->data(categories->index(0), Categories::Roles::RoleResults); | ||
748 | 128 | QVERIFY(results_var.canConvert<OverviewResultsModel*>()); | ||
749 | 129 | OverviewResultsModel* results = results_var.value<OverviewResultsModel*>(); | ||
750 | 130 | QCOMPARE(results->rowCount(), 0); | ||
751 | 131 | |||
752 | 132 | // favorite one scope, check if it appears in the favorites model | ||
753 | 133 | QStringList favs; | ||
754 | 134 | favs << "scope://mock-scope-departments"; | ||
755 | 135 | setFavouriteScopes(favs); | ||
756 | 136 | |||
757 | 137 | QTRY_COMPARE(results->rowCount(), 1); | ||
758 | 138 | |||
759 | 139 | // unfavorite it, verify it disappears from favorites model | ||
760 | 140 | setFavouriteScopes(QStringList()); | ||
761 | 141 | QTRY_COMPARE(results->rowCount(), 0); | ||
762 | 142 | } | ||
763 | 143 | |||
764 | 144 | void testGSettingsUpdates() | ||
765 | 145 | { | ||
766 | 146 | QStringList favs; | ||
767 | 147 | favs << "scope://mock-scope-departments" << "scope://mock-scope-double-nav"; | ||
768 | 148 | setFavouriteScopes(favs); | ||
769 | 149 | |||
770 | 150 | // should have two scopes | ||
771 | 151 | QTRY_COMPARE(m_scopes->rowCount(), 2); | ||
772 | 152 | auto scope1 = qobject_cast<scopes_ng::Scope*>(m_scopes->getScope(QString("mock-scope-departments"))); | ||
773 | 153 | QVERIFY(scope1 != nullptr); | ||
774 | 154 | |||
775 | 155 | // un-facorite one scope | ||
776 | 156 | scope1->setFavorite(false); | ||
777 | 157 | QTRY_COMPARE(getFavoriteScopes().size(), 1); | ||
778 | 158 | QCOMPARE(getFavoriteScopes().at(0), QString("scope://mock-scope-double-nav")); | ||
779 | 159 | } | ||
780 | 160 | |||
781 | 161 | void cleanup() | ||
782 | 162 | { | ||
783 | 163 | m_scopes.reset(); | ||
784 | 164 | } | ||
785 | 165 | }; | ||
786 | 166 | |||
787 | 167 | QTEST_GUILESS_MAIN(FavoritesTest) | ||
788 | 168 | #include <favoritestest.moc> | ||
789 | 0 | 169 | ||
790 | === modified file 'tests/overviewtest.cpp' | |||
791 | --- tests/overviewtest.cpp 2014-07-31 11:23:44 +0000 | |||
792 | +++ tests/overviewtest.cpp 2014-08-25 17:04:42 +0000 | |||
793 | @@ -60,6 +60,10 @@ | |||
794 | 60 | 60 | ||
795 | 61 | void init() | 61 | void init() |
796 | 62 | { | 62 | { |
797 | 63 | QStringList favs; | ||
798 | 64 | favs << "scope://mock-scope-departments" << "scope://mock-scope-double-nav"; | ||
799 | 65 | setFavouriteScopes(favs); | ||
800 | 66 | |||
801 | 63 | m_scopes.reset(new Scopes(nullptr)); | 67 | m_scopes.reset(new Scopes(nullptr)); |
802 | 64 | // no scopes on startup | 68 | // no scopes on startup |
803 | 65 | QCOMPARE(m_scopes->rowCount(), 0); | 69 | QCOMPARE(m_scopes->rowCount(), 0); |
804 | 66 | 70 | ||
805 | === modified file 'tests/previewtest.cpp' | |||
806 | --- tests/previewtest.cpp 2014-08-06 08:35:20 +0000 | |||
807 | +++ tests/previewtest.cpp 2014-08-25 17:04:42 +0000 | |||
808 | @@ -58,6 +58,9 @@ | |||
809 | 58 | 58 | ||
810 | 59 | void init() | 59 | void init() |
811 | 60 | { | 60 | { |
812 | 61 | const QStringList favs {"scope://mock-scope-departments", "scope://mock-scope-double-nav", "scope://mock-scope"}; | ||
813 | 62 | setFavouriteScopes(favs); | ||
814 | 63 | |||
815 | 61 | m_scopes.reset(new Scopes(nullptr)); | 64 | m_scopes.reset(new Scopes(nullptr)); |
816 | 62 | // no scopes on startup | 65 | // no scopes on startup |
817 | 63 | QCOMPARE(m_scopes->rowCount(), 0); | 66 | QCOMPARE(m_scopes->rowCount(), 0); |
818 | 64 | 67 | ||
819 | === modified file 'tests/resultstest.cpp' | |||
820 | --- tests/resultstest.cpp 2014-08-14 10:23:34 +0000 | |||
821 | +++ tests/resultstest.cpp 2014-08-25 17:04:42 +0000 | |||
822 | @@ -104,6 +104,9 @@ | |||
823 | 104 | 104 | ||
824 | 105 | void init() | 105 | void init() |
825 | 106 | { | 106 | { |
826 | 107 | const QStringList favs {"scope://mock-scope", "scope://mock-scope-ttl", "scope://mock-scope-info"}; | ||
827 | 108 | setFavouriteScopes(favs); | ||
828 | 109 | |||
829 | 107 | m_scopes.reset(new Scopes(nullptr)); | 110 | m_scopes.reset(new Scopes(nullptr)); |
830 | 108 | // no scopes on startup | 111 | // no scopes on startup |
831 | 109 | QCOMPARE(m_scopes->rowCount(), 0); | 112 | QCOMPARE(m_scopes->rowCount(), 0); |
832 | 110 | 113 | ||
833 | === modified file 'tests/settingsendtoendtest.cpp' | |||
834 | --- tests/settingsendtoendtest.cpp 2014-08-06 08:35:20 +0000 | |||
835 | +++ tests/settingsendtoendtest.cpp 2014-08-25 17:04:42 +0000 | |||
836 | @@ -26,6 +26,7 @@ | |||
837 | 26 | 26 | ||
838 | 27 | #include <unity/shell/scopes/SettingsModelInterface.h> | 27 | #include <unity/shell/scopes/SettingsModelInterface.h> |
839 | 28 | 28 | ||
840 | 29 | #include "test-utils.h" | ||
841 | 29 | #include "registry-spawner.h" | 30 | #include "registry-spawner.h" |
842 | 30 | 31 | ||
843 | 31 | using namespace scopes_ng; | 32 | using namespace scopes_ng; |
844 | @@ -52,6 +53,9 @@ | |||
845 | 52 | 53 | ||
846 | 53 | void init() | 54 | void init() |
847 | 54 | { | 55 | { |
848 | 56 | const QStringList favs {"scope://mock-scope-departments", "scope://mock-scope-double-nav", "scope://mock-scope"}; | ||
849 | 57 | setFavouriteScopes(favs); | ||
850 | 58 | |||
851 | 55 | m_scopes.reset(new Scopes(nullptr)); | 59 | m_scopes.reset(new Scopes(nullptr)); |
852 | 56 | // no scopes on startup | 60 | // no scopes on startup |
853 | 57 | QCOMPARE(m_scopes->rowCount(), 0); | 61 | QCOMPARE(m_scopes->rowCount(), 0); |
854 | 58 | 62 | ||
855 | === modified file 'tests/test-utils.h' | |||
856 | --- tests/test-utils.h 2014-07-22 13:49:50 +0000 | |||
857 | +++ tests/test-utils.h 2014-08-25 17:04:42 +0000 | |||
858 | @@ -98,3 +98,21 @@ | |||
859 | 98 | 98 | ||
860 | 99 | return true; | 99 | return true; |
861 | 100 | } | 100 | } |
862 | 101 | |||
863 | 102 | void setFavouriteScopes(const QStringList& cannedQueries) | ||
864 | 103 | { | ||
865 | 104 | setenv("GSETTINGS_BACKEND", "memory", 1); | ||
866 | 105 | QGSettings settings("com.canonical.Unity.Dash", QByteArray(), nullptr); | ||
867 | 106 | settings.set("favoriteScopes", QVariant(cannedQueries)); | ||
868 | 107 | } | ||
869 | 108 | |||
870 | 109 | QStringList getFavoriteScopes() | ||
871 | 110 | { | ||
872 | 111 | setenv("GSETTINGS_BACKEND", "memory", 1); | ||
873 | 112 | QGSettings settings("com.canonical.Unity.Dash", QByteArray(), nullptr); | ||
874 | 113 | QStringList favs; | ||
875 | 114 | for (auto const favvar: settings.get("favoriteScopes").toList()) { | ||
876 | 115 | favs.push_back(favvar.toString()); | ||
877 | 116 | } | ||
878 | 117 | return favs; | ||
879 | 118 | } |
A quick question before reading in detail. Do the tests mess around with the your real settings for the favourite scopes?