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
=== modified file 'debian/control'
--- debian/control 2014-06-26 08:16:37 +0000
+++ debian/control 2014-07-10 07:45:27 +0000
@@ -4,8 +4,9 @@
4Build-Depends: cmake,4Build-Depends: cmake,
5 debhelper (>= 9),5 debhelper (>= 9),
6 libunity-api-dev (>= 7.83),6 libunity-api-dev (>= 7.83),
7 libunity-scopes-dev (>= 0.5.1~),7 libunity-scopes-dev (>= 0.5.2~),
8 libgsettings-qt-dev (>= 0.1),8 libgsettings-qt-dev (>= 0.1),
9 libu1db-qt5-dev,
9 pkg-config,10 pkg-config,
10 qt5-default,11 qt5-default,
11 qtdeclarative5-dev,12 qtdeclarative5-dev,
1213
=== modified file 'src/Unity/CMakeLists.txt'
--- src/Unity/CMakeLists.txt 2014-06-05 10:37:50 +0000
+++ src/Unity/CMakeLists.txt 2014-07-10 07:45:27 +0000
@@ -2,15 +2,17 @@
2include(Plugins)2include(Plugins)
33
4# Dependencies4# Dependencies
5pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=1)5pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=2)
6pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=0.4.8)6pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=0.5.2)
7pkg_check_modules(GSETTINGSQT REQUIRED gsettings-qt)7pkg_check_modules(GSETTINGSQT REQUIRED gsettings-qt)
8pkg_check_modules(U1DB REQUIRED libu1db-qt5)
89
9include_directories(10include_directories(
10 ${CMAKE_CURRENT_SOURCE_DIR}11 ${CMAKE_CURRENT_SOURCE_DIR}
11 ${CMAKE_CURRENT_BINARY_DIR}12 ${CMAKE_CURRENT_BINARY_DIR}
12 ${SCOPESLIB_INCLUDE_DIRS}13 ${SCOPESLIB_INCLUDE_DIRS}
13 ${GSETTINGSQT_INCLUDE_DIRS}14 ${GSETTINGSQT_INCLUDE_DIRS}
15 ${U1DB_INCLUDE_DIRS}
14)16)
1517
16set(QMLPLUGIN_SRC18set(QMLPLUGIN_SRC
@@ -24,6 +26,7 @@
24 resultsmodel.cpp26 resultsmodel.cpp
25 scope.cpp27 scope.cpp
26 scopes.cpp28 scopes.cpp
29 settingsmodel.cpp
27 utils.cpp30 utils.cpp
28 plugin.cpp31 plugin.cpp
29 iconutils.cpp32 iconutils.cpp
@@ -37,6 +40,7 @@
37 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ResultsModelInterface.h40 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ResultsModelInterface.h
38 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ScopeInterface.h41 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ScopeInterface.h
39 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ScopesInterface.h42 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ScopesInterface.h
43 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/SettingsModelInterface.h
40)44)
4145
42add_library(Unity-qml SHARED ${QMLPLUGIN_SRC})46add_library(Unity-qml SHARED ${QMLPLUGIN_SRC})
@@ -44,6 +48,7 @@
44target_link_libraries(Unity-qml48target_link_libraries(Unity-qml
45 ${SCOPESLIB_LDFLAGS}49 ${SCOPESLIB_LDFLAGS}
46 ${GSETTINGSQT_LDFLAGS}50 ${GSETTINGSQT_LDFLAGS}
51 ${U1DB_LDFLAGS}
47 )52 )
4853
49qt5_use_modules(Unity-qml Qml Gui DBus)54qt5_use_modules(Unity-qml Qml Gui DBus)
5055
=== modified file 'src/Unity/plugin.cpp'
--- src/Unity/plugin.cpp 2014-06-03 15:51:13 +0000
+++ src/Unity/plugin.cpp 2014-07-10 07:45:27 +0000
@@ -32,6 +32,7 @@
32#include "previewstack.h"32#include "previewstack.h"
33#include "previewmodel.h"33#include "previewmodel.h"
34#include "previewwidgetmodel.h"34#include "previewwidgetmodel.h"
35#include "settingsmodel.h"
3536
36void UnityPlugin::registerTypes(const char *uri)37void UnityPlugin::registerTypes(const char *uri)
37{38{
@@ -42,6 +43,7 @@
42 qmlRegisterUncreatableType<unity::shell::scopes::ScopeInterface>(uri, 0, 2, "Scope", "Can't create Scope object in QML. Get them from Scopes instance.");43 qmlRegisterUncreatableType<unity::shell::scopes::ScopeInterface>(uri, 0, 2, "Scope", "Can't create Scope object in QML. Get them from Scopes instance.");
43 qmlRegisterUncreatableType<unity::shell::scopes::DepartmentInterface>(uri, 0, 2, "Department", "Can't create Department object in QML. Get them from Scope instance.");44 qmlRegisterUncreatableType<unity::shell::scopes::DepartmentInterface>(uri, 0, 2, "Department", "Can't create Department object in QML. Get them from Scope instance.");
44 qmlRegisterUncreatableType<unity::shell::scopes::CategoriesInterface>(uri, 0, 2, "Categories", "Can't create Categories object in QML. Get them from Scope instance.");45 qmlRegisterUncreatableType<unity::shell::scopes::CategoriesInterface>(uri, 0, 2, "Categories", "Can't create Categories object in QML. Get them from Scope instance.");
46 qmlRegisterUncreatableType<unity::shell::scopes::SettingsModelInterface>(uri, 0, 2, "Settings", "Can't create Settings object in QML. Get them from Scope instance.");
45 qmlRegisterUncreatableType<scopes_ng::ResultsModel>(uri, 0, 2, "ResultsModel", "Can't create new ResultsModel in QML. Get them from Categories instance.");47 qmlRegisterUncreatableType<scopes_ng::ResultsModel>(uri, 0, 2, "ResultsModel", "Can't create new ResultsModel in QML. Get them from Categories instance.");
46 qmlRegisterUncreatableType<unity::shell::scopes::PreviewModelInterface>(uri, 0, 2, "PreviewModel", "Can't create new PreviewModel in QML. Get them from PreviewStack instance.");48 qmlRegisterUncreatableType<unity::shell::scopes::PreviewModelInterface>(uri, 0, 2, "PreviewModel", "Can't create new PreviewModel in QML. Get them from PreviewStack instance.");
47 qmlRegisterUncreatableType<scopes_ng::PreviewWidgetModel>(uri, 0, 2, "PreviewWidgetModel", "Can't create new PreviewWidgetModel in QML. Get them from PreviewModel instance.");49 qmlRegisterUncreatableType<scopes_ng::PreviewWidgetModel>(uri, 0, 2, "PreviewWidgetModel", "Can't create new PreviewWidgetModel in QML. Get them from PreviewModel instance.");
4850
=== modified file 'src/Unity/scope.cpp'
--- src/Unity/scope.cpp 2014-06-25 17:00:21 +0000
+++ src/Unity/scope.cpp 2014-07-10 07:45:27 +0000
@@ -26,6 +26,7 @@
26#include "previewstack.h"26#include "previewstack.h"
27#include "utils.h"27#include "utils.h"
28#include "scopes.h"28#include "scopes.h"
29#include "settingsmodel.h"
2930
30// Qt31// Qt
31#include <QUrl>32#include <QUrl>
@@ -533,6 +534,21 @@
533 QVariant converted(scopeVariantToQVariant(scopes::Variant(m_scopeMetadata->appearance_attributes())));534 QVariant converted(scopeVariantToQVariant(scopes::Variant(m_scopeMetadata->appearance_attributes())));
534 m_customizations = converted.toMap();535 m_customizations = converted.toMap();
535 Q_EMIT customizationsChanged();536 Q_EMIT customizationsChanged();
537
538 try
539 {
540 scopes::Variant settings_definitions;
541 settings_definitions = m_scopeMetadata->settings_definitions();
542 m_settingsModel.reset(
543 new SettingsModel(QDir::home().filePath(".local/share"), id(),
544 scopeVariantToQVariant(settings_definitions), this));
545 }
546 catch (unity::scopes::NotFoundException&)
547 {
548 // If there's no settings data
549 m_settingsModel.reset();
550 }
551 Q_EMIT settingsChanged();
536}552}
537553
538QString Scope::id() const554QString Scope::id() const
@@ -599,6 +615,11 @@
599 return m_categories;615 return m_categories;
600}616}
601617
618unity::shell::scopes::SettingsModelInterface* Scope::settings() const
619{
620 return m_settingsModel.data();
621}
622
602/*623/*
603Filters* Scope::filters() const624Filters* Scope::filters() const
604{625{
605626
=== modified file 'src/Unity/scope.h'
--- src/Unity/scope.h 2014-06-25 16:57:46 +0000
+++ src/Unity/scope.h 2014-07-10 07:45:27 +0000
@@ -47,6 +47,7 @@
47class Categories;47class Categories;
48class PushEvent;48class PushEvent;
49class PreviewStack;49class PreviewStack;
50class SettingsModel;
5051
51class CollectionController52class CollectionController
52{53{
@@ -116,6 +117,7 @@
116 QString shortcut() const override;117 QString shortcut() const override;
117 bool searchInProgress() const override;118 bool searchInProgress() const override;
118 unity::shell::scopes::CategoriesInterface* categories() const override;119 unity::shell::scopes::CategoriesInterface* categories() const override;
120 unity::shell::scopes::SettingsModelInterface* settings() const override;
119 QString searchQuery() const override;121 QString searchQuery() const override;
120 QString noResultsHint() const override;122 QString noResultsHint() const override;
121 QString formFactor() const override;123 QString formFactor() const override;
@@ -189,6 +191,7 @@
189 unity::scopes::Department::SCPtr m_lastRootDepartment;191 unity::scopes::Department::SCPtr m_lastRootDepartment;
190 QGSettings* m_settings;192 QGSettings* m_settings;
191 Categories* m_categories;193 Categories* m_categories;
194 QScopedPointer<SettingsModel> m_settingsModel;
192 QSharedPointer<DepartmentNode> m_departmentTree;195 QSharedPointer<DepartmentNode> m_departmentTree;
193 QTimer m_aggregatorTimer;196 QTimer m_aggregatorTimer;
194 QTimer m_clearTimer;197 QTimer m_clearTimer;
195198
=== added file 'src/Unity/settingsmodel.cpp'
--- src/Unity/settingsmodel.cpp 1970-01-01 00:00:00 +0000
+++ src/Unity/settingsmodel.cpp 2014-07-10 07:45:27 +0000
@@ -0,0 +1,169 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * Authors:
5 * Pete Woods <pete.woods@canonical.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "settingsmodel.h"
21#include "utils.h"
22
23#include <QDebug>
24#include <QDir>
25#include <QTimer>
26
27using namespace scopes_ng;
28namespace sc = unity::scopes;
29
30static const QString SETTING_GROUP("default");
31
32static const QString SETTING_ID_PATTERN("%1-%2");
33
34SettingsModel::SettingsModel(const QDir& shareDir, const QString& scopeId,
35 const QVariant& settingsDefinitions, QObject* parent,
36 int settingsTimeout)
37 : SettingsModelInterface(parent), m_settingsTimeout(settingsTimeout)
38{
39 shareDir.mkdir(scopeId);
40 QDir databaseDir = shareDir.filePath(scopeId);
41 m_database.setPath(databaseDir.filePath("settings.db"));
42
43 for (const auto &it : settingsDefinitions.toList())
44 {
45 QVariantMap data = it.toMap();
46 QString id = data["id"].toString();
47 QString displayName = data["displayName"].toString();
48 QVariantMap properties = data["parameters"].toMap();
49 QString type = data["type"].toString();
50
51 QVariantMap defaults;
52 defaults["value"] = properties["defaultValue"];
53
54 QSharedPointer<U1db::Document> document(new U1db::Document);
55 document->setDocId(SETTING_ID_PATTERN.arg(SETTING_GROUP, id));
56 document->setDefaults(defaults);
57 document->setDatabase(&m_database);
58 document->setCreate(true);
59
60 m_documents[id] = document;
61
62 QSharedPointer<QTimer> timer(new QTimer());
63 timer->setProperty("setting_id", id);
64 timer->setSingleShot(true);
65 timer->setInterval(m_settingsTimeout);
66 timer->setTimerType(Qt::VeryCoarseTimer);
67 connect(timer.data(), SIGNAL(timeout()), this,
68 SLOT(settings_timeout()));
69 m_timers[id] = timer;
70
71 QSharedPointer<Data> setting(
72 new Data(id, displayName, type, properties));
73
74 m_data << setting;
75 }
76}
77
78QVariant SettingsModel::data(const QModelIndex& index, int role) const
79{
80 int row = index.row();
81 QVariant result;
82
83 if (row < m_data.size())
84 {
85 auto data = m_data[row];
86
87 switch (role)
88 {
89 case Roles::RoleSettingId:
90 result = data->id;
91 break;
92 case Roles::RoleDisplayName:
93 result = data->displayName;
94 break;
95 case Roles::RoleType:
96 result = data->type;
97 break;
98 case Roles::RoleProperties:
99 result = data->properties;
100 break;
101 case Roles::RoleValue:
102 {
103 QSharedPointer<U1db::Document> document = m_documents[data->id];
104 QVariantMap contents = document->getContents().toMap();
105 result = contents["value"];
106 break;
107 }
108 default:
109 break;
110 }
111 }
112
113 return result;
114}
115
116bool SettingsModel::setData(const QModelIndex &index, const QVariant &value,
117 int role)
118{
119 int row = index.row();
120 QVariant result;
121
122 if (row < m_data.size())
123 {
124 auto data = m_data[row];
125
126 switch (role)
127 {
128 case Roles::RoleValue:
129 {
130 QSharedPointer<QTimer> timer = m_timers[data->id];
131 timer->setProperty("value", value);
132 timer->start();
133
134 return true;
135 }
136 default:
137 break;
138 }
139 }
140
141 return false;
142}
143
144int SettingsModel::rowCount(const QModelIndex&) const
145{
146 return count();
147}
148
149int SettingsModel::count() const
150{
151 return m_data.size();
152}
153
154void SettingsModel::settings_timeout()
155{
156 QObject *timer = sender();
157 if (!timer)
158 {
159 return;
160 }
161
162 QString setting_id = timer->property("setting_id").toString();
163 QVariant value = timer->property("value");
164
165 QSharedPointer<U1db::Document> document = m_documents[setting_id];
166 QVariantMap map;
167 map["value"] = value;
168 document->setContents(map);
169}
0170
=== added file 'src/Unity/settingsmodel.h'
--- src/Unity/settingsmodel.h 1970-01-01 00:00:00 +0000
+++ src/Unity/settingsmodel.h 2014-07-10 07:45:27 +0000
@@ -0,0 +1,93 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * Authors:
5 * Pete Woods <pete.woods@canonical.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#ifndef NG_PREVIEW_SETTTINGSMODEL_H_
21#define NG_PREVIEW_SETTTINGSMODEL_H_
22
23#include <libu1db-qt5/database.h>
24#include <libu1db-qt5/document.h>
25#include <unity/SymbolExport.h>
26#include <unity/shell/scopes/SettingsModelInterface.h>
27
28#include <QAbstractListModel>
29#include <QList>
30#include <QSharedPointer>
31
32QT_BEGIN_NAMESPACE
33class QDir;
34class QTimer;
35QT_END_NAMESPACE
36
37namespace scopes_ng
38{
39
40class Q_DECL_EXPORT SettingsModel: public unity::shell::scopes::SettingsModelInterface
41{
42Q_OBJECT
43
44 struct Data
45 {
46 QString id;
47 QString displayName;
48 QString type;
49 QVariant properties;
50
51 Data(QString const& id_, QString const& displayName_,
52 QString const& type_, QVariant const& properties_)
53 : id(id_), displayName(displayName_), type(type_), properties(
54 properties_)
55 {
56 }
57 };
58
59public:
60 explicit SettingsModel(const QDir& shareDir, const QString& scopeId,
61 const QVariant& settingsDefinitions, QObject* parent = 0,
62 int settingsTimeout = 300);
63
64 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const
65 override;
66
67 bool setData(const QModelIndex&index, const QVariant& value, int role =
68 Qt::EditRole) override;
69
70 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
71
72 int count() const override;
73
74protected Q_SLOTS:
75 void settings_timeout();
76
77protected:
78 int m_settingsTimeout;
79
80 QList<QSharedPointer<Data>> m_data;
81
82 U1db::Database m_database;
83
84 QMap<QString, QSharedPointer<U1db::Document>> m_documents;
85
86 QMap<QString, QSharedPointer<QTimer>> m_timers;
87};
88
89}
90
91Q_DECLARE_METATYPE(scopes_ng::SettingsModel*)
92
93#endif /* NG_PREVIEW_SETTTINGSMODEL_H_ */
094
=== modified file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt 2014-06-05 10:37:50 +0000
+++ tests/CMakeLists.txt 2014-07-10 07:45:27 +0000
@@ -51,4 +51,7 @@
51 resultstest51 resultstest
52 previewtest52 previewtest
53 utilstest53 utilstest
54 settingstest
54 )55 )
56
57qt5_use_modules(settingstestExec Sql)
5558
=== added file 'tests/settingstest.cpp'
--- tests/settingstest.cpp 1970-01-01 00:00:00 +0000
+++ tests/settingstest.cpp 2014-07-10 07:45:27 +0000
@@ -0,0 +1,311 @@
1/*
2 * Copyright (C) 2013-2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Pete Woods <pete.woods@canonical.com>
18 */
19
20#include <QJsonDocument>
21#include <QObject>
22#include <QTemporaryDir>
23#include <QTest>
24
25#include <settingsmodel.h>
26
27using namespace scopes_ng;
28using namespace unity::shell::scopes;
29
30namespace
31{
32
33const static QByteArray BOOLEAN_DEFINITION =
34 R"(
35[
36 {
37 "id": "enabledSetting",
38 "displayName": "Enabled",
39 "type": "boolean",
40 "parameters": {
41 "defaultValue": true
42 }
43 }
44]
45)";
46
47const static QByteArray LIST_DEFINITION =
48 R"(
49[
50 {
51 "id": "unitTempSetting",
52 "displayName": "Temperature Units",
53 "type": "list",
54 "parameters": {
55 "defaultValue": 1,
56 "values": ["Celcius", "Fahrenheit"]
57 }
58 }
59]
60)";
61
62const static QByteArray NUMBER_DEFINITION =
63 R"(
64[
65 {
66 "id": "ageSetting",
67 "displayName": "Age",
68 "type": "number",
69 "parameters": {
70 "defaultValue": 23
71 }
72 }
73]
74)";
75
76const static QByteArray STRING_DEFINITION =
77 R"(
78[
79 {
80 "id": "locationSetting",
81 "displayName": "Location",
82 "type": "string",
83 "parameters": {
84 "defaultValue": "London"
85 }
86 }
87]
88)";
89
90const static QByteArray MIXED_DEFINITION =
91 R"(
92[
93 {
94 "id": "locationSetting",
95 "displayName": "Location",
96 "type": "string",
97 "parameters": {
98 "defaultValue": "London"
99 }
100 },
101 {
102 "id": "unitTempSetting",
103 "displayName": "Temperature Units",
104 "type": "list",
105 "parameters": {
106 "defaultValue": 1,
107 "values": ["Celcius", "Fahrenheit"]
108 }
109 },
110 {
111 "id": "ageSetting",
112 "displayName": "Age",
113 "type": "number",
114 "parameters": {
115 "defaultValue": 23
116 }
117 },
118 {
119 "id": "enabledSetting",
120 "displayName": "Enabled",
121 "type": "boolean",
122 "parameters": {
123 "defaultValue": true
124 }
125 }
126]
127)";
128
129class SettingsTest: public QObject
130{
131Q_OBJECT
132private:
133 QScopedPointer<QTemporaryDir> tempDir;
134
135 QSharedPointer<SettingsModelInterface> settings;
136
137 void newSettingsModel(const QString& id, const QByteArray& json)
138 {
139 QJsonDocument doc = QJsonDocument::fromJson(json);
140 QVariant definitions = doc.toVariant();
141 settings.reset(
142 new SettingsModel(tempDir->path(), id, definitions, 0, 0));
143 }
144
145 void verifyData(int index, const QString& id, const QString& displayName,
146 const QString& type, const QVariant& properties)
147 {
148 QCOMPARE(
149 settings->data(settings->index(index),
150 SettingsModelInterface::RoleSettingId), QVariant(id));
151 QCOMPARE(
152 settings->data(settings->index(index),
153 SettingsModelInterface::RoleDisplayName),
154 QVariant(displayName));
155 QCOMPARE(
156 settings->data(settings->index(index),
157 SettingsModelInterface::RoleType), QVariant(type));
158 QCOMPARE(
159 settings->data(settings->index(index),
160 SettingsModelInterface::RoleProperties), properties);
161 }
162
163 void verifyValue(int index, const QVariant& value)
164 {
165 // Using this "TRY" macro repeatedly attempts the comparison until a timeout
166 QTRY_COMPARE(
167 settings->data(settings->index(index),
168 SettingsModelInterface::RoleValue), value);
169 }
170
171 void setValue(int index, const QVariant& value)
172 {
173 QVERIFY(settings->setData(settings->index(index), value,
174 SettingsModelInterface::RoleValue));
175 }
176
177private Q_SLOTS:
178 void init()
179 {
180 tempDir.reset(new QTemporaryDir);
181 }
182
183 void testBooleanDefinition()
184 {
185 newSettingsModel("boolean", BOOLEAN_DEFINITION);
186 QCOMPARE(settings->rowCount(), 1);
187
188 // Check the various properties make it through
189 QVariantMap properties;
190 properties["defaultValue"] = true;
191 verifyData(0, "enabledSetting", "Enabled", "boolean", properties);
192
193 verifyValue(0, true);
194 }
195
196 void testListDefinition()
197 {
198 newSettingsModel("list", LIST_DEFINITION);
199 QCOMPARE(settings->rowCount(), 1);
200
201 // Check the various properties make it through
202 QVariantMap properties;
203 properties["defaultValue"] = 1;
204 properties["values"] = QVariantList() << "Celcius" << "Fahrenheit";
205 verifyData(0, "unitTempSetting", "Temperature Units", "list",
206 properties);
207
208 // Check the default value
209 verifyValue(0, 1);
210 }
211
212 void testNumberDefinition()
213 {
214 newSettingsModel("number", NUMBER_DEFINITION);
215 QCOMPARE(settings->rowCount(), 1);
216
217 // Check the various properties make it through
218 QVariantMap properties;
219 properties["defaultValue"] = 23;
220 verifyData(0, "ageSetting", "Age", "number", properties);
221
222 // Check the default value
223 verifyValue(0, 23);
224 }
225
226 void testStringDefinition()
227 {
228 newSettingsModel("string", STRING_DEFINITION);
229 QCOMPARE(settings->rowCount(), 1);
230
231 // Check the various properties make it through
232 QVariantMap properties;
233 properties["defaultValue"] = "London";
234 verifyData(0, "locationSetting", "Location", "string", properties);
235
236 // Check the default value
237 verifyValue(0, "London");
238 }
239
240 void testMixedDefinition()
241 {
242 newSettingsModel("mixed", MIXED_DEFINITION);
243 QCOMPARE(settings->rowCount(), 4);
244
245 {
246 QVariantMap properties;
247 properties["defaultValue"] = "London";
248 verifyData(0, "locationSetting", "Location", "string", properties);
249 verifyValue(0, "London");
250 }
251 {
252 QVariantMap properties;
253 properties["defaultValue"] = 1;
254 properties["values"] = QVariantList() << "Celcius" << "Fahrenheit";
255 verifyData(1, "unitTempSetting", "Temperature Units", "list",
256 properties);
257 verifyValue(1, 1);
258 }
259 {
260 QVariantMap properties;
261 properties["defaultValue"] = 23;
262 verifyData(2, "ageSetting", "Age", "number", properties);
263 verifyValue(2, 23);
264 }
265 {
266 QVariantMap properties;
267 properties["defaultValue"] = true;
268 verifyData(3, "enabledSetting", "Enabled", "boolean", properties);
269 verifyValue(3, true);
270 }
271 }
272
273 void testUpdateValue()
274 {
275 // Create an initial settings model
276 newSettingsModel("update", MIXED_DEFINITION);
277
278 // Verify the initial values
279 verifyValue(0, "London");
280 verifyValue(1, 1);
281 verifyValue(2, 23);
282 verifyValue(3, true);
283
284 // Update the string value
285 setValue(0, "Banana");
286 verifyValue(0, "Banana");
287
288 // Update the list value
289 setValue(1, 0);
290 verifyValue(1, 0);
291
292 // Update the number value
293 setValue(2, 123);
294 verifyValue(2, 123);
295
296 // Update the boolean value
297 setValue(3, false);
298 verifyValue(3, false);
299
300 // Make a new settings model to check the settings were saved to disk
301 newSettingsModel("update", MIXED_DEFINITION);
302 verifyValue(0, "Banana");
303 verifyValue(1, 0);
304 verifyValue(2, 123);
305 verifyValue(3, false);
306 }
307};
308
309}
310QTEST_GUILESS_MAIN(SettingsTest)
311#include <settingstest.moc>
0312
=== added directory 'tools'
=== added directory 'tools/settings'
=== added file 'tools/settings/Settings.qml'
--- tools/settings/Settings.qml 1970-01-01 00:00:00 +0000
+++ tools/settings/Settings.qml 2014-07-10 07:45:27 +0000
@@ -0,0 +1,68 @@
1import QtQuick 2.0
2import Ubuntu.Components 1.1
3import Unity 0.2
4
5MainView {
6 id: app
7 applicationName: "example"
8 width: units.gu(40)
9 height: units.gu(60)
10
11 Arguments {
12 id: args
13 defaultArgument.help: "Expects a scope ID"
14 defaultArgument.valueNames: ["SCOPE"]
15 }
16
17 Scopes {
18 id: scopes
19 onLoadedChanged: {
20 app.scope = scopes.getScope(args.defaultArgument.at(0))
21 }
22 }
23
24 property var scope: ListModel{}
25
26 function toFileName(type) {
27 return "widgets/" + type + "SettingsWidget.qml"
28 }
29
30 Page {
31 title: "Settings for " + scope.name
32
33 ListView {
34 anchors.fill: parent
35 spacing: units.gu(1)
36
37 model: scope.settings
38 delegate: Loader {
39 id: loader
40 source: toFileName(type)
41 onLoaded: {
42 item.properties = properties
43 item.value = value
44
45 if (type != "boolean") {
46 loader.width = parent.width
47 }
48 if (type == "list") {
49 loader.height = units.gu(10)
50 }
51
52 itemValue = Qt.binding(function() { return item.value })
53 }
54 property var itemValue
55 onItemValueChanged: {
56 if (value !== item.value) {
57 value = item.value
58 }
59 }
60 }
61 section.property: "displayName"
62 section.delegate: Text {
63 width: parent.width
64 text: section
65 }
66 }
67 }
68}
069
=== added file 'tools/settings/run.sh'
--- tools/settings/run.sh 1970-01-01 00:00:00 +0000
+++ tools/settings/run.sh 2014-07-10 07:45:27 +0000
@@ -0,0 +1,9 @@
1#!/bin/bash
2
3if [ "$#" -ne 1 ]; then
4 echo "ERROR: Usage $0 SCOPE_ID"
5 exit 1
6fi
7
8DIR="$(dirname "$0")"
9APP_ID="example" qmlscene "$DIR/Settings.qml" -- "$1"
010
=== added directory 'tools/settings/widgets'
=== added file 'tools/settings/widgets/booleanSettingsWidget.qml'
--- tools/settings/widgets/booleanSettingsWidget.qml 1970-01-01 00:00:00 +0000
+++ tools/settings/widgets/booleanSettingsWidget.qml 2014-07-10 07:45:27 +0000
@@ -0,0 +1,9 @@
1import QtQuick 2.0
2import Ubuntu.Components 1.1
3
4
5CheckBox {
6 id: box
7 property var properties
8 property alias value: box.checked
9}
010
=== added file 'tools/settings/widgets/listSettingsWidget.qml'
--- tools/settings/widgets/listSettingsWidget.qml 1970-01-01 00:00:00 +0000
+++ tools/settings/widgets/listSettingsWidget.qml 2014-07-10 07:45:27 +0000
@@ -0,0 +1,11 @@
1import QtQuick 2.0
2import Ubuntu.Components 1.1
3
4OptionSelector {
5 id: combo
6
7 property var properties
8 property alias value: combo.selectedIndex
9
10 model: properties["values"]
11}
012
=== added file 'tools/settings/widgets/numberSettingsWidget.qml'
--- tools/settings/widgets/numberSettingsWidget.qml 1970-01-01 00:00:00 +0000
+++ tools/settings/widgets/numberSettingsWidget.qml 2014-07-10 07:45:27 +0000
@@ -0,0 +1,10 @@
1import QtQuick 2.0
2import Ubuntu.Components 1.1
3
4
5TextField {
6 id: field
7 property var properties
8 property alias value: field.text
9 validator: DoubleValidator {}
10}
011
=== added file 'tools/settings/widgets/stringSettingsWidget.qml'
--- tools/settings/widgets/stringSettingsWidget.qml 1970-01-01 00:00:00 +0000
+++ tools/settings/widgets/stringSettingsWidget.qml 2014-07-10 07:45:27 +0000
@@ -0,0 +1,9 @@
1import QtQuick 2.0
2import Ubuntu.Components 1.1
3
4
5TextField {
6 id: field
7 property var properties
8 property alias value: field.text
9}

Subscribers

People subscribed via source and target branches

to all changes: