on xenial help sign in
Unhook Lights interface from indicator widgets Fixes: #1385331

Approved by: Albert Astals Cid

commit20b705ffcb29e4f58995efa11b1e3f0a578dd320 authorNick Dedekind <nick.dedekind@canonical.com> dateFri 9 Jan 2015, 10:41 (12 years ago) parenta642c5cc517433
36 files changed+1120−308
debian/control+3−3 plugins/Lights/Lights.cpp+9−24 plugins/Unity/Indicators/CMakeLists.txt+3−1 plugins/Unity/Indicators/Indicators.qmltypes+18−7 plugins/Unity/Indicators/actionrootstate.cpp+97 plugins/Unity/Indicators/actionrootstate.h+55 plugins/Unity/Indicators/modelactionrootstate.cpp+127 plugins/Unity/Indicators/modelactionrootstate.h+61 plugins/Unity/Indicators/plugin.cpp+4−2 plugins/Unity/Indicators/rootactionstate.cpp → plugins/Unity/Indicators/rootstateparser.cpp+74−141 plugins/Unity/Indicators/rootactionstate.h → plugins/Unity/Indicators/rootstateparser.h+18−34 qml/Greeter/Clock.qml+1−1 qml/Panel/IndicatorItem.qml−8 qml/Panel/Indicators/IndicatorBase.qml+1−1 qml/Panel/Indicators/IndicatorsLight.qml+19−1 qml/Panel/IndicatorsMenu.qml+5 tests/mocks/CMakeLists.txt+1 tests/mocks/Lights/CMakeLists.txt+8 tests/mocks/Lights/Lights.cpp+84 tests/mocks/Lights/Lights.h+66 tests/mocks/Lights/Lights.qmltypes+45 tests/mocks/Lights/plugin.cpp+33 tests/mocks/Lights/plugin.h+32 tests/mocks/Lights/qmldir+3 tests/mocks/QMenuModel/CMakeLists.txt+1 tests/mocks/QMenuModel/QDBusActionGroup.qml+20−67 tests/mocks/QMenuModel/actiondata.h+52 tests/mocks/QMenuModel/plugin.cpp+10 tests/mocks/Unity/Indicators/ActionRootState.qml+46 tests/mocks/Unity/Indicators/RootActionState.qml → tests/mocks/Unity/Indicators/ModelActionRootState.qml tests/mocks/Unity/Indicators/qmldir+2−1 tests/plugins/Unity/Indicators/rootactionstatetest.cpp+4−4 tests/qmltests/CMakeLists.txt+1 tests/qmltests/Panel/Indicators/tst_IndicatorsLight.qml+129 tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml+52−1 tests/qmltests/Wizard/tst_Wizard.qml+36−12
debian/control+3−3
@@ -24,7 +24,7 @@ Build-Depends: cmake,
2424 libpam0g-dev,
2525 libpay2-dev,
2626 libpulse-dev,
27− libqmenumodel-dev (>= 0.2.8),
27+ libqmenumodel-dev (>= 0.2.9),
2828 libqt5xmlpatterns5-dev,
2929 libsystemsettings-dev,
3030 libunity-api-dev (>= 7.94),
@@ -63,7 +63,7 @@ X-Ubuntu-Use-Langpack: yes
6363
6464Package: indicators-client
6565Architecture: amd64 armhf i386
66−Depends: qmenumodel-qml (>= 0.2.8),
66+Depends: qmenumodel-qml (>= 0.2.9),
6767 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.1.1239) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.1.1239),
6868 unity8 (= ${binary:Version}),
6969 ${misc:Depends},
@@ -87,7 +87,7 @@ Provides: indicator-renderer,
8787Depends: gsettings-desktop-schemas,
8888 libcap2-bin,
8989 libglib2.0-bin,
90− qmenumodel-qml (>= 0.2.8),
90+ qmenumodel-qml (>= 0.2.9),
9191 qml-module-qt-labs-folderlistmodel,
9292 qml-module-qtquick-xmllistmodel,
9393 qtdeclarative5-gsettings1.0,
plugins/Lights/Lights.cpp+9−24
@@ -44,12 +44,20 @@ Lights::~Lights()
4444
4545void Lights::setState(Lights::State newState)
4646{
47+ if (!init()) {
48+ qWarning() << "No lights device";
49+ return;
50+ }
51+
4752 if (m_state != newState) {
4853 if (newState == Lights::On) {
4954 turnOn();
5055 } else {
5156 turnOff();
5257 }
58+
59+ m_state = newState;
60+ Q_EMIT stateChanged(m_state);
5361 }
5462}
5563
@@ -115,6 +123,7 @@ bool Lights::init()
115123 err = module->methods->open(module, LIGHT_ID_NOTIFICATIONS, &device);
116124 if (err == 0) {
117125 m_lightDevice = (light_device_t*)device;
126+ turnOff();
118127 return true;
119128 } else {
120129 qWarning() << "Failed to access notification lights";
@@ -127,15 +136,6 @@ bool Lights::init()
127136
128137void Lights::turnOn()
129138{
130− if (!init()) {
131− qWarning() << "No lights device";
132− return;
133− }
134−
135− if (m_state == Lights::On) {
136− return;
137− }
138−
139139 // pulse
140140 light_state_t state;
141141 memset(&state, 0, sizeof(light_state_t));
@@ -147,23 +147,11 @@ void Lights::turnOn()
147147
148148 if (m_lightDevice->set_light(m_lightDevice, &state) != 0) {
149149 qWarning() << "Failed to turn the light off";
150− } else {
151− m_state = Lights::On;
152− Q_EMIT stateChanged(m_state);
153150 }
154151}
155152
156153void Lights::turnOff()
157154{
158− if (!init()) {
159− qWarning() << "No lights device";
160− return;
161− }
162−
163− if (m_state == Lights::Off) {
164− return;
165− }
166−
167155 light_state_t state;
168156 memset(&state, 0, sizeof(light_state_t));
169157 state.color = 0x00000000;
@@ -174,8 +162,5 @@ void Lights::turnOff()
174162
175163 if (m_lightDevice->set_light(m_lightDevice, &state) != 0) {
176164 qWarning() << "Failed to turn the light off";
177− } else {
178− m_state = Lights::Off;
179− Q_EMIT stateChanged(m_state);
180165 }
181166}
plugins/Unity/Indicators/CMakeLists.txt+3−1
@@ -16,14 +16,16 @@ include_directories(
1616)
1717
1818set(IndicatorsQML_SOURCES
19+ actionrootstate.cpp
1920 indicator.cpp
2021 indicators.h
2122 indicatorsmanager.cpp
2223 indicatorsmodel.cpp
2324 menucontentactivator.cpp
25+ modelactionrootstate.cpp
2426 modelprinter.cpp
2527 plugin.cpp
26− rootactionstate.cpp
28+ rootstateparser.cpp
2729 sharedunitymenumodel.cpp
2830 unitymenumodelcache.cpp
2931 unitymenumodelstack.cpp
plugins/Unity/Indicators/Indicators.qmltypes+18−7
@@ -7,6 +7,14 @@ import QtQuick.tooling 1.1
77// 'qmlplugindump -notrelocatable Unity.Indicators 0.1 plugins'
88
99Module {
10+ Component {
11+ name: "ActionRootState"
12+ prototype: "RootStateObject"
13+ exports: ["Unity.Indicators/ActionRootState 0.1"]
14+ exportMetaObjectRevisions: [0]
15+ Property { name: "actionGroup"; type: "QDBusActionGroup"; isPointer: true }
16+ Property { name: "actionName"; type: "string" }
17+ }
1018 Component {
1119 name: "ActionState"
1220 prototype: "QObject"
@@ -23,7 +31,6 @@ Module {
2331 }
2432 }
2533 }
26− Component { name: "ActionStateParser"; prototype: "QObject" }
2734 Component {
2835 name: "FlatMenuProxyModelRole"
2936 prototype: "QObject"
@@ -146,6 +153,14 @@ Module {
146153 exportMetaObjectRevisions: [0]
147154 Property { name: "active"; type: "bool"; isReadonly: true }
148155 }
156+ Component {
157+ name: "ModelActionRootState"
158+ prototype: "RootStateObject"
159+ exports: ["Unity.Indicators/ModelActionRootState 0.1"]
160+ exportMetaObjectRevisions: [0]
161+ Property { name: "menu"; type: "UnityMenuModel"; isPointer: true }
162+ Signal { name: "indexChanged" }
163+ }
149164 Component {
150165 name: "ModelPrinter"
151166 prototype: "QObject"
@@ -192,11 +207,8 @@ Module {
192207 }
193208 Component { name: "QIdentityProxyModel"; prototype: "QAbstractProxyModel" }
194209 Component {
195− name: "RootActionState"
196− prototype: "ActionStateParser"
197− exports: ["Unity.Indicators/RootActionState 0.1"]
198− exportMetaObjectRevisions: [0]
199− Property { name: "menu"; type: "UnityMenuModel"; isPointer: true }
210+ name: "RootStateObject"
211+ prototype: "QObject"
200212 Property { name: "valid"; type: "bool"; isReadonly: true }
201213 Property { name: "title"; type: "string"; isReadonly: true }
202214 Property { name: "leftLabel"; type: "string"; isReadonly: true }
@@ -205,7 +217,6 @@ Module {
205217 Property { name: "accessibleName"; type: "string"; isReadonly: true }
206218 Property { name: "visible"; type: "bool"; isReadonly: true }
207219 Signal { name: "updated" }
208− Signal { name: "indexChanged" }
209220 }
210221 Component {
211222 name: "SharedUnityMenuModel"
plugins/Unity/Indicators/actionrootstate.cppnew file+97
@@ -0,0 +1,97 @@
1+/*
2+ * Copyright 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 Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "actionrootstate.h"
18+
19+#include <qdbusactiongroup.h>
20+#include <QDebug>
21+
22+ActionRootState::ActionRootState(QObject *parent)
23+ : RootStateObject(parent)
24+ , m_actionGroup(nullptr)
25+{
26+}
27+
28+QDBusActionGroup *ActionRootState::actionGroup() const
29+{
30+ return m_actionGroup;
31+}
32+
33+void ActionRootState::setActionGroup(QDBusActionGroup *actionGroup)
34+{
35+ if (m_actionGroup != actionGroup) {
36+ bool wasValid = valid();
37+
38+ if (m_actionGroup) {
39+ disconnect(m_actionGroup, 0, this, 0);
40+ }
41+ m_actionGroup = actionGroup;
42+
43+ if (m_actionGroup) {
44+ connect(m_actionGroup, &QDBusActionGroup::statusChanged, this, [&](bool) { updateActionState(); });
45+ connect(m_actionGroup, &QDBusActionGroup::actionAppear, this, [&](const QString&) { updateActionState(); });
46+ connect(m_actionGroup, &QDBusActionGroup::actionVanish, this, [&](const QString&) { updateActionState(); });
47+ connect(m_actionGroup, &QDBusActionGroup::actionStateChanged, this, [&](QVariant) { updateActionState(); });
48+
49+ connect(m_actionGroup, &QObject::destroyed, this, [&](QObject*) { updateActionState(); });
50+ }
51+ updateActionState();
52+ Q_EMIT actionGroupChanged();
53+
54+ if (wasValid != valid()) Q_EMIT validChanged();
55+ }
56+}
57+
58+QString ActionRootState::actionName() const
59+{
60+ return m_actionName;
61+}
62+
63+void ActionRootState::setActionName(const QString &actionName)
64+{
65+ if (m_actionName != actionName) {
66+ bool wasValid = valid();
67+
68+ m_actionName = actionName;
69+ updateActionState();
70+
71+ Q_EMIT actionNameChanged();
72+
73+ if (wasValid != valid()) Q_EMIT validChanged();
74+ }
75+}
76+
77+bool ActionRootState::valid() const
78+{
79+ return m_actionGroup && m_actionGroup->status() == DBusEnums::Connected &&
80+ !m_actionName.isEmpty() && m_actionGroup->hasAction(m_actionName);
81+}
82+
83+void ActionRootState::updateActionState()
84+{
85+ if (valid()) {
86+ ActionStateParser* oldParser = m_actionGroup->actionStateParser();
87+ m_actionGroup->setActionStateParser(&m_parser);
88+
89+ QVariantMap state = m_actionGroup->actionState(m_actionName).toMap();
90+
91+ m_actionGroup->setActionStateParser(oldParser);
92+
93+ setCurrentState(state);
94+ } else {
95+ setCurrentState(QVariantMap());
96+ }
97+}
plugins/Unity/Indicators/actionrootstate.hnew file+55
@@ -0,0 +1,55 @@
1+/*
2+ * Copyright 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 Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef ACTIONROOTSTATE_H
18+#define ACTIONROOTSTATE_H
19+
20+#include "unityindicatorsglobal.h"
21+
22+#include "rootstateparser.h"
23+
24+class QDBusActionGroup;
25+
26+class UNITYINDICATORS_EXPORT ActionRootState : public RootStateObject
27+{
28+ Q_OBJECT
29+ Q_PROPERTY(QDBusActionGroup* actionGroup READ actionGroup WRITE setActionGroup NOTIFY actionGroupChanged)
30+ Q_PROPERTY(QString actionName READ actionName WRITE setActionName NOTIFY actionNameChanged)
31+
32+public:
33+ ActionRootState(QObject *parent = 0);
34+
35+ QDBusActionGroup *actionGroup() const;
36+ void setActionGroup(QDBusActionGroup *actionGroup);
37+
38+ QString actionName() const;
39+ void setActionName(const QString& actionName);
40+
41+ bool valid() const override;
42+
43+Q_SIGNALS:
44+ void actionGroupChanged();
45+ void actionNameChanged();
46+
47+private Q_SLOTS:
48+ void updateActionState();
49+
50+private:
51+ QDBusActionGroup* m_actionGroup;
52+ QString m_actionName;
53+};
54+
55+#endif // ACTIONROOTSTATE_H
plugins/Unity/Indicators/modelactionrootstate.cppnew file+127
@@ -0,0 +1,127 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#include "modelactionrootstate.h"
21+#include "indicators.h"
22+
23+#include <unitymenumodel.h>
24+#include <QVariant>
25+#include <QIcon>
26+
27+extern "C" {
28+#include <glib.h>
29+#include <gio/gio.h>
30+}
31+
32+ModelActionRootState::ModelActionRootState(QObject *parent)
33+ : RootStateObject(parent),
34+ m_menu(nullptr)
35+{
36+}
37+
38+ModelActionRootState::~ModelActionRootState()
39+{
40+}
41+
42+UnityMenuModel* ModelActionRootState::menu() const
43+{
44+ return m_menu;
45+}
46+
47+void ModelActionRootState::setMenu(UnityMenuModel* menu)
48+{
49+ if (m_menu != menu) {
50+ bool wasValid = valid();
51+
52+ if (m_menu) {
53+ m_menu->disconnect(this);
54+ }
55+ m_menu = menu;
56+
57+ if (m_menu) {
58+ connect(m_menu, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(onModelRowsAdded(const QModelIndex&, int, int)));
59+ connect(m_menu, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SLOT(onModelRowsRemoved(const QModelIndex&, int, int)));
60+ connect(m_menu, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)), SLOT(onModelDataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
61+
62+ connect(m_menu, SIGNAL(destroyed()), SLOT(reset()));
63+ }
64+ updateActionState();
65+ Q_EMIT menuChanged();
66+
67+ if (wasValid != valid())
68+ Q_EMIT validChanged();
69+ }
70+}
71+
72+bool ModelActionRootState::valid() const
73+{
74+ return m_menu && m_menu->rowCount() > 0;
75+}
76+
77+void ModelActionRootState::onModelRowsAdded(const QModelIndex& parent, int start, int end)
78+{
79+ Q_UNUSED(parent);
80+ if (start == 0 && end >= 0) {
81+ updateActionState();
82+ }
83+}
84+
85+void ModelActionRootState::onModelRowsRemoved(const QModelIndex& parent, int start, int end)
86+{
87+ Q_UNUSED(parent);
88+ if (start == 0 && end >= 0) {
89+ updateActionState();
90+ }
91+}
92+
93+void ModelActionRootState::onModelDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles)
94+{
95+ Q_UNUSED(roles);
96+ if (!topLeft.isValid() || !bottomRight.isValid()) {
97+ return;
98+ }
99+
100+ if (topLeft.row() <= 0 && bottomRight.row() >= 0) {
101+ updateActionState();
102+ }
103+}
104+
105+void ModelActionRootState::reset()
106+{
107+ m_menu = nullptr;
108+
109+ Q_EMIT menuChanged();
110+ setCurrentState(QVariantMap());
111+}
112+
113+void ModelActionRootState::updateActionState()
114+{
115+ if (m_menu && m_menu->rowCount() > 0) {
116+ ActionStateParser* oldParser = m_menu->actionStateParser();
117+ m_menu->setActionStateParser(&m_parser);
118+
119+ QVariantMap state = m_menu->get(0, "actionState").toMap();
120+
121+ m_menu->setActionStateParser(oldParser);
122+
123+ setCurrentState(state);
124+ } else {
125+ setCurrentState(QVariantMap());
126+ }
127+}
plugins/Unity/Indicators/modelactionrootstate.hnew file+61
@@ -0,0 +1,61 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#ifndef MODELACTIONROOTSTATE_H
21+#define MODELACTIONROOTSTATE_H
22+
23+#include "unityindicatorsglobal.h"
24+
25+#include "rootstateparser.h"
26+
27+class UnityMenuModel;
28+
29+class UNITYINDICATORS_EXPORT ModelActionRootState : public RootStateObject
30+{
31+ Q_OBJECT
32+ Q_PROPERTY(UnityMenuModel* menu READ menu WRITE setMenu NOTIFY menuChanged)
33+public:
34+ ModelActionRootState(QObject *parent = 0);
35+ virtual ~ModelActionRootState();
36+
37+ UnityMenuModel* menu() const;
38+ void setMenu(UnityMenuModel* menu);
39+
40+ int index() const;
41+ void setIndex(int index);
42+
43+ bool valid() const override;
44+
45+Q_SIGNALS:
46+ void menuChanged();
47+ void indexChanged();
48+
49+private Q_SLOTS:
50+ void onModelRowsAdded(const QModelIndex& parent, int start, int end);
51+ void onModelRowsRemoved(const QModelIndex& parent, int start, int end);
52+ void onModelDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>&);
53+ void reset();
54+
55+private:
56+ void updateActionState();
57+
58+ UnityMenuModel* m_menu;
59+};
60+
61+#endif // MODELACTIONROOTSTATE_H
plugins/Unity/Indicators/plugin.cpp+4−2
@@ -24,12 +24,13 @@
2424#include "plugin.h"
2525
2626// local
27+#include "actionrootstate.h"
2728#include "indicators.h"
2829#include "indicatorsmanager.h"
2930#include "indicatorsmodel.h"
3031#include "menucontentactivator.h"
32+#include "modelactionrootstate.h"
3133#include "modelprinter.h"
32−#include "rootactionstate.h"
3334#include "sharedunitymenumodel.h"
3435#include "unitymenumodelcache.h"
3536#include "unitymenumodelstack.h"
@@ -51,7 +52,8 @@ void IndicatorsPlugin::registerTypes(const char *uri)
5152 qmlRegisterType<IndicatorsModel>(uri, 0, 1, "IndicatorsModel");
5253 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
5354 qmlRegisterType<UnityMenuModelStack>(uri, 0, 1, "UnityMenuModelStack");
54− qmlRegisterType<RootActionState>(uri, 0, 1, "RootActionState");
55+ qmlRegisterType<ModelActionRootState>(uri, 0, 1, "ModelActionRootState");
56+ qmlRegisterType<ActionRootState>(uri, 0, 1, "ActionRootState");
5557 qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
5658 qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
5759 qmlRegisterType<SharedUnityMenuModel>(uri, 0, 1, "SharedUnityMenuModel");
plugins/Unity/Indicators/rootactionstate.cpp → plugins/Unity/Indicators/rootstateparser.cpprenamed+74−141
@@ -12,155 +12,18 @@
1212 *
1313 * You should have received a copy of the GNU Lesser General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors:
17− * Nick Dedekind <nick.dedekind@canonical.com>
1815 */
1916
20−#include "rootactionstate.h"
21−#include "indicators.h"
22−
23−#include <unitymenumodel.h>
24−#include <QVariant>
25−#include <QIcon>
17+#include "rootstateparser.h"
2618
2719extern "C" {
2820#include <glib.h>
2921#include <gio/gio.h>
3022}
3123
32−RootActionState::RootActionState(QObject *parent)
33− : ActionStateParser(parent),
34− m_menu(nullptr)
35−{
36−}
37−
38−RootActionState::~RootActionState()
39−{
40−}
41−
42−UnityMenuModel* RootActionState::menu() const
43−{
44− return m_menu;
45−}
46−
47−void RootActionState::setMenu(UnityMenuModel* menu)
48−{
49− if (m_menu != menu) {
50− if (m_menu) {
51− m_menu->disconnect(this);
52− }
53− m_menu = menu;
54−
55− if (m_menu) {
56− connect(m_menu, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(onModelRowsAdded(const QModelIndex&, int, int)));
57− connect(m_menu, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SLOT(onModelRowsRemoved(const QModelIndex&, int, int)));
58− connect(m_menu, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)), SLOT(onModelDataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
59−
60− connect(m_menu, SIGNAL(destroyed()), SLOT(reset()));
61− }
62− updateActionState();
63− Q_EMIT menuChanged();
64− }
65−}
66−
67−void RootActionState::onModelRowsAdded(const QModelIndex& parent, int start, int end)
24+RootStateParser::RootStateParser(QObject* parent)
25+ : ActionStateParser(parent)
6826{
69− Q_UNUSED(parent);
70− if (start == 0 && end >= 0) {
71− updateActionState();
72− }
73−}
74−
75−void RootActionState::onModelRowsRemoved(const QModelIndex& parent, int start, int end)
76−{
77− Q_UNUSED(parent);
78− if (start == 0 && end >= 0) {
79− updateActionState();
80− }
81−}
82−
83−void RootActionState::onModelDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles)
84−{
85− Q_UNUSED(roles);
86− if (!topLeft.isValid() || !bottomRight.isValid()) {
87− return;
88− }
89−
90− if (topLeft.row() <= 0 && bottomRight.row() >= 0) {
91− updateActionState();
92− }
93−}
94−
95−void RootActionState::reset()
96−{
97− m_cachedState.clear();
98− m_menu = nullptr;
99−
100− Q_EMIT menuChanged();
101− Q_EMIT updated();
102−}
103−
104−void RootActionState::updateActionState()
105−{
106− if (m_menu && m_menu->rowCount() > 0) {
107− ActionStateParser* oldParser = m_menu->actionStateParser();
108− m_menu->setActionStateParser(this);
109−
110− m_cachedState = m_menu->get(0, "actionState").toMap();
111−
112− m_menu->setActionStateParser(oldParser);
113− } else {
114− m_cachedState.clear();
115− }
116− Q_EMIT updated();
117−}
118−
119−bool RootActionState::isValid() const
120−{
121− return m_menu && m_menu->rowCount() > 0;
122−}
123−
124−QString RootActionState::title() const
125−{
126− if (!isValid()) return QString();
127−
128− return m_cachedState.value("title", QVariant::fromValue(QString())).toString();
129−}
130−
131−QString RootActionState::leftLabel() const
132−{
133− if (!isValid()) return QString();
134−
135− return m_cachedState.value("pre-label", QVariant::fromValue(QString())).toString();
136−}
137−
138−QString RootActionState::rightLabel() const
139−{
140− if (!isValid()) return QString();
141−
142− return m_cachedState.value("label", QVariant::fromValue(QString())).toString();
143−}
144−
145−QStringList RootActionState::icons() const
146−{
147− if (!isValid()) return QStringList();
148−
149− return m_cachedState.value("icons", QVariant::fromValue(QStringList())).toStringList();
150−}
151−
152−QString RootActionState::accessibleName() const
153−{
154− if (!isValid()) return QString();
155−
156− return m_cachedState.value("accessible-desc", QVariant::fromValue(QString())).toString();
157−}
158−
159−bool RootActionState::indicatorVisible() const
160−{
161− if (!isValid()) return false;
162−
163− return m_cachedState.value("visible", QVariant::fromValue(true)).toBool();
16427}
16528
16629static QString iconUri(GIcon *icon)
@@ -209,7 +72,7 @@ static QString iconUri(GIcon *icon)
20972 return uri;
21073}
21174
212−QVariant RootActionState::toQVariant(GVariant* state) const
75+QVariant RootStateParser::toQVariant(GVariant* state) const
21376{
21477 if (!state) {
21578 return QVariant();
@@ -296,3 +159,73 @@ QVariant RootActionState::toQVariant(GVariant* state) const
296159 }
297160 return ActionStateParser::toQVariant(state);
298161}
162+
163+
164+RootStateObject::RootStateObject(QObject* parent)
165+ : QObject(parent)
166+{
167+}
168+
169+QString RootStateObject::title() const
170+{
171+ if (!valid()) return QString();
172+
173+ return m_currentState.value("title", QVariant::fromValue(QString())).toString();
174+}
175+
176+QString RootStateObject::leftLabel() const
177+{
178+ if (!valid()) return QString();
179+
180+ return m_currentState.value("pre-label", QVariant::fromValue(QString())).toString();
181+}
182+
183+QString RootStateObject::rightLabel() const
184+{
185+ if (!valid()) return QString();
186+
187+ return m_currentState.value("label", QVariant::fromValue(QString())).toString();
188+}
189+
190+QStringList RootStateObject::icons() const
191+{
192+ if (!valid()) return QStringList();
193+
194+ return m_currentState.value("icons", QVariant::fromValue(QStringList())).toStringList();
195+}
196+
197+QString RootStateObject::accessibleName() const
198+{
199+ if (!valid()) return QString();
200+
201+ return m_currentState.value("accessible-desc", QVariant::fromValue(QString())).toString();
202+}
203+
204+bool RootStateObject::indicatorVisible() const
205+{
206+ if (!valid()) return false;
207+
208+ return m_currentState.value("visible", QVariant::fromValue(true)).toBool();
209+}
210+
211+void RootStateObject::setCurrentState(const QVariantMap& newState)
212+{
213+ QString oldTitle = title();
214+ QString oldLeftLabel = leftLabel();
215+ QString oldRightLabel = rightLabel();
216+ QStringList oldIcons = icons();
217+ QString oldAccessibleName = accessibleName();
218+ bool oldIndicatorVisible = indicatorVisible();
219+
220+ if (m_currentState != newState) {
221+ m_currentState = newState;
222+ Q_EMIT updated();
223+
224+ if (oldTitle != title()) Q_EMIT titleChanged();
225+ if (oldLeftLabel != leftLabel()) Q_EMIT leftLabelChanged();
226+ if (oldRightLabel != rightLabel()) Q_EMIT rightLabelChanged();
227+ if (oldIcons != icons()) Q_EMIT iconsChanged();
228+ if (oldAccessibleName != accessibleName()) Q_EMIT accessibleNameChanged();
229+ if (oldIndicatorVisible != indicatorVisible()) Q_EMIT indicatorVisibleChanged();
230+ }
231+}
plugins/Unity/Indicators/rootactionstate.h → plugins/Unity/Indicators/rootstateparser.hrenamed+18−34
@@ -12,26 +12,27 @@
1212 *
1313 * You should have received a copy of the GNU Lesser General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors:
17− * Nick Dedekind <nick.dedekind@canonical.com>
1815 */
1916
20−#ifndef ROOTACTIONSTATE_H
21−#define ROOTACTIONSTATE_H
17+#ifndef ROOTSTATEPARSER_H
18+#define ROOTSTATEPARSER_H
2219
2320#include "unityindicatorsglobal.h"
2421
2522#include <actionstateparser.h>
2623
27−class UnityMenuModel;
24+class UNITYINDICATORS_EXPORT RootStateParser : public ActionStateParser
25+{
26+public:
27+ RootStateParser(QObject* parent = nullptr);
28+ virtual QVariant toQVariant(GVariant* state) const override;
29+};
2830
29−class UNITYINDICATORS_EXPORT RootActionState : public ActionStateParser
31+class UNITYINDICATORS_EXPORT RootStateObject : public QObject
3032{
3133 Q_OBJECT
32− Q_PROPERTY(UnityMenuModel* menu READ menu WRITE setMenu NOTIFY menuChanged)
3334
34− Q_PROPERTY(bool valid READ isValid NOTIFY validChanged)
35+ Q_PROPERTY(bool valid READ valid NOTIFY validChanged)
3536 Q_PROPERTY(QString title READ title NOTIFY titleChanged)
3637 Q_PROPERTY(QString leftLabel READ leftLabel NOTIFY leftLabelChanged)
3738 Q_PROPERTY(QString rightLabel READ rightLabel NOTIFY rightLabelChanged)
@@ -39,16 +40,10 @@ class UNITYINDICATORS_EXPORT RootActionState : public ActionStateParser
3940 Q_PROPERTY(QString accessibleName READ accessibleName NOTIFY accessibleNameChanged)
4041 Q_PROPERTY(bool indicatorVisible READ indicatorVisible NOTIFY indicatorVisibleChanged)
4142public:
42− RootActionState(QObject *parent = 0);
43− virtual ~RootActionState();
43+ RootStateObject(QObject* parent = 0);
4444
45− UnityMenuModel* menu() const;
46− void setMenu(UnityMenuModel* menu);
45+ virtual bool valid() const = 0;
4746
48− int index() const;
49− void setIndex(int index);
50−
51− bool isValid() const;
5247 QString title() const;
5348 QString leftLabel() const;
5449 QString rightLabel() const;
@@ -56,15 +51,12 @@ public:
5651 QString accessibleName() const;
5752 bool indicatorVisible() const;
5853
59− // from ActionStateParser
60− virtual QVariant toQVariant(GVariant* state) const override;
54+ QVariantMap currentState() const { return m_currentState; }
55+ void setCurrentState(const QVariantMap& currentState);
6156
6257Q_SIGNALS:
6358 void updated();
6459
65− void menuChanged();
66− void indexChanged();
67−
6860 void validChanged();
6961 void titleChanged();
7062 void leftLabelChanged();
@@ -73,17 +65,9 @@ Q_SIGNALS:
7365 void accessibleNameChanged();
7466 void indicatorVisibleChanged();
7567
76−private Q_SLOTS:
77− void onModelRowsAdded(const QModelIndex& parent, int start, int end);
78− void onModelRowsRemoved(const QModelIndex& parent, int start, int end);
79− void onModelDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>&);
80− void reset();
81−
82−private:
83− void updateActionState();
84−
85− UnityMenuModel* m_menu;
86− QVariantMap m_cachedState;
68+protected:
69+ RootStateParser m_parser;
70+ QVariantMap m_currentState;
8771};
8872
89−#endif // ROOTACTIONSTATE_H
73+#endif // ROOTSTATEPARSER_H
qml/Greeter/Clock.qml+1−1
@@ -53,7 +53,7 @@ Item {
5353 menuObjectPath: clock.visible ? "/com/canonical/indicator/datetime/phone" : ""
5454 }
5555
56− Indicators.RootActionState {
56+ Indicators.ModelActionRootState {
5757 menu: timeModel.model
5858 onUpdated: {
5959 if (timeLabel.text != rightLabel) {
qml/Panel/IndicatorItem.qml−8
@@ -45,14 +45,6 @@ IndicatorDelegate {
4545 onClicked: parent.clicked()
4646 }
4747
48− // FIXME: For now we will enable led indicator support only for messaging indicator
49− // in the future we should export a led API insted of doing that,
50− Loader {
51− id: indicatorLed
52− // only load source Component if the icons contains the new message icon
53− source: (root.icons && (String(root.icons).indexOf("indicator-messages-new") != -1)) ? Qt.resolvedUrl("Indicators/IndicatorsLight.qml") : ""
54− }
55−
5648 Item {
5749 id: mainItems
5850 anchors.centerIn: parent
qml/Panel/Indicators/IndicatorBase.qml+1−1
@@ -40,7 +40,7 @@ Item {
4040 menuObjectPath: indicatorItem.menuObjectPath
4141 }
4242
43− RootActionState {
43+ ModelActionRootState {
4444 id: rootAction
4545 menu: menuModel ? menuModel : null
4646 onUpdated: indicatorItem.rootActionStateChanged()
qml/Panel/Indicators/IndicatorsLight.qml+19−1
@@ -20,16 +20,34 @@
2020import QtQuick 2.0
2121import Powerd 0.1
2222import Lights 0.1
23+import QMenuModel 0.1 as QMenuModel
24+import Unity.Indicators 0.1 as Indicators
2325
2426QtObject {
2527 id: root
2628
29+ property var _actionGroup: QMenuModel.QDBusActionGroup {
30+ busType: 1
31+ busName: "com.canonical.indicator.messages"
32+ objectPath: "/com/canonical/indicator/messages"
33+ }
34+
35+ property var _rootState: Indicators.ActionRootState {
36+ actionGroup: _actionGroup
37+ actionName: "messages"
38+ Component.onCompleted: actionGroup.start()
39+
40+ property bool hasMessages: valid && (String(icons).indexOf("indicator-messages-new") != -1)
41+ }
42+
2743 Component.onDestruction: Lights.state = Lights.Off
2844
2945 // QtObject does not have children
3046 property var _binding: Binding {
3147 target: Lights
3248 property: "state"
33− value: (Powerd.status === Powerd.Off) ? Lights.On : Lights.Off
49+ value: {
50+ return (Powerd.status === Powerd.Off && _rootState.hasMessages) ? Lights.On : Lights.Off
51+ }
3452 }
3553}
qml/Panel/IndicatorsMenu.qml+5
@@ -18,6 +18,7 @@ import QtQuick 2.2
1818import Ubuntu.Components 1.1
1919import Ubuntu.Gestures 0.1
2020import "../Components"
21+import "Indicators"
2122
2223Showable {
2324 id: root
@@ -60,6 +61,10 @@ Showable {
6061 onUnitProgressChanged: d.updateState()
6162 clip: root.partiallyOpened
6263
64+ IndicatorsLight {
65+ id: indicatorLights
66+ }
67+
6368 // eater
6469 MouseArea {
6570 anchors.fill: parent
tests/mocks/CMakeLists.txt+1
@@ -32,6 +32,7 @@ add_subdirectory(AccountsService)
3232add_subdirectory(GSettings.1.0)
3333add_subdirectory(libusermetrics)
3434add_subdirectory(LightDM)
35+add_subdirectory(Lights)
3536add_subdirectory(MeeGo)
3637add_subdirectory(Powerd)
3738add_subdirectory(QMenuModel)
tests/mocks/Lights/CMakeLists.txtnew file+8
@@ -0,0 +1,8 @@
1+add_library(MockLights-qml MODULE
2+ plugin.cpp
3+ Lights.cpp
4+ )
5+
6+qt5_use_modules(MockLights-qml Qml Gui)
7+
8+add_unity8_mock(Lights 0.1 Lights TARGETS MockLights-qml)
tests/mocks/Lights/Lights.cppnew file+84
@@ -0,0 +1,84 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "Lights.h"
18+
19+#include <QDebug>
20+
21+Lights::Lights(QObject* parent)
22+ : QObject(parent),
23+ m_color("blue"),
24+ m_state(Lights::Off),
25+ m_onMs(1000),
26+ m_offMs(3000)
27+{
28+}
29+
30+Lights::~Lights()
31+{
32+}
33+
34+void Lights::setState(Lights::State newState)
35+{
36+ if (m_state != newState) {
37+ m_state = newState;
38+ Q_EMIT stateChanged(m_state);
39+ }
40+}
41+
42+Lights::State Lights::state() const
43+{
44+ return m_state;
45+}
46+
47+void Lights::setColor(const QColor &color)
48+{
49+ if (m_color != color) {
50+ m_color = color;
51+ Q_EMIT colorChanged(m_color);
52+ }
53+}
54+
55+QColor Lights::color() const
56+{
57+ return m_color;
58+}
59+
60+int Lights::onMillisec() const
61+{
62+ return m_onMs;
63+}
64+
65+void Lights::setOnMillisec(int onMs)
66+{
67+ if (m_onMs != onMs) {
68+ m_onMs = onMs;
69+ Q_EMIT onMillisecChanged(m_onMs);
70+ }
71+}
72+
73+int Lights::offMillisec() const
74+{
75+ return m_offMs;
76+}
77+
78+void Lights::setOffMillisec(int offMs)
79+{
80+ if (m_offMs != offMs) {
81+ m_offMs = offMs;
82+ Q_EMIT offMillisecChanged(m_offMs);
83+ }
84+}
tests/mocks/Lights/Lights.hnew file+66
@@ -0,0 +1,66 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef UNITY_MOCK_LIGHTS_H
18+#define UNITY_MOCK_LIGHTS_H
19+
20+#include <QtCore/QObject>
21+#include <QtGui/QColor>
22+
23+class Lights: public QObject
24+{
25+ Q_OBJECT
26+ Q_ENUMS(State)
27+ Q_PROPERTY(State state READ state WRITE setState NOTIFY stateChanged)
28+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
29+ Q_PROPERTY(int onMillisec READ onMillisec WRITE setOnMillisec NOTIFY onMillisecChanged)
30+ Q_PROPERTY(int offMillisec READ offMillisec WRITE setOffMillisec NOTIFY offMillisecChanged)
31+
32+public:
33+ enum State {
34+ Off,
35+ On,
36+ };
37+
38+ explicit Lights(QObject *parent = 0);
39+ ~Lights();
40+
41+ void setState(State newState);
42+ State state() const;
43+
44+ void setColor(const QColor &color);
45+ QColor color() const;
46+
47+ int onMillisec() const;
48+ void setOnMillisec(int onMs);
49+
50+ int offMillisec() const;
51+ void setOffMillisec(int offMs);
52+
53+Q_SIGNALS:
54+ void stateChanged(State newState);
55+ void colorChanged(const QColor &color);
56+ void onMillisecChanged(int onMs);
57+ void offMillisecChanged(int offMs);
58+
59+private:
60+ QColor m_color;
61+ State m_state;
62+ int m_onMs;
63+ int m_offMs;
64+};
65+
66+#endif
tests/mocks/Lights/Lights.qmltypesnew file+45
@@ -0,0 +1,45 @@
1+import QtQuick.tooling 1.1
2+
3+// This file describes the plugin-supplied types contained in the library.
4+// It is used for QML tooling purposes only.
5+//
6+// This file was auto-generated by:
7+// 'qmlplugindump -notrelocatable Lights 0.1 tests/mocks'
8+
9+Module {
10+ Component {
11+ name: "Lights"
12+ prototype: "QObject"
13+ exports: ["Lights/Lights 0.1"]
14+ isCreatable: false
15+ isSingleton: true
16+ exportMetaObjectRevisions: [0]
17+ Enum {
18+ name: "State"
19+ values: {
20+ "Off": 0,
21+ "On": 1
22+ }
23+ }
24+ Property { name: "state"; type: "State" }
25+ Property { name: "color"; type: "QColor" }
26+ Property { name: "onMillisec"; type: "int" }
27+ Property { name: "offMillisec"; type: "int" }
28+ Signal {
29+ name: "stateChanged"
30+ Parameter { name: "newState"; type: "State" }
31+ }
32+ Signal {
33+ name: "colorChanged"
34+ Parameter { name: "color"; type: "QColor" }
35+ }
36+ Signal {
37+ name: "onMillisecChanged"
38+ Parameter { name: "onMs"; type: "int" }
39+ }
40+ Signal {
41+ name: "offMillisecChanged"
42+ Parameter { name: "offMs"; type: "int" }
43+ }
44+ }
45+}
tests/mocks/Lights/plugin.cppnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2012,2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "plugin.h"
18+#include "Lights.h"
19+
20+#include <QtQml/qqml.h>
21+
22+static QObject *lights_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
23+{
24+ Q_UNUSED(engine)
25+ Q_UNUSED(scriptEngine)
26+ return new Lights();
27+}
28+
29+void PowerdPlugin::registerTypes(const char *uri)
30+{
31+ Q_ASSERT(uri == QLatin1String("Lights"));
32+ qmlRegisterSingletonType<Lights>(uri, 0, 1, "Lights", lights_provider);
33+}
tests/mocks/Lights/plugin.hnew file+32
@@ -0,0 +1,32 @@
1+/*
2+ * Copyright (C) 2012,2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef MOCK_POWER_PLUGIN_H
18+#define MOCK_POWER_PLUGIN_H
19+
20+#include <QtQml/QQmlEngine>
21+#include <QtQml/QQmlExtensionPlugin>
22+
23+class PowerdPlugin : public QQmlExtensionPlugin
24+{
25+ Q_OBJECT
26+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27+
28+public:
29+ void registerTypes(const char *uri);
30+};
31+
32+#endif
tests/mocks/Lights/qmldirnew file+3
@@ -0,0 +1,3 @@
1+module Lights
2+plugin MockLights-qml
3+typeinfo Lights.qmltypes
tests/mocks/QMenuModel/CMakeLists.txt+1
@@ -4,6 +4,7 @@ include_directories(
44)
55
66set(QMenuModelQml_SOURCES
7+ actiondata.h
78 actionstateparser.cpp
89 dbus-enums.h
910 plugin.cpp
tests/mocks/QMenuModel/QDBusActionGroup.qml+20−67
@@ -19,81 +19,34 @@
1919
2020import QtQuick 2.0
2121import Ubuntu.Settings.Menus 0.1 as Menus
22+import QMenuModel 0.1
2223
2324QtObject {
2425 id: actionGroup
2526 property int busType
2627 property string busName
2728 property string objectPath
29+ property var actions: ActionData ? ActionData.data : undefined
30+
31+ signal dataChanged
32+
2833 function start() {}
34+
2935 function action(actionName) {
30− switch (actionName) {
31− case "transfer-state.queued":
32− return {
33− 'valid': true,
34− 'state': {
35− 'state': Menus.TransferState.Queued,
36− 'percent': 0.0
37− }
38− }
39− case "transfer-state.running":
40− return {
41− 'valid': true,
42− 'state': {
43− 'state': Menus.TransferState.Running,
44− 'seconds-left': 100,
45− 'percent': 0.1
46− }
47− }
48− case "transfer-state.paused":
49− return {
50− 'valid': true,
51− 'state': {
52− 'state': Menus.TransferState.Paused,
53− 'seconds-left': 100,
54− 'percent': 0.5
55− }
56− }
57− case "transfer-state.cancelled":
58− return {
59− 'valid': true,
60− 'state': {
61− 'state': Menus.TransferState.Canceled,
62− 'percent': 0.4
63− }
64− }
65− case "transfer-state.finished": return {
66− 'valid': true,
67− 'state': {
68− 'state': Menus.TransferState.Finished,
69− 'seconds-left': 0,
70− 'percent': 1.0
71− }
72− }
73− case "transfer-state.error":
74− return {
75− 'valid': true,
76− 'state': {
77− 'state': Menus.TransferState.Error,
78− 'seconds-left': 100,
79− 'percent': 0.0
80− }
81− }
82− case "gps-detection-enabled":
83− case "location-detection-enabled":
84− return Qt.createQmlObject("
85−import QtQuick 2.3
86−QtObject {
87− property bool valid: true
88− property bool state: false
89− function activate() {
90− state = !state;
91− }
92−}", actionGroup);
93− default:
94− break;
95− }
36+ return Qt.createQmlObject("
37+ import QtQuick 2.3
38+ import QMenuModel 0.1
39+
40+ QtObject {
41+ signal activated
42+
43+ property string actionName: \"" + actionName + "\"
44+ property bool valid: ActionData.data[actionName] != undefined ? ActionData.data[actionName].valid : false
45+ property var state: ActionData.data[actionName] != undefined ? ActionData.data[actionName].state : undefined
9646
97− return null;
47+ function activate() {
48+ activated();
49+ }
50+ }", actionGroup);
9851 }
9952}
tests/mocks/QMenuModel/actiondata.hnew file+52
@@ -0,0 +1,52 @@
1+/*
2+ * Copyright (C) 2013 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: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+#ifndef ACTIONDATA_H
20+#define ACTIONDATA_H
21+
22+#include <QObject>
23+#include <QVariant>
24+
25+typedef struct _GVariant GVariant;
26+
27+class Q_DECL_EXPORT ActionData : public QObject
28+{
29+ Q_OBJECT
30+ Q_PROPERTY(QVariant data READ data WRITE setData NOTIFY dataChanged)
31+public:
32+ ActionData(QObject* parent = 0)
33+ : QObject(parent)
34+ {}
35+
36+ QVariant data() const { return m_data; }
37+ void setData(const QVariant& data)
38+ {
39+ if (m_data != data) {
40+ m_data = data;
41+ Q_EMIT dataChanged();
42+ }
43+ }
44+
45+Q_SIGNALS:
46+ void dataChanged();
47+
48+private:
49+ QVariant m_data;
50+};
51+
52+#endif // ACTIONDATA_H
tests/mocks/QMenuModel/plugin.cpp+10
@@ -18,11 +18,19 @@
1818
1919#include "plugin.h"
2020#include "unitymenumodel.h"
21+#include "actiondata.h"
2122#include "actionstateparser.h"
2223#include "dbus-enums.h"
2324
2425#include <QtQml/qqml.h>
2526
27+static QObject* actionDataSingleton(QQmlEngine* engine, QJSEngine* scriptEngine)
28+{
29+ Q_UNUSED(engine);
30+ Q_UNUSED(scriptEngine);
31+ return new ActionData;
32+}
33+
2634void QMenuModelPlugin::registerTypes(const char *uri)
2735{
2836 Q_ASSERT(uri == QLatin1String("QMenuModel"));
@@ -30,4 +38,6 @@ void QMenuModelPlugin::registerTypes(const char *uri)
3038 "DBus is only a namespace");
3139 qmlRegisterType<UnityMenuModel>(uri, 0, 1, "UnityMenuModel");
3240 qmlRegisterType<ActionStateParser>(uri, 0, 1, "ActionStateParser");
41+
42+ qmlRegisterSingletonType<ActionData>(uri, 0, 1, "ActionData", actionDataSingleton);
3343}
tests/mocks/Unity/Indicators/ActionRootState.qmlnew file+46
@@ -0,0 +1,46 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+
22+QtObject {
23+ property var actionGroup
24+ property var actionName
25+
26+ property var action: actionGroup ? actionGroup.actions[actionName] : undefined
27+ property var state: action && action.hasOwnProperty("state") ? action.state : undefined
28+
29+ property bool valid: action && action.hasOwnProperty("valid") ? action.valid : false
30+ property string title: state && state.hasOwnProperty("title") ? state["title"] : ""
31+ property string leftLabel: state && state.hasOwnProperty("pre-label") ? state["pre-label"] : ""
32+ property string rightLabel: state && state.hasOwnProperty("label") ? state["label"] : ""
33+ property var icons: state && state.hasOwnProperty("icons") ? state["icons"] : []
34+ property string accessibleName: state && state.hasOwnProperty("accessible-desc") ? state["accessible-desc"] : ""
35+ property bool visible: state && state.hasOwnProperty("visible") ? state["visible"] : true
36+
37+ signal updated
38+
39+ onValidChanged: updated()
40+ onTitleChanged: updated()
41+ onLeftLabelChanged: updated()
42+ onRightLabelChanged: updated()
43+ onIconsChanged: updated()
44+ onAccessibleNameChanged: updated()
45+ onVisibleChanged: updated()
46+}
tests/mocks/Unity/Indicators/RootActionState.qml → tests/mocks/Unity/Indicators/ModelActionRootState.qmlrenamed

The content is the same.

tests/mocks/Unity/Indicators/qmldir+2−1
@@ -2,5 +2,6 @@ module Unity.Indicators
22plugin IndicatorsFakeQml
33typeinfo Indicators.qmltypes
44
5+ActionRootState 0.1 ActionRootState.qml
56IndicatorsModel 0.1 IndicatorsModel.qml
6−RootActionState 0.1 RootActionState.qml
7+ModelActionRootState 0.1 ModelActionRootState.qml
tests/plugins/Unity/Indicators/rootactionstatetest.cpp+4−4
@@ -17,7 +17,7 @@
1717 * Nick Dedekind <nick.dedekind@canonical.com>
1818 */
1919
20−#include "rootactionstate.h"
20+#include "modelactionrootstate.h"
2121
2222#include <unitymenumodel.h>
2323#include <QtTest>
@@ -32,7 +32,7 @@ private Q_SLOTS:
3232 {
3333 UnityMenuModel* menuModel = new UnityMenuModel();
3434 ActionStateParser* originalParser = menuModel->actionStateParser();
35− RootActionState* rootState = new RootActionState();
35+ ModelActionRootState* rootState = new ModelActionRootState();
3636
3737 rootState->setMenu(menuModel);
3838
@@ -44,7 +44,7 @@ private Q_SLOTS:
4444 void testDeleteUnityMenuModel()
4545 {
4646 UnityMenuModel* menuModel = new UnityMenuModel();
47− RootActionState* rootState = new RootActionState();
47+ ModelActionRootState* rootState = new ModelActionRootState();
4848
4949 rootState->setMenu(menuModel);
5050
@@ -77,7 +77,7 @@ private Q_SLOTS:
7777
7878 GVariant* params = g_variant_builder_end (&builderParams);
7979
80− RootActionState rootState;
80+ RootStateParser rootState;
8181 QVariant result = rootState.toQVariant(params);
8282 g_variant_unref(params);
8383
tests/qmltests/CMakeLists.txt+1
@@ -79,6 +79,7 @@ add_qml_test(Panel IndicatorsMenu ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DI
7979add_qml_test(Panel MenuContent ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
8080add_qml_test(Panel Panel ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
8181add_qml_test(Panel SearchIndicator)
82+add_qml_test(Panel/Indicators IndicatorsLight ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
8283# These MenuItemFactory tests need the test/mocks/ to come before plugins/
8384add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
8485add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
tests/qmltests/Panel/Indicators/tst_IndicatorsLight.qmlnew file+129
@@ -0,0 +1,129 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtQuick.Layouts 1.1
19+import Unity.Test 0.1 as UT
20+import Unity.Indicators 0.1 as Indicators
21+import Ubuntu.Components 1.1
22+import Powerd 0.1
23+import Lights 0.1
24+import QMenuModel 0.1
25+import "../../../../qml/Panel/Indicators"
26+
27+Item {
28+ id: root
29+ width: units.gu(30)
30+ height: units.gu(30)
31+
32+ property var newMessage: {
33+ "messages" : {
34+ 'valid': true,
35+ 'state': {
36+ 'icons': [ 'indicator-messages-new' ]
37+ }
38+ }
39+ };
40+ property var noNewMessage: {
41+ "messages" : {
42+ 'valid': true,
43+ 'state': {
44+ 'icons': [ 'indicator-messages' ]
45+ }
46+ }
47+ };
48+
49+ Component {
50+ id: light
51+ IndicatorsLight {}
52+ }
53+
54+ Loader {
55+ id: loader
56+ sourceComponent: light
57+ }
58+
59+ Component.onCompleted: {
60+ ActionData.data = newMessage;
61+ Powerd.status = Powerd.On
62+ }
63+
64+ RowLayout {
65+ anchors.fill: parent
66+ anchors.margins: units.gu(1)
67+
68+ Item {
69+ Layout.fillWidth: true
70+ Layout.fillHeight: true
71+
72+ Rectangle {
73+ width: units.gu(4)
74+ height: width
75+ radius: width / 2
76+ anchors.centerIn: parent
77+
78+ color: Lights.state === Lights.On ? Lights.color : "transparent"
79+
80+ border.color: "black"
81+ border.width: 1
82+ }
83+ }
84+
85+ ColumnLayout {
86+ Layout.alignment: Qt.AlignTop
87+ Layout.fillWidth: false
88+ Layout.preferredWidth: units.gu(15)
89+
90+ Button {
91+ Layout.fillWidth: true
92+ text: Powerd.status === Powerd.On ? "Power Off" : "Power On"
93+ onClicked: {
94+ if (Powerd.status === Powerd.On) Powerd.status = Powerd.Off;
95+ else Powerd.status = Powerd.On;
96+ }
97+ }
98+ }
99+ }
100+
101+ UT.UnityTestCase {
102+ name: "IndicatorsLight"
103+ when: windowShown
104+
105+ function init() {
106+ // reload
107+ ActionData.data = noNewMessage;
108+ loader.sourceComponent = undefined;
109+ loader.sourceComponent = light;
110+ Powerd.status = Powerd.On
111+ }
112+
113+ function test_LightsStatus_data() {
114+ return [
115+ { tag: "Powerd.On with No Message", powerd: Powerd.On, actionData: noNewMessage, expected: Lights.Off },
116+ { tag: "Powerd.Off with No Message", powerd: Powerd.Off, actionData: noNewMessage, expected: Lights.Off },
117+ { tag: "Powerd.On with New Message", powerd: Powerd.On, actionData: newMessage, expected: Lights.Off },
118+ { tag: "Powerd.Off with New Message", powerd: Powerd.Off, actionData: newMessage, expected: Lights.On },
119+ ]
120+ }
121+
122+ function test_LightsStatus(data) {
123+ Powerd.status = data.powerd;
124+ ActionData.data = data.actionData;
125+
126+ compare(Lights.state, data.expected, "Light does not match expected value");
127+ }
128+ }
129+}
tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml+52−1
@@ -17,6 +17,7 @@
1717import QtQuick 2.0
1818import QtTest 1.0
1919import Unity.Test 0.1 as UT
20+import Ubuntu.Settings.Menus 0.1 as Menus
2021import QMenuModel 0.1
2122import Utils 0.1 as Utils
2223import "../../../../qml/Panel/Indicators"
@@ -448,9 +449,59 @@ Item {
448449 }
449450
450451 function test_create_transferMenu(data) {
452+ ActionData.data = {
453+ "transfer-state.queued": {
454+ 'valid': true,
455+ 'state': {
456+ 'state': Menus.TransferState.Queued,
457+ 'percent': data.progress
458+ }
459+ },
460+ "transfer-state.running": {
461+ 'valid': true,
462+ 'state': {
463+ 'state': Menus.TransferState.Running,
464+ 'seconds-left': 100,
465+ 'percent': data.progress
466+ }
467+ },
468+ "transfer-state.paused": {
469+ 'valid': true,
470+ 'state': {
471+ 'state': Menus.TransferState.Paused,
472+ 'seconds-left': 100,
473+ 'percent': data.progress
474+ }
475+ },
476+ "transfer-state.cancelled": {
477+ 'valid': true,
478+ 'state': {
479+ 'state': Menus.TransferState.Canceled,
480+ 'percent': data.progress
481+ }
482+ },
483+ "transfer-state.finished": {
484+ 'valid': true,
485+ 'state': {
486+ 'state': Menus.TransferState.Finished,
487+ 'seconds-left': 0,
488+ 'percent': data.progress
489+ }
490+ },
491+ "transfer-state.error": {
492+ 'valid': true,
493+ 'state': {
494+ 'state': Menus.TransferState.Error,
495+ 'seconds-left': 100,
496+ 'percent': data.progress
497+ }
498+ }
499+ };
500+
451501 menuData.type = "com.canonical.indicator.transfer";
452502 menuData.label = data.label;
453503 menuData.sensitive = data.enabled;
504+ menuData.isToggled = data.active;
454505 menuData.icon = data.icon;
455506 menuData.ext = {
456507 'xCanonicalUid': data.tag
@@ -464,7 +515,7 @@ Item {
464515 compare(loader.item.text, data.label, "Label does not match data");
465516 compare(loader.item.iconSource, data.icon, "Icon does not match data");
466517 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
467− compare(loader.item.progress, data.progress, "Icon does not match data");
518+ compare(loader.item.progress, data.progress, "Progress does not match data");
468519 compare(loader.item.active, data.active, "Active does not match data");
469520 compare(loader.item.stateText, data.stateText, "State text does not match data");
470521 }
tests/qmltests/Wizard/tst_Wizard.qml+36−12
@@ -18,6 +18,7 @@ import QtQuick 2.3
1818import QtTest 1.0
1919import AccountsService 0.1
2020import MeeGo.QOfono 0.2
21+import QMenuModel 0.1
2122import Ubuntu.Components 1.1
2223import Ubuntu.SystemSettings.SecurityPrivacy 1.0
2324import Unity.Test 0.1 as UT
@@ -58,6 +59,16 @@ Item {
5859 signalName: "setSecurityCalled"
5960 }
6061
62+ SignalSpy {
63+ id: activateLocationSpy
64+ signalName: "activated"
65+ }
66+
67+ SignalSpy {
68+ id: activateGPSSpy
69+ signalName: "activated"
70+ }
71+
6172 function setup() {
6273 AccountsService.hereEnabled = false;
6374 AccountsService.hereLicensePath = Qt.resolvedUrl("licenses");
@@ -68,6 +79,19 @@ Item {
6879
6980 updateSessionLanguageSpy.clear();
7081 setSecuritySpy.clear();
82+ activateLocationSpy.clear();
83+ activateGPSSpy.clear();
84+
85+ ActionData.data = {
86+ "location-detection-enabled": {
87+ 'valid': true,
88+ 'state': false
89+ },
90+ "gps-detection-enabled": {
91+ 'valid': true,
92+ 'state': false
93+ }
94+ };
7195 }
7296
7397 Component.onCompleted: {
@@ -360,8 +384,8 @@ Item {
360384 var nopeCheck = findChild(page, "nopeCheck");
361385
362386 var locationActionGroup = findInvisibleChild(page, "locationActionGroup");
363− tryCompare(locationActionGroup.location, "state", false);
364− tryCompare(locationActionGroup.gps, "state", false);
387+ activateLocationSpy.target = locationActionGroup.location;
388+ activateGPSSpy.target = locationActionGroup.gps;
365389
366390 tap(gpsCheck);
367391 tryCompare(gpsCheck, "checked", true);
@@ -370,8 +394,8 @@ Item {
370394
371395 tap(findChild(page, "forwardButton"));
372396 tryCompare(AccountsService, "hereEnabled", false);
373− tryCompare(locationActionGroup.location, "state", true);
374− tryCompare(locationActionGroup.gps, "state", true);
397+ tryCompare(activateLocationSpy, "count", 1)
398+ tryCompare(activateGPSSpy, "count", 1)
375399 }
376400
377401 function test_locationNope() {
@@ -381,8 +405,8 @@ Item {
381405 var nopeCheck = findChild(page, "nopeCheck");
382406
383407 var locationActionGroup = findInvisibleChild(page, "locationActionGroup");
384− tryCompare(locationActionGroup.location, "state", false);
385− tryCompare(locationActionGroup.gps, "state", false);
408+ activateLocationSpy.target = locationActionGroup.location;
409+ activateGPSSpy.target = locationActionGroup.gps;
386410
387411 tap(nopeCheck);
388412 tryCompare(gpsCheck, "checked", false);
@@ -391,8 +415,8 @@ Item {
391415
392416 tap(findChild(page, "forwardButton"));
393417 tryCompare(AccountsService, "hereEnabled", false);
394− tryCompare(locationActionGroup.location, "state", false);
395− tryCompare(locationActionGroup.gps, "state", false);
418+ tryCompare(activateLocationSpy, "count", 0)
419+ tryCompare(activateGPSSpy, "count", 0)
396420 }
397421
398422 function test_locationHere() {
@@ -402,8 +426,8 @@ Item {
402426 var nopeCheck = findChild(page, "nopeCheck");
403427
404428 var locationActionGroup = findInvisibleChild(page, "locationActionGroup");
405− tryCompare(locationActionGroup.location, "state", false);
406− tryCompare(locationActionGroup.gps, "state", false);
429+ activateLocationSpy.target = locationActionGroup.location;
430+ activateGPSSpy.target = locationActionGroup.gps;
407431
408432 // no tap because HERE is the default
409433 tryCompare(gpsCheck, "checked", false);
@@ -412,8 +436,8 @@ Item {
412436
413437 tap(findChild(page, "forwardButton"));
414438 tryCompare(AccountsService, "hereEnabled", true);
415− tryCompare(locationActionGroup.location, "state", true);
416− tryCompare(locationActionGroup.gps, "state", true);
439+ tryCompare(activateLocationSpy, "count", 1)
440+ tryCompare(activateGPSSpy, "count", 1)
417441 }
418442
419443 function test_locationHereTerms() {