Merge lp:~unity-api-team/unity-scopes-shell/scope-settings into lp:unity-scopes-shell
- scope-settings
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Michal Hruby |
Approved revision: | 155 |
Merged at revision: | 105 |
Proposed branch: | lp:~unity-api-team/unity-scopes-shell/scope-settings |
Merge into: | lp:unity-scopes-shell |
Diff against target: |
905 lines (+727/-3) 15 files modified
debian/control (+2/-1) src/Unity/CMakeLists.txt (+7/-2) src/Unity/plugin.cpp (+2/-0) src/Unity/scope.cpp (+21/-0) src/Unity/scope.h (+3/-0) src/Unity/settingsmodel.cpp (+169/-0) src/Unity/settingsmodel.h (+93/-0) tests/CMakeLists.txt (+3/-0) tests/settingstest.cpp (+311/-0) tools/settings/Settings.qml (+68/-0) tools/settings/run.sh (+9/-0) tools/settings/widgets/booleanSettingsWidget.qml (+9/-0) tools/settings/widgets/listSettingsWidget.qml (+11/-0) tools/settings/widgets/numberSettingsWidget.qml (+10/-0) tools/settings/widgets/stringSettingsWidget.qml (+9/-0) |
To merge this branch: | bzr merge lp:~unity-api-team/unity-scopes-shell/scope-settings |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michal Hruby (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Needs Fixing | |
Review via email:
|
Commit message
Add settings support
Description of the change
Add settings support
- 151. By Pete Woods
-
Increase version requirement
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:151
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:152
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 152. By Pete Woods
-
Merge trunk
- 153. By Pete Woods
-
Increment build deps
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:153
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 154. By Pete Woods
-
Implement the count method
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:154
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Michal Hruby (mhr3) wrote : | # |
33 +pkg_check_
Missing in debian/control
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Michal Hruby (mhr3) wrote : | # |
110 + new SettingsModel(
111 + scopeVariantToQ
Isn't the .local/
211 + QVariantMap data = it.toMap();
Can we rely on the data being valid? Shouldn't it check first that all the necessary keys are in there?
234 + m_timers[id] = timer;
A timer per-setting? Why's that needed?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Pete Woods (pete-woods) wrote : | # |
> 110 + new
> SettingsModel(
> 111 + scopeVariantToQ
> this));
>
> Isn't the .local/
> scopes? Can unity even write in there?
>
This is the standard place for apps to write settings AFAIK. It's different to the path "~/.local/
> 211 + QVariantMap data = it.toMap();
>
> Can we rely on the data being valid? Shouldn't it check first that all the
> necessary keys are in there?
>
Yes. I removed the validation code because Michi performs all these checks in the scopes API itself.
> 234 + m_timers[id] = timer;
>
> A timer per-setting? Why's that needed?
The individual timers are used to avoid the need for a queue of changes, and generally keep the timing logic simple. Each timer just gets a cached version of the newest value of the setting it manages set as a property while it's active. Given there are only going to be < 10 of them, they shouldn't be an expensive overhead.
- 155. By Pete Woods
-
Change timer type to VeryCoarseTimer
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Michal Hruby (mhr3) wrote : | # |
> Change timer type to VeryCoarseTimer
Ok, that makes me happier.
- 156. By Pete Woods
-
Re-order to help with performance
- 157. By Pete Woods
-
Add missing build dependency
Preview Diff
1 | === modified file 'debian/control' |
2 | --- debian/control 2014-06-26 08:16:37 +0000 |
3 | +++ debian/control 2014-07-10 07:45:27 +0000 |
4 | @@ -4,8 +4,9 @@ |
5 | Build-Depends: cmake, |
6 | debhelper (>= 9), |
7 | libunity-api-dev (>= 7.83), |
8 | - libunity-scopes-dev (>= 0.5.1~), |
9 | + libunity-scopes-dev (>= 0.5.2~), |
10 | libgsettings-qt-dev (>= 0.1), |
11 | + libu1db-qt5-dev, |
12 | pkg-config, |
13 | qt5-default, |
14 | qtdeclarative5-dev, |
15 | |
16 | === modified file 'src/Unity/CMakeLists.txt' |
17 | --- src/Unity/CMakeLists.txt 2014-06-05 10:37:50 +0000 |
18 | +++ src/Unity/CMakeLists.txt 2014-07-10 07:45:27 +0000 |
19 | @@ -2,15 +2,17 @@ |
20 | include(Plugins) |
21 | |
22 | # Dependencies |
23 | -pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=1) |
24 | -pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=0.4.8) |
25 | +pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=2) |
26 | +pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=0.5.2) |
27 | pkg_check_modules(GSETTINGSQT REQUIRED gsettings-qt) |
28 | +pkg_check_modules(U1DB REQUIRED libu1db-qt5) |
29 | |
30 | include_directories( |
31 | ${CMAKE_CURRENT_SOURCE_DIR} |
32 | ${CMAKE_CURRENT_BINARY_DIR} |
33 | ${SCOPESLIB_INCLUDE_DIRS} |
34 | ${GSETTINGSQT_INCLUDE_DIRS} |
35 | + ${U1DB_INCLUDE_DIRS} |
36 | ) |
37 | |
38 | set(QMLPLUGIN_SRC |
39 | @@ -24,6 +26,7 @@ |
40 | resultsmodel.cpp |
41 | scope.cpp |
42 | scopes.cpp |
43 | + settingsmodel.cpp |
44 | utils.cpp |
45 | plugin.cpp |
46 | iconutils.cpp |
47 | @@ -37,6 +40,7 @@ |
48 | ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ResultsModelInterface.h |
49 | ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ScopeInterface.h |
50 | ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ScopesInterface.h |
51 | + ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/SettingsModelInterface.h |
52 | ) |
53 | |
54 | add_library(Unity-qml SHARED ${QMLPLUGIN_SRC}) |
55 | @@ -44,6 +48,7 @@ |
56 | target_link_libraries(Unity-qml |
57 | ${SCOPESLIB_LDFLAGS} |
58 | ${GSETTINGSQT_LDFLAGS} |
59 | + ${U1DB_LDFLAGS} |
60 | ) |
61 | |
62 | qt5_use_modules(Unity-qml Qml Gui DBus) |
63 | |
64 | === modified file 'src/Unity/plugin.cpp' |
65 | --- src/Unity/plugin.cpp 2014-06-03 15:51:13 +0000 |
66 | +++ src/Unity/plugin.cpp 2014-07-10 07:45:27 +0000 |
67 | @@ -32,6 +32,7 @@ |
68 | #include "previewstack.h" |
69 | #include "previewmodel.h" |
70 | #include "previewwidgetmodel.h" |
71 | +#include "settingsmodel.h" |
72 | |
73 | void UnityPlugin::registerTypes(const char *uri) |
74 | { |
75 | @@ -42,6 +43,7 @@ |
76 | qmlRegisterUncreatableType<unity::shell::scopes::ScopeInterface>(uri, 0, 2, "Scope", "Can't create Scope object in QML. Get them from Scopes instance."); |
77 | qmlRegisterUncreatableType<unity::shell::scopes::DepartmentInterface>(uri, 0, 2, "Department", "Can't create Department object in QML. Get them from Scope instance."); |
78 | qmlRegisterUncreatableType<unity::shell::scopes::CategoriesInterface>(uri, 0, 2, "Categories", "Can't create Categories object in QML. Get them from Scope instance."); |
79 | + qmlRegisterUncreatableType<unity::shell::scopes::SettingsModelInterface>(uri, 0, 2, "Settings", "Can't create Settings object in QML. Get them from Scope instance."); |
80 | qmlRegisterUncreatableType<scopes_ng::ResultsModel>(uri, 0, 2, "ResultsModel", "Can't create new ResultsModel in QML. Get them from Categories instance."); |
81 | qmlRegisterUncreatableType<unity::shell::scopes::PreviewModelInterface>(uri, 0, 2, "PreviewModel", "Can't create new PreviewModel in QML. Get them from PreviewStack instance."); |
82 | qmlRegisterUncreatableType<scopes_ng::PreviewWidgetModel>(uri, 0, 2, "PreviewWidgetModel", "Can't create new PreviewWidgetModel in QML. Get them from PreviewModel instance."); |
83 | |
84 | === modified file 'src/Unity/scope.cpp' |
85 | --- src/Unity/scope.cpp 2014-06-25 17:00:21 +0000 |
86 | +++ src/Unity/scope.cpp 2014-07-10 07:45:27 +0000 |
87 | @@ -26,6 +26,7 @@ |
88 | #include "previewstack.h" |
89 | #include "utils.h" |
90 | #include "scopes.h" |
91 | +#include "settingsmodel.h" |
92 | |
93 | // Qt |
94 | #include <QUrl> |
95 | @@ -533,6 +534,21 @@ |
96 | QVariant converted(scopeVariantToQVariant(scopes::Variant(m_scopeMetadata->appearance_attributes()))); |
97 | m_customizations = converted.toMap(); |
98 | Q_EMIT customizationsChanged(); |
99 | + |
100 | + try |
101 | + { |
102 | + scopes::Variant settings_definitions; |
103 | + settings_definitions = m_scopeMetadata->settings_definitions(); |
104 | + m_settingsModel.reset( |
105 | + new SettingsModel(QDir::home().filePath(".local/share"), id(), |
106 | + scopeVariantToQVariant(settings_definitions), this)); |
107 | + } |
108 | + catch (unity::scopes::NotFoundException&) |
109 | + { |
110 | + // If there's no settings data |
111 | + m_settingsModel.reset(); |
112 | + } |
113 | + Q_EMIT settingsChanged(); |
114 | } |
115 | |
116 | QString Scope::id() const |
117 | @@ -599,6 +615,11 @@ |
118 | return m_categories; |
119 | } |
120 | |
121 | +unity::shell::scopes::SettingsModelInterface* Scope::settings() const |
122 | +{ |
123 | + return m_settingsModel.data(); |
124 | +} |
125 | + |
126 | /* |
127 | Filters* Scope::filters() const |
128 | { |
129 | |
130 | === modified file 'src/Unity/scope.h' |
131 | --- src/Unity/scope.h 2014-06-25 16:57:46 +0000 |
132 | +++ src/Unity/scope.h 2014-07-10 07:45:27 +0000 |
133 | @@ -47,6 +47,7 @@ |
134 | class Categories; |
135 | class PushEvent; |
136 | class PreviewStack; |
137 | +class SettingsModel; |
138 | |
139 | class CollectionController |
140 | { |
141 | @@ -116,6 +117,7 @@ |
142 | QString shortcut() const override; |
143 | bool searchInProgress() const override; |
144 | unity::shell::scopes::CategoriesInterface* categories() const override; |
145 | + unity::shell::scopes::SettingsModelInterface* settings() const override; |
146 | QString searchQuery() const override; |
147 | QString noResultsHint() const override; |
148 | QString formFactor() const override; |
149 | @@ -189,6 +191,7 @@ |
150 | unity::scopes::Department::SCPtr m_lastRootDepartment; |
151 | QGSettings* m_settings; |
152 | Categories* m_categories; |
153 | + QScopedPointer<SettingsModel> m_settingsModel; |
154 | QSharedPointer<DepartmentNode> m_departmentTree; |
155 | QTimer m_aggregatorTimer; |
156 | QTimer m_clearTimer; |
157 | |
158 | === added file 'src/Unity/settingsmodel.cpp' |
159 | --- src/Unity/settingsmodel.cpp 1970-01-01 00:00:00 +0000 |
160 | +++ src/Unity/settingsmodel.cpp 2014-07-10 07:45:27 +0000 |
161 | @@ -0,0 +1,169 @@ |
162 | +/* |
163 | + * Copyright (C) 2014 Canonical, Ltd. |
164 | + * |
165 | + * Authors: |
166 | + * Pete Woods <pete.woods@canonical.com> |
167 | + * |
168 | + * This program is free software; you can redistribute it and/or modify |
169 | + * it under the terms of the GNU General Public License as published by |
170 | + * the Free Software Foundation; version 3. |
171 | + * |
172 | + * This program is distributed in the hope that it will be useful, |
173 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
174 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
175 | + * GNU General Public License for more details. |
176 | + * |
177 | + * You should have received a copy of the GNU General Public License |
178 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
179 | + */ |
180 | + |
181 | +#include "settingsmodel.h" |
182 | +#include "utils.h" |
183 | + |
184 | +#include <QDebug> |
185 | +#include <QDir> |
186 | +#include <QTimer> |
187 | + |
188 | +using namespace scopes_ng; |
189 | +namespace sc = unity::scopes; |
190 | + |
191 | +static const QString SETTING_GROUP("default"); |
192 | + |
193 | +static const QString SETTING_ID_PATTERN("%1-%2"); |
194 | + |
195 | +SettingsModel::SettingsModel(const QDir& shareDir, const QString& scopeId, |
196 | + const QVariant& settingsDefinitions, QObject* parent, |
197 | + int settingsTimeout) |
198 | + : SettingsModelInterface(parent), m_settingsTimeout(settingsTimeout) |
199 | +{ |
200 | + shareDir.mkdir(scopeId); |
201 | + QDir databaseDir = shareDir.filePath(scopeId); |
202 | + m_database.setPath(databaseDir.filePath("settings.db")); |
203 | + |
204 | + for (const auto &it : settingsDefinitions.toList()) |
205 | + { |
206 | + QVariantMap data = it.toMap(); |
207 | + QString id = data["id"].toString(); |
208 | + QString displayName = data["displayName"].toString(); |
209 | + QVariantMap properties = data["parameters"].toMap(); |
210 | + QString type = data["type"].toString(); |
211 | + |
212 | + QVariantMap defaults; |
213 | + defaults["value"] = properties["defaultValue"]; |
214 | + |
215 | + QSharedPointer<U1db::Document> document(new U1db::Document); |
216 | + document->setDocId(SETTING_ID_PATTERN.arg(SETTING_GROUP, id)); |
217 | + document->setDefaults(defaults); |
218 | + document->setDatabase(&m_database); |
219 | + document->setCreate(true); |
220 | + |
221 | + m_documents[id] = document; |
222 | + |
223 | + QSharedPointer<QTimer> timer(new QTimer()); |
224 | + timer->setProperty("setting_id", id); |
225 | + timer->setSingleShot(true); |
226 | + timer->setInterval(m_settingsTimeout); |
227 | + timer->setTimerType(Qt::VeryCoarseTimer); |
228 | + connect(timer.data(), SIGNAL(timeout()), this, |
229 | + SLOT(settings_timeout())); |
230 | + m_timers[id] = timer; |
231 | + |
232 | + QSharedPointer<Data> setting( |
233 | + new Data(id, displayName, type, properties)); |
234 | + |
235 | + m_data << setting; |
236 | + } |
237 | +} |
238 | + |
239 | +QVariant SettingsModel::data(const QModelIndex& index, int role) const |
240 | +{ |
241 | + int row = index.row(); |
242 | + QVariant result; |
243 | + |
244 | + if (row < m_data.size()) |
245 | + { |
246 | + auto data = m_data[row]; |
247 | + |
248 | + switch (role) |
249 | + { |
250 | + case Roles::RoleSettingId: |
251 | + result = data->id; |
252 | + break; |
253 | + case Roles::RoleDisplayName: |
254 | + result = data->displayName; |
255 | + break; |
256 | + case Roles::RoleType: |
257 | + result = data->type; |
258 | + break; |
259 | + case Roles::RoleProperties: |
260 | + result = data->properties; |
261 | + break; |
262 | + case Roles::RoleValue: |
263 | + { |
264 | + QSharedPointer<U1db::Document> document = m_documents[data->id]; |
265 | + QVariantMap contents = document->getContents().toMap(); |
266 | + result = contents["value"]; |
267 | + break; |
268 | + } |
269 | + default: |
270 | + break; |
271 | + } |
272 | + } |
273 | + |
274 | + return result; |
275 | +} |
276 | + |
277 | +bool SettingsModel::setData(const QModelIndex &index, const QVariant &value, |
278 | + int role) |
279 | +{ |
280 | + int row = index.row(); |
281 | + QVariant result; |
282 | + |
283 | + if (row < m_data.size()) |
284 | + { |
285 | + auto data = m_data[row]; |
286 | + |
287 | + switch (role) |
288 | + { |
289 | + case Roles::RoleValue: |
290 | + { |
291 | + QSharedPointer<QTimer> timer = m_timers[data->id]; |
292 | + timer->setProperty("value", value); |
293 | + timer->start(); |
294 | + |
295 | + return true; |
296 | + } |
297 | + default: |
298 | + break; |
299 | + } |
300 | + } |
301 | + |
302 | + return false; |
303 | +} |
304 | + |
305 | +int SettingsModel::rowCount(const QModelIndex&) const |
306 | +{ |
307 | + return count(); |
308 | +} |
309 | + |
310 | +int SettingsModel::count() const |
311 | +{ |
312 | + return m_data.size(); |
313 | +} |
314 | + |
315 | +void SettingsModel::settings_timeout() |
316 | +{ |
317 | + QObject *timer = sender(); |
318 | + if (!timer) |
319 | + { |
320 | + return; |
321 | + } |
322 | + |
323 | + QString setting_id = timer->property("setting_id").toString(); |
324 | + QVariant value = timer->property("value"); |
325 | + |
326 | + QSharedPointer<U1db::Document> document = m_documents[setting_id]; |
327 | + QVariantMap map; |
328 | + map["value"] = value; |
329 | + document->setContents(map); |
330 | +} |
331 | |
332 | === added file 'src/Unity/settingsmodel.h' |
333 | --- src/Unity/settingsmodel.h 1970-01-01 00:00:00 +0000 |
334 | +++ src/Unity/settingsmodel.h 2014-07-10 07:45:27 +0000 |
335 | @@ -0,0 +1,93 @@ |
336 | +/* |
337 | + * Copyright (C) 2014 Canonical, Ltd. |
338 | + * |
339 | + * Authors: |
340 | + * Pete Woods <pete.woods@canonical.com> |
341 | + * |
342 | + * This program is free software; you can redistribute it and/or modify |
343 | + * it under the terms of the GNU General Public License as published by |
344 | + * the Free Software Foundation; version 3. |
345 | + * |
346 | + * This program is distributed in the hope that it will be useful, |
347 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
348 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
349 | + * GNU General Public License for more details. |
350 | + * |
351 | + * You should have received a copy of the GNU General Public License |
352 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
353 | + */ |
354 | + |
355 | +#ifndef NG_PREVIEW_SETTTINGSMODEL_H_ |
356 | +#define NG_PREVIEW_SETTTINGSMODEL_H_ |
357 | + |
358 | +#include <libu1db-qt5/database.h> |
359 | +#include <libu1db-qt5/document.h> |
360 | +#include <unity/SymbolExport.h> |
361 | +#include <unity/shell/scopes/SettingsModelInterface.h> |
362 | + |
363 | +#include <QAbstractListModel> |
364 | +#include <QList> |
365 | +#include <QSharedPointer> |
366 | + |
367 | +QT_BEGIN_NAMESPACE |
368 | +class QDir; |
369 | +class QTimer; |
370 | +QT_END_NAMESPACE |
371 | + |
372 | +namespace scopes_ng |
373 | +{ |
374 | + |
375 | +class Q_DECL_EXPORT SettingsModel: public unity::shell::scopes::SettingsModelInterface |
376 | +{ |
377 | +Q_OBJECT |
378 | + |
379 | + struct Data |
380 | + { |
381 | + QString id; |
382 | + QString displayName; |
383 | + QString type; |
384 | + QVariant properties; |
385 | + |
386 | + Data(QString const& id_, QString const& displayName_, |
387 | + QString const& type_, QVariant const& properties_) |
388 | + : id(id_), displayName(displayName_), type(type_), properties( |
389 | + properties_) |
390 | + { |
391 | + } |
392 | + }; |
393 | + |
394 | +public: |
395 | + explicit SettingsModel(const QDir& shareDir, const QString& scopeId, |
396 | + const QVariant& settingsDefinitions, QObject* parent = 0, |
397 | + int settingsTimeout = 300); |
398 | + |
399 | + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const |
400 | + override; |
401 | + |
402 | + bool setData(const QModelIndex&index, const QVariant& value, int role = |
403 | + Qt::EditRole) override; |
404 | + |
405 | + int rowCount(const QModelIndex& parent = QModelIndex()) const override; |
406 | + |
407 | + int count() const override; |
408 | + |
409 | +protected Q_SLOTS: |
410 | + void settings_timeout(); |
411 | + |
412 | +protected: |
413 | + int m_settingsTimeout; |
414 | + |
415 | + QList<QSharedPointer<Data>> m_data; |
416 | + |
417 | + U1db::Database m_database; |
418 | + |
419 | + QMap<QString, QSharedPointer<U1db::Document>> m_documents; |
420 | + |
421 | + QMap<QString, QSharedPointer<QTimer>> m_timers; |
422 | +}; |
423 | + |
424 | +} |
425 | + |
426 | +Q_DECLARE_METATYPE(scopes_ng::SettingsModel*) |
427 | + |
428 | +#endif /* NG_PREVIEW_SETTTINGSMODEL_H_ */ |
429 | |
430 | === modified file 'tests/CMakeLists.txt' |
431 | --- tests/CMakeLists.txt 2014-06-05 10:37:50 +0000 |
432 | +++ tests/CMakeLists.txt 2014-07-10 07:45:27 +0000 |
433 | @@ -51,4 +51,7 @@ |
434 | resultstest |
435 | previewtest |
436 | utilstest |
437 | + settingstest |
438 | ) |
439 | + |
440 | +qt5_use_modules(settingstestExec Sql) |
441 | |
442 | === added file 'tests/settingstest.cpp' |
443 | --- tests/settingstest.cpp 1970-01-01 00:00:00 +0000 |
444 | +++ tests/settingstest.cpp 2014-07-10 07:45:27 +0000 |
445 | @@ -0,0 +1,311 @@ |
446 | +/* |
447 | + * Copyright (C) 2013-2014 Canonical, Ltd. |
448 | + * |
449 | + * This program is free software; you can redistribute it and/or modify |
450 | + * it under the terms of the GNU General Public License as published by |
451 | + * the Free Software Foundation; version 3. |
452 | + * |
453 | + * This program is distributed in the hope that it will be useful, |
454 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
455 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
456 | + * GNU General Public License for more details. |
457 | + * |
458 | + * You should have received a copy of the GNU General Public License |
459 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
460 | + * |
461 | + * Authors: |
462 | + * Pete Woods <pete.woods@canonical.com> |
463 | + */ |
464 | + |
465 | +#include <QJsonDocument> |
466 | +#include <QObject> |
467 | +#include <QTemporaryDir> |
468 | +#include <QTest> |
469 | + |
470 | +#include <settingsmodel.h> |
471 | + |
472 | +using namespace scopes_ng; |
473 | +using namespace unity::shell::scopes; |
474 | + |
475 | +namespace |
476 | +{ |
477 | + |
478 | +const static QByteArray BOOLEAN_DEFINITION = |
479 | + R"( |
480 | +[ |
481 | + { |
482 | + "id": "enabledSetting", |
483 | + "displayName": "Enabled", |
484 | + "type": "boolean", |
485 | + "parameters": { |
486 | + "defaultValue": true |
487 | + } |
488 | + } |
489 | +] |
490 | +)"; |
491 | + |
492 | +const static QByteArray LIST_DEFINITION = |
493 | + R"( |
494 | +[ |
495 | + { |
496 | + "id": "unitTempSetting", |
497 | + "displayName": "Temperature Units", |
498 | + "type": "list", |
499 | + "parameters": { |
500 | + "defaultValue": 1, |
501 | + "values": ["Celcius", "Fahrenheit"] |
502 | + } |
503 | + } |
504 | +] |
505 | +)"; |
506 | + |
507 | +const static QByteArray NUMBER_DEFINITION = |
508 | + R"( |
509 | +[ |
510 | + { |
511 | + "id": "ageSetting", |
512 | + "displayName": "Age", |
513 | + "type": "number", |
514 | + "parameters": { |
515 | + "defaultValue": 23 |
516 | + } |
517 | + } |
518 | +] |
519 | +)"; |
520 | + |
521 | +const static QByteArray STRING_DEFINITION = |
522 | + R"( |
523 | +[ |
524 | + { |
525 | + "id": "locationSetting", |
526 | + "displayName": "Location", |
527 | + "type": "string", |
528 | + "parameters": { |
529 | + "defaultValue": "London" |
530 | + } |
531 | + } |
532 | +] |
533 | +)"; |
534 | + |
535 | +const static QByteArray MIXED_DEFINITION = |
536 | + R"( |
537 | +[ |
538 | + { |
539 | + "id": "locationSetting", |
540 | + "displayName": "Location", |
541 | + "type": "string", |
542 | + "parameters": { |
543 | + "defaultValue": "London" |
544 | + } |
545 | + }, |
546 | + { |
547 | + "id": "unitTempSetting", |
548 | + "displayName": "Temperature Units", |
549 | + "type": "list", |
550 | + "parameters": { |
551 | + "defaultValue": 1, |
552 | + "values": ["Celcius", "Fahrenheit"] |
553 | + } |
554 | + }, |
555 | + { |
556 | + "id": "ageSetting", |
557 | + "displayName": "Age", |
558 | + "type": "number", |
559 | + "parameters": { |
560 | + "defaultValue": 23 |
561 | + } |
562 | + }, |
563 | + { |
564 | + "id": "enabledSetting", |
565 | + "displayName": "Enabled", |
566 | + "type": "boolean", |
567 | + "parameters": { |
568 | + "defaultValue": true |
569 | + } |
570 | + } |
571 | +] |
572 | +)"; |
573 | + |
574 | +class SettingsTest: public QObject |
575 | +{ |
576 | +Q_OBJECT |
577 | +private: |
578 | + QScopedPointer<QTemporaryDir> tempDir; |
579 | + |
580 | + QSharedPointer<SettingsModelInterface> settings; |
581 | + |
582 | + void newSettingsModel(const QString& id, const QByteArray& json) |
583 | + { |
584 | + QJsonDocument doc = QJsonDocument::fromJson(json); |
585 | + QVariant definitions = doc.toVariant(); |
586 | + settings.reset( |
587 | + new SettingsModel(tempDir->path(), id, definitions, 0, 0)); |
588 | + } |
589 | + |
590 | + void verifyData(int index, const QString& id, const QString& displayName, |
591 | + const QString& type, const QVariant& properties) |
592 | + { |
593 | + QCOMPARE( |
594 | + settings->data(settings->index(index), |
595 | + SettingsModelInterface::RoleSettingId), QVariant(id)); |
596 | + QCOMPARE( |
597 | + settings->data(settings->index(index), |
598 | + SettingsModelInterface::RoleDisplayName), |
599 | + QVariant(displayName)); |
600 | + QCOMPARE( |
601 | + settings->data(settings->index(index), |
602 | + SettingsModelInterface::RoleType), QVariant(type)); |
603 | + QCOMPARE( |
604 | + settings->data(settings->index(index), |
605 | + SettingsModelInterface::RoleProperties), properties); |
606 | + } |
607 | + |
608 | + void verifyValue(int index, const QVariant& value) |
609 | + { |
610 | + // Using this "TRY" macro repeatedly attempts the comparison until a timeout |
611 | + QTRY_COMPARE( |
612 | + settings->data(settings->index(index), |
613 | + SettingsModelInterface::RoleValue), value); |
614 | + } |
615 | + |
616 | + void setValue(int index, const QVariant& value) |
617 | + { |
618 | + QVERIFY(settings->setData(settings->index(index), value, |
619 | + SettingsModelInterface::RoleValue)); |
620 | + } |
621 | + |
622 | +private Q_SLOTS: |
623 | + void init() |
624 | + { |
625 | + tempDir.reset(new QTemporaryDir); |
626 | + } |
627 | + |
628 | + void testBooleanDefinition() |
629 | + { |
630 | + newSettingsModel("boolean", BOOLEAN_DEFINITION); |
631 | + QCOMPARE(settings->rowCount(), 1); |
632 | + |
633 | + // Check the various properties make it through |
634 | + QVariantMap properties; |
635 | + properties["defaultValue"] = true; |
636 | + verifyData(0, "enabledSetting", "Enabled", "boolean", properties); |
637 | + |
638 | + verifyValue(0, true); |
639 | + } |
640 | + |
641 | + void testListDefinition() |
642 | + { |
643 | + newSettingsModel("list", LIST_DEFINITION); |
644 | + QCOMPARE(settings->rowCount(), 1); |
645 | + |
646 | + // Check the various properties make it through |
647 | + QVariantMap properties; |
648 | + properties["defaultValue"] = 1; |
649 | + properties["values"] = QVariantList() << "Celcius" << "Fahrenheit"; |
650 | + verifyData(0, "unitTempSetting", "Temperature Units", "list", |
651 | + properties); |
652 | + |
653 | + // Check the default value |
654 | + verifyValue(0, 1); |
655 | + } |
656 | + |
657 | + void testNumberDefinition() |
658 | + { |
659 | + newSettingsModel("number", NUMBER_DEFINITION); |
660 | + QCOMPARE(settings->rowCount(), 1); |
661 | + |
662 | + // Check the various properties make it through |
663 | + QVariantMap properties; |
664 | + properties["defaultValue"] = 23; |
665 | + verifyData(0, "ageSetting", "Age", "number", properties); |
666 | + |
667 | + // Check the default value |
668 | + verifyValue(0, 23); |
669 | + } |
670 | + |
671 | + void testStringDefinition() |
672 | + { |
673 | + newSettingsModel("string", STRING_DEFINITION); |
674 | + QCOMPARE(settings->rowCount(), 1); |
675 | + |
676 | + // Check the various properties make it through |
677 | + QVariantMap properties; |
678 | + properties["defaultValue"] = "London"; |
679 | + verifyData(0, "locationSetting", "Location", "string", properties); |
680 | + |
681 | + // Check the default value |
682 | + verifyValue(0, "London"); |
683 | + } |
684 | + |
685 | + void testMixedDefinition() |
686 | + { |
687 | + newSettingsModel("mixed", MIXED_DEFINITION); |
688 | + QCOMPARE(settings->rowCount(), 4); |
689 | + |
690 | + { |
691 | + QVariantMap properties; |
692 | + properties["defaultValue"] = "London"; |
693 | + verifyData(0, "locationSetting", "Location", "string", properties); |
694 | + verifyValue(0, "London"); |
695 | + } |
696 | + { |
697 | + QVariantMap properties; |
698 | + properties["defaultValue"] = 1; |
699 | + properties["values"] = QVariantList() << "Celcius" << "Fahrenheit"; |
700 | + verifyData(1, "unitTempSetting", "Temperature Units", "list", |
701 | + properties); |
702 | + verifyValue(1, 1); |
703 | + } |
704 | + { |
705 | + QVariantMap properties; |
706 | + properties["defaultValue"] = 23; |
707 | + verifyData(2, "ageSetting", "Age", "number", properties); |
708 | + verifyValue(2, 23); |
709 | + } |
710 | + { |
711 | + QVariantMap properties; |
712 | + properties["defaultValue"] = true; |
713 | + verifyData(3, "enabledSetting", "Enabled", "boolean", properties); |
714 | + verifyValue(3, true); |
715 | + } |
716 | + } |
717 | + |
718 | + void testUpdateValue() |
719 | + { |
720 | + // Create an initial settings model |
721 | + newSettingsModel("update", MIXED_DEFINITION); |
722 | + |
723 | + // Verify the initial values |
724 | + verifyValue(0, "London"); |
725 | + verifyValue(1, 1); |
726 | + verifyValue(2, 23); |
727 | + verifyValue(3, true); |
728 | + |
729 | + // Update the string value |
730 | + setValue(0, "Banana"); |
731 | + verifyValue(0, "Banana"); |
732 | + |
733 | + // Update the list value |
734 | + setValue(1, 0); |
735 | + verifyValue(1, 0); |
736 | + |
737 | + // Update the number value |
738 | + setValue(2, 123); |
739 | + verifyValue(2, 123); |
740 | + |
741 | + // Update the boolean value |
742 | + setValue(3, false); |
743 | + verifyValue(3, false); |
744 | + |
745 | + // Make a new settings model to check the settings were saved to disk |
746 | + newSettingsModel("update", MIXED_DEFINITION); |
747 | + verifyValue(0, "Banana"); |
748 | + verifyValue(1, 0); |
749 | + verifyValue(2, 123); |
750 | + verifyValue(3, false); |
751 | + } |
752 | +}; |
753 | + |
754 | +} |
755 | +QTEST_GUILESS_MAIN(SettingsTest) |
756 | +#include <settingstest.moc> |
757 | |
758 | === added directory 'tools' |
759 | === added directory 'tools/settings' |
760 | === added file 'tools/settings/Settings.qml' |
761 | --- tools/settings/Settings.qml 1970-01-01 00:00:00 +0000 |
762 | +++ tools/settings/Settings.qml 2014-07-10 07:45:27 +0000 |
763 | @@ -0,0 +1,68 @@ |
764 | +import QtQuick 2.0 |
765 | +import Ubuntu.Components 1.1 |
766 | +import Unity 0.2 |
767 | + |
768 | +MainView { |
769 | + id: app |
770 | + applicationName: "example" |
771 | + width: units.gu(40) |
772 | + height: units.gu(60) |
773 | + |
774 | + Arguments { |
775 | + id: args |
776 | + defaultArgument.help: "Expects a scope ID" |
777 | + defaultArgument.valueNames: ["SCOPE"] |
778 | + } |
779 | + |
780 | + Scopes { |
781 | + id: scopes |
782 | + onLoadedChanged: { |
783 | + app.scope = scopes.getScope(args.defaultArgument.at(0)) |
784 | + } |
785 | + } |
786 | + |
787 | + property var scope: ListModel{} |
788 | + |
789 | + function toFileName(type) { |
790 | + return "widgets/" + type + "SettingsWidget.qml" |
791 | + } |
792 | + |
793 | + Page { |
794 | + title: "Settings for " + scope.name |
795 | + |
796 | + ListView { |
797 | + anchors.fill: parent |
798 | + spacing: units.gu(1) |
799 | + |
800 | + model: scope.settings |
801 | + delegate: Loader { |
802 | + id: loader |
803 | + source: toFileName(type) |
804 | + onLoaded: { |
805 | + item.properties = properties |
806 | + item.value = value |
807 | + |
808 | + if (type != "boolean") { |
809 | + loader.width = parent.width |
810 | + } |
811 | + if (type == "list") { |
812 | + loader.height = units.gu(10) |
813 | + } |
814 | + |
815 | + itemValue = Qt.binding(function() { return item.value }) |
816 | + } |
817 | + property var itemValue |
818 | + onItemValueChanged: { |
819 | + if (value !== item.value) { |
820 | + value = item.value |
821 | + } |
822 | + } |
823 | + } |
824 | + section.property: "displayName" |
825 | + section.delegate: Text { |
826 | + width: parent.width |
827 | + text: section |
828 | + } |
829 | + } |
830 | + } |
831 | +} |
832 | |
833 | === added file 'tools/settings/run.sh' |
834 | --- tools/settings/run.sh 1970-01-01 00:00:00 +0000 |
835 | +++ tools/settings/run.sh 2014-07-10 07:45:27 +0000 |
836 | @@ -0,0 +1,9 @@ |
837 | +#!/bin/bash |
838 | + |
839 | +if [ "$#" -ne 1 ]; then |
840 | + echo "ERROR: Usage $0 SCOPE_ID" |
841 | + exit 1 |
842 | +fi |
843 | + |
844 | +DIR="$(dirname "$0")" |
845 | +APP_ID="example" qmlscene "$DIR/Settings.qml" -- "$1" |
846 | |
847 | === added directory 'tools/settings/widgets' |
848 | === added file 'tools/settings/widgets/booleanSettingsWidget.qml' |
849 | --- tools/settings/widgets/booleanSettingsWidget.qml 1970-01-01 00:00:00 +0000 |
850 | +++ tools/settings/widgets/booleanSettingsWidget.qml 2014-07-10 07:45:27 +0000 |
851 | @@ -0,0 +1,9 @@ |
852 | +import QtQuick 2.0 |
853 | +import Ubuntu.Components 1.1 |
854 | + |
855 | + |
856 | +CheckBox { |
857 | + id: box |
858 | + property var properties |
859 | + property alias value: box.checked |
860 | +} |
861 | |
862 | === added file 'tools/settings/widgets/listSettingsWidget.qml' |
863 | --- tools/settings/widgets/listSettingsWidget.qml 1970-01-01 00:00:00 +0000 |
864 | +++ tools/settings/widgets/listSettingsWidget.qml 2014-07-10 07:45:27 +0000 |
865 | @@ -0,0 +1,11 @@ |
866 | +import QtQuick 2.0 |
867 | +import Ubuntu.Components 1.1 |
868 | + |
869 | +OptionSelector { |
870 | + id: combo |
871 | + |
872 | + property var properties |
873 | + property alias value: combo.selectedIndex |
874 | + |
875 | + model: properties["values"] |
876 | +} |
877 | |
878 | === added file 'tools/settings/widgets/numberSettingsWidget.qml' |
879 | --- tools/settings/widgets/numberSettingsWidget.qml 1970-01-01 00:00:00 +0000 |
880 | +++ tools/settings/widgets/numberSettingsWidget.qml 2014-07-10 07:45:27 +0000 |
881 | @@ -0,0 +1,10 @@ |
882 | +import QtQuick 2.0 |
883 | +import Ubuntu.Components 1.1 |
884 | + |
885 | + |
886 | +TextField { |
887 | + id: field |
888 | + property var properties |
889 | + property alias value: field.text |
890 | + validator: DoubleValidator {} |
891 | +} |
892 | |
893 | === added file 'tools/settings/widgets/stringSettingsWidget.qml' |
894 | --- tools/settings/widgets/stringSettingsWidget.qml 1970-01-01 00:00:00 +0000 |
895 | +++ tools/settings/widgets/stringSettingsWidget.qml 2014-07-10 07:45:27 +0000 |
896 | @@ -0,0 +1,9 @@ |
897 | +import QtQuick 2.0 |
898 | +import Ubuntu.Components 1.1 |
899 | + |
900 | + |
901 | +TextField { |
902 | + id: field |
903 | + property var properties |
904 | + property alias value: field.text |
905 | +} |
FAILED: Continuous integration, rev:150 jenkins. qa.ubuntu. com/job/ unity-scopes- shell-ci/ 138/ jenkins. qa.ubuntu. com/job/ unity-scopes- shell-utopic- amd64-ci/ 35/console jenkins. qa.ubuntu. com/job/ unity-scopes- shell-utopic- armhf-ci/ 35/console jenkins. qa.ubuntu. com/job/ unity-scopes- shell-utopic- i386-ci/ 35/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/unity- scopes- shell-ci/ 138/rebuild
http://