Merge lp:~unity-api-team/unity-scopes-shell/scope-settings into lp:unity-scopes-shell

Proposed by Pete Woods
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
Reviewer Review Type Date Requested Status
Michal Hruby (community) Approve
PS Jenkins bot (community) continuous-integration Needs Fixing
Review via email: mp+224606@code.launchpad.net

Commit message

Add settings support

Description of the change

Add settings support

To post a comment you must log in.
151. By Pete Woods

Increase version requirement

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
152. By Pete Woods

Merge trunk

153. By Pete Woods

Increment build deps

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
154. By Pete Woods

Implement the count method

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michal Hruby (mhr3) wrote :

33 +pkg_check_modules(U1DB REQUIRED libu1db-qt5)

Missing in debian/control

Revision history for this message
Michal Hruby (mhr3) wrote :

110 + new SettingsModel(QDir::home().filePath(".local/share"), id(),
111 + scopeVariantToQVariant(settings_definitions), this));

Isn't the .local/share/scope_id just a symlink to /opt/... for click-packaged scopes? Can unity even write in there?

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?

review: Needs Information
Revision history for this message
Pete Woods (pete-woods) wrote :

> 110 + new
> SettingsModel(QDir::home().filePath(".local/share"), id(),
> 111 + scopeVariantToQVariant(settings_definitions),
> this));
>
> Isn't the .local/share/scope_id just a symlink to /opt/... for click-packaged
> 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/share/unity-scopes/ID". Confined scopes can't actually read/write this path either, so this prevents them from modifying their own settings.

> 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

Revision history for this message
Michal Hruby (mhr3) wrote :

> Change timer type to VeryCoarseTimer

Ok, that makes me happier.

review: Approve
156. By Pete Woods

Re-order to help with performance

157. By Pete Woods

Add missing build dependency

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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+}

Subscribers

People subscribed via source and target branches

to all changes: