on xenial help sign in
Merge lp:~saviq/unity8/revert-resolution-flickable
bb8f8afMichał Sawicz
plugins/AccountsService/AccountsService.qmltypes+2
@@ -23,6 +23,8 @@ Module {
23 }
23 }
24 Property { name: "user"; type: "string" }
24 Property { name: "user"; type: "string" }
25 Property { name: "demoEdges"; type: "bool" }
25 Property { name: "demoEdges"; type: "bool" }
26+ Property { name: "enableLauncherWhileLocked"; type: "bool"; isReadonly: true }
27+ Property { name: "enableIndicatorsWhileLocked"; type: "bool"; isReadonly: true }
26 Property { name: "backgroundFile"; type: "string"; isReadonly: true }
28 Property { name: "backgroundFile"; type: "string"; isReadonly: true }
27 Property { name: "statsWelcomeScreen"; type: "bool"; isReadonly: true }
29 Property { name: "statsWelcomeScreen"; type: "bool"; isReadonly: true }
28 Property { name: "passwordDisplayHint"; type: "PasswordDisplayHint"; isReadonly: true }
30 Property { name: "passwordDisplayHint"; type: "PasswordDisplayHint"; isReadonly: true }
plugins/Dash/Dash.qmltypes+3
@@ -40,9 +40,12 @@ Module {
40 Property { name: "forceNoClip"; type: "bool" }
40 Property { name: "forceNoClip"; type: "bool" }
41 Property { name: "stickyHeaderHeight"; type: "int"; isReadonly: true }
41 Property { name: "stickyHeaderHeight"; type: "int"; isReadonly: true }
42 Property { name: "headerItemShownHeight"; type: "double"; isReadonly: true }
42 Property { name: "headerItemShownHeight"; type: "double"; isReadonly: true }
43+ Property { name: "cacheBuffer"; type: "double" }
43 Signal { name: "headerChanged" }
44 Signal { name: "headerChanged" }
44 Method { name: "positionAtBeginning" }
45 Method { name: "positionAtBeginning" }
45 Method { name: "showHeader" }
46 Method { name: "showHeader" }
47+ Method { name: "firstCreatedIndex"; type: "int" }
48+ Method { name: "createdItemCount"; type: "int" }
46 Method {
49 Method {
47 name: "item"
50 name: "item"
48 type: "QQuickItem*"
51 type: "QQuickItem*"
plugins/LightDM/LightDM.qmltypes+1
@@ -37,6 +37,7 @@ Module {
37 Signal { name: "isActiveChanged" }
37 Signal { name: "isActiveChanged" }
38 Signal { name: "isAuthenticatedChanged" }
38 Signal { name: "isAuthenticatedChanged" }
39 Signal { name: "showGreeter" }
39 Signal { name: "showGreeter" }
40+ Signal { name: "hideGreeter" }
40 Signal {
41 Signal {
41 name: "requestAuthenticationUser"
42 name: "requestAuthenticationUser"
42 Parameter { name: "user"; type: "string" }
43 Parameter { name: "user"; type: "string" }
plugins/Ubuntu/Payments/Payments.qmltypes+1
@@ -37,6 +37,7 @@ Module {
37 Parameter { name: "error"; type: "string" }
37 Parameter { name: "error"; type: "string" }
38 }
38 }
39 Signal { name: "purchaseCompleted" }
39 Signal { name: "purchaseCompleted" }
40+ Signal { name: "purchaseCancelled" }
40 Method { name: "start" }
41 Method { name: "start" }
41 }
42 }
42}
43}
plugins/Unity/DashCommunicator/DashCommunicator.qmltypes+16−3
@@ -9,14 +9,14 @@ import QtQuick.tooling 1.1
9Module {
9Module {
10 Component {
10 Component {
11 name: "DashCommunicator"
11 name: "DashCommunicator"
12− prototype: "QObject"
12+ prototype: "QThread"
13 exports: ["Unity.DashCommunicator/DashCommunicator 0.1"]
13 exports: ["Unity.DashCommunicator/DashCommunicator 0.1"]
14 exportMetaObjectRevisions: [0]
14 exportMetaObjectRevisions: [0]
15 Method {
15 Method {
16 name: "setCurrentScope"
16 name: "setCurrentScope"
17 Parameter { name: "scopeId"; type: "string" }
17 Parameter { name: "scopeId"; type: "string" }
18 Parameter { name: "animate"; type: "bool" }
18 Parameter { name: "animate"; type: "bool" }
19− Parameter { name: "reset"; type: "bool" }
19+ Parameter { name: "isSwipe"; type: "bool" }
20 }
20 }
21 }
21 }
22 Component {
22 Component {
@@ -28,7 +28,20 @@ Module {
28 name: "setCurrentScopeRequested"
28 name: "setCurrentScopeRequested"
29 Parameter { name: "scopeId"; type: "string" }
29 Parameter { name: "scopeId"; type: "string" }
30 Parameter { name: "animate"; type: "bool" }
30 Parameter { name: "animate"; type: "bool" }
31− Parameter { name: "reset"; type: "bool" }
31+ Parameter { name: "isSwipe"; type: "bool" }
32+ }
33+ }
34+ Component {
35+ name: "QThread"
36+ prototype: "QObject"
37+ Signal { name: "started" }
38+ Signal { name: "finished" }
39+ Method {
40+ name: "start"
41+ Parameter { type: "Priority" }
32 }
42 }
43+ Method { name: "start" }
44+ Method { name: "terminate" }
45+ Method { name: "quit" }
33 }
46 }
34}
47}
plugins/Unity/Indicators/CMakeLists.txt+1
@@ -24,6 +24,7 @@ set(IndicatorsQML_SOURCES
24 modelprinter.cpp
24 modelprinter.cpp
25 plugin.cpp
25 plugin.cpp
26 rootactionstate.cpp
26 rootactionstate.cpp
27+ sharedunitymenumodel.cpp
27 unitymenumodelcache.cpp
28 unitymenumodelcache.cpp
28 unitymenumodelstack.cpp
29 unitymenumodelstack.cpp
29 visibleindicatorsmodel.cpp
30 visibleindicatorsmodel.cpp
plugins/Unity/Indicators/Indicators.qmltypes+12−11
@@ -99,10 +99,8 @@ Module {
99 values: {
99 values: {
100 "Identifier": 0,
100 "Identifier": 0,
101 "Position": 1,
101 "Position": 1,
102− "WidgetSource": 2,
102+ "IndicatorProperties": 2,
103− "PageSource": 3,
103+ "IsVisible": 3
104− "IndicatorProperties": 4,
105− "IsVisible": 5
106 }
104 }
107 }
105 }
108 }
106 }
@@ -209,6 +207,16 @@ Module {
209 Signal { name: "updated" }
207 Signal { name: "updated" }
210 Signal { name: "indexChanged" }
208 Signal { name: "indexChanged" }
211 }
209 }
210+ Component {
211+ name: "SharedUnityMenuModel"
212+ prototype: "QObject"
213+ exports: ["Unity.Indicators/SharedUnityMenuModel 0.1"]
214+ exportMetaObjectRevisions: [0]
215+ Property { name: "busName"; type: "QByteArray" }
216+ Property { name: "menuObjectPath"; type: "QByteArray" }
217+ Property { name: "actions"; type: "QVariantMap" }
218+ Property { name: "model"; type: "UnityMenuModel"; isReadonly: true; isPointer: true }
219+ }
212 Component {
220 Component {
213 name: "UnityMenuModelCache"
221 name: "UnityMenuModelCache"
214 prototype: "QObject"
222 prototype: "QObject"
@@ -216,13 +224,6 @@ Module {
216 isCreatable: false
224 isCreatable: false
217 isSingleton: true
225 isSingleton: true
218 exportMetaObjectRevisions: [0]
226 exportMetaObjectRevisions: [0]
219− Method {
220− name: "model"
221− type: "UnityMenuModel*"
222− Parameter { name: "bus"; type: "QByteArray" }
223− Parameter { name: "path"; type: "QByteArray" }
224− Parameter { name: "actions"; type: "QVariantMap" }
225− }
226 Method {
227 Method {
227 name: "contains"
228 name: "contains"
228 type: "bool"
229 type: "bool"
plugins/Unity/Indicators/indicators.h−2
@@ -74,8 +74,6 @@ public:
74 enum Roles {
74 enum Roles {
75 Identifier = 0,
75 Identifier = 0,
76 Position,
76 Position,
77− WidgetSource,
78− PageSource,
79 IndicatorProperties,
77 IndicatorProperties,
80 IsVisible
78 IsVisible
81 };
79 };
plugins/Unity/Indicators/indicatorsmodel.cpp−6
@@ -202,8 +202,6 @@ QHash<int, QByteArray> IndicatorsModel::roleNames() const
202 {
202 {
203 roles[IndicatorsModelRole::Identifier] = "identifier";
203 roles[IndicatorsModelRole::Identifier] = "identifier";
204 roles[IndicatorsModelRole::Position] = "position";
204 roles[IndicatorsModelRole::Position] = "position";
205− roles[IndicatorsModelRole::WidgetSource] = "widgetSource";
206− roles[IndicatorsModelRole::PageSource] = "pageSource";
207 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
205 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
208 }
206 }
209 return roles;
207 return roles;
@@ -248,10 +246,6 @@ QVariant IndicatorsModel::data(const QModelIndex &index, int role) const
248 return QVariant(indicator->indicatorProperties());
246 return QVariant(indicator->indicatorProperties());
249 }
247 }
250 break;
248 break;
251− case IndicatorsModelRole::WidgetSource:
252− return qmlDirectory()+"/Panel/Indicators/DefaultIndicatorWidget.qml";
253− case IndicatorsModelRole::PageSource:
254− return qmlDirectory()+"/Panel/Indicators/DefaultIndicatorPage.qml";
255 default:
249 default:
256 break;
250 break;
257 }
251 }
plugins/Unity/Indicators/plugin.cpp+8−2
@@ -30,18 +30,23 @@
30#include "menucontentactivator.h"
30#include "menucontentactivator.h"
31#include "modelprinter.h"
31#include "modelprinter.h"
32#include "rootactionstate.h"
32#include "rootactionstate.h"
33+#include "sharedunitymenumodel.h"
33#include "unitymenumodelcache.h"
34#include "unitymenumodelcache.h"
34#include "unitymenumodelstack.h"
35#include "unitymenumodelstack.h"
35#include "visibleindicatorsmodel.h"
36#include "visibleindicatorsmodel.h"
36
37
38+#include <unitymenumodel.h>
39+
37static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
40static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
38 Q_UNUSED(engine);
41 Q_UNUSED(engine);
39 Q_UNUSED(scriptEngine);
42 Q_UNUSED(scriptEngine);
40− return new UnityMenuModelCache;
43+ return UnityMenuModelCache::singleton();
41}
44}
42
45
43−void Indicators2Plugin::registerTypes(const char *uri)
46+void IndicatorsPlugin::registerTypes(const char *uri)
44{
47{
48+ qRegisterMetaType<UnityMenuModel*>("UnityMenuModel*");
49+
45 qmlRegisterType<IndicatorsManager>(uri, 0, 1, "IndicatorsManager");
50 qmlRegisterType<IndicatorsManager>(uri, 0, 1, "IndicatorsManager");
46 qmlRegisterType<IndicatorsModel>(uri, 0, 1, "IndicatorsModel");
51 qmlRegisterType<IndicatorsModel>(uri, 0, 1, "IndicatorsModel");
47 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
52 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
@@ -49,6 +54,7 @@ void Indicators2Plugin::registerTypes(const char *uri)
49 qmlRegisterType<RootActionState>(uri, 0, 1, "RootActionState");
54 qmlRegisterType<RootActionState>(uri, 0, 1, "RootActionState");
50 qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
55 qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
51 qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
56 qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
57+ qmlRegisterType<SharedUnityMenuModel>(uri, 0, 1, "SharedUnityMenuModel");
52
58
53 qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
59 qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
54
60
plugins/Unity/Indicators/plugin.h+1−1
@@ -22,7 +22,7 @@
22
22
23#include <QtQml/QQmlExtensionPlugin>
23#include <QtQml/QQmlExtensionPlugin>
24
24
25−class Indicators2Plugin : public QQmlExtensionPlugin
25+class IndicatorsPlugin : public QQmlExtensionPlugin
26{
26{
27 Q_OBJECT
27 Q_OBJECT
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
plugins/Unity/Indicators/rootactionstate.cpp+1−1
@@ -156,7 +156,7 @@ QString RootActionState::accessibleName() const
156 return m_cachedState.value("accessible-desc", QVariant::fromValue(QString())).toString();
156 return m_cachedState.value("accessible-desc", QVariant::fromValue(QString())).toString();
157}
157}
158
158
159−bool RootActionState::isVisible() const
159+bool RootActionState::indicatorVisible() const
160{
160{
161 if (!isValid()) return false;
161 if (!isValid()) return false;
162
162
plugins/Unity/Indicators/rootactionstate.h+4−4
@@ -37,7 +37,7 @@ class UNITYINDICATORS_EXPORT RootActionState : public ActionStateParser
37 Q_PROPERTY(QString rightLabel READ rightLabel NOTIFY rightLabelChanged)
37 Q_PROPERTY(QString rightLabel READ rightLabel NOTIFY rightLabelChanged)
38 Q_PROPERTY(QStringList icons READ icons NOTIFY iconsChanged)
38 Q_PROPERTY(QStringList icons READ icons NOTIFY iconsChanged)
39 Q_PROPERTY(QString accessibleName READ accessibleName NOTIFY accessibleNameChanged)
39 Q_PROPERTY(QString accessibleName READ accessibleName NOTIFY accessibleNameChanged)
40− Q_PROPERTY(bool visible READ isVisible NOTIFY visibleChanged)
40+ Q_PROPERTY(bool indicatorVisible READ indicatorVisible NOTIFY indicatorVisibleChanged)
41public:
41public:
42 RootActionState(QObject *parent = 0);
42 RootActionState(QObject *parent = 0);
43 virtual ~RootActionState();
43 virtual ~RootActionState();
@@ -54,10 +54,10 @@ public:
54 QString rightLabel() const;
54 QString rightLabel() const;
55 QStringList icons() const;
55 QStringList icons() const;
56 QString accessibleName() const;
56 QString accessibleName() const;
57− bool isVisible() const;
57+ bool indicatorVisible() const;
58
58
59 // from ActionStateParser
59 // from ActionStateParser
60− virtual QVariant toQVariant(GVariant* state) const;
60+ virtual QVariant toQVariant(GVariant* state) const override;
61
61
62Q_SIGNALS:
62Q_SIGNALS:
63 void updated();
63 void updated();
@@ -71,7 +71,7 @@ Q_SIGNALS:
71 void rightLabelChanged();
71 void rightLabelChanged();
72 void iconsChanged();
72 void iconsChanged();
73 void accessibleNameChanged();
73 void accessibleNameChanged();
74− void visibleChanged();
74+ void indicatorVisibleChanged();
75
75
76private Q_SLOTS:
76private Q_SLOTS:
77 void onModelRowsAdded(const QModelIndex& parent, int start, int end);
77 void onModelRowsAdded(const QModelIndex& parent, int start, int end);
plugins/Unity/Indicators/sharedunitymenumodel.cppnew file+92
@@ -0,0 +1,92 @@
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+
18+#include "sharedunitymenumodel.h"
19+#include "unitymenumodelcache.h"
20+
21+#include <unitymenumodel.h>
22+
23+SharedUnityMenuModel::SharedUnityMenuModel(QObject* parent)
24+ : QObject(parent)
25+{
26+}
27+
28+QByteArray SharedUnityMenuModel::busName() const
29+{
30+ return m_busName;
31+}
32+
33+void SharedUnityMenuModel::setBusName(const QByteArray& busName)
34+{
35+ if (m_busName != busName) {
36+ m_busName = busName;
37+ Q_EMIT busNameChanged();
38+ initialize();
39+ }
40+}
41+
42+QByteArray SharedUnityMenuModel::menuObjectPath() const
43+{
44+ return m_menuObjectPath;
45+}
46+
47+void SharedUnityMenuModel::setMenuObjectPath(const QByteArray& menuObjectPath)
48+{
49+ if (m_menuObjectPath != menuObjectPath) {
50+ m_menuObjectPath = menuObjectPath;
51+ Q_EMIT menuObjectPathChanged();
52+ initialize();
53+ }
54+}
55+
56+QVariantMap SharedUnityMenuModel::actions() const
57+{
58+ return m_actions;
59+}
60+
61+void SharedUnityMenuModel::setActions(const QVariantMap& actions)
62+{
63+ if (m_actions != actions) {
64+ m_actions = actions;
65+ Q_EMIT actionsChanged();
66+ initialize();
67+ }
68+}
69+
70+UnityMenuModel* SharedUnityMenuModel::model() const
71+{
72+ return m_model ? m_model.data() : nullptr;
73+}
74+
75+void SharedUnityMenuModel::initialize()
76+{
77+ if (m_busName.isEmpty() || m_menuObjectPath.isEmpty() || m_actions.isEmpty()) {
78+ if (!m_model.isNull()) {
79+ m_model.clear();
80+ Q_EMIT modelChanged();
81+ }
82+ } else {
83+ QSharedPointer<UnityMenuModel> model = UnityMenuModelCache::singleton()->model(m_menuObjectPath);
84+ if (model != m_model) {
85+ if (model->busName() != m_busName) model->setBusName(m_busName);
86+ if (model->actions() != m_actions) model->setActions(m_actions);
87+
88+ m_model = model;
89+ Q_EMIT modelChanged();
90+ }
91+ }
92+}
plugins/Unity/Indicators/sharedunitymenumodel.hnew file+66
@@ -0,0 +1,66 @@
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+
18+#ifndef SHAREDUNITYMENUMODEL_H
19+#define SHAREDUNITYMENUMODEL_H
20+
21+#include "unityindicatorsglobal.h"
22+
23+#include <QObject>
24+#include <QSharedPointer>
25+#include <QVariantMap>
26+
27+class UnityMenuModel;
28+
29+class UNITYINDICATORS_EXPORT SharedUnityMenuModel : public QObject
30+{
31+ Q_OBJECT
32+ Q_PROPERTY(QByteArray busName READ busName WRITE setBusName NOTIFY busNameChanged)
33+ Q_PROPERTY(QByteArray menuObjectPath READ menuObjectPath WRITE setMenuObjectPath NOTIFY menuObjectPathChanged)
34+ Q_PROPERTY(QVariantMap actions READ actions WRITE setActions NOTIFY actionsChanged)
35+ Q_PROPERTY(UnityMenuModel* model READ model NOTIFY modelChanged)
36+
37+public:
38+ SharedUnityMenuModel(QObject* parent = nullptr);
39+
40+ QByteArray busName() const;
41+ void setBusName(const QByteArray&);
42+
43+ QByteArray menuObjectPath() const;
44+ void setMenuObjectPath(const QByteArray&);
45+
46+ QVariantMap actions() const;
47+ void setActions(const QVariantMap&);
48+
49+ UnityMenuModel* model() const;
50+
51+Q_SIGNALS:
52+ void busNameChanged();
53+ void menuObjectPathChanged();
54+ void actionsChanged();
55+ void modelChanged();
56+
57+private:
58+ void initialize();
59+
60+ QByteArray m_busName;
61+ QByteArray m_menuObjectPath;
62+ QVariantMap m_actions;
63+ QSharedPointer<UnityMenuModel> m_model;
64+};
65+
66+#endif // SHAREDUNITYMENUMODEL_H
plugins/Unity/Indicators/unitymenumodelcache.cpp+25−14
@@ -20,34 +20,45 @@
20#include "unitymenumodelcache.h"
20#include "unitymenumodelcache.h"
21#include <unitymenumodel.h>
21#include <unitymenumodel.h>
22
22
23−UnityMenuModelCache::UnityMenuModelCache(QObject* parent)
23+#include <QQmlEngine>
24− : QObject(parent)
24+
25+QPointer<UnityMenuModelCache> UnityMenuModelCache::theCache = nullptr;
26+
27+UnityMenuModelCache* UnityMenuModelCache::singleton()
25{
28{
29+ if (theCache.isNull()) {
30+ theCache = new UnityMenuModelCache();
31+ }
32+ return theCache.data();
26}
33}
27
34
28−UnityMenuModelCache::~UnityMenuModelCache()
35+UnityMenuModelCache::UnityMenuModelCache(QObject* parent)
36+ : QObject(parent)
29{
37{
30}
38}
31
39
32−UnityMenuModel* UnityMenuModelCache::model(const QByteArray& bus,
40+QSharedPointer<UnityMenuModel> UnityMenuModelCache::model(const QByteArray& path)
33− const QByteArray& path,
34− const QVariantMap& actions)
35{
41{
36 if (m_registry.contains(path))
42 if (m_registry.contains(path))
37 return m_registry[path];
43 return m_registry[path];
38
44
39− UnityMenuModel* menuModel = new UnityMenuModel;
45+ UnityMenuModel* model = new UnityMenuModel;
46+ QQmlEngine::setObjectOwnership(model, QQmlEngine::CppOwnership);
47+
48+ QSharedPointer<UnityMenuModel> menuModel(model);
40− connect(menuModel, &QObject::destroyed, this, [menuModel, this](QObject*) {
49+ connect(model, &QObject::destroyed, this, [this] {
41− QList<QByteArray> keys = m_registry.keys(menuModel);
50+ QMutableHashIterator<QByteArray, QWeakPointer<UnityMenuModel>> iter(m_registry);
42− Q_FOREACH(const QByteArray& key, keys) {
51+ while(iter.hasNext()) {
52+ auto keyVal = iter.next();
53+ if (keyVal.value().isNull()) {
43− m_registry.remove(key);
54+ iter.remove();
55+ break;
56+ }
44 }
57 }
45 });
58 });
46− m_registry[path] = menuModel;
59+ m_registry[path] = menuModel.toWeakRef();
47
60
48− menuModel->setBusName(bus);
49 menuModel->setMenuObjectPath(path);
61 menuModel->setMenuObjectPath(path);
50− menuModel->setActions(actions);
51 return menuModel;
62 return menuModel;
52}
63}
53
64
plugins/Unity/Indicators/unitymenumodelcache.h+11−7
@@ -24,6 +24,8 @@
24
24
25#include <QObject>
25#include <QObject>
26#include <QHash>
26#include <QHash>
27+#include <QPointer>
28+#include <QWeakPointer>
27
29
28class UnityMenuModel;
30class UnityMenuModel;
29
31
@@ -32,15 +34,17 @@ class UNITYINDICATORS_EXPORT UnityMenuModelCache : public QObject
32 Q_OBJECT
34 Q_OBJECT
33public:
35public:
34 UnityMenuModelCache(QObject*parent=nullptr);
36 UnityMenuModelCache(QObject*parent=nullptr);
35− ~UnityMenuModelCache();
36
37
37− Q_INVOKABLE UnityMenuModel* model(const QByteArray& bus,
38+ static UnityMenuModelCache* singleton();
38− const QByteArray& path,
39− const QVariantMap& actions);
40− Q_INVOKABLE bool contains(const QByteArray& path);
41
39
42−private:
40+ virtual QSharedPointer<UnityMenuModel> model(const QByteArray& path);
41+
42+ // for tests use
43+ Q_INVOKABLE virtual bool contains(const QByteArray& path);
44+
45+protected:
43− QHash<QByteArray, UnityMenuModel*> m_registry;
46+ QHash<QByteArray, QWeakPointer<UnityMenuModel>> m_registry;
47+ static QPointer<UnityMenuModelCache> theCache;
44};
48};
45
49
46#endif // UNITYMENUMODELCACHE_H
50#endif // UNITYMENUMODELCACHE_H
plugins/Unity/Indicators/visibleindicatorsmodel.cpp−2
@@ -33,8 +33,6 @@ QHash<int, QByteArray> VisibleIndicatorsModel::roleNames() const
33 {
33 {
34 roles[IndicatorsModelRole::Identifier] = "identifier";
34 roles[IndicatorsModelRole::Identifier] = "identifier";
35 roles[IndicatorsModelRole::Position] = "position";
35 roles[IndicatorsModelRole::Position] = "position";
36− roles[IndicatorsModelRole::WidgetSource] = "widgetSource";
37− roles[IndicatorsModelRole::PageSource] = "pageSource";
38 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
36 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
39 roles[IndicatorsModelRole::IsVisible] = "isVisible";
37 roles[IndicatorsModelRole::IsVisible] = "isVisible";
40 }
38 }
plugins/Unity/Launcher/Launcher.qmltypes+17−12
@@ -13,14 +13,6 @@ Module {
13 exports: ["Unity.Launcher/LauncherItem 0.1"]
13 exports: ["Unity.Launcher/LauncherItem 0.1"]
14 isCreatable: false
14 isCreatable: false
15 exportMetaObjectRevisions: [0]
15 exportMetaObjectRevisions: [0]
16− Signal {
17− name: "favoriteChanged"
18− Parameter { name: "favorite"; type: "bool" }
19− }
20− Signal {
21− name: "runningChanged"
22− Parameter { name: "running"; type: "bool" }
23− }
24 }
16 }
25 Component {
17 Component {
26 name: "LauncherModel"
18 name: "LauncherModel"
@@ -29,6 +21,10 @@ Module {
29 isCreatable: false
21 isCreatable: false
30 isSingleton: true
22 isSingleton: true
31 exportMetaObjectRevisions: [0]
23 exportMetaObjectRevisions: [0]
24+ Method {
25+ name: "requestRemove"
26+ Parameter { name: "appId"; type: "string" }
27+ }
32 Method {
28 Method {
33 name: "get"
29 name: "get"
34 type: "unity::shell::launcher::LauncherItemInterface*"
30 type: "unity::shell::launcher::LauncherItemInterface*"
@@ -48,10 +44,6 @@ Module {
48 name: "pin"
44 name: "pin"
49 Parameter { name: "appId"; type: "string" }
45 Parameter { name: "appId"; type: "string" }
50 }
46 }
51− Method {
52− name: "requestRemove"
53− Parameter { name: "appId"; type: "string" }
54− }
55 Method {
47 Method {
56 name: "quickListActionInvoked"
48 name: "quickListActionInvoked"
57 Parameter { name: "appId"; type: "string" }
49 Parameter { name: "appId"; type: "string" }
@@ -88,6 +80,7 @@ Module {
88 Property { name: "recent"; type: "bool"; isReadonly: true }
80 Property { name: "recent"; type: "bool"; isReadonly: true }
89 Property { name: "progress"; type: "int"; isReadonly: true }
81 Property { name: "progress"; type: "int"; isReadonly: true }
90 Property { name: "count"; type: "int"; isReadonly: true }
82 Property { name: "count"; type: "int"; isReadonly: true }
83+ Property { name: "countVisible"; type: "bool"; isReadonly: true }
91 Property { name: "focused"; type: "bool"; isReadonly: true }
84 Property { name: "focused"; type: "bool"; isReadonly: true }
92 Property {
85 Property {
93 name: "quickList"
86 name: "quickList"
@@ -95,6 +88,14 @@ Module {
95 isReadonly: true
88 isReadonly: true
96 isPointer: true
89 isPointer: true
97 }
90 }
91+ Signal {
92+ name: "nameChanged"
93+ Parameter { name: "name"; type: "string" }
94+ }
95+ Signal {
96+ name: "iconChanged"
97+ Parameter { name: "icon"; type: "string" }
98+ }
98 Signal {
99 Signal {
99 name: "pinnedChanged"
100 name: "pinnedChanged"
100 Parameter { name: "pinned"; type: "bool" }
101 Parameter { name: "pinned"; type: "bool" }
@@ -115,6 +116,10 @@ Module {
115 name: "countChanged"
116 name: "countChanged"
116 Parameter { name: "count"; type: "int" }
117 Parameter { name: "count"; type: "int" }
117 }
118 }
119+ Signal {
120+ name: "countVisibleChanged"
121+ Parameter { name: "countVisible"; type: "bool" }
122+ }
118 Signal {
123 Signal {
119 name: "focusedChanged"
124 name: "focusedChanged"
120 Parameter { name: "focused"; type: "bool" }
125 Parameter { name: "focused"; type: "bool" }
plugins/Unity/Session/Session.qmltypes+10
@@ -34,4 +34,14 @@ Module {
34 Method { name: "RequestReboot" }
34 Method { name: "RequestReboot" }
35 Method { name: "RequestShutdown" }
35 Method { name: "RequestShutdown" }
36 }
36 }
37+ Component {
38+ name: "OrientationLock"
39+ prototype: "QObject"
40+ exports: ["Unity.Session/OrientationLock 0.1"]
41+ isCreatable: false
42+ isSingleton: true
43+ exportMetaObjectRevisions: [0]
44+ Property { name: "enabled"; type: "bool"; isReadonly: true }
45+ Property { name: "savedOrientation"; type: "Qt::ScreenOrientation" }
46+ }
37}
47}
plugins/Utils/Utils.qmltypes+6
@@ -106,6 +106,12 @@ Module {
106 Parameter { name: "row"; type: "int" }
106 Parameter { name: "row"; type: "int" }
107 }
107 }
108 }
108 }
109+ Component {
110+ name: "RelativeTimeFormatter"
111+ prototype: "TimeFormatter"
112+ exports: ["Utils/RelativeTimeFormatter 0.1"]
113+ exportMetaObjectRevisions: [0]
114+ }
109 Component {
115 Component {
110 name: "TimeFormatter"
116 name: "TimeFormatter"
111 prototype: "QObject"
117 prototype: "QObject"
qml/Components/Carousel.qml+3−2
@@ -17,7 +17,6 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import "carousel.js" as CarouselJS
19import "carousel.js" as CarouselJS
20−import "Flickables" as Flickables
21
20
22/*! The Carousel component presents the items of a model in a carousel view. It's similar to a
21/*! The Carousel component presents the items of a model in a carousel view. It's similar to a
23 cover flow. But it stops at it's boundaries (therefore no PathView is used).
22 cover flow. But it stops at it's boundaries (therefore no PathView is used).
@@ -64,7 +63,7 @@ Item {
64 and footer item to compensate that.
63 and footer item to compensate that.
65
64
66 The scaling of the items is controlled by the variable continuousIndex, described below. */
65 The scaling of the items is controlled by the variable continuousIndex, described below. */
67− Flickables.ListView {
66+ ListView {
68 id: listView
67 id: listView
69 objectName: "listView"
68 objectName: "listView"
70
69
@@ -136,6 +135,8 @@ Item {
136
135
137 boundsBehavior: Flickable.DragOverBounds
136 boundsBehavior: Flickable.DragOverBounds
138 cacheBuffer: carousel.cacheBuffer
137 cacheBuffer: carousel.cacheBuffer
138+ flickDeceleration: Math.max(1500 * Math.pow(realWidth / referenceWidth, 1.5), 1500) // 1500 is platform default
139+ maximumFlickVelocity: Math.max(2500 * Math.pow(realWidth / referenceWidth, 1.5), 2500) // 2500 is platform default
139 orientation: ListView.Horizontal
140 orientation: ListView.Horizontal
140
141
141 function getXFromContinuousIndex(index) {
142 function getXFromContinuousIndex(index) {
qml/Components/DragHandle.qml+6−1
@@ -68,6 +68,7 @@ EdgeDragArea {
68 }
68 }
69
69
70 property real hintDisplacement: 0
70 property real hintDisplacement: 0
71+ property var overrideStartValue: undefined
71 SmoothedAnimation {
72 SmoothedAnimation {
72 id: hintingAnimation
73 id: hintingAnimation
73 target: hintingAnimation
74 target: hintingAnimation
@@ -196,7 +197,11 @@ EdgeDragArea {
196 } else /* Undecided || Recognized */ {
197 } else /* Undecided || Recognized */ {
197 if (d.previousStatus === DirectionalDragArea.WaitingForTouch) {
198 if (d.previousStatus === DirectionalDragArea.WaitingForTouch) {
198 dragEvaluator.reset();
199 dragEvaluator.reset();
200+ if (overrideStartValue !== undefined) {
201+ d.startValue = overrideStartValue;
202+ } else {
199− d.startValue = parent[d.targetProp];
203+ d.startValue = parent[d.targetProp];
204+ }
200
205
201 if (hintDisplacement > 0) {
206 if (hintDisplacement > 0) {
202 hintingAnimation.targetValue = d.startValue;
207 hintingAnimation.targetValue = d.startValue;
qml/Components/EdgeDemo.qml+13−10
@@ -23,7 +23,7 @@ Item {
23
23
24 property Item greeter
24 property Item greeter
25 property Item launcher
25 property Item launcher
26− property Item indicators
26+ property Item panel
27 property Item stages
27 property Item stages
28
28
29 property bool launcherEnabled: true
29 property bool launcherEnabled: true
@@ -139,11 +139,11 @@ Item {
139 function startTopEdgeDemo() {
139 function startTopEdgeDemo() {
140 demo.panelEnabled = true;
140 demo.panelEnabled = true;
141 if (demo.stages) {
141 if (demo.stages) {
142− d.topEdgeDemo = d.overlay.createObject(demo.stages, {
142+ d.topEdgeDemo = d.overlay.createObject(demo.panel, {
143 "edge": "top",
143 "edge": "top",
144 "title": i18n.tr("Top edge"),
144 "title": i18n.tr("Top edge"),
145 "text": i18n.tr("Try swiping from the top edge to access the indicators"),
145 "text": i18n.tr("Try swiping from the top edge to access the indicators"),
146− "anchors.fill": demo.stages,
146+ "anchors.fill": demo.panel,
147 });
147 });
148 }
148 }
149 if (d.topEdgeDemo) {
149 if (d.topEdgeDemo) {
@@ -154,9 +154,9 @@ Item {
154 }
154 }
155
155
156 Connections {
156 Connections {
157− target: demo.indicators
157+ target: demo.panel.indicators
158 onFullyOpenedChanged: {
158 onFullyOpenedChanged: {
159− if (d.topEdgeDemo && d.topEdgeDemo.available && demo.indicators.fullyOpened) {
159+ if (d.topEdgeDemo && d.topEdgeDemo.available && demo.panel.indicators.fullyOpened) {
160 d.topEdgeDemo.hideNow()
160 d.topEdgeDemo.hideNow()
161 startBottomEdgeDemo()
161 startBottomEdgeDemo()
162 }
162 }
@@ -164,12 +164,12 @@ Item {
164 }
164 }
165
165
166 function startBottomEdgeDemo() {
166 function startBottomEdgeDemo() {
167− if (demo.indicators) {
167+ if (demo.panel.indicators) {
168− d.bottomEdgeDemo = d.overlay.createObject(demo.indicators, {
168+ d.bottomEdgeDemo = d.overlay.createObject(demo.panel.indicators, {
169 "edge": "bottom",
169 "edge": "bottom",
170 "title": i18n.tr("Close"),
170 "title": i18n.tr("Close"),
171 "text": i18n.tr("Swipe up again to close the settings screen"),
171 "text": i18n.tr("Swipe up again to close the settings screen"),
172− "anchors.fill": demo.indicators.content,
172+ "anchors.fill": demo.panel.indicators,
173 });
173 });
174 }
174 }
175 if (d.bottomEdgeDemo) {
175 if (d.bottomEdgeDemo) {
@@ -180,9 +180,12 @@ Item {
180 }
180 }
181
181
182 Connections {
182 Connections {
183− target: demo.indicators
183+ target: demo.panel.indicators
184 onPartiallyOpenedChanged: {
184 onPartiallyOpenedChanged: {
185+ if (d.bottomEdgeDemo &&
186+ d.bottomEdgeDemo.available &&
185− if (d.bottomEdgeDemo && d.bottomEdgeDemo.available && !demo.indicators.partiallyOpened && !demo.indicators.fullyOpened) {
187+ !demo.panel.indicators.partiallyOpened &&
188+ !demo.panel.indicators.fullyOpened) {
186 d.bottomEdgeDemo.hideNow()
189 d.bottomEdgeDemo.hideNow()
187 startLeftEdgeDemo()
190 startLeftEdgeDemo()
188 }
191 }
qml/Components/Flickables/GridView.qmldeleted−25
@@ -1,25 +0,0 @@
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−import QtQuick 2.3 as QtQuick
18−import Ubuntu.Components 1.1
19−
20−QtQuick.GridView {
21− // Attached components and usages like GridView.onRemove are known not to work
22− // please use GridView directly from QtQuick if needed
23− flickDeceleration: 1500 * units.gridUnit / 8
24− maximumFlickVelocity: 2500 * units.gridUnit / 8
25−}
qml/Components/Flickables/ListView.qmldeleted−25
@@ -1,25 +0,0 @@
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−import QtQuick 2.3 as QtQuick
18−import Ubuntu.Components 1.1
19−
20−QtQuick.ListView {
21− // Attached components and usages like ListView.onRemove are known not to work
22− // please use ListView directly from QtQuick if needed
23− flickDeceleration: 1500 * units.gridUnit / 8
24− maximumFlickVelocity: 2500 * units.gridUnit / 8
25−}
qml/Components/Flickables/ListViewWithPageHeader.qmldeleted−23
@@ -1,23 +0,0 @@
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−import Dash 0.1
18−import Ubuntu.Components 1.1
19−
20−ListViewWithPageHeader {
21− flickDeceleration: 1500 * units.gridUnit / 8
22− maximumFlickVelocity: 2500 * units.gridUnit / 8
23−}
qml/Components/ListItems/VerticalThinDivider.qmldeleted−26
@@ -1,26 +0,0 @@
1−/*
2− * Copyright (C) 2012 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−
19−Image {
20− anchors {
21− top: (parent) ? parent.top : null
22− bottom: (parent) ? parent.bottom : null
23− }
24− width: (visible) ? units.dp(2) : 0
25− source: "graphics/ListItemDividerVertical.png"
26−}
qml/Components/ListItems/graphics/ListItemDividerVertical@18.pngdeleted

Binary file not shown.

qml/Components/ResponsiveGridView.qml+1−2
@@ -16,7 +16,6 @@
16
16
17import QtQuick 2.3
17import QtQuick 2.3
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19−import "Flickables" as Flickables
20
19
21/*
20/*
22 Essentially a GridView where you can specify the maximum number of columns it can have.
21 Essentially a GridView where you can specify the maximum number of columns it can have.
@@ -56,7 +55,7 @@ Item {
56 return rows * height
55 return rows * height
57 }
56 }
58
57
59− Flickables.GridView {
58+ GridView {
60 id: gridView
59 id: gridView
61 objectName: "responsiveGridViewGrid"
60 objectName: "responsiveGridViewGrid"
62 anchors {
61 anchors {
qml/Components/ScrollCalculator.qmlnew file+81
@@ -0,0 +1,81 @@
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+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+
20+Item {
21+ id: scrollArea
22+
23+ readonly property bool areaActive: lateralPosition >= 0
24+ property real stopScrollThreshold: units.gu(2)
25+ property int direction: Qt.LeftToRight
26+ property real baseScrollAmount: units.dp(3)
27+ property real maximumScrollAmount: units.dp(8)
28+ property real lateralPosition: -1
29+ property real forceScrollingPercentage: 0.4
30+
31+ signal scroll(real scrollAmount)
32+
33+ width: units.gu(5)
34+ rotation: direction === Qt.LeftToRight ? 0 : 180
35+
36+ onAreaActiveChanged: areaActive ? handleEnter() : handleExit()
37+
38+ function handleEnter() {
39+ d.thresholdAreaX = -scrollArea.stopScrollThreshold;
40+ scrollTimer.restart();
41+ }
42+
43+ function handleExit() {
44+ d.thresholdAreaX = -scrollArea.stopScrollThreshold;
45+ scrollTimer.stop();
46+ }
47+
48+ onLateralPositionChanged: {
49+ if (scrollArea.areaActive) {
50+ if (lateralPosition > width * (1 - forceScrollingPercentage)) {
51+ d.thresholdAreaX = width * (1 - forceScrollingPercentage);
52+ if (!scrollTimer.running) scrollTimer.restart();
53+ } else if (lateralPosition > d.thresholdAreaX + scrollArea.stopScrollThreshold) {
54+ d.thresholdAreaX = lateralPosition - scrollArea.stopScrollThreshold;
55+ if (!scrollTimer.running) scrollTimer.restart();
56+ } else if (lateralPosition < d.thresholdAreaX) {
57+ d.thresholdAreaX = lateralPosition;
58+ scrollTimer.stop();
59+ }
60+
61+ d.progression = lateralPosition / width;
62+ }
63+ }
64+
65+ Timer {
66+ id: scrollTimer
67+ interval: 16
68+ repeat: true
69+
70+ onTriggered: {
71+ var scrollAmount = scrollArea.baseScrollAmount + scrollArea.maximumScrollAmount * d.progression;
72+ scrollArea.scroll(scrollAmount);
73+ }
74+ }
75+
76+ QtObject {
77+ id: d
78+ property real progression: 0
79+ property real thresholdAreaX: -scrollArea.stopScrollThreshold
80+ }
81+}
qml/Components/ZoomableImage.qml+2−2
@@ -16,7 +16,7 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19−import "Flickables" as Flickables
19+import "../Components"
20
20
21/*! \brief Zoomable for image.
21/*! \brief Zoomable for image.
22
22
@@ -31,7 +31,7 @@ Item {
31 property alias imageStatus: imageRenderer.status
31 property alias imageStatus: imageRenderer.status
32 property alias asynchronous: imageRenderer.asynchronous
32 property alias asynchronous: imageRenderer.asynchronous
33
33
34− Flickables.Flickable {
34+ Flickable {
35 id: flickable
35 id: flickable
36 objectName: "flickable"
36 objectName: "flickable"
37 clip: true // FIXME maybe we can remove this, or just not clip in few cases
37 clip: true // FIXME maybe we can remove this, or just not clip in few cases
qml/Dash/CardHorizontalList.qml+1−2
@@ -17,7 +17,6 @@
17import QtQuick 2.2
17import QtQuick 2.2
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19import "../Components"
19import "../Components"
20−import "../Components/Flickables" as Flickables
21
20
22DashRenderer {
21DashRenderer {
23 id: root
22 id: root
@@ -26,7 +25,7 @@ DashRenderer {
26 collapsedHeight: expandedHeight
25 collapsedHeight: expandedHeight
27 clip: true
26 clip: true
28
27
29− Flickables.ListView {
28+ ListView {
30 id: listView
29 id: listView
31 anchors {
30 anchors {
32 fill: parent
31 fill: parent
qml/Dash/DashContent.qml+3−2
@@ -19,7 +19,6 @@ import Ubuntu.Components 0.1
19import Unity 0.2
19import Unity 0.2
20import Utils 0.1
20import Utils 0.1
21import "../Components"
21import "../Components"
22−import "../Components/Flickables" as Flickables
23
22
24Item {
23Item {
25 id: dashContent
24 id: dashContent
@@ -99,7 +98,7 @@ Item {
99 anchors.fill: parent
98 anchors.fill: parent
100 }
99 }
101
100
102− Flickables.ListView {
101+ ListView {
103 id: dashContentList
102 id: dashContentList
104 objectName: "dashContentList"
103 objectName: "dashContentList"
105
104
@@ -108,6 +107,8 @@ Item {
108 anchors.fill: parent
107 anchors.fill: parent
109 orientation: ListView.Horizontal
108 orientation: ListView.Horizontal
110 boundsBehavior: Flickable.DragAndOvershootBounds
109 boundsBehavior: Flickable.DragAndOvershootBounds
110+ flickDeceleration: units.gu(625)
111+ maximumFlickVelocity: width * 5
111 snapMode: ListView.SnapOneItem
112 snapMode: ListView.SnapOneItem
112 highlightMoveDuration: 250
113 highlightMoveDuration: 250
113 highlightRangeMode: ListView.StrictlyEnforceRange
114 highlightRangeMode: ListView.StrictlyEnforceRange
qml/Dash/DashNavigationButton.qml+1−2
@@ -16,7 +16,6 @@
16
16
17import QtQuick 2.2
17import QtQuick 2.2
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19−import "../Components/Flickables" as Flickables
20
19
21AbstractButton {
20AbstractButton {
22 id: root
21 id: root
@@ -76,7 +75,7 @@ AbstractButton {
76 }
75 }
77
76
78 // navigationListView is outside root
77 // navigationListView is outside root
79− Flickables.ListView {
78+ ListView {
80 id: navigationListView
79 id: navigationListView
81 objectName: "navigationListView"
80 objectName: "navigationListView"
82 visible: height > 0
81 visible: height > 0
qml/Dash/DashNavigationList.qml+1−2
@@ -17,7 +17,6 @@
17import QtQuick 2.2
17import QtQuick 2.2
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19import "../Components"
19import "../Components"
20−import "../Components/Flickables" as Flickables
21
20
22Item {
21Item {
23 id: root
22 id: root
@@ -46,7 +45,7 @@ Item {
46 }
45 }
47 }
46 }
48
47
49− Flickables.Flickable {
48+ Flickable {
50 id: flickable
49 id: flickable
51
50
52 anchors.fill: parent
51 anchors.fill: parent
qml/Dash/PageHeader.qml+1−2
@@ -21,7 +21,6 @@ import Ubuntu.Components.Popups 1.0
21import Ubuntu.Components.ListItems 1.0
21import Ubuntu.Components.ListItems 1.0
22import "../Components"
22import "../Components"
23import "../Components/SearchHistoryModel"
23import "../Components/SearchHistoryModel"
24−import "../Components/Flickables" as Flickables
25
24
26Item {
25Item {
27 id: root
26 id: root
@@ -128,7 +127,7 @@ Item {
128 }
127 }
129 }
128 }
130
129
131− Flickables.Flickable {
130+ Flickable {
132 id: headerContainer
131 id: headerContainer
133 objectName: "headerContainer"
132 objectName: "headerContainer"
134 clip: contentY < height
133 clip: contentY < height
qml/Dash/PreviewListView.qml+3−2
@@ -18,7 +18,6 @@ import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import Unity 0.2
19import Unity 0.2
20import "../Components"
20import "../Components"
21−import "../Components/Flickables" as Flickables
22import "Previews" as Previews
21import "Previews" as Previews
23
22
24Item {
23Item {
@@ -53,7 +52,7 @@ Item {
53 onBackClicked: root.backClicked()
52 onBackClicked: root.backClicked()
54 }
53 }
55
54
56− Flickables.ListView {
55+ ListView {
57 id: previewListView
56 id: previewListView
58 objectName: "listView"
57 objectName: "listView"
59 anchors {
58 anchors {
@@ -67,6 +66,8 @@ Item {
67 snapMode: ListView.SnapOneItem
66 snapMode: ListView.SnapOneItem
68 boundsBehavior: Flickable.DragAndOvershootBounds
67 boundsBehavior: Flickable.DragAndOvershootBounds
69 highlightMoveDuration: 250
68 highlightMoveDuration: 250
69+ flickDeceleration: units.gu(625)
70+ maximumFlickVelocity: width * 5
70 cacheBuffer: 0
71 cacheBuffer: 0
71
72
72 property bool open: false
73 property bool open: false
qml/Dash/Previews/Preview.qml+1−2
@@ -16,7 +16,6 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19−import "../../Components/Flickables" as Flickables
20
19
21/*! \brief This component constructs the Preview UI.
20/*! \brief This component constructs the Preview UI.
22 *
21 *
@@ -64,7 +63,7 @@ Item {
64 Repeater {
63 Repeater {
65 model: previewModel
64 model: previewModel
66
65
67− delegate: Flickables.ListView {
66+ delegate: ListView {
68 id: column
67 id: column
69 anchors { top: parent.top; bottom: parent.bottom }
68 anchors { top: parent.top; bottom: parent.bottom }
70 width: row.columnWidth
69 width: row.columnWidth
qml/Dash/Previews/PreviewImageGallery.qml+2−3
@@ -17,7 +17,6 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19import "../../Components"
19import "../../Components"
20−import "../../Components/Flickables" as Flickables
21
20
22/*! This preview widget shows a horizontal list of images.
21/*! This preview widget shows a horizontal list of images.
23 * The URIs for the images should be an array in widgetData["sources"].
22 * The URIs for the images should be an array in widgetData["sources"].
@@ -29,7 +28,7 @@ PreviewWidget {
29
28
30 property Item rootItem: QuickUtils.rootItem(root)
29 property Item rootItem: QuickUtils.rootItem(root)
31
30
32− Flickables.ListView {
31+ ListView {
33 id: previewImageListView
32 id: previewImageListView
34 objectName: "previewImageListView"
33 objectName: "previewImageListView"
35 spacing: units.gu(1)
34 spacing: units.gu(1)
@@ -83,7 +82,7 @@ PreviewWidget {
83 height: parent.height
82 height: parent.height
84 initialScale: previewImageListView.height / rootItem.height
83 initialScale: previewImageListView.height / rootItem.height
85
84
86− delegate: Flickables.ListView {
85+ delegate: ListView {
87 id: overlayListView
86 id: overlayListView
88 objectName: "overlayListView"
87 objectName: "overlayListView"
89 anchors.fill: parent
88 anchors.fill: parent
qml/Dash/ScopeListView.qml+6−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2013 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -14,8 +14,11 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import "../Components/Flickables" as Flickables
17+import QtQuick 2.0
18+import Dash 0.1
18
19
19−Flickables.ListViewWithPageHeader {
20+ListViewWithPageHeader {
21+ maximumFlickVelocity: height * 10
22+ flickDeceleration: height * 2
20 cacheBuffer: Number.MAX_VALUE
23 cacheBuffer: Number.MAX_VALUE
21}
24}
qml/Dash/ScopeSettingsPage.qml+1−2
@@ -18,7 +18,6 @@ import QtQuick 2.2
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19import Unity 0.2
19import Unity 0.2
20import "../Components"
20import "../Components"
21−import "../Components/Flickables" as Flickables
22import "ScopeSettings"
21import "ScopeSettings"
23
22
24Item {
23Item {
@@ -40,7 +39,7 @@ Item {
40 onBackClicked: root.backClicked()
39 onBackClicked: root.backClicked()
41 }
40 }
42
41
43− Flickables.ListView {
42+ ListView {
44 id: scopeSettings
43 id: scopeSettings
45 objectName: "scopeSettings"
44 objectName: "scopeSettings"
46 anchors {
45 anchors {
qml/Dash/ScopesOverviewAll.qml+1−2
@@ -16,9 +16,8 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19−import "../Components/Flickables" as Flickables
20
19
21−Flickables.Flickable {
20+Flickable {
22 id: root
21 id: root
23
22
24 property alias model: cardGrid.model
23 property alias model: cardGrid.model
qml/Dash/ScopesOverviewFavorites.qml+2−4
@@ -14,11 +14,9 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.3
17+import QtQuick 2.0
18−import Ubuntu.Components 1.1
19−import "../Components/Flickables" as Flickables
20
18
21−Flickables.Flickable {
19+Flickable {
22 id: root
20 id: root
23
21
24 signal clicked(int index, var result, var itemModel)
22 signal clicked(int index, var result, var itemModel)
qml/Greeter/Clock.qml+9−9
@@ -34,21 +34,21 @@ Item {
34 }
34 }
35 }
35 }
36
36
37− CachedUnityMenuModel {
37+ Indicators.SharedUnityMenuModel {
38 id: timeModel
38 id: timeModel
39 objectName: "timeModel"
39 objectName: "timeModel"
40
40
41 busName: "com.canonical.indicator.datetime"
41 busName: "com.canonical.indicator.datetime"
42− actionsObjectPath: "/com/canonical/indicator/datetime"
42+ actions: { "indicator": "/com/canonical/indicator/datetime" }
43 menuObjectPath: clock.visible ? "/com/canonical/indicator/datetime/phone" : ""
43 menuObjectPath: clock.visible ? "/com/canonical/indicator/datetime/phone" : ""
44+ }
44
45
45− Indicators.RootActionState {
46+ Indicators.RootActionState {
46− menu: timeModel.model
47+ menu: timeModel.model
47− onUpdated: {
48+ onUpdated: {
48− if (timeLabel.text != rightLabel) {
49+ if (timeLabel.text != rightLabel) {
49− timeLabel.text = rightLabel;
50+ if (rightLabel != "") timeLabel.text = rightLabel;
50− clock.currentDate = new Date();
51+ clock.currentDate = new Date();
51− }
52 }
52 }
53 }
53 }
54 }
54 }
qml/Greeter/LoginList.qml+2−2
@@ -18,7 +18,6 @@ import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import LightDM 0.1 as LightDM
19import LightDM 0.1 as LightDM
20import "../Components"
20import "../Components"
21−import "../Components/Flickables" as Flickables
22
21
23Item {
22Item {
24 id: root
23 id: root
@@ -69,7 +68,7 @@ Item {
69 antialiasing: true
68 antialiasing: true
70 }
69 }
71
70
72− Flickables.ListView {
71+ ListView {
73 id: userList
72 id: userList
74 objectName: "userList"
73 objectName: "userList"
75
74
@@ -79,6 +78,7 @@ Item {
79 preferredHighlightEnd: userList.height / 2 - root.highlightedHeight / 2
78 preferredHighlightEnd: userList.height / 2 - root.highlightedHeight / 2
80 highlightRangeMode: ListView.StrictlyEnforceRange
79 highlightRangeMode: ListView.StrictlyEnforceRange
81 highlightMoveDuration: root.moveDuration
80 highlightMoveDuration: root.moveDuration
81+ flickDeceleration: 10000
82
82
83 readonly property bool movingInternally: moveTimer.running || userList.moving
83 readonly property bool movingInternally: moveTimer.running || userList.moving
84
84
qml/Hud/Hud.qml+1−2
@@ -19,7 +19,6 @@ import Ubuntu.Components 0.1
19import HudClient 0.1
19import HudClient 0.1
20
20
21import "../Components"
21import "../Components"
22−import "../Components/Flickables" as Flickables
23
22
24Item {
23Item {
25 id: hud
24 id: hud
@@ -401,7 +400,7 @@ Item {
401 }
400 }
402 progress: MathUtils.clamp((y - hudShowable.y + height - units.gu(8))/elementsYSliding, 0, 1)
401 progress: MathUtils.clamp((y - hudShowable.y + height - units.gu(8))/elementsYSliding, 0, 1)
403
402
404− Flickables.Flickable {
403+ Flickable {
405 anchors.left: parent.left
404 anchors.left: parent.left
406 anchors.right: parent.right
405 anchors.right: parent.right
407 contentHeight: resultList.height
406 contentHeight: resultList.height
qml/Hud/HudParametrizedActionsPage.qml+1−2
@@ -18,7 +18,6 @@ import QtQuick 2.0
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19import Ubuntu.Components.Themes.Ambiance 1.1
19import Ubuntu.Components.Themes.Ambiance 1.1
20import "../Components"
20import "../Components"
21−import "../Components/Flickables" as Flickables
22
21
23Item {
22Item {
24 property alias header: header.title
23 property alias header: header.title
@@ -75,7 +74,7 @@ Item {
75 return values
74 return values
76 }
75 }
77
76
78− Flickables.Flickable {
77+ Flickable {
79 anchors.top: parent.top
78 anchors.top: parent.top
80 anchors.bottom: buttons.top
79 anchors.bottom: buttons.top
81 anchors.left: parent.left
80 anchors.left: parent.left
qml/Launcher/LauncherPanel.qml+1−2
@@ -21,7 +21,6 @@ import Unity.Launcher 0.1
21import Ubuntu.Components.Popups 0.1
21import Ubuntu.Components.Popups 0.1
22import "../Components/ListItems"
22import "../Components/ListItems"
23import "../Components/"
23import "../Components/"
24−import "../Components/Flickables" as Flickables
25
24
26Rectangle {
25Rectangle {
27 id: root
26 id: root
@@ -76,7 +75,7 @@ Rectangle {
76 anchors.fill: parent
75 anchors.fill: parent
77 clip: true
76 clip: true
78
77
79− Flickables.ListView {
78+ ListView {
80 id: launcherListView
79 id: launcherListView
81 objectName: "launcherListView"
80 objectName: "launcherListView"
82 anchors {
81 anchors {
qml/Notifications/Notification.qml+1−2
@@ -20,7 +20,6 @@ import Ubuntu.Components 1.1
20import Unity.Notifications 1.0
20import Unity.Notifications 1.0
21import QMenuModel 0.1
21import QMenuModel 0.1
22import Utils 0.1
22import Utils 0.1
23−import "../Components/Flickables" as Flickables
24
23
25import Ubuntu.Components.ListItems 0.1 as ListItem
24import Ubuntu.Components.ListItems 0.1 as ListItem
26
25
@@ -456,7 +455,7 @@ Item {
456 onClicked: notification.notification.invokeAction(comboRepeater.itemAt(2).actionId)
455 onClicked: notification.notification.invokeAction(comboRepeater.itemAt(2).actionId)
457 expanded: false
456 expanded: false
458 expandedHeight: (comboRepeater.count - 2) * units.gu(4) + units.gu(.5)
457 expandedHeight: (comboRepeater.count - 2) * units.gu(4) + units.gu(.5)
459− comboList: Flickables.Flickable {
458+ comboList: Flickable {
460 // this has to be wrapped inside a flickable
459 // this has to be wrapped inside a flickable
461 // to work around a feature/bug? of the
460 // to work around a feature/bug? of the
462 // ComboButton SDK-element, making a regular
461 // ComboButton SDK-element, making a regular
qml/Notifications/Notifications.qml+1−2
@@ -19,9 +19,8 @@ import Ubuntu.Components 0.1
19import Unity.Notifications 1.0 as UnityNotifications
19import Unity.Notifications 1.0 as UnityNotifications
20import Utils 0.1
20import Utils 0.1
21import "../Components"
21import "../Components"
22−import "../Components/Flickables" as Flickables
23
22
24−Flickables.ListView {
23+ListView {
25 id: notificationList
24 id: notificationList
26
25
27 objectName: "notificationList"
26 objectName: "notificationList"
qml/Components/Flickables/Flickable.qml → qml/Panel/Handle.qmlrenamed+24−4
@@ -14,10 +14,30 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.3 as QtQuick
17+import QtQuick 2.2
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19
19
20−QtQuick.Flickable {
20+Rectangle {
21− flickDeceleration: 1500 * units.gridUnit / 8
21+ id: handle
22− maximumFlickVelocity: 2500 * units.gridUnit / 8
22+ color: "#333333"
23+ height: units.gu(2)
24+ property bool active: false
25+
26+ Row {
27+ id: dots
28+ width: childrenRect.width
29+ height: childrenRect.height
30+ anchors.centerIn: parent
31+ spacing: units.gu(0.5)
32+ Repeater {
33+ model: 3
34+ delegate: Rectangle {
35+ id: dot
36+ width: units.dp(3)
37+ height: width
38+ color: handle.active ? "#de4814" : "#717171"
39+ radius: units.dp(1)
40+ }
41+ }
42+ }
23}
43}
qml/Panel/IndicatorItem.qml+232−27
@@ -1,53 +1,258 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5+ * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
6 * the Free Software Foundation; version 3.
7 *
7 *
8 * This program is distributed in the hope that it will be useful,
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
11+ * GNU Lesser General Public License for more details.
12 *
12 *
13− * You should have received a copy of the GNU General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.2
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
19−import Unity.Indicators 0.1 as Indicators
19+import Ubuntu.Settings.Components 0.1
20−import "../Components"
20+import "Indicators"
21
21
22−Loader {
22+IndicatorDelegate {
23 id: root
23 id: root
24
24
25− property alias widgetSource: root.source
26− property bool dimmed: false
27− property var indicatorProperties: undefined
28− property bool indicatorVisible: item ? item.enabled : false
29 property string identifier
25 property string identifier
26+ property string title: indicatorName.text
27+ property alias leftLabel: leftLabelItem.text
28+ property alias rightLabel: rightLabelItem.text
29+ property var icons: undefined
30+ property bool expanded: false
31+ property bool selected: false
32+ property real iconHeight: units.gu(2)
33+ readonly property color color: {
34+ if (!expanded) return "#ededed";
35+ if (!selected) return "#4c4c4c";
36+ return "#ededed";
37+ }
38+
39+ signal clicked()
40+
41+ implicitWidth: mainItems.width
30
42
31− opacity: dimmed ? 0.4 : 1
43+ MouseArea {
32− Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.BriskDuration } }
44+ anchors.fill: parent
45+ onClicked: parent.clicked()
46+ }
47+
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+ }
33
55
34− onLoaded: {
56+ Item {
35− for(var pName in indicatorProperties) {
57+ id: mainItems
36− if (item.hasOwnProperty(pName)) {
58+ anchors.centerIn: parent
37− item[pName] = indicatorProperties[pName];
59+
60+ width: leftLabelItem.width + iconsItem.width + rightLabelItem.width
61+ implicitHeight: units.gu(2)
62+
63+ Label {
64+ id: leftLabelItem
65+ objectName: "leftLabel"
66+
67+ anchors {
68+ left: mainItems.left
69+ verticalCenter: parent.verticalCenter
38 }
70 }
71+ width: contentWidth > 0 ? contentWidth + units.gu(1) : 0
72+ horizontalAlignment: Text.AlignHCenter
73+
74+ opacity: 1.0
75+ font.family: "Ubuntu"
76+ fontSize: "medium"
77+ color: root.color
78+ Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
39 }
79 }
80+
81+ Item {
82+ id: iconsItem
83+ objectName: "icons"
84+
85+ width: iconRow.width > 0 ? iconRow.width + units.gu(1) : 0
86+ anchors {
87+ left: leftLabelItem.right
88+ verticalCenter: parent.verticalCenter
89+ }
90+
91+ Row {
92+ id: iconRow
93+ anchors.centerIn: iconsItem
94+ spacing: units.gu(1)
95+
96+ Repeater {
97+ id: iconRepeater
98+ objectName: "iconRepeater"
99+
100+ model: d.useFallbackIcon ? [ "image://theme/settings" ] : root.icons
101+
102+ StatusIcon {
103+ id: itemImage
104+ objectName: "icon"+index
105+ height: iconHeight
106+ source: modelData
107+ sets: ["status", "actions"]
108+ color: root.color
109+ Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
110+
111+ opacity: {
112+ if (!expanded) return 1.0;
113+ if (!selected) return 0.6;
114+ return 1.0;
115+ }
116+ Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
117+ }
118+ }
119+ }
120+ }
121+
122+ Label {
123+ id: rightLabelItem
124+ objectName: "rightLabel"
125+
126+ anchors {
127+ left: iconsItem.right
128+ verticalCenter: parent.verticalCenter
129+ }
130+ width: contentWidth > 0 ? contentWidth + units.gu(1) : 0
131+ horizontalAlignment: Text.AlignHCenter
132+
133+ opacity: 1.0
134+ font.family: "Ubuntu"
135+ fontSize: "medium"
136+ color: root.color
137+ Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
138+ }
139+ }
140+
141+ Label {
142+ id: indicatorName
143+ objectName: "indicatorName"
144+
145+ anchors.top: mainItems.bottom
146+ anchors.topMargin: units.gu(0.5)
147+ anchors.horizontalCenter: parent.horizontalCenter
148+ width: contentWidth > 0 ? contentWidth + units.gu(1) : 0
149+
150+ text: title !== "" ? title : identifier
151+ fontSize: "x-small"
152+ horizontalAlignment: Text.AlignHCenter
153+ opacity: 0
154+ color: root.color
155+ Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
40 }
156 }
41
157
42− Binding {
158+ StateGroup {
43− target: item
159+ id: d
44− property: "identifier"
160+ property bool useFallbackIcon: false
45− value: identifier
161+
162+ states: [
163+ State {
164+ name: "minimised"
165+ when: !expanded && ((icons && icons.length > 0) || leftLabel !== "" || rightLabel !== "")
166+ PropertyChanges { target: indicatorName; opacity: 0}
167+ },
168+
169+ State {
170+ name: "minimised_fallback"
171+ when: !expanded && (!icons || icons.length === 0) && leftLabel == "" && rightLabel == ""
172+ PropertyChanges { target: indicatorName; opacity: 0}
173+ PropertyChanges { target: d; useFallbackIcon: true }
174+ },
175+
176+ State {
177+ name: "expanded"
178+ PropertyChanges { target: indicatorName; visible: true; opacity: 1}
179+ PropertyChanges { target: mainItems; anchors.verticalCenterOffset: -units.gu(1) }
180+ },
181+
182+ State {
183+ name: "expanded_icon"
184+ extend: "expanded"
185+ when: expanded && (icons && icons.length > 0)
186+ AnchorChanges { target: iconsItem; anchors.left: undefined; anchors.horizontalCenter: parent.horizontalCenter }
187+ AnchorChanges { target: leftLabelItem; anchors.left: undefined; anchors.right: iconsItem.left }
188+ PropertyChanges { target: leftLabelItem; opacity: 0 }
189+ PropertyChanges { target: leftLabelItem; opacity: 0 }
190+ PropertyChanges { target: rightLabelItem; opacity: 0 }
191+ PropertyChanges { target: root; width: Math.max(units.gu(10), Math.max(iconsItem.width, indicatorName.width)) }
192+ },
193+
194+ State {
195+ name: "expanded_fallback"
196+ extend: "expanded"
197+ when: expanded && (!icons || icons.length === 0) && leftLabel == "" && rightLabel == ""
198+ PropertyChanges { target: d; useFallbackIcon: true }
199+ AnchorChanges { target: iconsItem; anchors.left: undefined; anchors.horizontalCenter: parent.horizontalCenter }
200+ AnchorChanges { target: leftLabelItem; anchors.left: undefined; anchors.right: iconsItem.left }
201+ PropertyChanges { target: leftLabelItem; opacity: 0 }
202+ PropertyChanges { target: leftLabelItem; opacity: 0 }
203+ PropertyChanges { target: rightLabelItem; opacity: 0 }
204+ PropertyChanges { target: root; width: Math.max(units.gu(10), Math.max(iconsItem.width, indicatorName.width)) }
205+ },
206+
207+ State {
208+ name: "expanded_rightLabel"
209+ extend: "expanded"
210+ when: expanded && (!icons || icons.length === 0) && rightLabel !== ""
211+ AnchorChanges { target: rightLabelItem; anchors.left: undefined; anchors.horizontalCenter: parent.horizontalCenter }
212+ PropertyChanges { target: iconsItem; opacity: 0 }
213+ PropertyChanges { target: leftLabelItem; opacity: 0 }
214+ PropertyChanges { target: root; width: Math.max(units.gu(10), Math.max(rightLabelItem.width, indicatorName.width)) }
215+ },
216+
217+ State {
218+ name: "expanded_leftLabel"
219+ extend: "expanded"
220+ when: expanded && (!icons || icons.length === 0) && leftLabel !== ""
221+ AnchorChanges { target: leftLabelItem; anchors.left: undefined; anchors.horizontalCenter: parent.horizontalCenter }
222+ PropertyChanges { target: iconsItem; opacity: 0 }
223+ PropertyChanges { target: rightLabelItem; opacity: 0 }
224+ PropertyChanges { target: root; width: Math.max(units.gu(10), Math.max(leftLabelItem.width, indicatorName.width)) }
225+ }
226+ ]
227+
228+ transitions: [
229+ Transition {
230+ PropertyAction { target: d; property: "useFallbackIcon" }
231+ AnchorAnimation {
232+ targets: [ mainItems, iconsItem, leftLabelItem, rightLabelItem ]
233+ duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing
234+ }
235+ PropertyAnimation {
236+ targets: [ root, mainItems, iconsItem, leftLabelItem, rightLabelItem, indicatorName ]
237+ properties: "width, opacity, anchors.verticalCenterOffset";
238+ duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing
239+ }
240+ }
241+ ]
46 }
242 }
47
243
48− Binding {
244+ onRootActionStateChanged: {
49− target: item
245+ if (rootActionState == undefined) {
50− property: "objectName"
246+ title = "";
51− value: identifier + "-widget"
247+ leftLabel = "";
248+ rightLabel = "";
249+ icons = undefined;
250+ return;
251+ }
252+
253+ title = rootActionState.title ? rootActionState.title : "";
254+ leftLabel = rootActionState.leftLabel ? rootActionState.leftLabel : "";
255+ rightLabel = rootActionState.rightLabel ? rootActionState.rightLabel : "";
256+ icons = rootActionState.icons;
52 }
257 }
53}
258}
qml/Panel/IndicatorItemRow.qmlnew file+297
@@ -0,0 +1,297 @@
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+
17+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+
20+Item {
21+ id: root
22+ width: row.width
23+ height: units.gu(3)
24+
25+ property QtObject indicatorsModel: null
26+ property real overFlowWidth: width
27+ property bool expanded: false
28+ property var currentItem
29+ readonly property int currentItemIndex: currentItem ? currentItem.ownIndex : -1
30+
31+ property real unitProgress: 0.0
32+ property real selectionChangeBuffer: units.gu(2)
33+ property bool enableLateralChanges: false
34+ property color hightlightColor: "#ededed"
35+
36+ property real lateralPosition: -1
37+ onLateralPositionChanged: {
38+ updateItemFromLateralPosition();
39+ }
40+
41+ onEnableLateralChangesChanged: {
42+ updateItemFromLateralPosition();
43+ }
44+
45+ function updateItemFromLateralPosition() {
46+ if (currentItem && !enableLateralChanges) return;
47+ if (lateralPosition === -1) return;
48+
49+ if (!currentItem) {
50+ selectItemAt(lateralPosition);
51+ return;
52+ }
53+
54+ var maximumBufferOffset = selectionChangeBuffer * unitProgress;
55+ var proposedItem = indicatorAt(lateralPosition, 0);
56+ if (proposedItem) {
57+ var bufferExceeded = false;
58+
59+ if (proposedItem !== currentItem) {
60+ // Proposed item is not directly adjacent to current?
61+ if (Math.abs(proposedItem.ownIndex - currentItem.ownIndex) > 1) {
62+ bufferExceeded = true;
63+ } else { // no
64+ var currentItemLateralPosition = root.mapToItem(proposedItem, lateralPosition, 0).x;
65+
66+ // Is the distance into proposed item greater than max buffer?
67+ // Proposed item is before current item
68+ if (proposedItem.x < currentItem.x) {
69+ bufferExceeded = (proposedItem.width - currentItemLateralPosition) > maximumBufferOffset;
70+ } else { // After
71+ bufferExceeded = currentItemLateralPosition > maximumBufferOffset;
72+ }
73+ }
74+ if (bufferExceeded) {
75+ selectItemAt(lateralPosition);
76+ }
77+ }
78+ } else {
79+ selectItemAt(lateralPosition);
80+ }
81+ }
82+
83+ function indicatorAt(x, y) {
84+ var item = row.childAt(x, y);
85+ return item && item.hasOwnProperty("ownIndex") ? item : null;
86+ }
87+
88+ function resetCurrentItem() {
89+ d.firstItemSwitch = true;
90+ d.previousItem = undefined;
91+ currentItem = undefined;
92+ }
93+
94+ function setCurrentItemIndex(index) {
95+ for (var i = 0; i < row.children.length; i++) {
96+ var item = row.children[i];
97+ if (item.hasOwnProperty("ownIndex") && item.ownIndex === index) {
98+ if (currentItem !== item) currentItem = item;
99+ break;
100+ }
101+ }
102+ }
103+
104+ function selectItemAt(lateralPosition) {
105+ var item = indicatorAt(lateralPosition, 0);
106+ if (item && item.opacity > 0) {
107+ currentItem = item;
108+ } else {
109+ // Select default item.
110+ var searchIndex = lateralPosition > width ? repeater.count - 1 : 0;
111+
112+ for (var i = 0; i < row.children.length; i++) {
113+ if (row.children[i].hasOwnProperty("ownIndex") && row.children[i].ownIndex === searchIndex) {
114+ item = row.children[i];
115+ break;
116+ }
117+ }
118+ if (currentItem !== item) currentItem = item;
119+ }
120+ }
121+
122+ QtObject {
123+ id: d
124+ property bool firstItemSwitch: true
125+ property var previousItem
126+ property bool forceAlignmentAnimationDisabled: false
127+ }
128+
129+ onCurrentItemChanged: {
130+ if (d.previousItem) {
131+ d.firstItemSwitch = false;
132+ }
133+ d.previousItem = currentItem;
134+ }
135+
136+ Row {
137+ id: row
138+ anchors {
139+ top: parent.top
140+ bottom: parent.bottom
141+ }
142+
143+ // TODO: make this better
144+ // when the width changes, the highlight will lag behind due to animation, so we need to disable the animation
145+ // and adjust the highlight X immediately.
146+ width: implicitWidth
147+ Behavior on width {
148+ SequentialAnimation {
149+ ScriptAction {
150+ script: {
151+ d.forceAlignmentAnimationDisabled = true;
152+ highlight.currentItemX = Qt.binding(function() { return currentItem ? currentItem.x : 0 });
153+ d.forceAlignmentAnimationDisabled = false;
154+ }
155+ }
156+ }
157+ }
158+
159+ Repeater {
160+ id: repeater
161+ model: indicatorsModel
162+ visible: false
163+
164+ onItemRemoved: {
165+ // current item removed.
166+ if (currentItem === item) {
167+ var i = 0;
168+ while (i < row.children.length) {
169+ var childItem = row.children[i];
170+ if (childItem !== item) {
171+ setCurrentItemIndex(i);
172+ break;
173+ }
174+ i++;
175+ }
176+ }
177+ }
178+
179+
180+ delegate: IndicatorItem {
181+ id: indicatorItem
182+ objectName: identifier+"-panelItem"
183+
184+ property int ownIndex: index
185+ property bool overflow: row.width - x > overFlowWidth
186+ property bool hidden: !expanded && (overflow || !indicatorVisible)
187+
188+ height: row.height
189+ expanded: root.expanded
190+ selected: currentItem === this
191+
192+ identifier: model.identifier
193+ busName: indicatorProperties.busName
194+ actionsObjectPath: indicatorProperties.actionsObjectPath
195+ menuObjectPath: indicatorProperties.menuObjectPath
196+
197+ opacity: hidden ? 0.0 : 1.0
198+ Behavior on opacity {
199+ NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing }
200+ }
201+
202+ width: (expanded || indicatorVisible) ? implicitWidth : 0
203+
204+ Behavior on width {
205+ NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing }
206+ }
207+
208+ Component.onDestruction: {
209+ // current item removed.
210+ if (currentItem === this) {
211+ var i = 0;
212+ while (i < row.children.length) {
213+ var childItem = row.children[i];
214+ if (childItem !== this) {
215+ setCurrentItemIndex(i);
216+ break;
217+ }
218+ i++;
219+ }
220+ }
221+ }
222+ }
223+ }
224+ }
225+
226+ Rectangle {
227+ id: highlight
228+ objectName: "highlight"
229+
230+ anchors.bottom: row.bottom
231+ height: units.dp(2)
232+ color: root.hightlightColor
233+ visible: currentItem !== undefined
234+ opacity: 0.0
235+
236+ width: currentItem ? currentItem.width : 0
237+ Behavior on width {
238+ enabled: !d.firstItemSwitch && expanded
239+ UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
240+ }
241+
242+ // micromovements of the highlight line when user moves the finger across the items while pulling
243+ // the handle downwards.
244+ property real highlightCenterOffset: {
245+ if (!currentItem || lateralPosition == -1 || !enableLateralChanges) return 0;
246+
247+ var itemMapped = root.mapToItem(currentItem, lateralPosition, 0);
248+
249+ var distanceFromCenter = itemMapped.x - currentItem.width / 2;
250+ if (distanceFromCenter > 0) {
251+ distanceFromCenter = Math.max(0, distanceFromCenter - currentItem.width / 8);
252+ } else {
253+ distanceFromCenter = Math.min(0, distanceFromCenter + currentItem.width / 8);
254+ }
255+
256+ if (currentItem && currentItem.ownIndex === 0 && distanceFromCenter < 0) {
257+ return 0;
258+ } else if (currentItem && currentItem.ownIndex === repeater.count-1 & distanceFromCenter > 0) {
259+ return 0;
260+ }
261+ return (distanceFromCenter / (currentItem.width / 4)) * units.gu(1);
262+ }
263+ Behavior on highlightCenterOffset {
264+ NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
265+ }
266+
267+ property real currentItemX: currentItem ? currentItem.x : 0
268+ Behavior on currentItemX {
269+ id: currentItemXBehavior
270+ enabled: !d.firstItemSwitch && expanded && !d.forceAlignmentAnimationDisabled
271+ NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
272+ }
273+ x: currentItemX + highlightCenterOffset
274+ }
275+
276+ states: [
277+ State {
278+ name: "minimised"
279+ when: !expanded
280+ },
281+ State {
282+ name: "expanded"
283+ when: expanded
284+ PropertyChanges { target: highlight; opacity: 0.9 }
285+ }
286+ ]
287+
288+ transitions: [
289+ Transition {
290+ PropertyAnimation {
291+ properties: "opacity";
292+ duration: UbuntuAnimation.SnapDuration
293+ easing: UbuntuAnimation.StandardEasing
294+ }
295+ }
296+ ]
297+}
qml/Panel/Indicators/DefaultIndicatorPage.qml → qml/Panel/IndicatorPage.qmlrenamed+10−17
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU Lesser General Public License as published by
@@ -12,16 +12,12 @@
12 *
12 *
13 * You should have received a copy of the GNU Lesser General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors:
17− * Renato Araujo Oliveira Filho <renato@canonical.com>
18− * Nick Dedekind <nick.dedekind@canonical.com>
19 */
15 */
20
16
21import QtQuick 2.0
17import QtQuick 2.0
22import Ubuntu.Components 0.1 as Components
18import Ubuntu.Components 0.1 as Components
23import Unity.Indicators 0.1 as Indicators
19import Unity.Indicators 0.1 as Indicators
24−import "../../Components/Flickables" as Flickables
20+import "Indicators"
25
21
26IndicatorBase {
22IndicatorBase {
27 id: main
23 id: main
@@ -52,13 +48,13 @@ IndicatorBase {
52
48
53 Connections {
49 Connections {
54 target: menuStack.tail
50 target: menuStack.tail
51+
52+ // fix async creation with signal from model before it's finished.
53+ Component.onCompleted: update();
55− onRowsInserted: {
54+ onRowsInserted: update();
56− if (menuStack.rootMenu !== menuStack.tail && menuStack.tail.get(0, "type") === rootMenuType) {
57− menuStack.rootMenu = menuStack.tail.submenu(0);
58− menuStack.push(menuStack.rootMenu, 0);
59− }
60− }
61− onModelReset: {
55+ onModelReset: update();
56+
57+ function update() {
62 if (menuStack.rootMenu !== menuStack.tail && menuStack.tail.get(0, "type") === rootMenuType) {
58 if (menuStack.rootMenu !== menuStack.tail && menuStack.tail.get(0, "type") === rootMenuType) {
63 menuStack.rootMenu = menuStack.tail.submenu(0);
59 menuStack.rootMenu = menuStack.tail.submenu(0);
64 menuStack.push(menuStack.rootMenu, 0);
60 menuStack.push(menuStack.rootMenu, 0);
@@ -66,7 +62,7 @@ IndicatorBase {
66 }
62 }
67 }
63 }
68
64
69− Flickables.ListView {
65+ ListView {
70 id: mainMenu
66 id: mainMenu
71 objectName: "mainMenu"
67 objectName: "mainMenu"
72 model: menuStack.rootMenu
68 model: menuStack.rootMenu
@@ -92,8 +88,6 @@ IndicatorBase {
92
88
93 // Only allow flicking if the content doesn't fit on the page
89 // Only allow flicking if the content doesn't fit on the page
94 interactive: contentHeight > height
90 interactive: contentHeight > height
95− // FIXME - https://bugreports.qt-project.org/browse/QTBUG-41207
96− boundsBehavior: Flickable.StopAtBounds
97
91
98 property int selectedIndex: -1
92 property int selectedIndex: -1
99 property bool blockCurrentIndexChange: false
93 property bool blockCurrentIndexChange: false
@@ -126,7 +120,6 @@ IndicatorBase {
126 delegate: Loader {
120 delegate: Loader {
127 id: loader
121 id: loader
128 objectName: "menuItem" + index
122 objectName: "menuItem" + index
129− asynchronous: true
130 width: ListView.view.width
123 width: ListView.view.width
131 visible: status == Loader.Ready
124 visible: status == Loader.Ready
132
125
qml/Panel/IndicatorRow.qmldeleted−163
@@ -1,163 +0,0 @@
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−
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−import Unity.Indicators 0.1 as Indicators
20−import "../Components"
21−import "../Components/Flickables" as Flickables
22−
23−Item {
24− id: indicatorRow
25−
26− readonly property alias currentItem : itemView.currentItem
27− readonly property alias currentItemIndex: itemView.currentIndex
28− readonly property alias row: itemView
29− property QtObject indicatorsModel: null
30− property int overFlowWidth: width
31− property bool showAll: false
32− property real currentItemOffset: 0.0
33− property real unitProgress: 0.0
34−
35− width: units.gu(40)
36− height: units.gu(3)
37−
38− function setDefaultItem() {
39− // The leftmost indicator
40− setCurrentItemIndex(0);
41− }
42−
43− function setCurrentItemIndex(index) {
44− itemView.currentIndex = index;
45− }
46−
47− function setCurrentItem(item) {
48− if (item && item.hasOwnProperty("ownIndex")) {
49− itemView.currentIndex = item.ownIndex;
50− } else {
51− itemView.currentIndex = -1;
52− }
53− }
54−
55− Timer {
56− id: allVisible
57− interval: 1000
58−
59− onTriggered: {
60− showAll = false;
61− }
62− }
63−
64− Flickables.ListView {
65− id: itemView
66− objectName: "indicatorRowItems"
67− interactive: false
68− model: indicatorsModel ? indicatorsModel : null
69−
70− width: childrenRect.width
71− height: indicatorRow.height
72− anchors.right: parent.right
73− orientation: ListView.Horizontal
74−
75− property int lastCount: 0
76− onCountChanged: {
77− if (lastCount < count) {
78− showAll = true;
79− allVisible.start();
80− }
81− lastCount = count;
82− }
83−
84− delegate: Item {
85− id: itemWrapper
86− objectName: "item" + index
87− height: indicatorRow.height
88− width: indicatorItem.width
89− opacity: 1 - indicatorRow.unitProgress
90− y: 0
91− state: "standard"
92−
93− property int ownIndex: index
94− property bool highlighted: indicatorRow.unitProgress > 0 ? ListView.isCurrentItem : false
95− property bool dimmed: indicatorRow.unitProgress > 0 ? !ListView.isCurrentItem : false
96−
97− property bool hidden: !showAll && !highlighted && (indicatorRow.state == "locked" || indicatorRow.state == "commit")
98− property bool overflow: row.width - itemWrapper.x > overFlowWidth
99−
100− IndicatorItem {
101− id: indicatorItem
102− identifier: model.identifier
103− height: parent.height
104−
105− dimmed: itemWrapper.dimmed
106−
107− widgetSource: model.widgetSource
108− indicatorProperties : model.indicatorProperties
109− }
110−
111− states: [
112− State {
113− name: "standard"
114− when: !hidden && !overflow && !highlighted
115− },
116− State {
117− name: "highlighted"
118− when: highlighted
119− PropertyChanges { target: itemWrapper; opacity: 1.0 }
120− },
121− State {
122− name: "hidden"
123− when: hidden || overflow
124− PropertyChanges { target: itemWrapper; opacity: 0.0 }
125− }
126− ]
127−
128− Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.BriskDuration } }
129− }
130− }
131−
132−
133− Rectangle {
134− id: highlight
135− color: Theme.palette.selected.foreground
136− objectName: "highlight"
137− height: units.dp(2)
138− anchors.top: row.bottom
139− visible: indicatorRow.currentItem != null
140−
141− property real intendedX: row.x + (indicatorRow.currentItem != null ? (indicatorRow.currentItem.x - row.originX) + centerOffset : 0)
142− x: intendedX >= row.x ? (intendedX + width <= row.x + row.width ? intendedX : row.x + row.width - width) : row.x // listview boundaries
143− width: indicatorRow.currentItem != null ? indicatorRow.currentItem.width : 0
144−
145− property real centerOffset: {
146− if (indicatorRow.currentItemOffset > 0.1) {
147− return (indicatorRow.currentItemOffset - 0.1) * units.gu(0.4);
148− } else if (indicatorRow.currentItemOffset < -0.1) {
149− return (indicatorRow.currentItemOffset + 0.1) * units.gu(0.4);
150− }
151− return 0.0;
152− }
153−
154− Behavior on width {
155− enabled: unitProgress > 0;
156− UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
157− }
158− Behavior on x {
159− enabled: unitProgress > 0;
160− UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
161− }
162− }
163−}
qml/Panel/Indicators.qmldeleted−415
@@ -1,415 +0,0 @@
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−
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−import Ubuntu.Gestures 0.1
20−import Unity.Indicators 0.1 as Indicators
21−
22−import "../Components"
23−import "../Components/ListItems"
24−import "Indicators"
25−
26−Showable {
27− id: indicators
28−
29− property real openedHeight: units.gu(71)
30− property int panelHeight: units.gu(3)
31− property alias overFlowWidth: indicatorRow.overFlowWidth
32− property alias showAll: indicatorRow.showAll
33− // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
34− property string profile: indicatorProfile
35−
36− readonly property real hintValue: panelHeight + menuContent.headerHeight
37− readonly property int lockThreshold: openedHeight / 2
38− property bool fullyOpened: height == openedHeight
39− property bool partiallyOpened: height > panelHeight && !fullyOpened
40− property bool fullyClosed: height <= panelHeight
41− property bool contentEnabled: true
42− property bool initalizeItem: true
43− readonly property alias content: menuContent
44− property real unitProgress: (height - panelHeight) / (openedHeight - panelHeight)
45− property bool enableHint: true
46− property real showHintBottomMargin: 0
47−
48− signal showTapped(point position)
49−
50− // TODO: Perhaps we need a animation standard for showing/hiding? Each showable seems to
51− // use its own values. Need to ask design about this.
52− showAnimation: StandardAnimation {
53− property: "height"
54− to: openedHeight
55− }
56−
57− hideAnimation: StandardAnimation {
58− property: "height"
59− duration: 350
60− to: panelHeight
61− easing.type: Easing.OutCubic
62− }
63−
64− onOpenedHeightChanged: {
65− if (showAnimation.running) {
66− showAnimation.restart();
67− } else if (indicators.shown) {
68− height = openedHeight;
69− }
70− }
71−
72− height: panelHeight
73− onHeightChanged: updateRevealProgressState(indicators.height - panelHeight - showHintBottomMargin, true)
74−
75− function updateRevealProgressState(revealProgress, enableRelease) {
76− if (!showAnimation.running && !hideAnimation.running) {
77− if (revealProgress === 0) {
78− indicators.state = "initial";
79− } else if (enableHint && revealProgress > 0 && revealProgress <= hintValue) {
80− indicators.state = "hint";
81− } else if ((!enableHint || revealProgress > hintValue) && revealProgress < lockThreshold) {
82− indicators.state = "reveal";
83− } else if (revealProgress >= lockThreshold && lockThreshold > 0) {
84− indicators.state = "locked";
85− }
86− }
87− }
88−
89− function calculateCurrentItem(xValue, useBuffer) {
90− var rowCoordinates;
91− var itemCoordinates;
92− var currentItem;
93− var distanceFromRightEdge;
94− var bufferExceeded = false;
95−
96− if (indicators.state == "commit" || indicators.state == "locked" || showAnimation.running || hideAnimation.running) return;
97−
98− /*
99− If user drags the indicator handle bar down a distance hintValue or less, this is 0.
100− If bar is dragged down a distance greater than or equal to lockThreshold, this is 1.
101− Otherwise it contains the bar's location as a fraction of the distance between hintValue (is 0) and lockThreshold (is 1).
102− */
103− var verticalProgress =
104− MathUtils.clamp((indicators.height - handle.height - hintValue) /
105− (lockThreshold - hintValue), 0, 1);
106−
107− /*
108− Vertical velocity check. Don't change the indicator if we're moving too quickly.
109− */
110− var verticalSpeed = Math.abs(yVelocityCalculator.calculate());
111− if (verticalSpeed >= 0.05 && !initalizeItem) {
112− return;
113− }
114−
115− /*
116− Percentage of an indicator icon's width the user's press can stray horizontally from the
117− focused icon before we change focus to another icon. E.g. a value of 0.5 means you must
118− go right a distance of half an icon's width before focus moves to the icon on the right
119− */
120− var maxBufferThreshold = 0.5;
121−
122− /*
123− To help users find the indicator of their choice while opening the indicators, we add logic to add a
124− left/right buffer to each icon so it is harder for the focus to be moved accidentally to another icon,
125− as the user moves their finger down, but yet allows them to switch indicator if they want.
126− This buffer is wider the further the user's finger is from the top of the screen.
127− */
128− var effectiveBufferThreshold = maxBufferThreshold * verticalProgress;
129−
130− rowCoordinates = indicatorRow.mapToItem(indicatorRow.row, xValue, 0);
131− // get the current delegate
132− currentItem = indicatorRow.row.itemAt(rowCoordinates.x, 0);
133− if (currentItem) {
134− itemCoordinates = indicatorRow.row.mapToItem(currentItem, rowCoordinates.x, 0);
135− distanceFromRightEdge = (currentItem.width - itemCoordinates.x) / (currentItem.width);
136− if (currentItem != indicatorRow.currentItem) {
137− if (Math.abs(currentItem.ownIndex - indicatorRow.currentItemIndex) > 1) {
138− bufferExceeded = true;
139− } else {
140− if (indicatorRow.currentItemIndex < currentItem.ownIndex && distanceFromRightEdge < (1 - effectiveBufferThreshold)) {
141− bufferExceeded = true;
142− } else if (indicatorRow.currentItemIndex > currentItem.ownIndex && distanceFromRightEdge > effectiveBufferThreshold) {
143− bufferExceeded = true;
144− }
145− }
146− if ((!useBuffer || (useBuffer && bufferExceeded)) || indicatorRow.currentItemIndex < 0 || indicatorRow.currentItem == null) {
147− indicatorRow.setCurrentItem(currentItem);
148− }
149−
150− // need to re-init the distanceFromRightEdge for offset calculation
151− itemCoordinates = indicatorRow.row.mapToItem(indicatorRow.currentItem, rowCoordinates.x, 0);
152− distanceFromRightEdge = (indicatorRow.currentItem.width - itemCoordinates.x) / (indicatorRow.currentItem.width);
153− }
154− indicatorRow.currentItemOffset = 1 - (distanceFromRightEdge * 2);
155− } else if (initalizeItem) {
156− indicatorRow.setDefaultItem();
157− indicatorRow.currentItemOffset = 0;
158− }
159− initalizeItem = indicatorRow.currentItem == null;
160− }
161−
162− // eater
163− MouseArea {
164− anchors {
165− top: parent.top
166− bottom: handle.bottom
167− left: parent.left
168− right: parent.right
169− }
170− }
171−
172− VisibleIndicators {
173− id: visibleIndicators
174− }
175−
176− MenuContent {
177− id: menuContent
178− objectName: "menuContent"
179−
180− anchors {
181− left: parent.left
182− right: parent.right
183− top: indicatorRow.bottom
184− bottom: handle.top
185− }
186− indicatorsModel: visibleIndicators.model
187− visible: indicators.partiallyOpened || indicators.fullyOpened
188− clip: indicators.partiallyOpened
189− enabled: contentEnabled
190−
191− //small shadow gradient at bottom of menu
192− Rectangle {
193− anchors {
194− left: parent.left
195− right: parent.right
196− bottom: parent.bottom
197− }
198− height: units.gu(0.5)
199− gradient: Gradient {
200− GradientStop { position: 0.0; color: "transparent" }
201− GradientStop { position: 1.0; color: "black" }
202− }
203− opacity: 0.4
204− }
205− }
206−
207− Rectangle {
208− id: handle
209−
210− color: menuContent.color
211−
212− anchors {
213− left: parent.left
214− right: parent.right
215− bottom: parent.bottom
216− }
217− height: Math.max(Math.min(handleImage.height, indicators.height - handleImage.height), 0)
218− clip: height < handleImage.height
219− visible: menuContent.visible
220−
221− BorderImage {
222− id: handleImage
223− source: "graphics/handle.sci"
224− height: panelHeight
225− anchors {
226− left: parent.left
227− right: parent.right
228− bottom: parent.bottom
229− }
230− }
231− MouseArea { //prevent clicks passing through
232− anchors.fill: parent
233− }
234− }
235−
236− IndicatorRow {
237− id: indicatorRow
238− objectName: "indicatorRow"
239− anchors {
240− left: parent.left
241− right: parent.right
242− }
243− height: indicators.panelHeight
244− indicatorsModel: visibleIndicators.model
245− state: indicators.state
246− unitProgress: indicators.unitProgress
247−
248− EdgeDragArea {
249− id: rowDragArea
250− anchors.fill: indicatorRow
251− direction: Direction.Downwards
252− maxSilenceTime: 2000
253− distanceThreshold: 0
254−
255− enabled: fullyOpened
256− onDraggingChanged: {
257− if (dragging) {
258− initalizeItem = true;
259− updateRevealProgressState(Math.max(touchSceneY - panelHeight, hintValue), false);
260− indicators.calculateCurrentItem(touchX, false);
261− } else {
262− indicators.state = "commit";
263− indicatorRow.currentItemOffset = 0;
264− }
265− }
266−
267− onTouchXChanged: {
268− indicators.calculateCurrentItem(touchX, true);
269− }
270− onTouchSceneYChanged: {
271− updateRevealProgressState(Math.max(touchSceneY - panelHeight, hintValue), false);
272− yVelocityCalculator.trackedPosition = touchSceneY;
273− }
274− }
275− }
276−
277− Connections {
278− target: showAnimation
279− onRunningChanged: {
280− if (showAnimation.running) {
281− indicators.state = "commit";
282− indicatorRow.currentItemOffset = 0;
283− }
284− }
285− }
286−
287− Connections {
288− target: hideAnimation
289− onRunningChanged: {
290− if (hideAnimation.running) {
291− indicators.state = "initial";
292− initalizeItem = true;
293− indicatorRow.currentItemOffset = 0;
294− }
295− }
296− }
297−
298− QtObject {
299− id: d
300− property bool enableIndexChangeSignal: true
301− property var activeDragHandle: showDragHandle.dragging ? showDragHandle : hideDragHandle.dragging ? hideDragHandle : null
302− }
303−
304− Connections {
305− target: menuContent
306− onCurrentMenuIndexChanged: {
307− var oldActive = d.enableIndexChangeSignal;
308− if (!oldActive) return;
309− d.enableIndexChangeSignal = false;
310−
311− indicatorRow.setCurrentItemIndex(menuContent.currentMenuIndex);
312−
313− d.enableIndexChangeSignal = oldActive;
314− }
315− }
316−
317− Connections {
318− target: indicatorRow
319− onCurrentItemIndexChanged: {
320− var oldActive = d.enableIndexChangeSignal;
321− if (!oldActive) return;
322− d.enableIndexChangeSignal = false;
323−
324− menuContent.setCurrentMenuIndex(indicatorRow.currentItemIndex, fullyOpened || partiallyOpened);
325−
326− d.enableIndexChangeSignal = oldActive;
327− }
328− }
329− // connections to the active drag handle
330− Connections {
331− target: d.activeDragHandle
332− onTouchXChanged: {
333− indicators.calculateCurrentItem(d.activeDragHandle.touchX, true);
334− }
335− onTouchSceneYChanged: {
336− yVelocityCalculator.trackedPosition = d.activeDragHandle.touchSceneY;
337− }
338− }
339−
340− DragHandle {
341− id: showDragHandle
342− anchors.bottom: parent.bottom
343− // go beyond parent so that it stays reachable, at the top of the screen.
344− anchors.bottomMargin: showHintBottomMargin
345− anchors.left: parent.left
346− anchors.right: parent.right
347− height: panelHeight
348− direction: Direction.Downwards
349− enabled: !indicators.shown && indicators.available
350− hintDisplacement: enableHint ? indicators.hintValue : 0
351− autoCompleteDragThreshold: maxTotalDragDistance / 2
352− stretch: true
353− maxTotalDragDistance: openedHeight - panelHeight
354− distanceThreshold: panelHeight
355−
356− onTapped: showTapped(Qt.point(touchSceneX, touchSceneY));
357− }
358−
359− DragHandle {
360− id: hideDragHandle
361− anchors.fill: handle
362− direction: Direction.Upwards
363− enabled: indicators.shown && indicators.available
364− hintDisplacement: indicators.hintValue
365− autoCompleteDragThreshold: maxTotalDragDistance / 6
366− stretch: true
367− maxTotalDragDistance: openedHeight - panelHeight
368− distanceThreshold: 0
369− }
370−
371− AxisVelocityCalculator {
372− id: yVelocityCalculator
373− }
374−
375− states: [
376− State {
377− name: "initial"
378− },
379− State {
380− name: "hint"
381− StateChangeScript {
382− script: {
383− if (d.activeDragHandle) {
384− calculateCurrentItem(d.activeDragHandle.touchX, false);
385− }
386− }
387− }
388− },
389− State {
390− name: "reveal"
391− extend: "hint"
392− },
393− State {
394− name: "locked"
395− extend: "hint"
396− },
397− State {
398− name: "commit"
399− extend: "hint"
400− }
401− ]
402− state: "initial"
403−
404− transitions: [
405− Transition {
406− NumberAnimation {targets: [indicatorRow, menuContent]; property: "y"; duration: 300; easing.type: Easing.OutCubic}
407− }
408− ]
409−
410− Component.onCompleted: initialise();
411− function initialise() {
412− visibleIndicators.load(profile);
413− indicatorRow.setDefaultItem();
414− }
415−}
qml/Panel/Indicators/CachedUnityMenuModel.qmldeleted−39
@@ -1,39 +0,0 @@
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−import QMenuModel 0.1
22−import Unity.Indicators 0.1 as Indicators
23−
24−// Make sure we don't duplicate models.
25−Item {
26− id: cachedModel
27− property string busName
28− property string actionsObjectPath
29− property string menuObjectPath
30− readonly property bool ready: busName!=="" && actionsObjectPath!=="" && menuObjectPath!==""
31−
32− property var model: {
33− if (!ready) return null;
34−
35− return Indicators.UnityMenuModelCache.model(cachedModel.busName,
36− cachedModel.menuObjectPath,
37− { "indicator": cachedModel.actionsObjectPath });
38− }
39−}
qml/Panel/Indicators/DefaultIndicatorWidget.qmldeleted−119
@@ -1,119 +0,0 @@
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−import Ubuntu.Components 0.1
22−import Ubuntu.Settings.Components 0.1
23−
24−IndicatorBase {
25− id: indicatorWidget
26−
27− property int iconSize: units.gu(2)
28− property alias leftLabel: itemLeftLabel.text
29− property alias rightLabel: itemRightLabel.text
30− property var icons: undefined
31−
32− width: itemRow.width
33− enabled: false
34−
35− // FIXME: For now we will enable led indicator support only for messaging indicator
36− // in the future we should export a led API insted of doing that,
37− Loader {
38− id: indicatorLed
39− // only load source Component if the icons contains the new message icon
40− source: (indicatorWidget.icons && (String(indicatorWidget.icons).indexOf("indicator-messages-new") != -1)) ? Qt.resolvedUrl("IndicatorsLight.qml") : ""
41− }
42−
43− Row {
44− id: itemRow
45− objectName: "itemRow"
46− anchors {
47− top: parent.top
48− bottom: parent.bottom
49− horizontalCenter: parent.horizontalCenter
50− }
51−
52− Label {
53− id: itemLeftLabel
54− width: contentWidth + units.gu(1)
55− objectName: "leftLabel"
56− color: Theme.palette.selected.backgroundText
57− opacity: 0.8
58− font.family: "Ubuntu"
59− fontSize: "medium"
60− anchors.verticalCenter: parent.verticalCenter
61− visible: text != ""
62− horizontalAlignment: Text.AlignHCenter
63− }
64−
65− Row {
66− id: iconRow
67− anchors {
68− top: parent.top
69− bottom: parent.bottom
70− }
71−
72− Repeater {
73− model: indicatorWidget.icons
74−
75− Item {
76− width: itemImage.width + units.gu(1)
77− height: iconRow.height
78−
79− StatusIcon {
80− id: itemImage
81− height: indicatorWidget.iconSize
82− anchors.centerIn: parent
83− source: modelData
84− sets: ["status", "actions"]
85− color: "#CCCCCC"
86− }
87− }
88− }
89− }
90−
91− Label {
92− id: itemRightLabel
93− width: contentWidth + units.gu(1)
94− objectName: "rightLabel"
95− color: Theme.palette.selected.backgroundText
96− opacity: 0.8
97− font.family: "Ubuntu"
98− fontSize: "medium"
99− anchors.verticalCenter: parent.verticalCenter
100− visible: text != ""
101− horizontalAlignment: Text.AlignHCenter
102− }
103− }
104−
105− onRootActionStateChanged: {
106− if (rootActionState == undefined) {
107− leftLabel = "";
108− rightLabel = "";
109− icons = undefined;
110− enabled = false;
111− return;
112− }
113−
114− leftLabel = rootActionState.leftLabel ? rootActionState.leftLabel : "";
115− rightLabel = rootActionState.rightLabel ? rootActionState.rightLabel : "";
116− icons = rootActionState.icons;
117− enabled = rootActionState.visible;
118− }
119−}
qml/Panel/Indicators/IndicatorBase.qml+3−7
@@ -23,8 +23,6 @@ import Unity.Indicators 0.1
23Item {
23Item {
24 id: indicatorItem
24 id: indicatorItem
25
25
26− enabled: menuObjectPath != ""
27−
28 //const
26 //const
29 property string identifier
27 property string identifier
30 property string busName
28 property string busName
@@ -32,16 +30,14 @@ Item {
32 property string menuObjectPath
30 property string menuObjectPath
33 property string rootMenuType: "com.canonical.indicator.root"
31 property string rootMenuType: "com.canonical.indicator.root"
34
32
35− property string deviceMenuObjectPath: menuObjectPath
36−
37 property alias menuModel: cachedModel.model
33 property alias menuModel: cachedModel.model
38 property alias rootActionState: rootAction
34 property alias rootActionState: rootAction
39
35
40− CachedUnityMenuModel {
36+ SharedUnityMenuModel {
41 id: cachedModel
37 id: cachedModel
42 busName: indicatorItem.busName
38 busName: indicatorItem.busName
43− actionsObjectPath: indicatorItem.actionsObjectPath
39+ actions: { "indicator": indicatorItem.actionsObjectPath }
44− menuObjectPath: indicatorItem.deviceMenuObjectPath
40+ menuObjectPath: indicatorItem.menuObjectPath
45 }
41 }
46
42
47 RootActionState {
43 RootActionState {
qml/Panel/Indicators/IndicatorDelegate.qml+2−2
@@ -20,6 +20,6 @@
20import QtQuick 2.0
20import QtQuick 2.0
21
21
22IndicatorBase {
22IndicatorBase {
23− enabled: rootActionState.visible
23+ readonly property bool indicatorVisible: rootActionState.indicatorVisible
24− property string title: rootActionState.title
24+ readonly property string title: rootActionState.title
25}
25}
qml/Panel/Indicators/VisibleIndicators.qml+4−5
@@ -24,6 +24,10 @@ import Utils 0.1
24Item {
24Item {
25 property SortFilterProxyModel model: filterModel
25 property SortFilterProxyModel model: filterModel
26
26
27+ function initialise(profile) {
28+ indicatorsModel.load(profile);
29+ }
30+
27 SortFilterProxyModel {
31 SortFilterProxyModel {
28 id: filterModel
32 id: filterModel
29 filterRole: Indicators.IndicatorsModelRole.IsVisible
33 filterRole: Indicators.IndicatorsModelRole.IsVisible
@@ -36,7 +40,6 @@ Item {
36 }
40 }
37 }
41 }
38
42
39−
40 Indicators.IndicatorsModel {
43 Indicators.IndicatorsModel {
41 id: indicatorsModel
44 id: indicatorsModel
42 }
45 }
@@ -78,8 +81,4 @@ Item {
78 }
81 }
79 }
82 }
80 }
83 }
81−
82− function load(profile) {
83− indicatorsModel.load(profile);
84− }
85}
84}
qml/Panel/Indicators/client/IndicatorsPage.qml → qml/Panel/Indicators/client/IndicatorRepresentation.qmlrenamed+21−10
@@ -21,13 +21,13 @@
21import QtQuick 2.0
21import QtQuick 2.0
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23import Ubuntu.Components.ListItems 0.1 as ListItem
23import Ubuntu.Components.ListItems 0.1 as ListItem
24+import "../.."
24
25
25Page {
26Page {
26− id: page
27+ id: root
27
28
28 title: indicatorProperties && indicatorProperties.title ? indicatorProperties.title : ""
29 title: indicatorProperties && indicatorProperties.title ? indicatorProperties.title : ""
29 property variant indicatorProperties
30 property variant indicatorProperties
30− property string pageSource : pageLoader.source
31
31
32 anchors.fill: parent
32 anchors.fill: parent
33
33
@@ -44,6 +44,7 @@ Page {
44 id: pageLoader
44 id: pageLoader
45 objectName: "pageLoader"
45 objectName: "pageLoader"
46 clip:true
46 clip:true
47+ asynchronous: true
47
48
48 Rectangle {
49 Rectangle {
49 anchors.fill: pageLoader
50 anchors.fill: pageLoader
@@ -58,15 +59,25 @@ Page {
58 topMargin: units.gu(2)
59 topMargin: units.gu(2)
59 bottomMargin: units.gu(2)
60 bottomMargin: units.gu(2)
60 }
61 }
61− source : visualCheck.checked ? page.pageSource : "IndicatorsTree.qml"
62+ sourceComponent: visualCheck.checked ? page : tree
62
63
63− onLoaded: {
64+ Component {
65+ id: page
66+ IndicatorPage {
67+ identifier: model.identifier
64− for(var pName in indicatorProperties) {
68+ busName: indicatorProperties.busName
65− if (item.hasOwnProperty(pName)) {
69+ actionsObjectPath: indicatorProperties.actionsObjectPath
70+ menuObjectPath: indicatorProperties.menuObjectPath
71+ }
72+ }
73+ Component {
74+ id: tree
75+ IndicatorsTree {
76+ identifier: model.identifier
77+ busName: indicatorProperties.busName
66− item[pName] = indicatorProperties[pName];
78+ actionsObjectPath: indicatorProperties.actionsObjectPath
67− }
79+ menuObjectPath: indicatorProperties.menuObjectPath
68 }
80 }
69− item.start();
70 }
81 }
71 }
82 }
72
83
@@ -85,7 +96,7 @@ Page {
85 left: parent.left
96 left: parent.left
86 }
97 }
87 text: "Back"
98 text: "Back"
88− onClicked: page.pageStack.reset()
99+ onClicked: root.pageStack.reset()
89 }
100 }
90 }
101 }
91}
102}
qml/Panel/Indicators/client/IndicatorsList.qml+3−4
@@ -22,7 +22,6 @@ import Ubuntu.Components 0.1
22import Ubuntu.Components.ListItems 0.1 as ListItem
22import Ubuntu.Components.ListItems 0.1 as ListItem
23import Unity.Indicators 0.1 as Indicators
23import Unity.Indicators 0.1 as Indicators
24import "../.."
24import "../.."
25−import "../../../Components/Flickables" as Flickables
26
25
27Page {
26Page {
28 id: page
27 id: page
@@ -36,7 +35,7 @@ Page {
36 Component.onCompleted: load(profile)
35 Component.onCompleted: load(profile)
37 }
36 }
38
37
39− Flickables.ListView {
38+ ListView {
40 id: mainMenu
39 id: mainMenu
41 objectName: "mainMenu"
40 objectName: "mainMenu"
42 anchors.fill: parent
41 anchors.fill: parent
@@ -58,8 +57,8 @@ Page {
58 text: identifier
57 text: identifier
59
58
60 onClicked: {
59 onClicked: {
61− var new_page = Qt.createComponent("IndicatorsPage.qml");
60+ var new_page = Qt.createComponent("IndicatorRepresentation.qml");
62− page.pageStack.push(new_page.createObject(pages), {"indicatorProperties" : model.indicatorProperties, "pageSource" : model.pageSource});
61+ page.pageStack.push(new_page.createObject(pages), {"indicatorProperties" : model.indicatorProperties });
63 }
62 }
64 }
63 }
65 }
64 }
qml/Panel/Indicators/client/IndicatorsTree.qml+5−28
@@ -20,40 +20,17 @@
20import QtQuick 2.0
20import QtQuick 2.0
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22import Unity.Indicators 0.1 as Indicators
22import Unity.Indicators 0.1 as Indicators
23−import QMenuModel 0.1
23+import ".."
24−import Ubuntu.Components.ListItems 0.1 as ListItem
25−import "../../../Components/Flickables" as Flickables
26
24
27−Page {
25+IndicatorBase {
28− id: page
26+ id: root
29− anchors.fill: parent
30−
31− property string busName: unityModel.busName
32− property string actionsObjectPath
33− property string menuObjectPath
34−
35− property string deviceMenuObjectPath: menuObjectPath
36−
37− function start() {
38− }
39−
40− UnityMenuModel {
41− id: unityModel
42− busName: page.busName
43− actions: { "indicator": page.actionsObjectPath }
44− menuObjectPath: page.deviceMenuObjectPath
45− }
46−
47− Indicators.RootActionState {
48− menu: unityModel
49− }
50
27
51 Indicators.ModelPrinter {
28 Indicators.ModelPrinter {
52 id: printer
29 id: printer
53− model: unityModel
30+ model: root.menuModel
54 }
31 }
55
32
56− Flickables.Flickable {
33+ Flickable {
57 anchors.fill: parent
34 anchors.fill: parent
58 contentHeight: all_data.height
35 contentHeight: all_data.height
59 clip:true
36 clip:true
qml/Panel/IndicatorsBar.qmlnew file+269
@@ -0,0 +1,269 @@
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+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+import "../Components"
20+
21+Item {
22+ id: root
23+ property alias expanded: row.expanded
24+ property alias interactive: flickable.interactive
25+ property alias indicatorsModel: row.indicatorsModel
26+ property alias unitProgress: row.unitProgress
27+ property alias enableLateralChanges: row.enableLateralChanges
28+ property alias overFlowWidth: row.overFlowWidth
29+ readonly property alias currentItemIndex: row.currentItemIndex
30+ property real lateralPosition: -1
31+
32+ function selectItemAt(lateralPosition) {
33+ if (!expanded) {
34+ row.resetCurrentItem();
35+ }
36+ var mapped = root.mapToItem(row, lateralPosition, 0);
37+ row.selectItemAt(mapped.x);
38+ }
39+
40+ function setCurrentItemIndex(index) {
41+ if (!expanded) {
42+ row.resetCurrentItem();
43+ }
44+ row.setCurrentItemIndex(index);
45+ }
46+
47+ function addScrollOffset(scrollAmmout) {
48+ if (scrollAmmout < 0) { // left scroll
49+ if (flickable.contentX < 0) return; // already off the left.
50+ if (flickable.contentX + scrollAmmout < 0) scrollAmmout = -flickable.contentX; // going to be off the left
51+ } else { // right scroll
52+ if (flickable.contentX + flickable.width > row.width) return; // already off the right.
53+ if (flickable.contentX + flickable.width + scrollAmmout > row.width) { // going to be off the right
54+ scrollAmmout = row.width - (flickable.contentX + flickable.width);
55+ }
56+ }
57+ d.scrollOffset = d.scrollOffset + scrollAmmout;
58+ }
59+
60+ QtObject {
61+ id: d
62+ property var initialItem
63+ // the non-expanded distance from row offset to center of initial item
64+ property real originalDistanceFromRight: -1
65+
66+ // calculate the distance from row offset to center of initial item
67+ property real distanceFromRight: {
68+ if (originalDistanceFromRight == -1) return 0;
69+ if (!initialItem) return 0;
70+ return row.width - initialItem.x - initialItem.width /2;
71+ }
72+
73+ // offset to the intially selected expanded item
74+ property real rowOffset: 0
75+ property real scrollOffset: 0
76+ property real alignmentAdjustment: 0
77+ property real combinedOffset: 0
78+
79+ // when the scroll offset changes, we need to reclaculate the relative lateral position
80+ onScrollOffsetChanged: root.lateralPositionChanged()
81+
82+ onInitialItemChanged: {
83+ originalDistanceFromRight = initialItem ? (row.width - initialItem.x - initialItem.width/2) : -1;
84+ }
85+
86+ Behavior on alignmentAdjustment {
87+ NumberAnimation { duration: UbuntuAnimation.BriskDuration; easing: UbuntuAnimation.StandardEasing}
88+ }
89+
90+ function alignIndicators() {
91+ flickable.resetContentXComponents();
92+
93+ if (expanded && !flickable.moving) {
94+
95+ // gap between left and row?
96+ if (flickable.contentX < 0) {
97+ d.alignmentAdjustment += flickable.contentX;
98+ // gap between right and row?
99+ } else if (flickable.contentX + flickable.width > row.width) {
100+ // row width is less than flickable
101+ if (row.width < flickable.width) {
102+ d.alignmentAdjustment += flickable.contentX;
103+ } else {
104+ d.alignmentAdjustment += ((flickable.contentX + flickable.width) - row.width);
105+ }
106+ // current item overlap on left
107+ } else if (row.currentItem && row.currentItem.x < flickable.contentX) {
108+ d.alignmentAdjustment -= (row.currentItem.x - flickable.contentX);
109+ // current item overlap on right
110+ } else if (row.currentItem && row.currentItem.x + row.currentItem.width > flickable.contentX + flickable.width) {
111+ d.alignmentAdjustment -= ((row.currentItem.x + row.currentItem.width) - (flickable.contentX + flickable.width));
112+ }
113+ }
114+ }
115+ }
116+
117+ Rectangle {
118+ id: grayLine
119+ height: units.dp(2)
120+ width: parent.width
121+ anchors.bottom: parent.bottom
122+
123+ color: "#4c4c4c"
124+ opacity: expanded ? 1.0 : 0.0
125+ Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } }
126+ }
127+
128+ Item {
129+ id: rowContainer
130+ anchors.fill: parent
131+ clip: expanded || row.width > rowContainer.width
132+
133+ Flickable {
134+ id: flickable
135+ objectName: "flickable"
136+
137+ anchors.fill: parent
138+ contentWidth: row.width
139+ interactive: false
140+ // align right + offset from row selection + scrolling
141+ contentX: row.width - flickable.width - d.combinedOffset
142+
143+ // contentX can change by user interaction as well as user offset changes
144+ // This function re-aligns the offsets so that the offsets match the contentX
145+ function resetContentXComponents() {
146+ d.scrollOffset += (flickable.contentX - (row.width - flickable.width - d.combinedOffset));
147+ }
148+
149+ rebound: Transition {
150+ NumberAnimation {
151+ properties: "x"
152+ duration: 600
153+ easing.type: Easing.OutCubic
154+ }
155+ }
156+
157+ IndicatorItemRow {
158+ id: row
159+ objectName: "indicatorItemRow"
160+ anchors {
161+ top: parent.top
162+ bottom: parent.bottom
163+ }
164+
165+ lateralPosition: {
166+ if (root.lateralPosition == -1) return -1;
167+
168+ var mapped = root.mapToItem(row, root.lateralPosition, 0);
169+ return Math.min(Math.max(mapped.x, 0), row.width);
170+ }
171+
172+ onCurrentItemChanged: {
173+ if (!currentItem) d.initialItem = undefined;
174+ else if (!d.initialItem) d.initialItem = currentItem;
175+ }
176+
177+ MouseArea {
178+ anchors.fill: parent
179+ enabled: root.expanded
180+ onClicked: {
181+ row.selectItemAt(mouse.x);
182+ d.alignIndicators();
183+ }
184+ }
185+ }
186+
187+ }
188+ }
189+
190+ Timer {
191+ id: alignmentTimer
192+ interval: UbuntuAnimation.FastDuration // enough for row animation.
193+ repeat: false
194+
195+ onTriggered: d.alignIndicators();
196+ }
197+
198+ states: [
199+ State {
200+ name: "minimized"
201+ when: !expanded
202+ PropertyChanges {
203+ target: d
204+ rowOffset: 0
205+ scrollOffset: 0
206+ alignmentAdjustment: 0
207+ combinedOffset: 0
208+ restoreEntryValues: false
209+ }
210+ },
211+ State {
212+ name: "expanded"
213+ when: expanded && !interactive
214+
215+ PropertyChanges {
216+ target: d
217+ combinedOffset: rowOffset + alignmentAdjustment - scrollOffset
218+ }
219+ PropertyChanges {
220+ target: d
221+ rowOffset: {
222+ if (!initialItem) return 0;
223+ if (distanceFromRight - initialItem.width <= 0) return 0;
224+
225+ var rowOffset = distanceFromRight - originalDistanceFromRight;
226+ return rowOffset;
227+ }
228+ restoreEntryValues: false
229+ }
230+ }
231+ , State {
232+ name: "interactive"
233+ when: expanded && interactive
234+
235+ StateChangeScript {
236+ script: {
237+ // don't use row offset anymore.
238+ d.scrollOffset -= d.rowOffset;
239+ d.rowOffset = 0;
240+ d.initialItem = undefined;
241+ alignmentTimer.start();
242+ }
243+ }
244+ PropertyChanges {
245+ target: d
246+ combinedOffset: rowOffset + alignmentAdjustment - scrollOffset
247+ restoreEntryValues: false
248+ }
249+ }
250+ ]
251+
252+ transitions: [
253+ Transition {
254+ from: "expanded"
255+ to: "minimized"
256+ PropertyAction {
257+ target: d
258+ properties: "rowOffset, scrollOffset, alignmentAdjustment"
259+ value: 0
260+ }
261+ PropertyAnimation {
262+ target: d
263+ properties: "combinedOffset"
264+ duration: UbuntuAnimation.SnapDuration
265+ easing: UbuntuAnimation.StandardEasing
266+ }
267+ }
268+ ]
269+}
qml/Panel/IndicatorsMenu.qmlnew file+318
@@ -0,0 +1,318 @@
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+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+import Ubuntu.Gestures 0.1
20+import "../Components"
21+
22+Showable {
23+ id: root
24+ property alias indicatorsModel: bar.indicatorsModel
25+ property alias showDragHandle: __showDragHandle
26+ property alias hideDragHandle: __hideDragHandle
27+ property alias overFlowWidth: bar.overFlowWidth
28+ property alias verticalVelocityThreshold: yVelocityCalculator.velocityThreshold
29+ property int minimizedPanelHeight: units.gu(3)
30+ property int expandedPanelHeight: units.gu(7)
31+ property real openedHeight: units.gu(71)
32+ readonly property real unitProgress: Math.max(0, (height - minimizedPanelHeight) / (openedHeight - minimizedPanelHeight))
33+ readonly property bool fullyOpened: unitProgress >= 1
34+ readonly property bool partiallyOpened: unitProgress > 0 && unitProgress < 1.0
35+ readonly property bool fullyClosed: unitProgress == 0
36+ property bool enableHint: true
37+ property bool contentEnabled: true
38+ property color panelColor: "black"
39+
40+ signal showTapped(point position)
41+
42+ // TODO: Perhaps we need a animation standard for showing/hiding? Each showable seems to
43+ // use its own values. Need to ask design about this.
44+ showAnimation: StandardAnimation {
45+ property: "height"
46+ to: openedHeight
47+ duration: UbuntuAnimation.BriskDuration
48+ easing.type: Easing.OutCubic
49+ }
50+
51+ hideAnimation: StandardAnimation {
52+ property: "height"
53+ to: minimizedPanelHeight
54+ duration: UbuntuAnimation.BriskDuration
55+ easing.type: Easing.OutCubic
56+ }
57+
58+ height: minimizedPanelHeight
59+
60+ onUnitProgressChanged: d.updateState()
61+ clip: root.partiallyOpened
62+
63+ // eater
64+ MouseArea {
65+ anchors.fill: parent
66+ }
67+
68+ MenuContent {
69+ id: content
70+ objectName: "menuContent"
71+
72+ anchors {
73+ left: parent.left
74+ right: parent.right
75+ top: bar.bottom
76+ }
77+ height: openedHeight - bar.height - handle.height
78+ indicatorsModel: root.indicatorsModel
79+ visible: root.unitProgress > 0
80+ enabled: contentEnabled
81+ currentMenuIndex: bar.currentItemIndex
82+ }
83+
84+ Handle {
85+ id: handle
86+ anchors {
87+ left: parent.left
88+ right: parent.right
89+ bottom: parent.bottom
90+ }
91+ height: units.gu(2)
92+ active: d.activeDragHandle ? true : false
93+
94+ //small shadow gradient at bottom of menu
95+ Rectangle {
96+ anchors {
97+ left: parent.left
98+ right: parent.right
99+ bottom: parent.top
100+ }
101+ height: units.gu(0.5)
102+ gradient: Gradient {
103+ GradientStop { position: 0.0; color: "transparent" }
104+ GradientStop { position: 1.0; color: "black" }
105+ }
106+ opacity: 0.3
107+ }
108+ }
109+
110+ Rectangle {
111+ anchors.fill: bar
112+ color: panelColor
113+ }
114+
115+ IndicatorsBar {
116+ id: bar
117+ objectName: "indicatorsBar"
118+
119+ anchors {
120+ left: parent.left
121+ right: parent.right
122+ }
123+ expanded: false
124+ enableLateralChanges: false
125+ lateralPosition: -1
126+ unitProgress: root.unitProgress
127+
128+ height: expanded ? expandedPanelHeight : minimizedPanelHeight
129+ Behavior on height { NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing } }
130+ }
131+
132+ ScrollCalculator {
133+ id: leftScroller
134+ width: units.gu(5)
135+ anchors.left: bar.left
136+ height: bar.height
137+
138+ forceScrollingPercentage: 0.33
139+ stopScrollThreshold: units.gu(0.75)
140+ direction: Qt.RightToLeft
141+ lateralPosition: -1
142+
143+ onScroll: bar.addScrollOffset(-scrollAmount);
144+ }
145+
146+ ScrollCalculator {
147+ id: rightScroller
148+ width: units.gu(5)
149+ anchors.right: bar.right
150+ height: bar.height
151+
152+ forceScrollingPercentage: 0.33
153+ stopScrollThreshold: units.gu(0.75)
154+ direction: Qt.LeftToRight
155+ lateralPosition: -1
156+
157+ onScroll: bar.addScrollOffset(scrollAmount);
158+ }
159+
160+ DragHandle {
161+ id: __showDragHandle
162+ anchors.bottom: parent.bottom
163+ anchors.left: parent.left
164+ anchors.right: parent.right
165+ height: minimizedPanelHeight
166+ direction: Direction.Downwards
167+ enabled: !root.shown && root.available
168+ autoCompleteDragThreshold: maxTotalDragDistance / 2
169+ stretch: true
170+ distanceThreshold: minimizedPanelHeight
171+ onTapped: showTapped(Qt.point(touchSceneX, touchSceneY));
172+
173+ // using hint regulates minimum to hint displacement, but in fullscreen mode, we need to do it manually.
174+ overrideStartValue: enableHint ? minimizedPanelHeight : expandedPanelHeight + handle.height
175+ maxTotalDragDistance: openedHeight - (enableHint ? minimizedPanelHeight : expandedPanelHeight + handle.height)
176+ hintDisplacement: enableHint ? expandedPanelHeight - minimizedPanelHeight + handle.height : 0
177+ }
178+
179+ DragHandle {
180+ id: __hideDragHandle
181+ anchors.fill: handle
182+ direction: Direction.Upwards
183+ enabled: root.shown && root.available
184+ hintDisplacement: units.gu(3)
185+ autoCompleteDragThreshold: maxTotalDragDistance / 6
186+ stretch: true
187+ maxTotalDragDistance: openedHeight - expandedPanelHeight - handle.height
188+ distanceThreshold: 0
189+
190+ onTouchSceneXChanged: {
191+ if (root.state === "locked") {
192+ d.xDisplacementSinceLock += (touchSceneX - d.lastHideTouchSceneX)
193+ d.lastHideTouchSceneX = touchSceneX;
194+ }
195+ }
196+ }
197+
198+ PanelVelocityCalculator {
199+ id: yVelocityCalculator
200+ velocityThreshold: d.hasCommitted ? 0.1 : 0.3
201+ trackedValue: d.activeDragHandle ? d.activeDragHandle.touchSceneY : 0
202+
203+ onVelocityAboveThresholdChanged: d.updateState()
204+ }
205+
206+ Connections {
207+ target: showAnimation
208+ onRunningChanged: {
209+ if (showAnimation.running) {
210+ root.state = "commit";
211+ }
212+ }
213+ }
214+
215+ Connections {
216+ target: hideAnimation
217+ onRunningChanged: {
218+ if (hideAnimation.running) {
219+ root.state = "initial";
220+ }
221+ }
222+ }
223+
224+ QtObject {
225+ id: d
226+ property var activeDragHandle: showDragHandle.dragging ? showDragHandle : hideDragHandle.dragging ? hideDragHandle : null
227+ property bool hasCommitted: false
228+ property real lastHideTouchSceneX: 0
229+ property real xDisplacementSinceLock: 0
230+ onXDisplacementSinceLockChanged: d.updateState()
231+
232+ property real rowMappedLateralPosition: {
233+ if (!d.activeDragHandle) return -1;
234+ return d.activeDragHandle.mapToItem(bar, d.activeDragHandle.touchX, 0).x;
235+ }
236+
237+ function updateState() {
238+ if (!showAnimation.running && !hideAnimation.running) {
239+ if (unitProgress <= 0) {
240+ root.state = "initial";
241+ // lock indicator if we've been committed and aren't moving too much laterally or too fast up.
242+ } else if (d.hasCommitted && (Math.abs(d.xDisplacementSinceLock) < units.gu(2) || yVelocityCalculator.velocityAboveThreshold)) {
243+ root.state = "locked";
244+ } else {
245+ root.state = "reveal";
246+ }
247+ }
248+ }
249+ }
250+
251+ states: [
252+ State {
253+ name: "initial"
254+ PropertyChanges { target: d; hasCommitted: false; restoreEntryValues: false }
255+ },
256+ State {
257+ name: "reveal"
258+ StateChangeScript {
259+ script: {
260+ yVelocityCalculator.reset();
261+ // initial item selection
262+ if (!d.hasCommitted) bar.selectItemAt(d.activeDragHandle ? d.activeDragHandle.touchX : -1);
263+ d.hasCommitted = false;
264+ }
265+ }
266+ PropertyChanges {
267+ target: bar
268+ expanded: true
269+ // changes to lateral touch position effect which indicator is selected
270+ lateralPosition: d.rowMappedLateralPosition
271+ // vertical velocity determines if changes in lateral position has an effect
272+ enableLateralChanges: d.activeDragHandle &&
273+ !yVelocityCalculator.velocityAboveThreshold
274+ }
275+ // left scroll bar handling
276+ PropertyChanges {
277+ target: leftScroller
278+ lateralPosition: {
279+ if (!d.activeDragHandle) return -1;
280+ var mapped = d.activeDragHandle.mapToItem(leftScroller, d.activeDragHandle.touchX, 0);
281+ return mapped.x;
282+ }
283+ }
284+ // right scroll bar handling
285+ PropertyChanges {
286+ target: rightScroller
287+ lateralPosition: {
288+ if (!d.activeDragHandle) return -1;
289+ var mapped = d.activeDragHandle.mapToItem(rightScroller, d.activeDragHandle.touchX, 0);
290+ return mapped.x;
291+ }
292+ }
293+ },
294+ State {
295+ name: "locked"
296+ StateChangeScript {
297+ script: {
298+ d.xDisplacementSinceLock = 0;
299+ d.lastHideTouchSceneX = hideDragHandle.touchSceneX;
300+ }
301+ }
302+ PropertyChanges { target: bar; expanded: true }
303+ },
304+ State {
305+ name: "commit"
306+ extend: "locked"
307+ PropertyChanges { target: bar; interactive: true }
308+ PropertyChanges {
309+ target: d;
310+ hasCommitted: true
311+ lastHideTouchSceneX: 0
312+ xDisplacementSinceLock: 0
313+ restoreEntryValues: false
314+ }
315+ }
316+ ]
317+ state: "initial"
318+}
qml/Panel/MenuContent.qml+26−83
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2014 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -14,126 +14,69 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.2
18import QtQuick.Layouts 1.1
18import QtQuick.Layouts 1.1
19import Ubuntu.Components 1.1
19import Ubuntu.Components 1.1
20import Unity.Indicators 0.1 as Indicators
20import Unity.Indicators 0.1 as Indicators
21import Utils 0.1
21import Utils 0.1
22import "../Components"
22import "../Components"
23−import "../Components/Flickables" as Flickables
24import "Indicators"
23import "Indicators"
25
24
26Rectangle {
25Rectangle {
27 id: content
26 id: content
28
27
29 property QtObject indicatorsModel: null
28 property QtObject indicatorsModel: null
30− readonly property alias currentMenuIndex: listViewHeader.currentIndex
29+ property int currentMenuIndex: -1
31 color: "#221e1c" // FIXME not in palette yet
30 color: "#221e1c" // FIXME not in palette yet
32− property real headerHeight: listViewHeader.height
33
31
34 width: units.gu(40)
32 width: units.gu(40)
35 height: units.gu(42)
33 height: units.gu(42)
36
34
37− function setCurrentMenuIndex(index, animate) {
35+ onCurrentMenuIndexChanged: {
38− // FIXME - https://bugreports.qt-project.org/browse/QTBUG-41229
36+ listViewContent.currentIndex = currentMenuIndex;
39− listViewHeader.currentIndex = -1;
40− listViewHeader.currentIndex = index;
41 }
37 }
42
38
43− Flickables.ListView {
44− id: listViewHeader
45− objectName: "indicatorsHeaderListView"
46− model: content.indicatorsModel
47− clip: true
48−
49− anchors {
50− left: parent.left
51− right: parent.right
52− }
53− height: units.gu(8.5)
54−
55− highlightFollowsCurrentItem: true
56− highlightMoveDuration: 0
57−
58− orientation: ListView.Horizontal
59− snapMode: ListView.SnapOneItem
60− highlightRangeMode: ListView.StrictlyEnforceRange
61− boundsBehavior: Flickable.StopAtBounds
62− // Load all the indicator menus (a big number)
63− cacheBuffer: 1073741823
64−
65− delegate: Header {
66− width: ListView.view.width
67− height: implicitHeight
68−
69− title: indicatorDelegate.title !== "" ? indicatorDelegate.title : identifier
70−
71− IndicatorDelegate {
72− id: indicatorDelegate
73− Component.onCompleted: {
74− for(var pName in indicatorProperties) {
75− if (indicatorDelegate.hasOwnProperty(pName)) {
76− indicatorDelegate[pName] = indicatorProperties[pName];
77− }
78− }
79− }
80− }
81− }
82− }
83−
84− Flickables.ListView {
39+ ListView {
85 id: listViewContent
40 id: listViewContent
86 objectName: "indicatorsContentListView"
41 objectName: "indicatorsContentListView"
87− anchors {
42+ anchors.fill: parent
88− left: parent.left
89− right: parent.right
90− top: listViewHeader.bottom
91− bottom: parent.bottom
92− }
93 model: content.indicatorsModel
43 model: content.indicatorsModel
94− clip: true
95
44
96− currentIndex: listViewHeader.currentIndex
45+ highlightFollowsCurrentItem: true
97 interactive: false
46 interactive: false
98 highlightMoveDuration: 0
47 highlightMoveDuration: 0
99 orientation: ListView.Horizontal
48 orientation: ListView.Horizontal
100 // Load all the indicator menus (a big number)
49 // Load all the indicator menus (a big number)
101 cacheBuffer: 1073741823
50 cacheBuffer: 1073741823
102
51
52+ // for additions/removals.
53+ onCountChanged: {
54+ listViewContent.currentIndex = content.currentMenuIndex;
55+ }
56+
103 delegate: Loader {
57 delegate: Loader {
104 id: loader
58 id: loader
59+
105 width: ListView.view.width
60 width: ListView.view.width
106 height: ListView.view.height
61 height: ListView.view.height
107 objectName: identifier
62 objectName: identifier
108−
109− source: pageSource
110 asynchronous: true
63 asynchronous: true
64+ visible: ListView.isCurrentItem
111
65
112− onVisibleChanged: {
66+ sourceComponent: IndicatorPage {
113− // Reset the indicator states
67+ objectName: identifier + "-page"
114− if (!visible && item && item["reset"]) {
115− item.reset()
116− }
117− }
118−
119− onLoaded: {
120− for(var pName in indicatorProperties) {
121− if (item.hasOwnProperty(pName)) {
122− item[pName] = indicatorProperties[pName]
123− }
124− }
125− }
126
68
127− Binding {
69+ identifier: model.identifier
128− target: loader.item
70+ busName: indicatorProperties.busName
129− property: "identifier"
71+ actionsObjectPath: indicatorProperties.actionsObjectPath
130− value: identifier
72+ menuObjectPath: indicatorProperties.menuObjectPath
131 }
73 }
132
74
133− Binding {
75+ onVisibleChanged: {
134− target: loader.item
76+ // Reset the indicator states
135− property: "objectName"
77+ if (!visible && status == Loader.Ready) {
136− value: identifier + "-page"
78+ item.reset();
79+ }
137 }
80 }
138 }
81 }
139 }
82 }
qml/Panel/Panel.qml+48−41
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2014 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -14,11 +14,11 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.2
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
19import Unity.Application 0.1
19import Unity.Application 0.1
20import "../Components"
20import "../Components"
21−import "../Components/ListItems"
21+import ".."
22
22
23Item {
23Item {
24 id: root
24 id: root
@@ -53,7 +53,9 @@ Item {
53
53
54 anchors.fill: parent
54 anchors.fill: parent
55
55
56− Behavior on anchors.topMargin { StandardAnimation {} }
56+ Behavior on anchors.topMargin {
57+ NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
58+ }
57
59
58 BorderImage {
60 BorderImage {
59 id: dropShadow
61 id: dropShadow
@@ -62,24 +64,10 @@ Item {
62 leftMargin: -units.gu(1)
64 leftMargin: -units.gu(1)
63 bottomMargin: -units.gu(1)
65 bottomMargin: -units.gu(1)
64 }
66 }
65− visible: indicators.height > indicators.panelHeight
67+ visible: !indicators.fullyClosed
66 source: "graphics/rectangular_dropshadow.sci"
68 source: "graphics/rectangular_dropshadow.sci"
67 }
69 }
68
70
69− VerticalThinDivider {
70− id: indicatorDividor
71− anchors {
72− top: indicators.top
73− bottom: indicators.bottom
74− right: indicators.left
75−
76− topMargin: indicatorArea.anchors.topMargin + indicators.panelHeight
77− }
78−
79− width: units.dp(2)
80− source: "graphics/VerticalDivider.png"
81− }
82−
83 Rectangle {
71 Rectangle {
84 id: indicatorAreaBackground
72 id: indicatorAreaBackground
85 color: callHint.visible ? "green" : "black"
73 color: callHint.visible ? "green" : "black"
@@ -88,13 +76,13 @@ Item {
88 left: parent.left
76 left: parent.left
89 right: parent.right
77 right: parent.right
90 }
78 }
91− height: indicators.panelHeight
79+ height: indicators.minimizedPanelHeight
92
80
93 Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration } }
81 Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration } }
94 }
82 }
95
83
96 PanelSeparatorLine {
84 PanelSeparatorLine {
97− id: nonIndicatorAreaSeparatorLine
85+ id: orangeLine
98 anchors {
86 anchors {
99 top: indicatorAreaBackground.bottom
87 top: indicatorAreaBackground.bottom
100 left: parent.left
88 left: parent.left
@@ -103,18 +91,29 @@ Item {
103 saturation: 1 - indicators.unitProgress
91 saturation: 1 - indicators.unitProgress
104 }
92 }
105
93
94+ Image {
95+ anchors {
96+ top: indicators.top
97+ bottom: indicators.bottom
98+ right: indicators.left
99+ topMargin: indicatorArea.anchors.topMargin + indicators.minimizedPanelHeight
100+ }
101+ width: units.dp(2)
102+ source: "graphics/VerticalDivider.png"
103+ }
104+
106 MouseArea {
105 MouseArea {
107 anchors {
106 anchors {
108 top: parent.top
107 top: parent.top
109 left: parent.left
108 left: parent.left
110 right: indicators.left
109 right: indicators.left
111 }
110 }
112− height: indicators.panelHeight
111+ height: indicators.minimizedPanelHeight
113 enabled: callHint.visible
112 enabled: callHint.visible
114 onClicked: callHint.showLiveCall()
113 onClicked: callHint.showLiveCall()
115 }
114 }
116
115
117− Indicators {
116+ IndicatorsMenu {
118 id: __indicators
117 id: __indicators
119 objectName: "indicators"
118 objectName: "indicators"
120
119
@@ -123,51 +122,55 @@ Item {
123 right: parent.right
122 right: parent.right
124 }
123 }
125
124
126− width: root.width
127 shown: false
125 shown: false
128− panelHeight: units.gu(3)
126+ width: root.width
127+ minimizedPanelHeight: units.gu(3)
128+ expandedPanelHeight: units.gu(7)
129− openedHeight: root.height
129+ openedHeight: root.height - indicatorOrangeLine.height
130+
130 overFlowWidth: {
131 overFlowWidth: {
131 if (callHint.visible) {
132 if (callHint.visible) {
132 return Math.max(root.width - (callHint.width + units.gu(2)), 0)
133 return Math.max(root.width - (callHint.width + units.gu(2)), 0)
133 }
134 }
134 return root.width
135 return root.width
135 }
136 }
136−
137 enableHint: !callHint.active && !fullscreenMode
137 enableHint: !callHint.active && !fullscreenMode
138− showHintBottomMargin: fullscreenMode ? -panelHeight : 0
138+ panelColor: indicatorAreaBackground.color
139
139
140 onShowTapped: {
140 onShowTapped: {
141 if (callHint.active) {
141 if (callHint.active) {
142 callHint.showLiveCall();
142 callHint.showLiveCall();
143 }
143 }
144 }
144 }
145− }
146
145
147− ActiveCallHint {
146+ hideDragHandle {
148− id: __callHint
147+ anchors.bottomMargin: -indicatorOrangeLine.height
149− anchors {
150− top: parent.top
151− left: parent.left
152 }
148 }
153− height: indicators.panelHeight
154− visible: active && indicators.state == "initial"
155 }
149 }
156
150
157 PanelSeparatorLine {
151 PanelSeparatorLine {
158− id: indicatorsSeparatorLine
152+ id: indicatorOrangeLine
159− visible: true
160 anchors {
153 anchors {
161 top: indicators.bottom
154 top: indicators.bottom
162− left: indicatorDividor.left
155+ left: indicators.left
163 right: indicators.right
156 right: indicators.right
164 }
157 }
165 }
158 }
159+
160+ ActiveCallHint {
161+ id: __callHint
162+ anchors {
163+ top: parent.top
164+ left: parent.left
165+ }
166+ height: indicators.minimizedPanelHeight
167+ visible: active && indicators.state == "initial"
168+ }
166 }
169 }
167
170
168 QtObject {
171 QtObject {
169 id: d
172 id: d
170− readonly property real indicatorHeight: indicators.panelHeight + indicatorsSeparatorLine.height
173+ readonly property real indicatorHeight: indicators.minimizedPanelHeight + indicatorOrangeLine.height
171 }
174 }
172
175
173 states: [
176 states: [
@@ -186,6 +189,10 @@ Item {
186 target: indicatorArea;
189 target: indicatorArea;
187 anchors.topMargin: indicators.state === "initial" ? -d.indicatorHeight : 0
190 anchors.topMargin: indicators.state === "initial" ? -d.indicatorHeight : 0
188 }
191 }
192+ PropertyChanges {
193+ target: indicators.showDragHandle;
194+ anchors.bottomMargin: -units.gu(1)
195+ }
189 }
196 }
190 ]
197 ]
191}
198}
qml/Panel/PanelVelocityCalculator.qmlnew file+54
@@ -0,0 +1,54 @@
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+import QtQuick 2.2
18+import Ubuntu.Gestures 0.1
19+
20+Item {
21+ id: root
22+ property real trackedValue: 0
23+ property bool velocityAboveThreshold: false
24+ property real velocityThreshold: 0.4
25+
26+ function reset() {
27+ velocityTimer.stop();
28+ velocityAboveThreshold = false;
29+ calc.reset();
30+ }
31+
32+ function update() {
33+ calc.trackedPosition = trackedValue;
34+ velocityAboveThreshold = Math.abs(calc.calculate()) > velocityThreshold;
35+
36+ if (velocityAboveThreshold) { // only start timer if we're above the threshold.
37+ velocityTimer.start();
38+ } else {
39+ velocityTimer.stop();
40+ }
41+ }
42+
43+ onTrackedValueChanged: update();
44+
45+ AxisVelocityCalculator {
46+ id: calc
47+ }
48+
49+ Timer {
50+ id: velocityTimer
51+ interval: 50
52+ onTriggered: update();
53+ }
54+}
qml/Shell.qml+16−5
@@ -36,6 +36,7 @@ import "Panel"
36import "Components"
36import "Components"
37import "Notifications"
37import "Notifications"
38import "Stages"
38import "Stages"
39+import "Panel/Indicators"
39import Unity.Notifications 1.0 as NotificationBackend
40import Unity.Notifications 1.0 as NotificationBackend
40import Unity.Session 0.1
41import Unity.Session 0.1
41import Unity.DashCommunicator 0.1
42import Unity.DashCommunicator 0.1
@@ -243,7 +244,7 @@ Item {
243 target: applicationsDisplayLoader.item
244 target: applicationsDisplayLoader.item
244 property: "maximizedAppTopMargin"
245 property: "maximizedAppTopMargin"
245 // Not just using panel.panelHeight as that changes depending on the focused app.
246 // Not just using panel.panelHeight as that changes depending on the focused app.
246− value: panel.indicators.panelHeight
247+ value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
247 }
248 }
248 Binding {
249 Binding {
249 target: applicationsDisplayLoader.item
250 target: applicationsDisplayLoader.item
@@ -646,7 +647,17 @@ Item {
646 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
647 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
647 contentEnabled: edgeDemo.panelContentEnabled
648 contentEnabled: edgeDemo.panelContentEnabled
648 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
649 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
649− panelHeight: units.gu(3)
650+
651+ minimizedPanelHeight: units.gu(3)
652+ expandedPanelHeight: units.gu(7)
653+
654+ indicatorsModel: visibleIndicators.model
655+ }
656+
657+ VisibleIndicators {
658+ id: visibleIndicators
659+ // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
660+ Component.onCompleted: initialise(indicatorProfile)
650 }
661 }
651
662
652 property bool topmostApplicationIsFullscreen:
663 property bool topmostApplicationIsFullscreen:
@@ -709,9 +720,9 @@ Item {
709 model: NotificationBackend.Model
720 model: NotificationBackend.Model
710 margin: units.gu(1)
721 margin: units.gu(1)
711
722
712− y: panel.panelHeight
723+ y: topmostIsFullscreen ? 0 : panel.panelHeight
713 width: parent.width
724 width: parent.width
714− height: parent.height - panel.panelHeight
725+ height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
715
726
716 states: [
727 states: [
717 State {
728 State {
@@ -763,7 +774,7 @@ Item {
763 paused: Powerd.status === Powerd.Off // Saves power
774 paused: Powerd.status === Powerd.Off // Saves power
764 greeter: greeter
775 greeter: greeter
765 launcher: launcher
776 launcher: launcher
766− indicators: panel.indicators
777+ panel: panel
767 stages: stages
778 stages: stages
768 }
779 }
769
780
qml/Stages/PhoneStage.qml+1−2
@@ -21,7 +21,6 @@ import Unity.Application 0.1
21import Unity.Session 0.1
21import Unity.Session 0.1
22import Utils 0.1
22import Utils 0.1
23import "../Components"
23import "../Components"
24−import "../Components/Flickables" as Flickables
25
24
26Rectangle {
25Rectangle {
27 id: root
26 id: root
@@ -121,7 +120,7 @@ Rectangle {
121
120
122 }
121 }
123
122
124− Flickables.Flickable {
123+ Flickable {
125 id: spreadView
124 id: spreadView
126 objectName: "spreadView"
125 objectName: "spreadView"
127 anchors.fill: parent
126 anchors.fill: parent
qml/Stages/TabletStage.qml+1−2
@@ -20,7 +20,6 @@ import Ubuntu.Gestures 0.1
20import Unity.Application 0.1
20import Unity.Application 0.1
21import Utils 0.1
21import Utils 0.1
22import "../Components"
22import "../Components"
23−import "../Components/Flickables" as Flickables
24
23
25Rectangle {
24Rectangle {
26 id: root
25 id: root
@@ -154,7 +153,7 @@ Rectangle {
154 }
153 }
155 }
154 }
156
155
157− Flickables.Flickable {
156+ Flickable {
158 id: spreadView
157 id: spreadView
159 anchors.fill: parent
158 anchors.fill: parent
160 interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
159 interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
tests/autopilot/unity8/indicators/tests/test_indicators.py+1−1
@@ -61,7 +61,7 @@ class IndicatorExistsTestCase(IndicatorTestCase):
61 self.skipTest('Nexus 10 does not have bluetooth at the moment.')
61 self.skipTest('Nexus 10 does not have bluetooth at the moment.')
62
62
63 def test_indicator_exists(self):
63 def test_indicator_exists(self):
64− self.main_window._get_indicator_widget(
64+ self.main_window._get_indicator_panel_item(
65 self.indicator_name
65 self.indicator_name
66 )
66 )
67
67
tests/autopilot/unity8/shell/emulators/main_window.py+6−6
@@ -75,15 +75,15 @@ class QQuickView(emulators.UnityEmulatorBase):
75 def get_pinentryField(self):
75 def get_pinentryField(self):
76 return self.select_single(objectName="pinentryField")
76 return self.select_single(objectName="pinentryField")
77
77
78− def _get_indicator_widget(self, indicator_name):
78+ def _get_indicator_panel_item(self, indicator_name):
79 return self.select_single(
79 return self.select_single(
80− 'DefaultIndicatorWidget',
80+ 'IndicatorItem',
81− objectName=indicator_name+'-widget'
81+ objectName=indicator_name+'-panelItem'
82 )
82 )
83
83
84 def _get_indicator_page(self, indicator_name):
84 def _get_indicator_page(self, indicator_name):
85 return self.select_single(
85 return self.select_single(
86− 'DefaultIndicatorPage',
86+ 'IndicatorPage',
87 objectName=indicator_name+'-page'
87 objectName=indicator_name+'-page'
88 )
88 )
89
89
@@ -93,12 +93,12 @@ class QQuickView(emulators.UnityEmulatorBase):
93
93
94 :returns: The indicator page.
94 :returns: The indicator page.
95 """
95 """
96− widget = self._get_indicator_widget(indicator_name)
96+ widget = self._get_indicator_panel_item(indicator_name)
97 start_x, start_y = input.get_center_point(widget)
97 start_x, start_y = input.get_center_point(widget)
98 end_x = start_x
98 end_x = start_x
99 end_y = self.height
99 end_y = self.height
100 self.pointing_device.drag(start_x, start_y, end_x, end_y)
100 self.pointing_device.drag(start_x, start_y, end_x, end_y)
101− self.wait_select_single('Indicators', fullyOpened=True)
101+ self.wait_select_single('IndicatorsMenu', fullyOpened=True)
102 return self._get_indicator_page(indicator_name)
102 return self._get_indicator_page(indicator_name)
103
103
104 @autopilot_logging.log_action(logger.info)
104 @autopilot_logging.log_action(logger.info)
tests/mocks/AccountsService/AccountsService.qmltypes+2
@@ -23,6 +23,8 @@ Module {
23 }
23 }
24 Property { name: "user"; type: "string" }
24 Property { name: "user"; type: "string" }
25 Property { name: "demoEdges"; type: "bool" }
25 Property { name: "demoEdges"; type: "bool" }
26+ Property { name: "enableLauncherWhileLocked"; type: "bool" }
27+ Property { name: "enableIndicatorsWhileLocked"; type: "bool" }
26 Property { name: "backgroundFile"; type: "string" }
28 Property { name: "backgroundFile"; type: "string" }
27 Property { name: "statsWelcomeScreen"; type: "bool" }
29 Property { name: "statsWelcomeScreen"; type: "bool" }
28 Property { name: "passwordDisplayHint"; type: "PasswordDisplayHint"; isReadonly: true }
30 Property { name: "passwordDisplayHint"; type: "PasswordDisplayHint"; isReadonly: true }
tests/mocks/LightDM/LightDM.qmltypes+1
@@ -37,6 +37,7 @@ Module {
37 Signal { name: "isActiveChanged" }
37 Signal { name: "isActiveChanged" }
38 Signal { name: "isAuthenticatedChanged" }
38 Signal { name: "isAuthenticatedChanged" }
39 Signal { name: "showGreeter" }
39 Signal { name: "showGreeter" }
40+ Signal { name: "hideGreeter" }
40 Signal {
41 Signal {
41 name: "requestAuthenticationUser"
42 name: "requestAuthenticationUser"
42 Parameter { name: "user"; type: "string" }
43 Parameter { name: "user"; type: "string" }
tests/mocks/QMenuModel/QMenuModel.qmltypes+1−1
@@ -18,12 +18,12 @@ Module {
18 prototype: "QAbstractListModel"
18 prototype: "QAbstractListModel"
19 exports: ["QMenuModel/UnityMenuModel 0.1"]
19 exports: ["QMenuModel/UnityMenuModel 0.1"]
20 exportMetaObjectRevisions: [0]
20 exportMetaObjectRevisions: [0]
21− Property { name: "modelData"; type: "QVariant" }
22 Property { name: "busName"; type: "QByteArray" }
21 Property { name: "busName"; type: "QByteArray" }
23 Property { name: "actions"; type: "QVariantMap" }
22 Property { name: "actions"; type: "QVariantMap" }
24 Property { name: "menuObjectPath"; type: "QByteArray" }
23 Property { name: "menuObjectPath"; type: "QByteArray" }
25 Property { name: "actionStateParser"; type: "ActionStateParser"; isPointer: true }
24 Property { name: "actionStateParser"; type: "ActionStateParser"; isPointer: true }
26 Property { name: "nameOwner"; type: "string"; isReadonly: true }
25 Property { name: "nameOwner"; type: "string"; isReadonly: true }
26+ Property { name: "modelData"; type: "QVariant" }
27 Method {
27 Method {
28 name: "insertRow"
28 name: "insertRow"
29 Parameter { name: "row"; type: "int" }
29 Parameter { name: "row"; type: "int" }
tests/mocks/QMenuModel/unitymenumodel.cpp+1−2
@@ -50,13 +50,12 @@ void UnityMenuModel::setModelData(const QVariant& data)
50{
50{
51 beginResetModel();
51 beginResetModel();
52
52
53− m_modelData.clear();
54 m_modelData = data.toList();
53 m_modelData = data.toList();
54+ Q_EMIT modelDataChanged();
55
55
56 endResetModel();
56 endResetModel();
57}
57}
58
58
59−
60void UnityMenuModel::insertRow(int row, const QVariant& data)
59void UnityMenuModel::insertRow(int row, const QVariant& data)
61{
60{
62 row = qMin(row, rowCount());
61 row = qMin(row, rowCount());
tests/mocks/QMenuModel/unitymenumodel.h+6−2
@@ -27,13 +27,15 @@ class ActionStateParser;
27class Q_DECL_EXPORT UnityMenuModel : public QAbstractListModel
27class Q_DECL_EXPORT UnityMenuModel : public QAbstractListModel
28{
28{
29 Q_OBJECT
29 Q_OBJECT
30− Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
31 Q_PROPERTY(QByteArray busName READ busName WRITE setBusName NOTIFY busNameChanged)
30 Q_PROPERTY(QByteArray busName READ busName WRITE setBusName NOTIFY busNameChanged)
32 Q_PROPERTY(QVariantMap actions READ actions WRITE setActions NOTIFY actionsChanged)
31 Q_PROPERTY(QVariantMap actions READ actions WRITE setActions NOTIFY actionsChanged)
33 Q_PROPERTY(QByteArray menuObjectPath READ menuObjectPath WRITE setMenuObjectPath NOTIFY menuObjectPathChanged)
32 Q_PROPERTY(QByteArray menuObjectPath READ menuObjectPath WRITE setMenuObjectPath NOTIFY menuObjectPathChanged)
34 Q_PROPERTY(ActionStateParser* actionStateParser READ actionStateParser WRITE setActionStateParser NOTIFY actionStateParserChanged)
33 Q_PROPERTY(ActionStateParser* actionStateParser READ actionStateParser WRITE setActionStateParser NOTIFY actionStateParserChanged)
35 Q_PROPERTY(QString nameOwner READ nameOwner NOTIFY nameOwnerChanged)
34 Q_PROPERTY(QString nameOwner READ nameOwner NOTIFY nameOwnerChanged)
36
35
36+ // internal mock properties
37+ Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
38+
37public:
39public:
38 UnityMenuModel(QObject *parent = nullptr);
40 UnityMenuModel(QObject *parent = nullptr);
39 virtual ~UnityMenuModel();
41 virtual ~UnityMenuModel();
@@ -81,9 +83,11 @@ Q_SIGNALS:
81 void actionsChanged();
83 void actionsChanged();
82 void menuObjectPathChanged();
84 void menuObjectPathChanged();
83 void actionStateParserChanged();
85 void actionStateParserChanged();
84− void modelDataChanged();
85 void nameOwnerChanged();
86 void nameOwnerChanged();
86
87
88+ // Internal mock usage
89+ void modelDataChanged();
90+
87private:
91private:
88 QVariantMap rowData(int row) const;
92 QVariantMap rowData(int row) const;
89 QVariant subMenuData(int row) const;
93 QVariant subMenuData(int row) const;
tests/mocks/Ubuntu/Payments/Payments.qmltypes+2
@@ -37,6 +37,8 @@ Module {
37 Parameter { name: "error"; type: "string" }
37 Parameter { name: "error"; type: "string" }
38 }
38 }
39 Signal { name: "purchaseCompleted" }
39 Signal { name: "purchaseCompleted" }
40+ Signal { name: "purchaseCancelled" }
40 Method { name: "start" }
41 Method { name: "start" }
42+ Method { name: "process" }
41 }
43 }
42}
44}
tests/mocks/Ubuntu/Telephony/Telephony.qmltypes+2−1
@@ -22,7 +22,8 @@ Module {
22 exportMetaObjectRevisions: [0]
22 exportMetaObjectRevisions: [0]
23 Property { name: "phoneNumber"; type: "string" }
23 Property { name: "phoneNumber"; type: "string" }
24 Property { name: "isConference"; type: "bool" }
24 Property { name: "isConference"; type: "bool" }
25− Property { name: "elapsedTime"; type: "int"; isReadonly: true }
25+ Property { name: "elapsedTime"; type: "int" }
26+ Property { name: "elapsedTimerRunning"; type: "bool" }
26 }
27 }
27 Component {
28 Component {
28 name: "MockCallManager"
29 name: "MockCallManager"
tests/mocks/Unity/Application/Application.qmltypes+13
@@ -238,6 +238,7 @@ Module {
238 Property { name: "state"; type: "State"; isReadonly: true }
238 Property { name: "state"; type: "State"; isReadonly: true }
239 Property { name: "name"; type: "string"; isReadonly: true }
239 Property { name: "name"; type: "string"; isReadonly: true }
240 Property { name: "live"; type: "bool"; isReadonly: true }
240 Property { name: "live"; type: "bool"; isReadonly: true }
241+ Property { name: "orientation"; type: "Qt::ScreenOrientation" }
241 Signal {
242 Signal {
242 name: "typeChanged"
243 name: "typeChanged"
243 Parameter { type: "Type" }
244 Parameter { type: "Type" }
@@ -268,9 +269,14 @@ Module {
268 exportMetaObjectRevisions: [0]
269 exportMetaObjectRevisions: [0]
269 Property { name: "name"; type: "string"; isReadonly: true }
270 Property { name: "name"; type: "string"; isReadonly: true }
270 Property { name: "surface"; type: "MirSurfaceItem"; isReadonly: true; isPointer: true }
271 Property { name: "surface"; type: "MirSurfaceItem"; isReadonly: true; isPointer: true }
272+ Property { name: "application"; type: "ApplicationInfo"; isReadonly: true; isPointer: true }
271 Property { name: "parentSession"; type: "Session"; isReadonly: true; isPointer: true }
273 Property { name: "parentSession"; type: "Session"; isReadonly: true; isPointer: true }
272 Property { name: "childSessions"; type: "SessionModel"; isReadonly: true; isPointer: true }
274 Property { name: "childSessions"; type: "SessionModel"; isReadonly: true; isPointer: true }
273 Property { name: "live"; type: "bool"; isReadonly: true }
275 Property { name: "live"; type: "bool"; isReadonly: true }
276+ Signal {
277+ name: "applicationChanged"
278+ Parameter { type: "ApplicationInfo"; isPointer: true }
279+ }
274 Signal {
280 Signal {
275 name: "surfaceChanged"
281 name: "surfaceChanged"
276 Parameter { type: "MirSurfaceItem"; isPointer: true }
282 Parameter { type: "MirSurfaceItem"; isPointer: true }
@@ -368,6 +374,12 @@ Module {
368 Property { name: "stage"; type: "Stage"; isReadonly: true }
374 Property { name: "stage"; type: "Stage"; isReadonly: true }
369 Property { name: "state"; type: "State"; isReadonly: true }
375 Property { name: "state"; type: "State"; isReadonly: true }
370 Property { name: "focused"; type: "bool"; isReadonly: true }
376 Property { name: "focused"; type: "bool"; isReadonly: true }
377+ Property { name: "splashTitle"; type: "string"; isReadonly: true }
378+ Property { name: "splashImage"; type: "QUrl"; isReadonly: true }
379+ Property { name: "splashShowHeader"; type: "bool"; isReadonly: true }
380+ Property { name: "splashColor"; type: "QColor"; isReadonly: true }
381+ Property { name: "splashColorHeader"; type: "QColor"; isReadonly: true }
382+ Property { name: "splashColorFooter"; type: "QColor"; isReadonly: true }
371 Signal {
383 Signal {
372 name: "nameChanged"
384 name: "nameChanged"
373 Parameter { name: "name"; type: "string" }
385 Parameter { name: "name"; type: "string" }
@@ -414,6 +426,7 @@ Module {
414 Property { name: "count"; type: "int"; isReadonly: true }
426 Property { name: "count"; type: "int"; isReadonly: true }
415 Property { name: "focusedApplicationId"; type: "string"; isReadonly: true }
427 Property { name: "focusedApplicationId"; type: "string"; isReadonly: true }
416 Property { name: "suspended"; type: "bool" }
428 Property { name: "suspended"; type: "bool" }
429+ Property { name: "forceDashActive"; type: "bool" }
417 Signal {
430 Signal {
418 name: "focusRequested"
431 name: "focusRequested"
419 Parameter { name: "appId"; type: "string" }
432 Parameter { name: "appId"; type: "string" }
tests/mocks/Unity/DashCommunicator/DashCommunicator.qmltypes+2−2
@@ -34,13 +34,13 @@ Module {
34 name: "setCurrentScopeRequested"
34 name: "setCurrentScopeRequested"
35 Parameter { name: "scopeId"; type: "string" }
35 Parameter { name: "scopeId"; type: "string" }
36 Parameter { name: "animate"; type: "bool" }
36 Parameter { name: "animate"; type: "bool" }
37− Parameter { name: "reset"; type: "bool" }
37+ Parameter { name: "isSwipe"; type: "bool" }
38 }
38 }
39 Method {
39 Method {
40 name: "mockSetCurrentScope"
40 name: "mockSetCurrentScope"
41 Parameter { name: "scopeId"; type: "string" }
41 Parameter { name: "scopeId"; type: "string" }
42 Parameter { name: "animate"; type: "bool" }
42 Parameter { name: "animate"; type: "bool" }
43− Parameter { name: "reset"; type: "bool" }
43+ Parameter { name: "isSwipe"; type: "bool" }
44 }
44 }
45 }
45 }
46}
46}
tests/mocks/Unity/Indicators/CMakeLists.txt+3−10
@@ -12,24 +12,17 @@ include_directories(
12
12
13set(IndicatorsFakeQml_SOURCES
13set(IndicatorsFakeQml_SOURCES
14 fakeplugin.cpp
14 fakeplugin.cpp
15+ fakeindicatorsmodel.cpp
16+ fakeunitymenumodelcache.cpp
15 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/indicators.h
17 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/indicators.h
16 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/menucontentactivator.cpp
18 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/menucontentactivator.cpp
19+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/sharedunitymenumodel.cpp
17 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/unitymenumodelcache.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/unitymenumodelcache.cpp
18 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/unitymenumodelstack.cpp
21 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/unitymenumodelstack.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/visibleindicatorsmodel.cpp
22 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/visibleindicatorsmodel.cpp
20− fakeindicatorsmodel.cpp
21−)
22−
23−set(IndicatorsFakeQml_RESOURCES
24− indicators_fake.qrc
25−)
26−
27−qt5_add_resources(IndicatorsFakeQml_RESOURCES_RCC
28− ${IndicatorsFakeQml_RESOURCES}
29)
23)
30
24
31add_library(IndicatorsFakeQml SHARED
25add_library(IndicatorsFakeQml SHARED
32− ${IndicatorsFakeQml_RESOURCES_RCC}
33 ${IndicatorsFakeQml_SOURCES}
26 ${IndicatorsFakeQml_SOURCES}
34)
27)
35add_definitions(-DUNITYINDICATORS_LIBRARY)
28add_definitions(-DUNITYINDICATORS_LIBRARY)
tests/mocks/Unity/Indicators/FakeMenuPage.qmldeleted−27
@@ -1,27 +0,0 @@
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 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−
19−Flickable {
20− objectName: "fakeMenuPlugin"
21− // Make it compatible with the PluginItem interface
22− function reset() {
23− if (shell != undefined && shell.indicator_status != undefined) {
24− shell.indicator_status[objectName].reset++;
25− }
26− }
27−}
tests/mocks/Unity/Indicators/Indicators.qmltypes+43−15
@@ -29,6 +29,7 @@ Module {
29 exports: ["Unity.Indicators/FakeIndicatorsModel 0.1"]
29 exports: ["Unity.Indicators/FakeIndicatorsModel 0.1"]
30 exportMetaObjectRevisions: [0]
30 exportMetaObjectRevisions: [0]
31 Property { name: "count"; type: "int"; isReadonly: true }
31 Property { name: "count"; type: "int"; isReadonly: true }
32+ Property { name: "modelData"; type: "QVariant" }
32 Method {
33 Method {
33 name: "load"
34 name: "load"
34 Parameter { name: "profile"; type: "string" }
35 Parameter { name: "profile"; type: "string" }
@@ -38,6 +39,15 @@ Module {
38 name: "append"
39 name: "append"
39 Parameter { name: "row"; type: "QVariantMap" }
40 Parameter { name: "row"; type: "QVariantMap" }
40 }
41 }
42+ Method {
43+ name: "insert"
44+ Parameter { name: "row"; type: "int" }
45+ Parameter { name: "data"; type: "QVariantMap" }
46+ }
47+ Method {
48+ name: "remove"
49+ Parameter { name: "row"; type: "int" }
50+ }
41 Method {
51 Method {
42 name: "data"
52 name: "data"
43 type: "QVariant"
53 type: "QVariant"
@@ -45,6 +55,27 @@ Module {
45 Parameter { name: "role"; type: "int" }
55 Parameter { name: "role"; type: "int" }
46 }
56 }
47 }
57 }
58+ Component {
59+ name: "FakeUnityMenuModelCache"
60+ prototype: "UnityMenuModelCache"
61+ exports: ["Unity.Indicators/UnityMenuModelCache 0.1"]
62+ isCreatable: false
63+ isSingleton: true
64+ exportMetaObjectRevisions: [0]
65+ Method {
66+ name: "setCachedModelData"
67+ Parameter { name: "bus"; type: "QByteArray" }
68+ Parameter { name: "path"; type: "QByteArray" }
69+ Parameter { name: "actions"; type: "QVariantMap" }
70+ Parameter { name: "data"; type: "QVariant" }
71+ }
72+ Method {
73+ name: "setCachedModelData"
74+ Parameter { name: "bus"; type: "QByteArray" }
75+ Parameter { name: "path"; type: "QByteArray" }
76+ Parameter { name: "actions"; type: "QVariantMap" }
77+ }
78+ }
48 Component {
79 Component {
49 name: "FlatMenuProxyModelRole"
80 name: "FlatMenuProxyModelRole"
50 prototype: "QObject"
81 prototype: "QObject"
@@ -74,10 +105,8 @@ Module {
74 values: {
105 values: {
75 "Identifier": 0,
106 "Identifier": 0,
76 "Position": 1,
107 "Position": 1,
77− "WidgetSource": 2,
108+ "IndicatorProperties": 2,
78− "PageSource": 3,
109+ "IsVisible": 3
79− "IndicatorProperties": 4,
80− "IsVisible": 5
81 }
110 }
82 }
111 }
83 }
112 }
@@ -160,19 +189,18 @@ Module {
160 }
189 }
161 Component { name: "QIdentityProxyModel"; prototype: "QAbstractProxyModel" }
190 Component { name: "QIdentityProxyModel"; prototype: "QAbstractProxyModel" }
162 Component {
191 Component {
163− name: "UnityMenuModelCache"
192+ name: "SharedUnityMenuModel"
164 prototype: "QObject"
193 prototype: "QObject"
165− exports: ["Unity.Indicators/UnityMenuModelCache 0.1"]
194+ exports: ["Unity.Indicators/SharedUnityMenuModel 0.1"]
166− isCreatable: false
167− isSingleton: true
168 exportMetaObjectRevisions: [0]
195 exportMetaObjectRevisions: [0]
169− Method {
196+ Property { name: "busName"; type: "QByteArray" }
170− name: "model"
197+ Property { name: "menuObjectPath"; type: "QByteArray" }
198+ Property { name: "actions"; type: "QVariantMap" }
171− type: "UnityMenuModel*"
199+ Property { name: "model"; type: "UnityMenuModel"; isReadonly: true; isPointer: true }
172− Parameter { name: "bus"; type: "QByteArray" }
200+ }
173− Parameter { name: "path"; type: "QByteArray" }
201+ Component {
174− Parameter { name: "actions"; type: "QVariantMap" }
202+ name: "UnityMenuModelCache"
175− }
203+ prototype: "QObject"
176 Method {
204 Method {
177 name: "contains"
205 name: "contains"
178 type: "bool"
206 type: "bool"
tests/mocks/Unity/Indicators/IndicatorsModel.qml+152−34
@@ -18,43 +18,161 @@ import QtQuick 2.0
18import Unity.Indicators 0.1 as Indicators
18import Unity.Indicators 0.1 as Indicators
19
19
20Indicators.FakeIndicatorsModel {
20Indicators.FakeIndicatorsModel {
21+ id: root
22+
23+ property var originalModelData: [
24+ {
25+ "identifier": "fake-indicator-bluetooth",
26+ "indicatorProperties": {
27+ "enabled": true,
28+ "busName": "com.canonical.indicators.fake1",
29+ "menuObjectPath": "/com/canonical/indicators/fake1",
30+ "actionsObjectPath": "/com/canonical/indicators/fake1"
31+ }
32+ },
33+ {
34+ "identifier": "fake-indicator-network",
35+ "indicatorProperties": {
36+ "enabled": true,
37+ "busName": "com.canonical.indicators.fake2",
38+ "menuObjectPath": "/com/canonical/indicators/fake2",
39+ "actionsObjectPath": "/com/canonical/indicators/fake2"
40+ }
41+ },
42+ {
43+ "identifier": "fake-indicator-messages",
44+ "indicatorProperties": {
45+ "enabled": true,
46+ "busName": "com.canonical.indicators.fake3",
47+ "menuObjectPath": "/com/canonical/indicators/fake3",
48+ "actionsObjectPath": "/com/canonical/indicators/fake3"
49+ }
50+ },
51+ {
52+ "identifier": "fake-indicator-sound",
53+ "indicatorProperties": {
54+ "enabled": true,
55+ "busName": "com.canonical.indicators.fake4",
56+ "menuObjectPath": "/com/canonical/indicators/fake4",
57+ "actionsObjectPath": "/com/canonical/indicators/fake4"
58+ }
59+ },
60+ {
61+ "identifier": "fake-indicator-power",
62+ "indicatorProperties": {
63+ "enabled": true,
64+ "busName": "com.canonical.indicators.fake5",
65+ "menuObjectPath": "/com/canonical/indicators/fake5",
66+ "actionsObjectPath": "/com/canonical/indicators/fake5"
67+ }
68+ },
69+ {
70+ "identifier": "fake-indicator-datetime",
71+ "indicatorProperties": {
72+ "enabled": true,
73+ "busName": "com.canonical.indicators.fake6",
74+ "menuObjectPath": "/com/canonical/indicators/fake6",
75+ "actionsObjectPath": "/com/canonical/indicators/fake6"
76+ }
77+ }
78+ ]
21
79
22 function load(profile) {
80 function load(profile) {
23 unload();
81 unload();
82+ root.modelData = originalModelData;
83+
84+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake1",
85+ getUnityMenuModelData("fake-indicator-bluetooth",
86+ "Bluetooth (F)",
87+ "",
88+ [ "image://theme/bluetooth-active" ]));
89+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake2",
90+ getUnityMenuModelData("fake-indicator-network",
91+ "Network (F)",
92+ "",
93+ [ "image://theme/simcard-error", "image://theme/wifi-high" ]));
94+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake3",
95+ getUnityMenuModelData("fake-indicator-messages",
96+ "Messages (F)",
97+ "",
98+ [ "image://theme/messages-new" ]));
99+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake4",
100+ getUnityMenuModelData("fake-indicator-sound",
101+ "Sound (F)",
102+ "",
103+ [ "image://theme/audio-volume-high" ]));
104+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake5",
105+ getUnityMenuModelData("fake-indicator-power",
106+ "Battery (F)",
107+ "",
108+ [ "image://theme/battery-020" ]));
109+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake6",
110+ getUnityMenuModelData("fake-indicator-datetime",
111+ "Upcoming Events (F)",
112+ "12:04",
113+ []));
114+ }
115+
116+ function getUnityMenuModelData(identifier, title, label, icons) {
117+ var root = [{
118+ "rowData": { // 1
119+ "label": "",
120+ "sensitive": true,
121+ "isSeparator": false,
122+ "icon": "",
123+ "type": "com.canonical.indicator.root",
124+ "ext": {},
125+ "action": "",
126+ "actionState": {
127+ "title": title,
128+ "label": label,
129+ "icons": icons,
130+ "visible": true
131+ },
132+ "isCheck": false,
133+ "isRadio": false,
134+ "isToggled": false
135+ },
136+ "submenu": []
137+ }];
138+
139+ var submenus = [];
140+ for (var i = 0; i < 8; i++) {
141+ var submenu = {
142+ "rowData": { // 1.1
143+ "label": identifier,
144+ "sensitive": true,
145+ "isSeparator": false,
146+ "icon": "",
147+ "type": undefined,
148+ "ext": {},
149+ "action": "",
150+ "actionState": {},
151+ "isCheck": false,
152+ "isRadio": false,
153+ "isToggled": false
154+ }};
155+ submenus.push(submenu);
156+ }
157+ root[0]["submenu"] = submenus;
158+
159+ return root;
160+ }
161+
162+ function setIndicatorVisible(identifier, visible) {
163+ for (var i = 0; i < originalModelData.length; i++) {
164+ if (originalModelData[i]["identifier"] === identifier) {
165+ var data = Indicators.UnityMenuModelCache.getCachedModelData(
166+ originalModelData[i]["indicatorProperties"]["menuObjectPath"]);
167+
168+ data[0]["rowData"]["actionState"]["visible"] = visible;
169+
170+ Indicators.UnityMenuModelCache.setCachedModelData(
171+ originalModelData[i]["indicatorProperties"]["menuObjectPath"],
172+ data);
173+ break;
174+ }
175+ }
24
176
25− append({ "identifier": "indicator-fake1",
26− "position": 0,
27− "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget1.qml",
28− "pageSource": "qrc:/tests/indciators/qml/fake_menu_page1.qml",
29− "indicatorProperties": { enabled: true }
30− });
31−
32− append({ "identifier": "indicator-fake2",
33− "position": 1,
34− "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget2.qml",
35− "pageSource": "qrc:/tests/indciators/qml/fake_menu_page2.qml",
36− "indicatorProperties": { enabled: true }
37− });
38−
39− append({ "identifier": "indicator-fake3",
40− "position": 2,
41− "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget3.qml",
42− "pageSource": "qrc:/tests/indciators/qml/fake_menu_page3.qml",
43− "indicatorProperties": { enabled: true }
44− });
45−
46− append({ "identifier": "indicator-fake4",
47− "position": 3,
48− "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget4.qml",
49− "pageSource": "qrc:/tests/indciators/qml/fake_menu_page4.qml",
50− "indicatorProperties": { enabled: true }
51− });
52−
53− append({ "identifier": "indicator-fake5",
54− "position": 4,
55− "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget5.qml",
56− "pageSource": "qrc:/tests/indciators/qml/fake_menu_page5.qml",
57− "indicatorProperties": { enabled: true }
58− });
59 }
177 }
60}
178}
tests/mocks/Unity/Indicators/RootActionState.qml+18−10
@@ -19,15 +19,23 @@
19
19
20import QtQuick 2.0
20import QtQuick 2.0
21
21
22−QtObject {
22+Item {
23− property var menu
23+ property var menu: null
24− property bool valid: false
24+ property bool valid: cachedState !== undefined
25− property string title
25+ property string title: cachedState && cachedState.hasOwnProperty("title") ? cachedState["title"] : ""
26− property string leftLabel
26+ property string leftLabel: cachedState && cachedState.hasOwnProperty("pre-label") ? cachedState["pre-label"] : ""
27− property string rightLabel
27+ property string rightLabel: cachedState && cachedState.hasOwnProperty("label") ? cachedState["label"] : ""
28− property var icons
28+ property var icons: cachedState && cachedState.hasOwnProperty("icons") ? cachedState["icons"] : []
29− property string accessibleName
29+ property string accessibleName: cachedState && cachedState.hasOwnProperty("accessible-desc") ? cachedState["accessible-desc"] : ""
30− property bool visible: true
30+ property bool indicatorVisible: cachedState && cachedState.hasOwnProperty("visible") ? cachedState["visible"] : true
31+
32+ property var cachedState: menu ? menu.get(0, "actionState") : undefined
33+ Connections {
34+ target: menu
35+ onModelDataChanged: {
36+ cachedState = menu.get(0, "actionState");
37+ }
38+ }
31
39
32 signal updated
40 signal updated
33
41
@@ -37,5 +45,5 @@ QtObject {
37 onRightLabelChanged: updated()
45 onRightLabelChanged: updated()
38 onIconsChanged: updated()
46 onIconsChanged: updated()
39 onAccessibleNameChanged: updated()
47 onAccessibleNameChanged: updated()
40− onVisibleChanged: updated()
48+ onIndicatorVisibleChanged: updated()
41}
49}
tests/mocks/Unity/Indicators/fakeindicatorsmodel.cpp+50−21
@@ -31,7 +31,6 @@ FakeIndicatorsModel::FakeIndicatorsModel(QObject *parent)
31/*! \internal */
31/*! \internal */
32FakeIndicatorsModel::~FakeIndicatorsModel()
32FakeIndicatorsModel::~FakeIndicatorsModel()
33{
33{
34− qDeleteAll(m_indicators);
35}
34}
36
35
37int FakeIndicatorsModel::count() const
36int FakeIndicatorsModel::count() const
@@ -47,31 +46,63 @@ void FakeIndicatorsModel::unload()
47{
46{
48 beginResetModel();
47 beginResetModel();
49
48
50− qDeleteAll(m_indicators);
49+ m_modelData.clear();
51− m_indicators.clear();
50+ Q_EMIT modelDataChanged();
52
51
53 endResetModel();
52 endResetModel();
54}
53}
55
54
56
55
57−void FakeIndicatorsModel::append(const QVariantMap& row)
56+void FakeIndicatorsModel::append(const QVariantMap& data)
58{
57{
59− Indicator* new_row = new QHash<int, QVariant>();
58+ QList<QVariant> allData = m_modelData.toList();
60− for (auto iter = row.begin(); iter != row.end(); ++iter )
59+ beginInsertRows(QModelIndex(), allData.count(), allData.count());
61− {
60+
62− int key = roleNames().key(iter.key().toUtf8(), -1);
61+ allData.append(data);
63− if (key != -1) {
62+ m_modelData = allData;
64− new_row->insert(key, iter.value());
63+ Q_EMIT modelDataChanged();
65− }
66− }
67
64
68− beginInsertRows(QModelIndex(), m_indicators.count(), m_indicators.count());
65+ endInsertRows();
66+}
69
67
70− m_indicators.append(new_row);
68+void FakeIndicatorsModel::insert(int row, const QVariantMap& data)
69+{
70+ QList<QVariant> allData = m_modelData.toList();
71+ row = qMax(0, qMin(row, allData.count()));
72+
73+ beginInsertRows(QModelIndex(), row, row);
74+
75+ allData.insert(row, data);
76+ m_modelData = allData;
77+ Q_EMIT modelDataChanged();
71
78
72 endInsertRows();
79 endInsertRows();
73}
80}
74
81
82+void FakeIndicatorsModel::remove(int row)
83+{
84+ QList<QVariant> allData = m_modelData.toList();
85+ row = qMax(0, qMin(row, allData.count()));
86+
87+ beginRemoveRows(QModelIndex(), row, row);
88+
89+ allData.removeAt(row);
90+ m_modelData = allData;
91+ Q_EMIT modelDataChanged();
92+
93+ endRemoveRows();
94+}
95+
96+void FakeIndicatorsModel::setModelData(const QVariant& modelData)
97+{
98+ beginResetModel();
99+
100+ m_modelData = modelData;
101+ Q_EMIT modelDataChanged();
102+
103+ endResetModel();
104+}
105+
75QHash<int, QByteArray> FakeIndicatorsModel::roleNames() const
106QHash<int, QByteArray> FakeIndicatorsModel::roleNames() const
76{
107{
77 static QHash<int, QByteArray> roles;
108 static QHash<int, QByteArray> roles;
@@ -79,8 +110,6 @@ QHash<int, QByteArray> FakeIndicatorsModel::roleNames() const
79 {
110 {
80 roles[IndicatorsModelRole::Identifier] = "identifier";
111 roles[IndicatorsModelRole::Identifier] = "identifier";
81 roles[IndicatorsModelRole::Position] = "position";
112 roles[IndicatorsModelRole::Position] = "position";
82− roles[IndicatorsModelRole::WidgetSource] = "widgetSource";
83− roles[IndicatorsModelRole::PageSource] = "pageSource";
84 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
113 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
85 }
114 }
86 return roles;
115 return roles;
@@ -91,18 +120,18 @@ int FakeIndicatorsModel::columnCount(const QModelIndex &) const
91 return 1;
120 return 1;
92}
121}
93
122
94−Q_INVOKABLE QVariant FakeIndicatorsModel::data(int row, int role) const
123+QVariant FakeIndicatorsModel::data(int row, int role) const
95{
124{
96 return data(index(row, 0), role);
125 return data(index(row, 0), role);
97}
126}
98
127
99QVariant FakeIndicatorsModel::data(const QModelIndex &index, int role) const
128QVariant FakeIndicatorsModel::data(const QModelIndex &index, int role) const
100{
129{
130+ QList<QVariant> dataList = m_modelData.toList();
101− if (!index.isValid() || index.row() >= m_indicators.size())
131+ if (!index.isValid() || index.row() >= dataList.size())
102 return QVariant();
132 return QVariant();
103
133
104− Indicator* indicator = m_indicators[index.row()];
134+ return dataList[index.row()].toMap()[roleNames()[role]];
105− return indicator->value(role, QVariant());
106}
135}
107
136
108QModelIndex FakeIndicatorsModel::parent(const QModelIndex&) const
137QModelIndex FakeIndicatorsModel::parent(const QModelIndex&) const
@@ -112,5 +141,5 @@ QModelIndex FakeIndicatorsModel::parent(const QModelIndex&) const
112
141
113int FakeIndicatorsModel::rowCount(const QModelIndex&) const
142int FakeIndicatorsModel::rowCount(const QModelIndex&) const
114{
143{
115− return m_indicators.count();
144+ return m_modelData.toList().count();
116}
145}
tests/mocks/Unity/Indicators/fakeindicatorsmodel.h+8−2
@@ -27,6 +27,7 @@ class FakeIndicatorsModel : public QAbstractListModel
27 Q_OBJECT
27 Q_OBJECT
28 Q_ENUMS(Roles)
28 Q_ENUMS(Roles)
29 Q_PROPERTY(int count READ count NOTIFY countChanged)
29 Q_PROPERTY(int count READ count NOTIFY countChanged)
30+ Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
30public:
31public:
31
32
32 FakeIndicatorsModel(QObject *parent=0);
33 FakeIndicatorsModel(QObject *parent=0);
@@ -36,9 +37,14 @@ public:
36 Q_INVOKABLE void unload();
37 Q_INVOKABLE void unload();
37
38
38 Q_INVOKABLE void append(const QVariantMap& row);
39 Q_INVOKABLE void append(const QVariantMap& row);
40+ Q_INVOKABLE void insert(int row, const QVariantMap& data);
41+ Q_INVOKABLE void remove(int row);
39
42
40 Q_INVOKABLE QVariant data(int row, int role) const;
43 Q_INVOKABLE QVariant data(int row, int role) const;
41
44
45+ void setModelData(const QVariant& data);
46+ QVariant modelData() const { return m_modelData; }
47+
42 /* QAbstractItemModel */
48 /* QAbstractItemModel */
43 QHash<int, QByteArray> roleNames() const;
49 QHash<int, QByteArray> roleNames() const;
44 int columnCount(const QModelIndex &parent = QModelIndex()) const;
50 int columnCount(const QModelIndex &parent = QModelIndex()) const;
@@ -48,12 +54,12 @@ public:
48
54
49Q_SIGNALS:
55Q_SIGNALS:
50 void countChanged();
56 void countChanged();
57+ void modelDataChanged();
51
58
52private:
59private:
53 int count() const;
60 int count() const;
54
61
55− typedef QHash<int, QVariant> Indicator;
62+ QVariant m_modelData;
56− QList<Indicator*> m_indicators;
57};
63};
58
64
59#endif // FAKE_INDICATORSMODEL_H
65#endif // FAKE_INDICATORSMODEL_H
tests/mocks/Unity/Indicators/fakeplugin.cpp+8−4
@@ -26,19 +26,22 @@
26#include "fakeindicatorsmodel.h"
26#include "fakeindicatorsmodel.h"
27#include "indicators.h"
27#include "indicators.h"
28#include "menucontentactivator.h"
28#include "menucontentactivator.h"
29−#include "unitymenumodelcache.h"
29+#include "sharedunitymenumodel.h"
30+#include "fakeunitymenumodelcache.h"
30#include "unitymenumodelstack.h"
31#include "unitymenumodelstack.h"
31#include "visibleindicatorsmodel.h"
32#include "visibleindicatorsmodel.h"
32
33
34+#include <unitymenumodel.h>
35+
33static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
36static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
34 Q_UNUSED(engine);
37 Q_UNUSED(engine);
35 Q_UNUSED(scriptEngine);
38 Q_UNUSED(scriptEngine);
36− return new UnityMenuModelCache;
39+ return FakeUnityMenuModelCache::singleton();
37}
40}
38
41
39void IndicatorsFakePlugin::registerTypes(const char * uri)
42void IndicatorsFakePlugin::registerTypes(const char * uri)
40{
43{
41− Q_INIT_RESOURCE(indicators_fake);
44+ qRegisterMetaType<UnityMenuModel*>("UnityMenuModel*");
42
45
43 // internal
46 // internal
44 qmlRegisterType<FakeIndicatorsModel>(uri, 0, 1, "FakeIndicatorsModel");
47 qmlRegisterType<FakeIndicatorsModel>(uri, 0, 1, "FakeIndicatorsModel");
@@ -47,8 +50,9 @@ void IndicatorsFakePlugin::registerTypes(const char * uri)
47 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
50 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
48 qmlRegisterType<UnityMenuModelStack>(uri, 0, 1, "UnityMenuModelStack");
51 qmlRegisterType<UnityMenuModelStack>(uri, 0, 1, "UnityMenuModelStack");
49 qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
52 qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
53+ qmlRegisterType<SharedUnityMenuModel>(uri, 0, 1, "SharedUnityMenuModel");
50
54
51− qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
55+ qmlRegisterSingletonType<FakeUnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
52
56
53 // external uncreatables
57 // external uncreatables
54 qmlRegisterUncreatableType<MenuContentState>(uri, 0, 1, "MenuContentState", "Can't create MenuContentState class");
58 qmlRegisterUncreatableType<MenuContentState>(uri, 0, 1, "MenuContentState", "Can't create MenuContentState class");
tests/mocks/Unity/Indicators/fakeunitymenumodelcache.cppnew file+59
@@ -0,0 +1,59 @@
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 "fakeunitymenumodelcache.h"
18+#include <unitymenumodel.h>
19+
20+QPointer<FakeUnityMenuModelCache> FakeUnityMenuModelCache::theFakeCache = nullptr;
21+
22+FakeUnityMenuModelCache* FakeUnityMenuModelCache::singleton()
23+{
24+ if (theFakeCache.isNull()) {
25+ theFakeCache = new FakeUnityMenuModelCache();
26+ }
27+ return theFakeCache.data();
28+}
29+
30+FakeUnityMenuModelCache::FakeUnityMenuModelCache(QObject* parent)
31+ : UnityMenuModelCache(parent)
32+{
33+}
34+
35+QSharedPointer<UnityMenuModel> FakeUnityMenuModelCache::model(const QByteArray& path)
36+{
37+ return UnityMenuModelCache::singleton()->model(path);
38+}
39+
40+bool FakeUnityMenuModelCache::contains(const QByteArray& path)
41+{
42+ return UnityMenuModelCache::singleton()->contains(path);
43+}
44+
45+void FakeUnityMenuModelCache::setCachedModelData(const QByteArray& path,
46+ const QVariant& data)
47+{
48+ // keep a ref forever!
49+ if (!m_models.contains(path)) {
50+ m_models[path] = model(path);
51+ }
52+ m_models[path]->setModelData(data);
53+}
54+
55+QVariant FakeUnityMenuModelCache::getCachedModelData(const QByteArray& path)
56+{
57+ QSharedPointer<UnityMenuModel> model = this->model(path);
58+ return model.isNull() ? QVariant() : model->modelData();
59+}
tests/mocks/Unity/Indicators/fakeunitymenumodelcache.hnew file+45
@@ -0,0 +1,45 @@
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 FAKEUNITYMENUMODELCACHE_H
18+#define FAKEUNITYMENUMODELCACHE_H
19+
20+#include "unitymenumodelcache.h"
21+
22+#include <QVariantMap>
23+
24+class FakeUnityMenuModelCache : public UnityMenuModelCache
25+{
26+ Q_OBJECT
27+public:
28+ FakeUnityMenuModelCache(QObject* parent = nullptr);
29+
30+ static FakeUnityMenuModelCache* singleton();
31+
32+ QSharedPointer<UnityMenuModel> model(const QByteArray& path) override;
33+ bool contains(const QByteArray& path) override;
34+
35+ Q_INVOKABLE void setCachedModelData(const QByteArray& path,
36+ const QVariant& data = QVariant());
37+
38+ Q_INVOKABLE QVariant getCachedModelData(const QByteArray& path);
39+
40+private:
41+ static QPointer<FakeUnityMenuModelCache> theFakeCache;
42+ QHash<QByteArray, QSharedPointer<UnityMenuModel>> m_models;
43+};
44+
45+#endif // FAKEUNITYMENUMODELCACHE_H
tests/mocks/Unity/Indicators/indicators_fake.qrcdeleted−14
@@ -1,14 +0,0 @@
1−<RCC>
2− <qresource prefix="/tests/indciators">
3− <file>qml/fake_menu_widget1.qml</file>
4− <file>qml/fake_menu_widget2.qml</file>
5− <file>qml/fake_menu_widget3.qml</file>
6− <file>qml/fake_menu_widget4.qml</file>
7− <file>qml/fake_menu_widget5.qml</file>
8− <file>qml/fake_menu_page1.qml</file>
9− <file>qml/fake_menu_page2.qml</file>
10− <file>qml/fake_menu_page3.qml</file>
11− <file>qml/fake_menu_page4.qml</file>
12− <file>qml/fake_menu_page5.qml</file>
13− </qresource>
14−</RCC>
tests/mocks/Unity/Indicators/qml/fake_menu_page1.qmldeleted−35
@@ -1,35 +0,0 @@
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 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 Unity.Indicators 0.1 as Indicators
19−
20−Indicators.FakeMenuPage {
21− id: menu_plugin1
22−
23− Rectangle {
24− id: contents
25− color: "red"
26−
27− height: 150
28− width: 150
29−
30− anchors {
31− horizontalCenter: parent.horizontalCenter
32− verticalCenter: parent.verticalCenter
33− }
34− }
35−}
tests/mocks/Unity/Indicators/qml/fake_menu_page2.qmldeleted−35
@@ -1,35 +0,0 @@
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 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 Unity.Indicators 0.1 as Indicators
19−
20−Indicators.FakeMenuPage {
21− id: menu_plugin2
22−
23− Rectangle {
24− id: contents
25− color: "blue"
26−
27− height: 150
28− width: 150
29−
30− anchors {
31− horizontalCenter: parent.horizontalCenter
32− verticalCenter: parent.verticalCenter
33− }
34− }
35−}
tests/mocks/Unity/Indicators/qml/fake_menu_page3.qmldeleted−35
@@ -1,35 +0,0 @@
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 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 Unity.Indicators 0.1 as Indicators
19−
20−Indicators.FakeMenuPage {
21− id: menu_plugin3
22−
23− Rectangle {
24− id: contents
25− color: "yellow"
26−
27− height: 150
28− width: 150
29−
30− anchors {
31− horizontalCenter: parent.horizontalCenter
32− verticalCenter: parent.verticalCenter
33− }
34− }
35−}
tests/mocks/Unity/Indicators/qml/fake_menu_page4.qmldeleted−35
@@ -1,35 +0,0 @@
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 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 Unity.Indicators 0.1 as Indicators
19−
20−Indicators.FakeMenuPage {
21− id: menu_plugin4
22−
23− Rectangle {
24− id: contents
25− color: "green"
26−
27− height: 150
28− width: 150
29−
30− anchors {
31− horizontalCenter: parent.horizontalCenter
32− verticalCenter: parent.verticalCenter
33− }
34− }
35−}
tests/mocks/Unity/Indicators/qml/fake_menu_page5.qmldeleted−35
@@ -1,35 +0,0 @@
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 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 Unity.Indicators 0.1 as Indicators
19−
20−Indicators.FakeMenuPage {
21− id: menu_plugin5
22−
23− Rectangle {
24− id: contents
25− color: "orange"
26−
27− height: 150
28− width: 150
29−
30− anchors {
31− horizontalCenter: parent.horizontalCenter
32− verticalCenter: parent.verticalCenter
33− }
34− }
35−}
tests/mocks/Unity/Indicators/qml/fake_menu_widget1.qmldeleted−23
@@ -1,23 +0,0 @@
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 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−
19−Rectangle {
20− width: 40
21− height: 40
22− color : "red"
23−}
tests/mocks/Unity/Indicators/qml/fake_menu_widget2.qmldeleted−23
@@ -1,23 +0,0 @@
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 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−
19−Rectangle {
20− width: 40
21− height: 40
22− color : "blue"
23−}
tests/mocks/Unity/Indicators/qml/fake_menu_widget3.qmldeleted−23
@@ -1,23 +0,0 @@
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 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−
19−Rectangle {
20− width: 40
21− height: 40
22− color : "yellow"
23−}
tests/mocks/Unity/Indicators/qml/fake_menu_widget4.qmldeleted−23
@@ -1,23 +0,0 @@
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 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−
19−Rectangle {
20− width: 40
21− height: 40
22− color : "green"
23−}
tests/mocks/Unity/Indicators/qml/fake_menu_widget5.qmldeleted−23
@@ -1,23 +0,0 @@
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 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−
19−Rectangle {
20− width: 40
21− height: 40
22− color : "orange"
23−}
tests/mocks/Unity/Indicators/qmldir−1
@@ -3,5 +3,4 @@ plugin IndicatorsFakeQml
3typeinfo Indicators.qmltypes
3typeinfo Indicators.qmltypes
4
4
5IndicatorsModel 0.1 IndicatorsModel.qml
5IndicatorsModel 0.1 IndicatorsModel.qml
6−FakeMenuPage 0.1 FakeMenuPage.qml
7RootActionState 0.1 RootActionState.qml
6RootActionState 0.1 RootActionState.qml
tests/mocks/Unity/Launcher/Launcher.qmltypes+13
@@ -85,6 +85,7 @@ Module {
85 Property { name: "recent"; type: "bool"; isReadonly: true }
85 Property { name: "recent"; type: "bool"; isReadonly: true }
86 Property { name: "progress"; type: "int"; isReadonly: true }
86 Property { name: "progress"; type: "int"; isReadonly: true }
87 Property { name: "count"; type: "int"; isReadonly: true }
87 Property { name: "count"; type: "int"; isReadonly: true }
88+ Property { name: "countVisible"; type: "bool"; isReadonly: true }
88 Property { name: "focused"; type: "bool"; isReadonly: true }
89 Property { name: "focused"; type: "bool"; isReadonly: true }
89 Property {
90 Property {
90 name: "quickList"
91 name: "quickList"
@@ -92,6 +93,14 @@ Module {
92 isReadonly: true
93 isReadonly: true
93 isPointer: true
94 isPointer: true
94 }
95 }
96+ Signal {
97+ name: "nameChanged"
98+ Parameter { name: "name"; type: "string" }
99+ }
100+ Signal {
101+ name: "iconChanged"
102+ Parameter { name: "icon"; type: "string" }
103+ }
95 Signal {
104 Signal {
96 name: "pinnedChanged"
105 name: "pinnedChanged"
97 Parameter { name: "pinned"; type: "bool" }
106 Parameter { name: "pinned"; type: "bool" }
@@ -112,6 +121,10 @@ Module {
112 name: "countChanged"
121 name: "countChanged"
113 Parameter { name: "count"; type: "int" }
122 Parameter { name: "count"; type: "int" }
114 }
123 }
124+ Signal {
125+ name: "countVisibleChanged"
126+ Parameter { name: "countVisible"; type: "bool" }
127+ }
115 Signal {
128 Signal {
116 name: "focusedChanged"
129 name: "focusedChanged"
117 Parameter { name: "focused"; type: "bool" }
130 Parameter { name: "focused"; type: "bool" }
tests/mocks/Unity/Unity.qmltypes+32
@@ -30,6 +30,25 @@ Module {
30 Parameter { name: "row"; type: "int" }
30 Parameter { name: "row"; type: "int" }
31 Parameter { name: "role"; type: "int" }
31 Parameter { name: "role"; type: "int" }
32 }
32 }
33+ Method {
34+ name: "setCount"
35+ Parameter { name: "count"; type: "int" }
36+ }
37+ Method {
38+ name: "resultModel"
39+ type: "ResultsModel*"
40+ Parameter { name: "row"; type: "int" }
41+ }
42+ Method {
43+ name: "setLayout"
44+ Parameter { name: "row"; type: "int" }
45+ Parameter { name: "layout"; type: "string" }
46+ }
47+ Method {
48+ name: "setHeaderLink"
49+ Parameter { name: "row"; type: "int" }
50+ Parameter { name: "headerLink"; type: "string" }
51+ }
33 }
52 }
34 Component {
53 Component {
35 name: "PreviewModel"
54 name: "PreviewModel"
@@ -66,6 +85,10 @@ Module {
66 exports: ["Unity/FakeResultsModel 0.2", "Unity/ResultsModel 0.2"]
85 exports: ["Unity/FakeResultsModel 0.2", "Unity/ResultsModel 0.2"]
67 isCreatable: false
86 isCreatable: false
68 exportMetaObjectRevisions: [0, 0]
87 exportMetaObjectRevisions: [0, 0]
88+ Method {
89+ name: "setResultCount"
90+ Parameter { name: "result_count"; type: "int" }
91+ }
69 }
92 }
70 Component {
93 Component {
71 name: "Scope"
94 name: "Scope"
@@ -76,6 +99,15 @@ Module {
76 name: "performQuery"
99 name: "performQuery"
77 Parameter { name: "query"; type: "string" }
100 Parameter { name: "query"; type: "string" }
78 }
101 }
102+ Signal { name: "refreshed" }
103+ Method {
104+ name: "setId"
105+ Parameter { name: "id"; type: "string" }
106+ }
107+ Method {
108+ name: "setName"
109+ Parameter { name: "name"; type: "string" }
110+ }
79 Method {
111 Method {
80 name: "setSearchInProgress"
112 name: "setSearchInProgress"
81 Parameter { name: "inProg"; type: "bool" }
113 Parameter { name: "inProg"; type: "bool" }
tests/plugins/Unity/Indicators/CMakeLists.txt+1
@@ -40,3 +40,4 @@ indicator_test(indicatorsmodeltest ADDITIONAL_LIBS IndicatorsQml)
40indicator_test(menucontentactivatortest ADDITIONAL_CPPS ${INDICATORS_DIR}/menucontentactivator.cpp)
40indicator_test(menucontentactivatortest ADDITIONAL_CPPS ${INDICATORS_DIR}/menucontentactivator.cpp)
41indicator_test(unitymenumodelstacktest ADDITIONAL_CPPS ${TEST_DIR}/mocks/QMenuModel/unitymenumodel.cpp ${INDICATORS_DIR}/unitymenumodelstack.cpp)
41indicator_test(unitymenumodelstacktest ADDITIONAL_CPPS ${TEST_DIR}/mocks/QMenuModel/unitymenumodel.cpp ${INDICATORS_DIR}/unitymenumodelstack.cpp)
42indicator_test(rootactionstatetest ADDITIONAL_LIBS IndicatorsQml)
42indicator_test(rootactionstatetest ADDITIONAL_LIBS IndicatorsQml)
43+indicator_test(sharedunitymenumodeltest ADDITIONAL_LIBS IndicatorsQml)
tests/plugins/Unity/Indicators/sharedunitymenumodeltest.cppnew file+102
@@ -0,0 +1,102 @@
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+
18+#include "sharedunitymenumodel.h"
19+#include "unitymenumodelcache.h"
20+
21+#include <QtTest>
22+#include <unitymenumodel.h>
23+
24+class SharedUnityMenuModelTest : public QObject
25+{
26+ Q_OBJECT
27+
28+ SharedUnityMenuModel* createFullModel(const QByteArray& testId)
29+ {
30+ SharedUnityMenuModel* model = new SharedUnityMenuModel;
31+ model->setBusName("com.canonical." + testId);
32+ model->setMenuObjectPath("/com/canonical/" + testId);
33+ QVariantMap actions;
34+ actions["test"] = QString("/com/canonical/%1/actions").arg(QString(testId));
35+ model->setActions(actions);
36+
37+ return model;
38+ }
39+
40+private Q_SLOTS:
41+
42+ void testCreateModel()
43+ {
44+ QSharedPointer<SharedUnityMenuModel> model(createFullModel("test1"));
45+ QVERIFY(model->model() != nullptr);
46+ }
47+
48+ void testDifferentDataCreatesDifferentModels()
49+ {
50+ QSharedPointer<SharedUnityMenuModel> model1(createFullModel("test1"));
51+ QSharedPointer<SharedUnityMenuModel> model2(createFullModel("test2"));
52+
53+ QVERIFY(model1->model() != model2->model());
54+ }
55+
56+ void testSameDataCreatesSameModels()
57+ {
58+ QSharedPointer<SharedUnityMenuModel> model1(createFullModel("test1"));
59+ QSharedPointer<SharedUnityMenuModel> model2(createFullModel("test1"));
60+
61+ QCOMPARE(model1->model(), model2->model());
62+ }
63+
64+ void testSharedOwnership()
65+ {
66+ QSharedPointer<SharedUnityMenuModel> model1(createFullModel("test1"));
67+ QSharedPointer<SharedUnityMenuModel> model2(createFullModel("test1"));
68+
69+ QCOMPARE(UnityMenuModelCache::singleton()->contains("/com/canonical/test1"), true);
70+ model1.clear();
71+ QCOMPARE(UnityMenuModelCache::singleton()->contains("/com/canonical/test1"), true);
72+ model2.clear();
73+ QCOMPARE(UnityMenuModelCache::singleton()->contains("/com/canonical/test1"), false);
74+ }
75+
76+ // Tests that changing cached model data does not change the model path of others
77+ void testLP1328646()
78+ {
79+ QSharedPointer<SharedUnityMenuModel> model1(createFullModel("test1"));
80+ QSharedPointer<SharedUnityMenuModel> model2(createFullModel("test1"));
81+
82+ model2->setMenuObjectPath("/com/canonical/LP1328646");
83+
84+ QVERIFY(model1->model() != model2->model());
85+ QCOMPARE(model1->model()->menuObjectPath(), QByteArray("/com/canonical/test1"));
86+ QCOMPARE(model2->model()->menuObjectPath(), QByteArray("/com/canonical/LP1328646"));
87+ }
88+
89+ // Tests that the cache is recreated if deleted.
90+ void testDeletedCache()
91+ {
92+ QSharedPointer<SharedUnityMenuModel> model1(createFullModel("test1"));
93+
94+ QCOMPARE(UnityMenuModelCache::singleton()->contains("/com/canonical/test1"), true);
95+ delete UnityMenuModelCache::singleton();
96+ QCOMPARE(UnityMenuModelCache::singleton()->contains("/com/canonical/test1"), false);
97+ }
98+
99+};
100+
101+QTEST_GUILESS_MAIN(SharedUnityMenuModelTest)
102+#include "sharedunitymenumodeltest.moc"
tests/qmltests/CMakeLists.txt+10−11
@@ -12,16 +12,15 @@ set(qmltest_DEFAULT_IMPORT_PATHS
12set(qmltest_DEFAULT_TARGETS qmlunittests)
12set(qmltest_DEFAULT_TARGETS qmlunittests)
13set(qmltest_DEFAULT_NO_ADD_TEST FALSE)
13set(qmltest_DEFAULT_NO_ADD_TEST FALSE)
14set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal")
14set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal")
15−add_qml_test(Panel IndicatorItem)
16add_qml_test(utils/Unity/Test UnityTest)
15add_qml_test(utils/Unity/Test UnityTest)
17
16
18set(qmltest_DEFAULT_TARGETS qmluitests)
17set(qmltest_DEFAULT_TARGETS qmluitests)
19set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
18set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
20set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
19set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
21
20
22−add_qml_test(. Shell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
21+add_qml_test(. Shell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single:${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
23−add_qml_test(. ShellWithPin ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single-pin")
22+add_qml_test(. ShellWithPin ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single-pin:${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
24−add_qml_test(. TabletShell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
23+add_qml_test(. TabletShell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full:${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
25add_qml_test(Components Background)
24add_qml_test(Components Background)
26add_qml_test(Components Carousel)
25add_qml_test(Components Carousel)
27add_qml_test(Components DraggingArea)
26add_qml_test(Components DraggingArea)
@@ -72,14 +71,14 @@ add_qml_test(Launcher Launcher)
72add_qml_test(Notifications Notifications)
71add_qml_test(Notifications Notifications)
73add_qml_test(Notifications VisualSnapDecisionsQueue)
72add_qml_test(Notifications VisualSnapDecisionsQueue)
74add_qml_test(Panel ActiveCallHint)
73add_qml_test(Panel ActiveCallHint)
75−add_qml_test(Panel IndicatorRow)
74+add_qml_test(Panel IndicatorItem)
76−add_qml_test(Panel Indicators)
75+add_qml_test(Panel IndicatorItemRow ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
76+add_qml_test(Panel IndicatorPage ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
77+add_qml_test(Panel IndicatorsBar ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
78+add_qml_test(Panel IndicatorsMenu ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
77−add_qml_test(Panel MenuContent)
79+add_qml_test(Panel MenuContent ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
78−add_qml_test(Panel Panel)
80+add_qml_test(Panel Panel ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
79add_qml_test(Panel SearchIndicator)
81add_qml_test(Panel SearchIndicator)
80−add_qml_test(Panel/Indicators DefaultIndicatorWidget ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
81−add_qml_test(Panel/Indicators DefaultIndicatorPage ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
82−add_qml_test(Panel/Indicators CachedUnityMenuModel ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
83# These MenuItemFactory tests need the test/mocks/ to come before plugins/
82# These MenuItemFactory tests need the test/mocks/ to come before plugins/
84add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
83add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
85add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
84add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
tests/qmltests/Greeter/tst_Clock.qml+49−34
@@ -19,7 +19,7 @@ import QtTest 1.0
19import ".."
19import ".."
20import "../../../qml/Greeter"
20import "../../../qml/Greeter"
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22−import QMenuModel 0.1
22+import Unity.Indicators 0.1 as Indicators
23import Unity.Test 0.1 as UT
23import Unity.Test 0.1 as UT
24
24
25Rectangle {
25Rectangle {
@@ -36,56 +36,71 @@ Rectangle {
36 }
36 }
37 }
37 }
38
38
39− UnityMenuModel {
39+ function updateDatetimeModelTime(label) {
40− id: menuModel
40+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicator/datetime/phone",
41− modelData: [{
41+ [{
42− "rowData": {
42+ "rowData": {
43− "actionState": { "label": Qt.formatTime(new Date("October 13, 1975 11:13:00")) }
43+ "actionState": { "label": label }
44− }
44+ }
45− }]
45+ }]);
46 }
46 }
47
47
48 UT.UnityTestCase {
48 UT.UnityTestCase {
49 name: "Clock"
49 name: "Clock"
50+ when: windowShown
50
51
51 function init() {
52 function init() {
52− var cachedModel = findChild(clock, "timeModel");
53+ updateDatetimeModelTime(Qt.formatTime(new Date("October 13, 1975 12:14:00")));
53− verify(cachedModel !== undefined);
54+ clock.visible = true;
54− cachedModel.model = menuModel;
55 }
55 }
56
56
57− function test_customDate() {
57+ // Test that the date portion of the clock updates with custom value.
58− var dateObj = new Date("October 13, 1975 11:13:00")
58+ // Time portion is controlled by indicators
59− var dateString = Qt.formatDate(dateObj, Qt.DefaultLocaleLongDate)
59+ function test_updateDate() {
60− var timeString = Qt.formatTime(dateObj)
60+ var dateLabel = findChild(clock, "dateLabel");
61+ var timeLabel = findChild(clock, "timeLabel");
61
62
62− clock.currentDate = dateObj
63+ var timeString = Qt.formatTime(new Date("October 13, 1975 12:14:00"));
63− var dateLabel = findChild(clock, "dateLabel")
64+
65+ // initial date.
66+ var dateObj = new Date("October 13, 1975 11:13:00");
67+ var dateString = Qt.formatDate(dateObj, Qt.DefaultLocaleLongDate);
68+ clock.currentDate = dateObj;
69+
64− compare(dateLabel.text, dateString, "Not the expected date")
70+ compare(dateLabel.text, dateString, "Not the expected date");
65− var timeLabel = findChild(clock, "timeLabel")
71+ compare(timeLabel.text, timeString, "Time should come from indicators");
72+
73+ // update date.
74+ var dateObj2 = new Date("October 14, 1976 13:15:00");
75+ var dateString2 = Qt.formatDate(dateObj2, Qt.DefaultLocaleLongDate);
76+ clock.currentDate = dateObj2;
77+
66− compare(timeLabel.text, timeString, "Not the expected time")
78+ compare(dateLabel.text, dateString2, "Not the expected date");
79+ compare(timeLabel.text,timeString, "Time should come from indicators");
67 }
80 }
68
81
69− function test_dateUpdate() {
82+ // Test that the date portion of the clock updates with custom value.
70− var dateObj = new Date("October 13, 1975 11:13:00")
83+ // Time portion is controlled by indicators
71− var dateString = Qt.formatDate(dateObj, Qt.DefaultLocaleLongDate)
84+ function test_updateTime() {
72− var timeString = Qt.formatTime(dateObj)
85+ var timeLabel = findChild(clock, "timeLabel");
73
86
74− clock.visible = false
87+ var timeString1 = Qt.formatTime(new Date("October 13, 1975 11:15:00"));
75− var timeModel = findInvisibleChild(clock, "timeModel")
88+ var timeString2 = Qt.formatTime(new Date("October 14, 1976 12:16:00"));
76
89
77− compare(timeModel.menuObjectPath, "", "Clock shouldn't be connected to Indicators when not visible.")
90+ updateDatetimeModelTime(timeString1);
91+ compare(timeLabel.text, timeString1, "Time should come from indicators");
78
92
79− clock.currentDate = dateObj
93+ updateDatetimeModelTime(timeString2);
94+ compare(timeLabel.text, timeString2, "Time should come from indicators");
95+ }
80
96
81− var dateLabel = findChild(clock, "dateLabel")
97+ function test_indicatorDisconnect() {
82− compare(dateLabel.text, dateString, "Not the expected date")
98+ clock.visible = false
83− var timeLabel = findChild(clock, "timeLabel")
99+ var timeModel = findInvisibleChild(clock, "timeModel")
84− compare(timeLabel.text, timeString, "Not the expected time")
100+ compare(timeModel.menuObjectPath, "", "Clock shouldn't be connected to Indicators when not visible.")
85
101
86 clock.visible = true
102 clock.visible = true
87−
88− verify(timeModel.menuObjectPath != "", "Should be connected to Indicators.")
103+ verify(timeModel.menuObjectPath !== "", "Should be connected to Indicators.")
89 }
104 }
90 }
105 }
91}
106}
tests/qmltests/Panel/IndicatorTest.qmlnew file+80
@@ -0,0 +1,80 @@
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.1
18+import QtQuick.Layouts 1.1
19+import QtTest 1.0
20+import "../../../qml/Panel"
21+import Ubuntu.Components 0.1
22+import Unity.Test 0.1 as UT
23+import Unity.Indicators 0.1 as Indicators
24+
25+Rectangle {
26+ id: root
27+ color: "white"
28+
29+ property alias indicatorsModel: __visibleIndicatorsModel
30+ property alias originalModelData: __indicatorsModel.originalModelData
31+
32+ Indicators.VisibleIndicatorsModel {
33+ id: __visibleIndicatorsModel
34+
35+ model: __indicatorsModel
36+ }
37+
38+ Indicators.IndicatorsModel {
39+ id: __indicatorsModel
40+ Component.onCompleted: load();
41+ }
42+
43+ function insertIndicator(index) {
44+ var i;
45+ var insertIndex = 0;
46+ var done = false;
47+ for (i = index; !done && i >= 1; i--) {
48+
49+ var lookFor = __indicatorsModel.originalModelData[i-1]["identifier"]
50+
51+ var j;
52+ for (j = __indicatorsModel.modelData.length-1; !done && j >= 0; j--) {
53+ if (__indicatorsModel.modelData[j]["identifier"] === lookFor) {
54+ insertIndex = j+1;
55+ done = true;
56+ }
57+ }
58+ }
59+ __indicatorsModel.insert(insertIndex, __indicatorsModel.originalModelData[index]);
60+ }
61+
62+ function removeIndicator(index) {
63+ var i;
64+ for (i = 0; i < __indicatorsModel.modelData.length; i++) {
65+ if (__indicatorsModel.modelData[i]["identifier"] === __indicatorsModel.originalModelData[index]["identifier"]) {
66+ __indicatorsModel.remove(i);
67+ break;
68+ }
69+ }
70+ }
71+
72+ function setIndicatorVisible(index, visible) {
73+ var identifier = __indicatorsModel.originalModelData[index]["identifier"];
74+ __indicatorsModel.setIndicatorVisible(identifier, visible);
75+ }
76+
77+ function resetData() {
78+ __indicatorsModel.load();
79+ }
80+}
tests/qmltests/Panel/Indicators/tst_CachedUnityMenuModel.qmldeleted−137
@@ -1,137 +0,0 @@
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 QtTest 1.0
19−import Unity.Test 0.1 as UT
20−import QMenuModel 0.1
21−import Unity.Indicators 0.1 as Indicators
22−import "../../../../qml/Panel/Indicators"
23−
24−Item {
25− id: root
26− width: units.gu(40)
27− height: units.gu(70)
28−
29− Component {
30− id: model
31− CachedUnityMenuModel {}
32− }
33−
34− UT.UnityTestCase {
35− name: "CachedUnityMenuModel"
36− when: windowShown
37−
38− function cleanup() {
39− doGC();
40− }
41−
42− function doGC() {
43− // need to put some wait cycles here to get gc going properly.
44− wait(10);
45− gc();
46− wait(10);
47− }
48−
49− function test_createDifferent() {
50− var cachedObject = model.createObject(null,
51− {
52− "busName": "com.canonical.test1",
53− "menuObjectPath": "/com/canonical/test1",
54− "actionsObjectPath": "/com/canonical/test1"
55− });
56−
57− var cachedObject2 = model.createObject(null,
58− {
59− "busName": "com.canonical.test2",
60− "menuObjectPath": "/com/canonical/test2",
61− "actionsObjectPath": "/com/canonical/test2"
62− });
63−
64− verify(cachedObject.model !== cachedObject2.model);
65− }
66−
67− function test_createSame() {
68− var cachedObject = model.createObject(null,
69− {
70− "busName": "com.canonical.test3",
71− "menuObjectPath": "/com/canonical/test3",
72− "actionsObjectPath": "/com/canonical/test3"
73− });
74−
75− var cachedObject2 = model.createObject(null,
76− {
77− "busName": "com.canonical.test3",
78− "menuObjectPath": "/com/canonical/test3",
79− "actionsObjectPath": "/com/canonical/test3"
80− });
81−
82− verify(cachedObject.model === cachedObject2.model);
83− }
84−
85− // Tests that changing cached model data does not change the model path of others
86− function test_lp1328646() {
87− var cachedObject = model.createObject(null,
88− {
89− "busName": "com.canonical.test4",
90− "menuObjectPath": "/com/canonical/test4",
91− "actionsObjectPath": "/com/canonical/test4"
92− });
93−
94− var cachedObject2 = model.createObject(null,
95− {
96− "busName": "com.canonical.test4",
97− "menuObjectPath": "/com/canonical/test4",
98− "actionsObjectPath": "/com/canonical/test4"
99− });
100−
101− cachedObject.menuObjectPath = "/com/canonical/test5";
102− compare(cachedObject.model.menuObjectPath, "/com/canonical/test5");
103− compare(cachedObject2.model.menuObjectPath, "/com/canonical/test4");
104−
105− verify(cachedObject.model !== cachedObject2.model);
106− }
107−
108− function createAndDestroy(test) {
109− var cachedObject = model.createObject(null,
110− {
111− "busName": "com.canonical."+test,
112− "menuObjectPath": "/com/canonical/"+test,
113− "actionsObjectPath": "/com/canonical/"+test
114− });
115− var cachedObject2 = model.createObject(null,
116− {
117− "busName": "com.canonical."+test,
118− "menuObjectPath": "/com/canonical/"+test,
119− "actionsObjectPath": "/com/canonical/"+test
120− });
121− cachedObject.destroy();
122− return cachedObject2;
123− }
124−
125− function test_destroyAllDeletesModel() {
126− createAndDestroy("test6");
127− doGC();
128− compare(Indicators.UnityMenuModelCache.contains("/com/canonical/test6"), false);
129− }
130−
131− function test_destroyPartialKeepsModel() {
132− var model = createAndDestroy("test7");
133− doGC();
134− compare(Indicators.UnityMenuModelCache.contains("/com/canonical/test7"), true);
135− }
136− }
137−}
tests/qmltests/Panel/Indicators/tst_DefaultIndicatorWidget.qmldeleted−60
@@ -1,60 +0,0 @@
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 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 QtTest 1.0
19−import Unity.Test 0.1 as UT
20−import QMenuModel 0.1
21−import "../../../../qml/Panel/Indicators"
22−
23−Item {
24− id: testView
25− width: units.gu(40)
26− height: units.gu(70)
27−
28− DefaultIndicatorWidget {
29− id: widget
30−
31− anchors {
32− left: parent.left
33− top: parent.top
34− }
35−
36− menuModel: UnityMenuModel {}
37− busName: "test"
38− actionsObjectPath: "test"
39− deviceMenuObjectPath: "test"
40−
41− rootMenuType: ""
42−
43− iconSize: units.gu(3.2)
44− height: units.gu(3)
45− }
46−
47− UT.UnityTestCase {
48− name: "DefaultIndicatorWidget"
49− when: windowShown
50−
51− function init() {
52− widget.rootActionState.icons = [];
53− widget.rootActionState.leftLabel = "";
54− widget.rootActionState.rightLabel = "";
55− waitForRendering(widget)
56− }
57−
58− // FIXME: add tests
59− }
60−}
tests/qmltests/Panel/tst_ActiveCallHint.qml−1
@@ -19,7 +19,6 @@ import QtTest 1.0
19import Unity.Test 0.1 as UT
19import Unity.Test 0.1 as UT
20import Ubuntu.Telephony 0.1 as Telephony
20import Ubuntu.Telephony 0.1 as Telephony
21import Unity.Application 0.1
21import Unity.Application 0.1
22−import ".."
23import "../../../qml/Panel"
22import "../../../qml/Panel"
24
23
25Item {
24Item {
tests/qmltests/Panel/tst_IndicatorItem.qml+173−18
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -14,37 +14,192 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.1
18+import QtQuick.Layouts 1.1
18import QtTest 1.0
19import QtTest 1.0
19−import ".."
20−import "../../../qml/Panel"
21import Ubuntu.Components 0.1
20import Ubuntu.Components 0.1
22import Unity.Test 0.1 as UT
21import Unity.Test 0.1 as UT
22+import "../../../qml/Panel"
23
23
24Rectangle {
24Rectangle {
25− width: units.gu(10)
25+ width: units.gu(80)
26− height: units.gu(5)
26+ height: units.gu(30)
27− color: "black"
27+ color: "white"
28
28
29− IndicatorItem {
29+ RowLayout {
30− id: indicatorItem
31 anchors.fill: parent
30 anchors.fill: parent
31+ anchors.margins: units.gu(1)
32+
33+ Rectangle {
34+ id: itemArea
35+ color: "blue"
36+ Layout.fillWidth: true
37+ Layout.fillHeight: true
38+
39+ Rectangle {
40+ color: "black"
41+ anchors.fill: indicatorItem
42+ }
43+
44+ IndicatorItem {
45+ id: indicatorItem
46+ height: expanded ? units.gu(7) : units.gu(3)
47+ anchors.centerIn: parent
48+ identifier: "indicator-test"
49+
50+ rootActionState {
51+ title: titleLabel.text
52+ leftLabel : leftLabel.text
53+ rightLabel : rightLabel.text
54+ icons : {
55+ var icons = [];
56+ var i = 0;
57+ if (iconEnabled.checked) {
58+ for (i = 0; i < String(iconCount.text); i++) {
59+ icons.push("image://theme/audio-volume-high");
60+ }
61+ }
62+ return icons;
63+ }
64+ }
65+
66+ Behavior on height {
67+ NumberAnimation {
68+ id: heightAnimation
69+ duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing
70+ }
71+ }
72+ }
73+ }
74+
75+ ColumnLayout {
76+ Layout.alignment: Qt.AlignTop
77+ Layout.fillWidth: false
78+
79+ Button {
80+ id: expandButton
81+ Layout.fillWidth: true
82+ text: indicatorItem.expanded ? "Collapse" : "Expand"
83+ onClicked: indicatorItem.expanded = !indicatorItem.expanded
84+ }
85+
86+ Button {
87+ id: selectButton
88+ Layout.fillWidth: true
89+ text: indicatorItem.selected ? "Unselect" : "Select"
90+ onClicked: indicatorItem.selected = !indicatorItem.selected
91+ }
92+
93+ Rectangle {
94+ Layout.preferredHeight: units.dp(1);
95+ Layout.fillWidth: true;
96+ color: "black"
97+ }
98+
99+ RowLayout {
100+ CheckBox { id: iconEnabled; checked: true }
101+ Label { text: "icons Count:" }
102+ TextField { id: iconCount; text: "1"; enabled: iconEnabled.checked }
103+ }
104+
105+ RowLayout {
106+ Label { text: "Left Label:" }
107+ TextField { id: leftLabel; text: "Left"}
108+ }
109+
110+ RowLayout {
111+ Label { text: "Right Label:" }
112+ TextField { id: rightLabel; text: "Right"}
113+ }
114+
115+ RowLayout {
116+ Label { text: "Title:" }
117+ TextField { id: titleLabel; text: "Title"}
118+ }
119+ }
32 }
120 }
33
121
34 UT.UnityTestCase {
122 UT.UnityTestCase {
35 name: "IndicatorItem"
123 name: "IndicatorItem"
124+ when: windowShown
125+
126+ function init() {
127+ indicatorItem.selected = false;
128+ indicatorItem.expanded = false;
129+ indicatorItem.rootActionState.title = "Test Title";
130+ indicatorItem.rootActionState.leftLabel = "TestLeftLabel";
131+ indicatorItem.rootActionState.rightLabel = "TestRightLabel";
132+ indicatorItem.rootActionState.icons = [ "image://theme/audio-volume-high" ];
36
133
37− function test_dimmed() {
134+ tryCompare(heightAnimation, "running", false);
38− indicatorItem.dimmed = false;
39− tryCompareFunction(function(){return indicatorItem.opacity}, 1.0);
40− indicatorItem.dimmed = true;
41− tryCompareFunction(function(){return indicatorItem.opacity < 1.0}, true);
42 }
135 }
43
136
44− function test_empty() {
137+ function test_expand() {
45− compare(indicatorItem.indicatorVisible, false, "IndicatorItem should not be visible.");
138+ indicatorItem.expanded = true;
46− indicatorItem.widgetSource = "qrc:/tests/indciators/qml/fake_menu_widget1.qml";
139+ tryCompare(indicatorItem, "height", units.gu(7));
140+ indicatorItem.expanded = false;
141+ tryCompare(indicatorItem, "height", units.gu(3));
142+ }
143+
144+ function test_minimizedVisibility() {
145+ compare(findChild(indicatorItem, "leftLabel").opacity, 1.0);
146+ compare(findChild(indicatorItem, "rightLabel").opacity, 1.0);
147+ compare(findChild(indicatorItem, "icons").opacity, 1.0);
148+ compare(findChild(indicatorItem, "indicatorName").opacity, 0.0);
149+ }
150+
151+ function test_expandIcon() {
152+ indicatorItem.expanded = true;
153+
154+ tryCompare(findChild(indicatorItem, "leftLabel"), "opacity", 0.0);
155+ tryCompare(findChild(indicatorItem, "rightLabel"), "opacity", 0.0);
47− tryCompare(indicatorItem, "indicatorVisible", true);
156+ tryCompare(findChild(indicatorItem, "icons"), "opacity", 1.0);
157+ tryCompare(findChild(indicatorItem, "indicatorName"), "opacity", 1.0);
158+ }
159+
160+ function test_expandRightLabel() {
161+ indicatorItem.expanded = true;
162+
163+ indicatorItem.rootActionState.icons = [];
164+
165+ tryCompare(findChild(indicatorItem, "leftLabel"), "opacity", 0.0);
166+ tryCompare(findChild(indicatorItem, "rightLabel"), "opacity", 1.0);
167+ tryCompare(findChild(indicatorItem, "icons"), "opacity", 0.0);
168+ tryCompare(findChild(indicatorItem, "indicatorName"), "opacity", 1.0);
169+ }
170+
171+ function test_expandLeftLabel() {
172+ indicatorItem.expanded = true;
173+
174+ indicatorItem.rootActionState.rightLabel = "";
175+ indicatorItem.rootActionState.icons = [];
176+
177+ tryCompare(findChild(indicatorItem, "rightLabel"), "opacity", 0.0);
178+ tryCompare(findChild(indicatorItem, "leftLabel"), "opacity", 1.0);
179+ tryCompare(findChild(indicatorItem, "icons"), "opacity", 0.0);
180+ tryCompare(findChild(indicatorItem, "indicatorName"), "opacity", 1.0);
181+ }
182+
183+ function test_select() {
184+ tryCompare(findChild(indicatorItem, "icon0"), "color", "#ededed");
185+ tryCompare(findChild(indicatorItem, "icon0"), "opacity", 1.0);
186+ tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#ededed");
187+ tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#ededed");
188+ tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#ededed");
189+
190+ indicatorItem.expanded = true;
191+ tryCompare(findChild(indicatorItem, "icon0"), "color", "#4c4c4c");
192+ tryCompare(findChild(indicatorItem, "icon0"), "opacity", 0.6);
193+ tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#4c4c4c");
194+ tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#4c4c4c");
195+ tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#4c4c4c");
196+
197+ indicatorItem.selected = true;
198+ tryCompare(findChild(indicatorItem, "icon0"), "color", "#ededed");
199+ tryCompare(findChild(indicatorItem, "icon0"), "opacity", 1.0);
200+ tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#ededed");
201+ tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#ededed");
202+ tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#ededed");
48 }
203 }
49 }
204 }
50}
205}
tests/qmltests/Panel/tst_IndicatorItemRow.qmlnew file+290
@@ -0,0 +1,290 @@
1+/*
2+ * Copyright 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+
17+import QtQuick 2.1
18+import QtQuick.Layouts 1.1
19+import QtTest 1.0
20+import "../../../qml/Panel"
21+import Ubuntu.Components 0.1
22+import Unity.Test 0.1 as UT
23+import Unity.Indicators 0.1 as Indicators
24+
25+IndicatorTest {
26+ id: root
27+ width: units.gu(120)
28+ height: units.gu(40)
29+ color: "white"
30+
31+ RowLayout {
32+ anchors.fill: parent
33+ anchors.margins: units.gu(1)
34+
35+ Rectangle {
36+ Layout.fillWidth: true
37+ Layout.fillHeight: true
38+
39+ id: itemArea
40+ color: "blue"
41+
42+ Rectangle {
43+ color: "black"
44+ anchors.fill: indicatorsRow
45+ }
46+
47+ IndicatorItemRow {
48+ id: indicatorsRow
49+ height: expanded ? units.gu(7) : units.gu(3)
50+ anchors.centerIn: parent
51+ indicatorsModel: root.indicatorsModel
52+ enableLateralChanges: ma.pressed
53+
54+ Behavior on height {
55+ NumberAnimation {
56+ id: heightAnimation
57+ duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing
58+ }
59+ }
60+
61+ MouseArea {
62+ id: ma
63+ anchors.fill: parent
64+ onPositionChanged: {
65+ indicatorsRow.lateralPosition = mouse.x;
66+ }
67+ onPressed: {
68+ if (pressed) {
69+ indicatorsRow.lateralPosition = mouse.x;
70+ indicatorsRow.selectItemAt(mouse.x);
71+ }
72+ }
73+ }
74+ }
75+ }
76+
77+ ColumnLayout {
78+ Layout.alignment: Qt.AlignTop
79+ Layout.fillWidth: false
80+
81+ Button {
82+ Layout.fillWidth: true
83+ text: indicatorsRow.expanded ? "Collapse" : "Expand"
84+ onClicked: indicatorsRow.expanded = !indicatorsRow.expanded
85+ }
86+
87+ Rectangle {
88+ Layout.preferredHeight: units.dp(1);
89+ Layout.fillWidth: true;
90+ color: "black"
91+ }
92+
93+ Repeater {
94+ model: root.originalModelData
95+ RowLayout {
96+ CheckBox {
97+ checked: true
98+ onCheckedChanged: checked ? insertIndicator(index) : removeIndicator(index);
99+ }
100+ Label {
101+ Layout.fillWidth: true
102+ text: modelData["identifier"]
103+ }
104+
105+ CheckBox {
106+ checked: true
107+ onCheckedChanged: setIndicatorVisible(index, checked);
108+ }
109+ Label {
110+ text: "visible"
111+ }
112+ }
113+ }
114+ }
115+ }
116+
117+ UT.UnityTestCase {
118+ name: "IndicatorItemRow"
119+ when: windowShown
120+
121+ function init() {
122+ root.resetData();
123+
124+ indicatorsRow.resetCurrentItem();
125+ indicatorsRow.lateralPosition = -1;
126+
127+ indicatorsRow.expanded = false;
128+ tryCompare(heightAnimation, "running", false);
129+ tryCompare(findChild(indicatorsRow, "highlight"), "highlightCenterOffset", 0);
130+ wait(1); // row seems to take a bit of time for item x values to update.
131+ }
132+
133+ function wait_for_expansion_to_settle() {
134+ tryCompare(heightAnimation, "running", false);
135+ wait(200); // put a little extra wait in for things to settle
136+ }
137+
138+ function test_indicatorRowChanges_data() {
139+ return [
140+ { remove: [0, 2] },
141+ { remove: [0, 1, 2, 3, 4] },
142+ ];
143+ }
144+
145+ // test the changes in the available indicators updates the
146+ // indicators that are visible.
147+ function test_indicatorRowChanges(data) {
148+ var i;
149+ var item;
150+ var itemsToRemove = [0, 2];
151+
152+ verify(root.originalModelData.length > 0);
153+ for (i = 0; i < root.originalModelData.length; i++) {
154+ item = findChild(indicatorsRow, root.originalModelData[i]["identifier"] + "-panelItem");
155+ verify(item);
156+
157+ compare(item.ownIndex, i, "Item at incorrect index");
158+ }
159+
160+ for (i = data.remove.length-1; i >= 0; i--) {
161+ removeIndicator(data.remove[i]);
162+ }
163+
164+ // test removals
165+ for (i = 0; i < root.originalModelData.length; i++) {
166+ item = findChild(indicatorsRow, root.originalModelData[i]["identifier"] + "-panelItem");
167+
168+ verify(data.remove.indexOf(i) !== -1 ? (item === null) : (item !== null));
169+ }
170+
171+ // test insertion
172+ for (i = 0; i < data.remove.length; i++) {
173+ insertIndicator(data.remove[i]);
174+ }
175+
176+ for (i = 0; i < root.originalModelData.length; i++) {
177+ item = findChild(indicatorsRow, root.originalModelData[i]["identifier"] + "-panelItem");
178+ verify(item);
179+
180+ compare(item.ownIndex, i, "Item at incorrect index");
181+ }
182+ }
183+
184+ function test_validCurrentItem_data() {
185+ return [
186+ { index: 0 },
187+ { index: 2 },
188+ { index: 4 }
189+ ];
190+ }
191+
192+ // test selecting the item at it's position sets the current item of the row.
193+ function test_validCurrentItem(data) {
194+ var dataItem = findChild(indicatorsRow, root.originalModelData[data.index]["identifier"] + "-panelItem");
195+ verify(dataItem !== null);
196+
197+ indicatorsRow.selectItemAt(dataItem.x + dataItem.width/2);
198+ compare(indicatorsRow.currentItem, dataItem);
199+ }
200+
201+ // tests item default selection (no item at position X)
202+ function test_invalidCurrentItem() {
203+ indicatorsRow.selectItemAt(-100);
204+ var item = findChild(indicatorsRow, root.originalModelData[0]["identifier"] + "-panelItem");
205+ compare(indicatorsRow.currentItem, item);
206+ }
207+
208+ // testing that changing the lateral position offset of the row changes the current item.
209+ function test_lateralPositionChangesCurrentItem_data() {
210+ return [
211+ { tag: "0 -> 4", from: 0, to: 4 },
212+ { tag: "3 -> 1", from: 3, to: 1 }
213+ ];
214+ }
215+
216+ function test_lateralPositionChangesCurrentItem(data) {
217+ indicatorsRow.expanded = true;
218+ wait_for_expansion_to_settle();
219+
220+ var fromItem = findChild(indicatorsRow, root.originalModelData[data.from]["identifier"] + "-panelItem");
221+ verify(fromItem !== null);
222+
223+ var toItem = findChild(indicatorsRow, root.originalModelData[data.to]["identifier"] + "-panelItem");
224+ verify(toItem !== null);
225+
226+ var fromPosition = indicatorsRow.mapFromItem(fromItem, fromItem.width/2, fromItem.height/2);
227+ var toPosition = indicatorsRow.mapFromItem(toItem, toItem.width/2, toItem.height/2);
228+
229+ mousePress(indicatorsRow, fromPosition.x, fromPosition.y);
230+ compare(indicatorsRow.currentItem, fromItem, "Initial item not selected");
231+
232+ // this uses the MouseArea above to change the indicatorRow lateralPosition
233+ mouseFlick(indicatorsRow, fromPosition.x, fromPosition.y, toPosition.x, toPosition.y, false, false, units.gu(5), 30);
234+
235+ mouseRelease(indicatorsRow, fromPosition.x, fromPosition.y);
236+ compare(indicatorsRow.currentItem, toItem, "Current item did not change to expected item");
237+ }
238+
239+ // testing that positive changes to the lateral position offset shifts the highlight offset to the right
240+ function test_positiveLateralPositionChangesHighlightOffset() {
241+ indicatorsRow.expanded = true;
242+ wait_for_expansion_to_settle();
243+
244+ var highlight = findChild(indicatorsRow, "highlight");
245+ var item = findChild(indicatorsRow, root.originalModelData[2]["identifier"] + "-panelItem");
246+ verify(item !== null);
247+ var mappedPosition = indicatorsRow.mapFromItem(item, item.width/2, item.height/2);
248+
249+ mousePress(indicatorsRow, mappedPosition.x, mappedPosition.y);
250+ var originalHightlightX = highlight.x;
251+ var offset = 1;
252+ while((highlight.x - originalHightlightX) <= units.gu(0.5) && offset < units.gu(10)) {
253+ mouseMove(indicatorsRow, mappedPosition.x + offset, mappedPosition.y, 10);
254+ offset = offset + 2;
255+ }
256+ // verify that we hit the offset
257+ verify((highlight.x - originalHightlightX) >= units.gu(0.5));
258+ mouseRelease(indicatorsRow);
259+
260+ // should go back to 0
261+ tryCompare(highlight, "highlightCenterOffset", 0);
262+ }
263+
264+ // testing that negative changes to the lateral position offset shifts the highlight offset to the left
265+ function test_negativeLateralPositionChangesHighlightOffset() {
266+ indicatorsRow.expanded = true;
267+ wait_for_expansion_to_settle();
268+
269+ var highlight = findChild(indicatorsRow, "highlight");
270+ var item = findChild(indicatorsRow, root.originalModelData[2]["identifier"] + "-panelItem");
271+ verify(item !== null);
272+ var mappedPosition = indicatorsRow.mapFromItem(item, item.width/2, item.height/2);
273+
274+ mousePress(indicatorsRow, mappedPosition.x, mappedPosition.y);
275+ var originalHightlightX = highlight.x;
276+ var offset = 1;
277+ while((highlight.x - originalHightlightX) >= -units.gu(0.5) && offset < units.gu(10)) {
278+ mouseMove(indicatorsRow, mappedPosition.x - offset, mappedPosition.y, 10);
279+ offset = offset + 2;
280+ }
281+
282+ // verify that we hit the offset
283+ verify((highlight.x - originalHightlightX) <= -units.gu(0.5));
284+ mouseRelease(indicatorsRow);
285+
286+ // should go back to 0
287+ tryCompare(findChild(indicatorsRow, "highlight"), "highlightCenterOffset", 0);
288+ }
289+ }
290+}
tests/qmltests/Panel/Indicators/tst_DefaultIndicatorPage.qml → tests/qmltests/Panel/tst_IndicatorPage.qmlrenamed+17−22
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -17,26 +17,22 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19import Unity.Test 0.1 as UT
19import Unity.Test 0.1 as UT
20−import QMenuModel 0.1
20+import Unity.Indicators 0.1 as Indicators
21−import "../../../../qml/Panel/Indicators"
21+import "../../../qml/Panel"
22
22
23Item {
23Item {
24 id: testView
24 id: testView
25 width: units.gu(40)
25 width: units.gu(40)
26 height: units.gu(70)
26 height: units.gu(70)
27
27
28− DefaultIndicatorPage {
28+ IndicatorPage {
29 id: page
29 id: page
30−
31 anchors.fill: parent
30 anchors.fill: parent
32
31
33− menuModel: UnityMenuModel {}
34− busName: "test"
35− actionsObjectPath: "test"
36− menuObjectPath: "test"
37−
38 identifier: "test-indicator"
32 identifier: "test-indicator"
39− rootMenuType: "com.canonical.indicator.root"
33+ busName: "com.caninical.indicator.test"
34+ actionsObjectPath: "/com/canonical/indicator/test"
35+ menuObjectPath: "/com/canonical/indicator/test"
40 }
36 }
41
37
42 property var fullMenuData: [{
38 property var fullMenuData: [{
@@ -113,29 +109,28 @@ Item {
113 "submenu": []
109 "submenu": []
114 }]; // end row 1
110 }]; // end row 1
115
111
112+ function initializeMenuData(data) {
113+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicator/test",
114+ data);
115+ }
116+
116 UT.UnityTestCase {
117 UT.UnityTestCase {
117− name: "DefaultIndicatorPage"
118+ name: "IndicatorPage"
118
119
119 function init() {
120 function init() {
120− var mainMenu = findChild(page, "mainMenu");
121+ initializeMenuData([]);
121− page.menuModel.modelData = [];
122− verify(mainMenu.model !== null);
123 }
122 }
124
123
125 function test_reloadData() {
124 function test_reloadData() {
126 var mainMenu = findChild(page, "mainMenu");
125 var mainMenu = findChild(page, "mainMenu");
127
126
128− page.menuModel.modelData = [];
129 tryCompare(mainMenu, "count", 0);
127 tryCompare(mainMenu, "count", 0);
130
128
131− page.menuModel.modelData = fullMenuData;
129+ initializeMenuData(fullMenuData);
132 tryCompare(mainMenu, "count", 3);
130 tryCompare(mainMenu, "count", 3);
133
131
134− page.menuModel.modelData = [];
132+ initializeMenuData([]);
135 tryCompare(mainMenu, "count", 0);
133 tryCompare(mainMenu, "count", 0);
136−
137− page.menuModel.modelData = fullMenuData;
138− tryCompare(mainMenu, "count", 3);
139 }
134 }
140
135
141 function test_traverse_rootMenuType_data() {
136 function test_traverse_rootMenuType_data() {
@@ -147,7 +142,7 @@ Item {
147
142
148 function test_traverse_rootMenuType(data) {
143 function test_traverse_rootMenuType(data) {
149 page.rootMenuType = data.rootMenuType;
144 page.rootMenuType = data.rootMenuType;
150− page.menuModel.modelData = fullMenuData;
145+ initializeMenuData(fullMenuData);
151
146
152 var mainMenu = findChild(page, "mainMenu");
147 var mainMenu = findChild(page, "mainMenu");
153 tryCompare(mainMenu, "count", data.expectedCount);
148 tryCompare(mainMenu, "count", data.expectedCount);
tests/qmltests/Panel/tst_IndicatorRow.qmldeleted−160
@@ -1,160 +0,0 @@
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 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 QtTest 1.0
19−import Unity.Test 0.1 as UT
20−import ".."
21−import "../../../qml/Panel"
22−import Unity.Indicators 0.1 as Indicators
23−
24−/*
25− This tests the IndicatorRow component by using a fake model to stage data in the indicators
26− A view will show with indicators at the top, as does in the shell.
27−*/
28−Item {
29− id: rootItem
30− width: units.gu(40)
31− height: units.gu(60)
32−
33− function init_test()
34− {
35− indicatorModel.load("test1");
36−
37− indicatorRow.state = "initial";
38− indicatorRow.setCurrentItemIndex(-1);
39− indicatorRow.unitProgress = 0.0;
40− }
41−
42− PanelBackground {
43− anchors.fill: indicatorRow
44− }
45−
46− IndicatorRow {
47− id: indicatorRow
48− anchors {
49− left: parent.left
50− right: parent.right
51− }
52−
53− indicatorsModel: indicatorModel
54−
55− Component.onCompleted: indicatorModel.load("test1")
56− }
57−
58− Indicators.IndicatorsModel {
59− id: indicatorModel
60− }
61−
62− UT.UnityTestCase {
63− name: "IndicatorRow"
64− when: windowShown
65−
66− function get_indicator_item(index) {
67− return findChild(indicatorRow.row, "item" + index);
68− }
69−
70− function test_set_current_item() {
71− init_test();
72− indicatorRow.setCurrentItemIndex(0);
73− compare(indicatorRow.indicatorsModel.data(indicatorRow.currentItemIndex, Indicators.IndicatorsModelRole.Identifier), "indicator-fake1", "Incorrect item at position 0");
74−
75− indicatorRow.setCurrentItemIndex(1);
76− compare(indicatorRow.indicatorsModel.data(indicatorRow.currentItemIndex, Indicators.IndicatorsModelRole.Identifier), "indicator-fake2", "Incorrect item at position 1");
77−
78− indicatorRow.setCurrentItemIndex(2);
79− compare(indicatorRow.indicatorsModel.data(indicatorRow.currentItemIndex, Indicators.IndicatorsModelRole.Identifier), "indicator-fake3", "Incorrect item at position 2");
80− }
81−
82− function test_highlight_data() {
83− return [
84− { index: 0, progress: 0.0, current: false, other: false },
85− { index: 0, progress: 0.1, current: true, other: false },
86− { index: 0, progress: 0.5, current: true, other: false },
87− { index: 0, progress: 1.0, current: true, other: false },
88− { index: 2, progress: 0.0, current: false, other: false },
89− { index: 2, progress: 0.1, current: true, other: false },
90− { index: 2, progress: 0.5, current: true, other: false },
91− { index: 2, progress: 1.0, current: true, other: false }
92− ];
93− }
94−
95− function test_highlight(data) {
96− init_test();
97−
98− indicatorRow.unitProgress = data.progress;
99− indicatorRow.setCurrentItemIndex(data.index);
100−
101− compare(indicatorRow.currentItem.highlighted, data.current, "Indicator hightlight did not match for current item");
102−
103− for (var i = 0; i < indicatorRow.row.count; i++) {
104− compare(get_indicator_item(i).highlighted, i === data.index ? data.current: data.other, "Indicator hightlight did not match for item iter");
105− }
106− }
107−
108− function test_opacity_data() {
109− return [
110− { index: 0, progress: 0.0, current: 1.0, other: 1.0 },
111− { index: 0, progress: 0.1, current: 1.0, other: 0.9 },
112− { index: 0, progress: 0.5, current: 1.0, other: 0.5 },
113− { index: 0, progress: 1.0, current: 1.0, other: 0.0 },
114− { index: 2, progress: 0.0, current: 1.0, other: 1.0 },
115− { index: 2, progress: 0.1, current: 1.0, other: 0.9 },
116− { index: 2, progress: 0.5, current: 1.0, other: 0.5 },
117− { index: 2, progress: 1.0, current: 1.0, other: 0.0 }
118− ];
119− }
120−
121− function test_opacity(data) {
122− init_test();
123−
124− indicatorRow.unitProgress = data.progress;
125− indicatorRow.setCurrentItemIndex(data.index);
126−
127− tryCompare(indicatorRow.currentItem, "opacity", data.current);
128−
129− for (var i = 0; i < indicatorRow.row.count; i++) {
130− tryCompare(get_indicator_item(i), "opacity", i === data.index ? data.current: data.other);
131− }
132− }
133−
134− function test_dimmed_data() {
135− return [
136− { index: 0, progress: 0.0, current: false, other: false },
137− { index: 0, progress: 0.1, current: false, other: true },
138− { index: 0, progress: 0.5, current: false, other: true },
139− { index: 0, progress: 1.0, current: false, other: true },
140− { index: 2, progress: 0.0, current: false, other: false },
141− { index: 2, progress: 0.1, current: false, other: true },
142− { index: 2, progress: 0.5, current: false, other: true },
143− { index: 2, progress: 1.0, current: false, other: true }
144− ];
145− }
146−
147− function test_dimmed(data) {
148− init_test();
149−
150− indicatorRow.unitProgress = data.progress;
151− indicatorRow.setCurrentItemIndex(data.index);
152−
153− compare(indicatorRow.currentItem.dimmed, data.current, "Indicator dim did not match for current item");
154−
155− for (var i = 0; i < indicatorRow.row.count; i++) {
156− compare(get_indicator_item(i).dimmed, i === data.index ? data.current: data.other, "Indicator dim did not match for item iter");
157− }
158− }
159− }
160−}
tests/qmltests/Panel/tst_Indicators.qmldeleted−230
@@ -1,230 +0,0 @@
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 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 QtTest 1.0
19−import Unity.Test 0.1 as UT
20−import Ubuntu.Components 0.1 as UC
21−import ".."
22−import "../../../qml/Panel"
23−import "../../../qml/Components"
24−
25−/*
26− This tests the Indicators component by using a fake model to stage data in the indicators
27− A view will show with indicators at the top, as does in the shell. There is a clickable area
28− marked "Click Me" which can be used to expose the indicators.
29−*/
30−Item {
31− id: shell
32− width: units.gu(40)
33− height: units.gu(80)
34−
35− PanelBackground {
36− anchors.fill: indicators
37− }
38−
39− Indicators {
40− id: indicators
41− anchors {
42− right: parent.right
43− }
44− width: (shell.width > units.gu(60)) ? units.gu(40) : shell.width
45− y: 0
46− shown: false
47− profile: "test1"
48−
49− openedHeight: parent.height - button.height
50− }
51−
52− UC.Button {
53− id: button
54− text: indicators.shown ? "Hide" : "Show"
55− anchors {
56− bottom: shell.bottom
57− left: parent.left
58− right: parent.right
59− }
60− height: 50
61−
62− onClicked: {
63− if (!indicators.shown) {
64− indicators.show();
65− } else {
66− indicators.hide();
67− }
68− }
69− }
70−
71− UT.UnityTestCase {
72− name: "Indicators"
73− when: windowShown
74−
75− function init() {
76− indicators.initialise();
77−
78− indicators.hide();
79− tryCompare(indicators.hideAnimation, "running", false);
80− tryCompare(indicators, "state", "initial");
81− }
82−
83− // Showing the indicators should fully open the indicator panel.
84− function test_show() {
85− indicators.show()
86− tryCompare(indicators, "fullyOpened", true);
87− }
88−
89− // Test the change in the revealer lateral position changes the current panel menu to fit the position
90− // of the indicator in the row.
91− function test_change_revealer_lateral_position()
92− {
93− // tests changing the lateral position of the revealer activates the correct indicator items.
94−
95− var indicatorRow = findChild(indicators, "indicatorRow")
96− verify(indicatorRow !== null);
97− var indicatorRowItems = findChild(indicatorRow, "indicatorRowItems");
98− verify(indicatorRowItems !== null);
99−
100− for (var i = 0; i < indicatorRowItems.count; i++) {
101− var indicatorItem = findChild(indicatorRowItems, "item" + i);
102−
103− if (!indicatorItem.visible)
104− continue;
105−
106− var indicatorPosition = indicators.mapFromItem(indicatorItem,
107− indicatorItem.width/2, indicatorItem.height/2);
108−
109− touchFlick(indicators,
110− indicatorPosition.x, indicatorPosition.y,
111− indicatorPosition.x, indicators.openedHeight * 0.4,
112− true /* beginTouch */, false /* endTouch */);
113−
114− compare(indicatorRow.currentItem, indicatorItem,
115− "Incorrect item activated at position " + i);
116−
117− touchFlick(indicators,
118− indicatorPosition.x, indicators.openedHeight * 0.4,
119− indicatorPosition.x, indicatorPosition.y,
120− false /* beginTouch */, true /* endTouch */);
121−
122− // wait until fully closed
123− tryCompare(indicators, "height", indicators.panelHeight);
124− }
125− }
126−
127− // values for specific state changes are subject to internal decisions, so we can't
128− // determine the true height value which would cause the state to change without making
129− // too many assuptyions
130− // However, we can assume that a partially opened panel will not be initial, and fully
131− // opened panel will be locked.
132−
133− function test_progress_changes_state_to_not_initial() {
134− indicators.height = indicators.openedHeight / 2
135− compare(indicators.state!="initial", true,
136− "Indicators should not be in initial state when partially opened.");
137− }
138−
139− function test_progress_changes_state_to_locked() {
140− indicators.height = indicators.openedHeight - indicators.panelHeight
141− compare(indicators.state, "locked", "Indicators should be locked when fully opened.");
142− }
143−
144− function test_partially_open() {
145− indicators.height = indicators.openedHeight / 2
146− compare(indicators.partiallyOpened, true,
147− "Indicator should show as partially opened when height is half of openedHeight");
148− compare(indicators.fullyOpened, false,
149− "Indicator should not show as fully opened when height is half of openedHeight");
150− }
151−
152− function test_fully_open() {
153− indicators.height = indicators.openedHeight
154− compare(indicators.partiallyOpened, false);
155− compare(indicators.fullyOpened, true);
156− }
157−
158− function init_invisible_indicator(identifier) {
159− tryCompareFunction(function() { return findChild(indicators, identifier+"-delegate") !== undefined }, true);
160− var item = findChild(indicators, identifier+"-delegate");
161−
162− item.enabled = false;
163− }
164−
165− function test_row_visible_menuContent_visible_data() { return [
166− {tag: "first", visible: [false, true, true, true, true] },
167− {tag: "adjacent", visible: [true, false, false, true, true] },
168− {tag: "bounds", visible: [false, true, true, true, false] },
169− {tag: "disjoint", visible: [true, false, true, false, true] },
170− {tag: "last", visible: [true, true, true, true, false] }];
171− }
172−
173− function test_row_visible_menuContent_visible(data) {
174− indicators.show();
175−
176− var contentListView = findChild(indicators, "indicatorsContentListView");
177− var indicatorRowItems = findChild(indicators, "indicatorRowItems");
178−
179− var count = data.visible.length
180− for (var i = 0; i< data.visible.length; i++) {
181− if (data.visible[i] === false) {
182− init_invisible_indicator("indicator-fake" + (i + 1));
183− count--;
184− }
185− }
186−
187− tryCompare(indicatorRowItems, "count", count);
188−
189− for (i = 0; i < data.visible.length; i++) {
190− var widgetName = "indicator-fake" + (i + 1 + "-widget");
191− var pageName = "indicator-fake" + (i + 1 + "-page");
192−
193− // check for item
194− tryCompareFunction(function() { return findChild(indicatorRowItems, widgetName) !== null }, data.visible[i]);
195−
196− // check for tab
197− tryCompareFunction(function() { return findChild(contentListView, pageName) !== null }, data.visible[i]);
198− }
199− }
200−
201− function test_indicator_visible_correct_menu_data() { return [
202− {tag: "current-first", currentIndex: 0, visible: [false, true, true, true, true], expectedIndex: 0, expextedMenu: "indicator-fake2" },
203− {tag: "current-last", currentIndex: 4, visible: [true, true, true, true, false], expectedIndex: 3, expextedMenu: "indicator-fake4" },
204− {tag: "after", currentIndex: 0, visible: [true, false, true, true, true], expectedIndex: 0, expextedMenu: "indicator-fake1" },
205− {tag: "before", currentIndex: 1, visible: [false, true, true, true, true], expectedIndex: 1, expextedMenu: "indicator-fake2" }];
206− }
207−
208− function test_indicator_visible_correct_menu(data) {
209− var contentListView = findChild(indicators, "indicatorsContentListView");
210− var indicatorRow = findChild(indicators, "indicatorRow");
211−
212− indicators.show();
213− indicatorRow.setCurrentItemIndex(data.currentIndex);
214− tryCompare(indicators, "fullyOpened", true);
215−
216− for (var i = 0; i< data.visible.length; i++) {
217− if (data.visible[i] === false) {
218− init_invisible_indicator("indicator-fake" + (i + 1));
219− }
220− }
221−
222− // check for current selected item
223− tryCompare(indicatorRow, "currentItemIndex", data.expectedIndex);
224−
225− // check for current selected tab
226− tryCompareFunction(function() { return findChild(contentListView, data.expextedMenu) === contentListView.currentItem }, true);
227−
228− }
229− }
230−}
tests/qmltests/Panel/tst_IndicatorsBar.qmlnew file+224
@@ -0,0 +1,224 @@
1+/*
2+ * Copyright 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+
17+import QtQuick 2.1
18+import QtQuick.Layouts 1.1
19+import QtTest 1.0
20+import "../../../qml/Panel"
21+import Ubuntu.Components 0.1
22+import Unity.Test 0.1 as UT
23+import Unity.Indicators 0.1 as Indicators
24+
25+IndicatorTest {
26+ id: root
27+ width: units.gu(100)
28+ height: units.gu(40)
29+
30+
31+ RowLayout {
32+ anchors.fill: parent
33+ anchors.margins: units.gu(1)
34+
35+ Rectangle {
36+ id: itemArea
37+ color: "blue"
38+ Layout.fillWidth: true
39+ Layout.fillHeight: true
40+
41+ Rectangle {
42+ color: "black"
43+ anchors.fill: indicatorsBar
44+ }
45+
46+ IndicatorsBar {
47+ id: indicatorsBar
48+ height: expanded ? units.gu(7) : units.gu(3)
49+ width: units.gu(30)
50+ anchors.centerIn: parent
51+ indicatorsModel: root.indicatorsModel
52+ interactive: expanded && height === units.gu(7)
53+
54+ Behavior on height {
55+ NumberAnimation {
56+ id: heightAnimation
57+ duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing
58+ }
59+ }
60+
61+ MouseArea {
62+ anchors.fill: parent
63+ enabled: !indicatorsBar.expanded
64+ onPressed: {
65+ indicatorsBar.selectItemAt(mouse.x);
66+ indicatorsBar.expanded = true
67+ }
68+ }
69+ }
70+ }
71+
72+ ColumnLayout {
73+ Layout.alignment: Qt.AlignTop
74+ Layout.fillWidth: false
75+
76+ Button {
77+ Layout.fillWidth: true
78+ text: indicatorsBar.expanded ? "Collapse" : "Expand"
79+ onClicked: indicatorsBar.expanded = !indicatorsBar.expanded
80+ }
81+
82+ Rectangle {
83+ Layout.preferredHeight: units.dp(1);
84+ Layout.fillWidth: true;
85+ color: "black"
86+ }
87+
88+ Repeater {
89+ model: root.originalModelData
90+ RowLayout {
91+ CheckBox {
92+ checked: true
93+ onCheckedChanged: checked ? insertIndicator(index) : removeIndicator(index);
94+ }
95+ Label {
96+ Layout.fillWidth: true
97+ text: modelData["identifier"]
98+ }
99+
100+ CheckBox {
101+ checked: true
102+ onCheckedChanged: setIndicatorVisible(index, checked);
103+ }
104+ Label {
105+ text: "visible"
106+ }
107+ }
108+ }
109+ }
110+ }
111+
112+ UT.UnityTestCase {
113+ name: "IndicatorsBar"
114+ when: windowShown
115+
116+ function init() {
117+ indicatorsBar.expanded = false;
118+ wait_for_expansion_to_settle();
119+ }
120+
121+ function test_expandSelectedItem_data() {
122+ return [
123+ { index: 0 },
124+ { index: 2 },
125+ { index: 4 }
126+ ];
127+ }
128+
129+ function wait_for_expansion_to_settle() {
130+ tryCompare(heightAnimation, "running", false);
131+ wait(UbuntuAnimation.SnapDuration); // put a little extra wait in for things to settle
132+ }
133+
134+ // Rough check that expanding a selected item keeps it within the area of the original item.
135+ function test_expandSelectedItem(data) {
136+ var dataItem = findChild(indicatorsBar, root.originalModelData[data.index]["identifier"] + "-panelItem");
137+ verify(dataItem !== null);
138+
139+ var mappedPosition = indicatorsBar.mapFromItem(dataItem, dataItem.width/2, dataItem.height/2);
140+
141+ indicatorsBar.selectItemAt(mappedPosition.x);
142+ indicatorsBar.expanded = true;
143+ wait_for_expansion_to_settle();
144+
145+
146+ // mappedPosition contained within mappedRect
147+ tryCompareFunction(function() {
148+ var mappedRect = indicatorsBar.mapFromItem(dataItem, 0, 0, dataItem.width, dataItem.height);
149+ return mappedRect.x <= mappedPosition.x; },
150+ true);
151+ tryCompareFunction(function() {
152+ var mappedRect = indicatorsBar.mapFromItem(dataItem, 0, 0, dataItem.width, dataItem.height);
153+ return mappedRect.x + mappedRect.width >= mappedPosition.x;
154+ }, true);
155+ }
156+
157+ function test_scrollOffset() {
158+ indicatorsBar.expanded = true;
159+ wait_for_expansion_to_settle();
160+
161+ var lastItemIndex = root.originalModelData.length-1;
162+ var dataItem = findChild(indicatorsBar, root.originalModelData[lastItemIndex]["identifier"] + "-panelItem");
163+ verify(dataItem !== null);
164+
165+ var row = findChild(indicatorsBar, "indicatorItemRow");
166+ // test will not work without these conditions
167+ verify(row.width >= indicatorsBar.width + dataItem.width);
168+
169+ var mappedPosition = indicatorsBar.mapFromItem(dataItem, dataItem.width/2, dataItem.height/2);
170+ indicatorsBar.addScrollOffset(-dataItem.width);
171+ var newMappedPosition = indicatorsBar.mapFromItem(dataItem, dataItem.width/2, dataItem.height/2);
172+
173+ compare(mappedPosition.x, newMappedPosition.x - dataItem.width);
174+ }
175+
176+ function test_selectItemWhenExpanded_data() {
177+ return [
178+ { index: 3 },
179+ { index: 4 }
180+ ];
181+ }
182+
183+ function test_selectItemWhenExpanded(data) {
184+ indicatorsBar.expanded = true;
185+ wait_for_expansion_to_settle();
186+
187+ var dataItem = findChild(indicatorsBar, root.originalModelData[data.index]["identifier"] + "-panelItem");
188+ if (indicatorsBar.mapFromItem(dataItem, dataItem.width/2, dataItem.height/2).x < 0) {
189+ skip("Out of bounds");
190+ }
191+ mouseClick(dataItem, dataItem.width/2, dataItem.height/2);
192+ verify(dataItem.selected === true);
193+ }
194+
195+ function test_visibleIndicators_data() {
196+ return [
197+ { visible: [true, false, true, false, true, true] },
198+ { visible: [false, false, false, false, false, false] }
199+ ];
200+ }
201+
202+ function test_visibleIndicators(data) {
203+ for (var i = 0; i < data.visible.length; i++) {
204+ var visible = data.visible[i];
205+ root.setIndicatorVisible(i, visible);
206+
207+ var dataItem = findChild(indicatorsBar, root.originalModelData[i]["identifier"] + "-panelItem");
208+ tryCompare(dataItem, "opacity", visible ? 1.0 : 0.0);
209+ tryCompareFunction(function() { return dataItem.width > 0.0; }, visible);
210+ }
211+
212+ indicatorsBar.expanded = true;
213+ wait_for_expansion_to_settle();
214+
215+ for (var i = 0; i < data.visible.length; i++) {
216+ root.setIndicatorVisible(i, data.visible[i]);
217+
218+ var dataItem = findChild(indicatorsBar, root.originalModelData[i]["identifier"] + "-panelItem");
219+ tryCompareFunction(function() { return dataItem.opacity > 0.0; }, true);
220+ tryCompareFunction(function() { return dataItem.width > 0.0; }, true);
221+ }
222+ }
223+ }
224+}
tests/qmltests/Panel/tst_IndicatorsMenu.qmlnew file+258
@@ -0,0 +1,258 @@
1+/*
2+ * Copyright 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+
17+import QtQuick 2.1
18+import QtQuick.Layouts 1.1
19+import QtTest 1.0
20+import "../../../qml/Panel"
21+import Ubuntu.Components 0.1
22+import Unity.Test 0.1 as UT
23+import Unity.Indicators 0.1 as Indicators
24+
25+IndicatorTest {
26+ id: root
27+ width: units.gu(80)
28+ height: units.gu(71)
29+ color: "white"
30+
31+ property string indicatorProfile: "phone"
32+
33+ RowLayout {
34+ anchors.fill: parent
35+ anchors.margins: units.gu(1)
36+
37+ Rectangle {
38+ Layout.fillWidth: true
39+ Layout.fillHeight: true
40+
41+ id: itemArea
42+ color: "blue"
43+
44+ IndicatorsMenu {
45+ id: indicatorsMenu
46+ width: units.gu(40)
47+ anchors {
48+ top: parent.top
49+ right: parent.right
50+ }
51+ minimizedPanelHeight: units.gu(3)
52+ expandedPanelHeight: units.gu(7)
53+ openedHeight: parent.height
54+ indicatorsModel: root.indicatorsModel
55+ shown: false
56+ }
57+ }
58+
59+ ColumnLayout {
60+ Layout.alignment: Qt.AlignTop
61+ Layout.fillWidth: false
62+
63+ Button {
64+ Layout.fillWidth: true
65+ text: indicatorsMenu.shown ? "Hide" : "Show"
66+ onClicked: {
67+ if (indicatorsMenu.shown) {
68+ indicatorsMenu.hide();
69+ } else {
70+ indicatorsMenu.show();
71+ }
72+ }
73+ }
74+
75+ Rectangle {
76+ Layout.preferredHeight: units.dp(1);
77+ Layout.fillWidth: true;
78+ color: "black"
79+ }
80+
81+ Repeater {
82+ model: root.originalModelData
83+ RowLayout {
84+ CheckBox {
85+ checked: true
86+ onCheckedChanged: checked ? insertIndicator(index) : removeIndicator(index);
87+ }
88+ Label {
89+ Layout.fillWidth: true
90+ text: modelData["identifier"]
91+ }
92+
93+ CheckBox {
94+ checked: true
95+ onCheckedChanged: setIndicatorVisible(index, checked);
96+ }
97+ Label {
98+ text: "visible"
99+ }
100+ }
101+ }
102+ }
103+ }
104+
105+ UT.UnityTestCase {
106+ id: testCase
107+ name: "IndicatorsMenu"
108+ when: windowShown
109+
110+ function init() {
111+ indicatorsMenu.hide();
112+ tryCompare(indicatorsMenu.hideAnimation, "running", false);
113+ compare(indicatorsMenu.state, "initial");
114+
115+ indicatorsMenu.verticalVelocityThreshold = 0.5
116+ }
117+
118+ function get_indicator_item(index) {
119+ var indicatorItem = findChild(indicatorsMenu, root.originalModelData[index]["identifier"] + "-panelItem");
120+ verify(indicatorItem !== null);
121+
122+ return indicatorItem;
123+ }
124+
125+ // Showing the indicators should fully open the indicator panel.
126+ function test_showAndHide() {
127+ indicatorsMenu.show();
128+ tryCompare(indicatorsMenu, "fullyOpened", true);
129+
130+ indicatorsMenu.hide();
131+ tryCompare(indicatorsMenu, "fullyClosed", true);
132+ }
133+
134+ // Test that closing the indicators ends up in the correct position.
135+ function test_hideEndsInCorrectPosition() {
136+ var indicatorsBar = findChild(indicatorsMenu, "indicatorsBar");
137+ var flickable = findChild(indicatorsBar, "flickable");
138+
139+ var originalContentX = flickable.contentX;
140+
141+ indicatorsMenu.show();
142+ indicatorsBar.setCurrentItemIndex(0);
143+ tryCompare(indicatorsMenu, "fullyOpened", true);
144+
145+ indicatorsMenu.hide();
146+ tryCompare(flickable, "contentX", originalContentX);
147+ }
148+
149+ function test_progress_changes_state_to_reveal() {
150+ indicatorsMenu.height = indicatorsMenu.openedHeight / 2;
151+ compare(indicatorsMenu.state, "reveal", "Indicators should be revealing when partially opened.");
152+
153+ indicatorsMenu.height = indicatorsMenu.openedHeight;
154+ compare(indicatorsMenu.state, "reveal", "Indicators should still be revealing when fully opened.");
155+ }
156+
157+ function test_open_state() {
158+ compare(indicatorsMenu.fullyClosed, true, "Indicator should show as fully closed.");
159+ compare(indicatorsMenu.partiallyOpened, false, "Indicator should not show as partially opened");
160+ compare(indicatorsMenu.fullyOpened, false, "Indicator should not show as fully opened");
161+
162+ indicatorsMenu.height = indicatorsMenu.openedHeight / 2
163+ compare(indicatorsMenu.fullyClosed, false, "Indicator should not show as fully closed.");
164+ compare(indicatorsMenu.partiallyOpened, true, "Indicator should show as partially opened");
165+ compare(indicatorsMenu.fullyOpened, false, "Indicator should not show as fully opened");
166+
167+ indicatorsMenu.height = indicatorsMenu.openedHeight;
168+ compare(indicatorsMenu.fullyClosed, false, "Indicator should not show as fully closed.");
169+ compare(indicatorsMenu.partiallyOpened, false, "Indicator should show as partially opened");
170+ compare(indicatorsMenu.fullyOpened, true, "Indicator should not show as fully opened");
171+ }
172+
173+ // Pressing on the indicator panel should activate the indicator hints
174+ // and expose the header
175+ function test_hint() {
176+ var indicatorItem = get_indicator_item(0);
177+ var mappedPosition = root.mapFromItem(indicatorItem, indicatorItem.width/2, indicatorItem.height/2);
178+
179+ touchPress(indicatorsMenu, mappedPosition.x, indicatorsMenu.minimizedPanelHeight / 2);
180+
181+ // hint animation should be run, meaning that indicators will move downwards
182+ // by hintValue pixels without any drag taking place
183+ tryCompareFunction(function() { return indicatorsMenu.height }, indicatorsMenu.expandedPanelHeight + units.gu(2));
184+ tryCompare(indicatorsMenu, "partiallyOpened", true);
185+
186+ touchRelease(indicatorsMenu, mappedPosition.x, indicatorsMenu.minimizedPanelHeight / 2);
187+ }
188+
189+ // tests swiping on an indicator item activates the correct item.
190+ function test_swipeForCurrentItem()
191+ {
192+ var indicatorItemRow = findChild(indicatorsMenu, "indicatorItemRow");
193+ verify(indicatorItemRow !== null);
194+
195+ for (var i = 0; i < root.originalModelData.length; i++) {
196+ var indicatorItem = get_indicator_item(i);
197+
198+ var mappedPosition = root.mapFromItem(indicatorItem, indicatorItem.width/2, indicatorItem.height/2);
199+
200+ touchFlick(indicatorsMenu,
201+ mappedPosition.x, mappedPosition.y,
202+ mappedPosition.x, indicatorsMenu.openedHeight / 2,
203+ true /* beginTouch */, false /* endTouch */);
204+
205+ compare(indicatorItemRow.currentItem, indicatorItem,
206+ "Incorrect item activated at position " + i);
207+
208+ touchFlick(indicatorItemRow,
209+ mappedPosition.x, indicatorsMenu.openedHeight / 2,
210+ mappedPosition.x, mappedPosition.y,
211+ false /* beginTouch */, true /* endTouch */);
212+
213+ // wait until fully closed
214+ tryCompare(indicatorsMenu, "fullyClosed", true);
215+ }
216+ }
217+
218+ // Test the vertical velocity check when flicking the indicators open at an angle.
219+ // If the vertical velocity is above a specific point, we shouldnt change active indicators
220+ // if the x position changes
221+ function test_verticalVelocityDetector() {
222+ indicatorsMenu.verticalVelocityThreshold = 0;
223+ verify(root.originalModelData.length >= 2);
224+
225+ var indicatorItemRow = findChild(indicatorsMenu, "indicatorItemRow");
226+ verify(indicatorItemRow !== null);
227+
228+ // Get the first indicator
229+ var firstItem = get_indicator_item(0);
230+ var firstItemMappedPosition = root.mapFromItem(firstItem, firstItem.width/2, firstItem.height/2);
231+
232+ // 1) Drag the mouse down to hint a bit
233+ touchFlick(indicatorsMenu,
234+ firstItemMappedPosition.x, indicatorsMenu.minimizedPanelHeight / 2,
235+ firstItemMappedPosition.x, indicatorsMenu.minimizedPanelHeight * 2,
236+ true /* beginTouch */, false /* endTouch */);
237+
238+ tryCompare(indicatorItemRow, "currentItem", firstItem)
239+
240+ // next time position will have moved.
241+ var nextItem = get_indicator_item(1);
242+ var nextItemMappedPosition = root.mapFromItem(nextItem, nextItem.width/2, nextItem.height/2);
243+
244+ // 1) Flick mouse down to bottom
245+ touchFlick(indicatorsMenu,
246+ firstItemMappedPosition.x, indicatorsMenu.minimizedPanelHeight * 2,
247+ nextItemMappedPosition.x, indicatorsMenu.openedHeight / 3,
248+ false /* beginTouch */, false /* endTouch */,
249+ units.gu(50) /* speed */, 5 /* iterations */); // more samples needed for accurate velocity
250+
251+ compare(indicatorItemRow.currentItem, firstItem, "First indicator should still be the current item");
252+ // after waiting in the same spot with touch down, it should update to the next item.
253+ tryCompare(indicatorItemRow, "currentItem", nextItem);
254+
255+ touchRelease(indicatorsMenu, nextItemMappedPosition.x, indicatorsMenu.openedHeight / 3);
256+ }
257+ }
258+}
tests/qmltests/Panel/tst_MenuContent.qml+9−25
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -17,36 +17,21 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19import Unity.Test 0.1 as UT
19import Unity.Test 0.1 as UT
20−import ".."
21import "../../../qml/Panel"
20import "../../../qml/Panel"
22import Unity.Indicators 0.1 as Indicators
21import Unity.Indicators 0.1 as Indicators
23
22
24−Item {
23+IndicatorTest {
25− id: shell
24+ id: root
26 width: units.gu(40)
25 width: units.gu(40)
27 height: units.gu(70)
26 height: units.gu(70)
28
27
29− property var indicator_status: {
30− 'indicator-fake1-page': { 'started': false, 'reset': 0 },
31− 'indicator-fake2-page': { 'started': false, 'reset': 0 },
32− 'indicator-fake3-page': { 'started': false, 'reset': 0 },
33− 'indicator-fake4-page': { 'started': false, 'reset': 0 },
34− 'indicator-fake5-page': { 'started': false, 'reset': 0 }
35− }
36−
37 // Dummy objects
28 // Dummy objects
38 Item { id: greeter }
29 Item { id: greeter }
39 Item { id: handle }
30 Item { id: handle }
40
31
41−
42− Indicators.IndicatorsModel {
43− id: indicatorsModel
44− Component.onCompleted: load("test1")
45− }
46−
47 MenuContent {
32 MenuContent {
48 id: menuContent
33 id: menuContent
49− indicatorsModel: indicatorsModel
34+ indicatorsModel: root.indicatorsModel
50 height: parent.height - 50
35 height: parent.height - 50
51 }
36 }
52
37
@@ -78,12 +63,12 @@ Item {
78 if (menuContent.currentMenuIndex == -1)
63 if (menuContent.currentMenuIndex == -1)
79 activate_content(0);
64 activate_content(0);
80 else
65 else
81− activate_content((menuContent.currentMenuIndex + 1) % indicatorsModel.count)
66+ activate_content((menuContent.currentMenuIndex + 1) % root.originalModelData.length)
82 }
67 }
83
68
84 function activate_content(index)
69 function activate_content(index)
85 {
70 {
86− menuContent.setCurrentMenuIndex(index)
71+ menuContent.currentMenuIndex = index;
87 }
72 }
88
73
89 function get_test_menu_objecName(index) {
74 function get_test_menu_objecName(index) {
@@ -115,7 +100,7 @@ Item {
115
100
116 // Check that the correct menus are displayed for the requested item.
101 // Check that the correct menus are displayed for the requested item.
117 function test_show_menu() {
102 function test_show_menu() {
118− var menuCount = indicatorsModel.count;
103+ var menuCount = root.originalModelData.length;
119 verify(menuCount > 0, "Menu count should be greater than zero");
104 verify(menuCount > 0, "Menu count should be greater than zero");
120
105
121 var listView = menu_content_test.findChild(menuContent, "indicatorsContentListView")
106 var listView = menu_content_test.findChild(menuContent, "indicatorsContentListView")
@@ -126,7 +111,7 @@ Item {
126 var menuIndex = i%menuCount;
111 var menuIndex = i%menuCount;
127
112
128 activate_content(menuIndex);
113 activate_content(menuIndex);
129− testItemObjectName = indicatorsModel.data(menuIndex, Indicators.IndicatorsModelRole.Identifier);
114+ testItemObjectName = indicatorsModel.model.data(menuIndex, Indicators.IndicatorsModelRole.Identifier);
130 compare(listView.currentIndex, menuIndex, "Current tab index does not match selected tab index");
115 compare(listView.currentIndex, menuIndex, "Current tab index does not match selected tab index");
131 tryCompareFunction(current_item_equals_test_item, true);
116 tryCompareFunction(current_item_equals_test_item, true);
132 }
117 }
@@ -134,14 +119,13 @@ Item {
134
119
135 // Tests QTBUG-30632 - asynchronous loader crashes when changing index quickly.
120 // Tests QTBUG-30632 - asynchronous loader crashes when changing index quickly.
136 function test_multi_activate() {
121 function test_multi_activate() {
137− var menuCount = indicatorsModel.count;
122+ var menuCount = root.originalModelData.length;
138 verify(menuCount > 0, "Menu count should be greater than zero");
123 verify(menuCount > 0, "Menu count should be greater than zero");
139
124
140 for (var i = 0; i < 100; i++) {
125 for (var i = 0; i < 100; i++) {
141 activate_content(i % menuCount);
126 activate_content(i % menuCount);
142 compare(menuContent.currentMenuIndex, i%menuCount);
127 compare(menuContent.currentMenuIndex, i%menuCount);
143 }
128 }
144− wait(100);
145 }
129 }
146 }
130 }
147}
131}
tests/qmltests/Panel/tst_Panel.qml+185−214
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -14,95 +14,106 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.1
18+import QtQuick.Layouts 1.1
18import QtTest 1.0
19import QtTest 1.0
19import Unity.Test 0.1 as UT
20import Unity.Test 0.1 as UT
20−import Ubuntu.Components 0.1 as UC
21+import Ubuntu.Components 0.1
22+import Unity.Indicators 0.1 as Indicators
21import Ubuntu.Telephony 0.1 as Telephony
23import Ubuntu.Telephony 0.1 as Telephony
22−import ".."
23import "../../../qml/Panel"
24import "../../../qml/Panel"
24
25
25−/*
26+IndicatorTest {
26− This tests the Panel component using a fake model to stage data in the indicators
27+ id: root
27− A view will show with indicators at the top, as does in the shell. This can be controlled
28− as in the shell.
29−*/
30−Item {
31− id: shell
32− width: units.gu(40)
28+ width: units.gu(100)
33− height: units.gu(80)
29+ height: units.gu(71)
34−
30+ color: "white"
35− Panel {
36− id: panel
37− anchors {
38− left: parent.left
39− right: parent.right
40− }
41− height: parent.height - row.height
42− fullscreenMode: false
43
31
44− indicators {
32+ property string indicatorProfile: "phone"
45− profile: "test1"
46− panelHeight: units.gu(5)
47− }
48− callHint {
49− height: units.gu(4)
50− }
51
33
52− property real panelAndSeparatorHeight: panel.indicators.panelHeight + units.dp(2)
34+ RowLayout {
53− }
35+ anchors.fill: parent
36+ anchors.margins: units.gu(1)
37+ clip: true
54
38
39+ Rectangle {
40+ Layout.fillWidth: true
41+ Layout.fillHeight: true
55
42
56− Row {
43+ id: itemArea
57− id: row
44+ color: "blue"
58− anchors {
59− bottom: shell.bottom
60− left: parent.left
61− right: parent.right
62− }
63− height: 50
64
45
65− UC.Button {
46+ Panel {
66− text: panel.indicators.shown ? "Hide" : "Show"
47+ id: panel
67− anchors {
48+ anchors.fill: parent
68− top: parent.top
49+ indicators {
69− bottom: parent.bottom
50+ width: parent.width > units.gu(60) ? units.gu(40) : parent.width
51+ indicatorsModel: root.indicatorsModel
52+ }
53+
54+ property real panelAndSeparatorHeight: panel.indicators.minimizedPanelHeight + units.dp(2)
70 }
55 }
71− width: parent.width/3
56+ }
72
57
58+ ColumnLayout {
59+ Layout.alignment: Qt.AlignTop
60+ Layout.fillWidth: false
61+
62+ Button {
63+ Layout.fillWidth: true
64+ text: panel.indicators.shown ? "Hide" : "Show"
73− onClicked: {
65+ onClicked: {
74− if (panel.indicators.shown) {
66+ if (panel.indicators.shown) {
75− panel.indicators.hide();
67+ panel.indicators.hide();
76− } else {
68+ } else {
77− panel.indicators.show();
69+ panel.indicators.show();
70+ }
78 }
71 }
79 }
72 }
80− }
81
73
82− UC.Button {
74+ Button {
83− text: panel.fullscreenMode ? "Maximize" : "FullScreen"
75+ text: panel.fullscreenMode ? "Maximize" : "FullScreen"
84− anchors {
76+ Layout.fillWidth: true
85− top: parent.top
77+ onClicked: panel.fullscreenMode = !panel.fullscreenMode
86− bottom: parent.bottom
87 }
78 }
88− width: parent.width/3
89
79
90− onClicked: panel.fullscreenMode = !panel.fullscreenMode
80+ Button {
81+ Layout.fillWidth: true
82+ text: callManager.hasCalls ? "Called" : "No Calls"
83+ onClicked: {
84+ if (callManager.foregroundCall) {
85+ callManager.foregroundCall = null;
86+ } else {
87+ callManager.foregroundCall = phoneCall;
88+ }
89+ }
91− }
90+ }
92
91
93− UC.Button {
92+ Rectangle {
94− text: callManager.hasCalls ? "Called" : "No Calls"
93+ Layout.preferredHeight: units.dp(1);
95− anchors {
94+ Layout.fillWidth: true;
96− top: parent.top
95+ color: "black"
97− bottom: parent.bottom
98 }
96 }
99− width: parent.width/3
100
97
101− onClicked: {
98+ Repeater {
102− if (callManager.foregroundCall) {
99+ model: root.originalModelData
103− callManager.foregroundCall = null;
100+ RowLayout {
104− } else {
101+ CheckBox {
105− callManager.foregroundCall = phoneCall;
102+ checked: true
103+ onCheckedChanged: checked ? insertIndicator(index) : removeIndicator(index);
104+ }
105+ Label {
106+ Layout.fillWidth: true
107+ text: modelData["identifier"]
108+ }
109+
110+ CheckBox {
111+ checked: true
112+ onCheckedChanged: setIndicatorVisible(index, checked);
113+ }
114+ Label {
115+ text: "visible"
116+ }
106 }
117 }
107 }
118 }
108 }
119 }
@@ -118,13 +129,12 @@ Item {
118 when: windowShown
129 when: windowShown
119
130
120 function init() {
131 function init() {
121− panel.indicators.initialise();
122 panel.fullscreenMode = false;
132 panel.fullscreenMode = false;
133+ callManager.foregroundCall = null;
123
134
124 panel.indicators.hide();
135 panel.indicators.hide();
125 // Wait for animation to complete
136 // Wait for animation to complete
126 tryCompare(panel.indicators.hideAnimation, "running", false);
137 tryCompare(panel.indicators.hideAnimation, "running", false);
127− callManager.foregroundCall = null;
128
138
129 // Wait for the indicators to get into position.
139 // Wait for the indicators to get into position.
130 // (switches between normal and fullscreen modes are animated)
140 // (switches between normal and fullscreen modes are animated)
@@ -133,100 +143,21 @@ Item {
133 }
143 }
134
144
135 function get_indicator_item(index) {
145 function get_indicator_item(index) {
136− var indicatorRow = findChild(panel.indicators, "indicatorRow");
146+ var indicatorItem = findChild(panel, root.originalModelData[index]["identifier"]+"-panelItem");
137− verify(indicatorRow !== null);
138−
139− return findChild(indicatorRow.row, "item" + index);
140− }
141−
142− function get_indicator_item_position(index) {
143− var indicatorRow = findChild(panel.indicators, "indicatorRow");
144− verify(indicatorRow !== null);
145−
146− var indicatorItem = get_indicator_item(index);
147 verify(indicatorItem !== null);
147 verify(indicatorItem !== null);
148
148
149− return panel.mapFromItem(indicatorItem, indicatorItem.width/2, indicatorItem.height/2);
149+ return indicatorItem;
150− }
151−
152− // Pressing on the indicator panel should activate the indicator hints
153− // and expose a portion of the conent.
154− function test_hint() {
155− var indicatorItemCoord = get_indicator_item_position(0);
156−
157− touchPress(panel, indicatorItemCoord.x, panel.indicators.panelHeight / 2);
158−
159− // hint animation should be run, meaning that indicators will move downwards
160− // by hintValue pixels without any drag taking place
161− tryCompareFunction(function() { return panel.indicators.height },
162− panel.indicators.panelHeight + panel.indicators.hintValue);
163− tryCompare(panel.indicators, "partiallyOpened", true);
164− tryCompare(panel.indicators, "fullyOpened", false);
165−
166− touchRelease(panel, indicatorItemCoord.x, panel.indicators.panelHeight/2);
167− }
168−
169− // Pressing on the top edge of the screen should have no effect if the panel
170− // is hidden (fullscreen), which is the case when a fullscreen app is being shown
171− function test_noHintOnFullscreenMode() {
172− panel.fullscreenMode = true;
173− // Wait for the indicators to get into position.
174− // (switches between normal and fullscreen modes are animated)
175− var indicatorArea = findChild(panel, "indicatorArea");
176− tryCompare(indicatorArea, "y", -panel.panelHeight);
177−
178− var indicatorItemCoord = get_indicator_item_position(0);
179−
180− touchPress(panel, indicatorItemCoord.x, panel.indicators.panelHeight / 2);
181−
182− // Give some time for a hint animation to change things, if any
183− wait(500);
184−
185− // no hint animation when fullscreen
186− compare(panel.indicators.partiallyOpened, false,
187− "Indicator should not be partially opened when panel is pressed in" +
188− " fullscreenmode");
189− compare(panel.indicators.fullyOpened, false, "Indicator should not be partially" +
190− " opened when panel is pressed in fullscreenmode");
191−
192− touchRelease(panel, indicatorItemCoord.x, panel.panelHeight/2);
193− }
194−
195− // Pressing on the top edge of the indicator should have no effect if the panel
196− // has an active call
197− function test_noHintOnActiveCall() {
198− callManager.foregroundCall = phoneCall;
199−
200− var indicatorItemCoord = get_indicator_item_position(0);
201−
202− touchPress(panel, indicatorItemCoord.x, panel.callHint.height + panel.indicators.panelHeight / 2);
203−
204− // Give some time for a hint animation to change things, if any
205− wait(500);
206−
207− // no hint animation when fullscreen
208− compare(panel.indicators.partiallyOpened, false,
209− "Indicator should not be partially opened when panel is pressed in" +
210− " fullscreenmode");
211− compare(panel.indicators.fullyOpened, false, "Indicator should not be partially" +
212− " opened when panel is pressed in fullscreenmode");
213−
214− touchRelease(panel, indicatorItemCoord.x, panel.panelHeight/2);
215 }
150 }
216
151
217 function test_drag_show_data() {
152 function test_drag_show_data() {
218 return [
153 return [
219− { tag: "pinned", fullscreenFlag: false, alreadyOpen: false, call: null,
154+ { tag: "pinned", fullscreen: false, call: null,
220 indicatorY: 0 },
155 indicatorY: 0 },
221− { tag: "fullscreen", fullscreenFlag: true, alreadyOpen: false, call: null,
156+ { tag: "fullscreen", fullscreen: true, call: null,
222 indicatorY: -panel.panelAndSeparatorHeight },
157 indicatorY: -panel.panelAndSeparatorHeight },
223− { tag: "pinned-alreadyOpen", fullscreenFlag: false, alreadyOpen: true, call: null,
224− indicatorY: 0 },
225− { tag: "fullscreen-alreadyOpen", fullscreenFlag: true, alreadyOpen: true, call: null,
226− indicatorY: 0 },
227− { tag: "pinned-callActive", fullscreenFlag: false, alreadyOpen: false, call: phoneCall,
158+ { tag: "pinned-callActive", fullscreen: false, call: phoneCall,
228 indicatorY: 0},
159 indicatorY: 0},
229− { tag: "fullscreen-callActive", fullscreenFlag: true, alreadyOpen: false, call: phoneCall,
160+ { tag: "fullscreen-callActive", fullscreen: true, call: phoneCall,
230 indicatorY: -panel.panelAndSeparatorHeight }
161 indicatorY: -panel.panelAndSeparatorHeight }
231 ];
162 ];
232 }
163 }
@@ -234,16 +165,12 @@ Item {
234 // Dragging from a indicator item in the panel will gradually expose the
165 // Dragging from a indicator item in the panel will gradually expose the
235 // indicators, first by running the hint animation, then after dragging down will
166 // indicators, first by running the hint animation, then after dragging down will
236 // expose more of the panel, binding it to the selected indicator and opening it's menu.
167 // expose more of the panel, binding it to the selected indicator and opening it's menu.
168+ // Tested from first Y pixel to check for swipe from offscreen.
237 function test_drag_show(data) {
169 function test_drag_show(data) {
238− panel.fullscreenMode = data.fullscreenFlag;
170+ panel.fullscreenMode = data.fullscreen;
239 callManager.foregroundCall = data.call;
171 callManager.foregroundCall = data.call;
240
172
241− if (data.alreadyOpen) {
242− panel.indicators.show();
243− tryCompare(panel.indicators, "fullyOpened", true);
244− }
245−
246− var indicatorRow = findChild(panel.indicators, "indicatorRow");
173+ var indicatorRow = findChild(panel.indicators, "indicatorItemRow");
247 verify(indicatorRow !== null);
174 verify(indicatorRow !== null);
248
175
249 var menuContent = findChild(panel.indicators, "menuContent");
176 var menuContent = findChild(panel.indicators, "menuContent");
@@ -256,25 +183,16 @@ Item {
256 // (switches between normal and fullscreen modes are animated)
183 // (switches between normal and fullscreen modes are animated)
257 tryCompareFunction(function() { return indicatorArea.y }, data.indicatorY);
184 tryCompareFunction(function() { return indicatorArea.y }, data.indicatorY);
258
185
259− // do this for each indicator item
186+ for (var i = 0; i < root.originalModelData.length; i++) {
260− for (var i = 0; i < indicatorRow.row.count; i++) {
261−
262 var indicatorItem = get_indicator_item(i);
187 var indicatorItem = get_indicator_item(i);
263− verify(indicatorItem !== null);
264
188
265− if (!indicatorItem.visible)
189+ var startXPosition = root.mapFromItem(indicatorItem, indicatorItem.width / 2, 0).x;
266− continue;
190+ var startYPosition = root.mapFromItem(panel, 0, 0).y;
267
191
268− var indicatorItemCoord = get_indicator_item_position(i);
269−
270− touchPress(panel,
271− indicatorItemCoord.x, panel.indicators.panelHeight / 2);
272−
273− // 1) Drag the mouse down
274 touchFlick(panel,
192 touchFlick(panel,
275− indicatorItemCoord.x, panel.indicators.panelHeight / 2,
193+ startXPosition, startYPosition,
276− indicatorItemCoord.x, panel.height,
194+ startXPosition, panel.height,
277− false /* beginTouch */, false /* endTouch */);
195+ true /* beginTouch */, false /* endTouch */, units.gu(5), 15);
278
196
279 // Indicators height should follow the drag, and therefore increase accordingly.
197 // Indicators height should follow the drag, and therefore increase accordingly.
280 // They should be at least half-way through the screen
198 // They should be at least half-way through the screen
@@ -282,56 +200,109 @@ Item {
282 function() {return panel.indicators.height >= panel.height * 0.5},
200 function() {return panel.indicators.height >= panel.height * 0.5},
283 true);
201 true);
284
202
285− touchRelease(panel, indicatorItemCoord.x, panel.height);
203+ touchRelease(panel, startXPosition, panel.height);
286
204
287− compare(indicatorRow.currentItem, indicatorItem,
288− "Incorrect item activated at position " + i);
205+ compare(indicatorRow.currentItemIndex, i, "Indicator item should be activated at position " + i);
289− compare(menuContent.currentMenuIndex, i, "Menu conetent should be enabled for item at position " + i);
206+ compare(menuContent.currentMenuIndex, i, "Menu conetent should be activated for item at position " + i);
290
207
291 // init for next indicatorItem
208 // init for next indicatorItem
292− if (!data.alreadyOpen) {
293− panel.indicators.hide();
209+ panel.indicators.hide();
294− tryCompare(panel.indicators.hideAnimation, "running", false);
210+ tryCompare(panel.indicators.hideAnimation, "running", false);
295− tryCompare(panel.indicators, "state", "initial");
211+ tryCompare(panel.indicators, "state", "initial");
296− }
297 }
212 }
298 }
213 }
299
214
300− // Test the vertical velocity check when flicking the indicators open at an angle.
215+ function test_drag_hide_data() {
301− // If the vertical velocity is above a specific point, we shouldnt change active indicators
216+ return [
302− // if the x position changes
217+ { tag: "pinned", fullscreen: false, call: null,
303− function test_vertical_velocity_detector() {
218+ indicatorY: 0 },
219+ { tag: "fullscreen", fullscreen: true, call: null,
220+ indicatorY: -panel.panelAndSeparatorHeight },
221+ { tag: "pinned-callActive", fullscreen: false, call: phoneCall,
222+ indicatorY: 0},
223+ { tag: "fullscreen-callActive", fullscreen: true, call: phoneCall,
224+ indicatorY: -panel.panelAndSeparatorHeight }
225+ ];
226+ }
227+
228+ // Dragging the shown indicators up from bottom of panel will hide the indicators
229+ // Tested from last Y pixel to check for swipe from offscreen.
230+ function test_drag_hide(data) {
304− panel.fullscreenMode = false;
231+ panel.fullscreenMode = data.fullscreen;
232+ callManager.foregroundCall = data.call;
305
233
306− var indicatorRow = findChild(panel.indicators, "indicatorRow");
234+ var indicatorRow = findChild(panel.indicators, "indicatorItemRow");
307 verify(indicatorRow !== null);
235 verify(indicatorRow !== null);
308
236
309− // Get the first indicator
237+ var menuContent = findChild(panel.indicators, "menuContent");
310− var indicatorItemFirst = get_indicator_item(0);
311− verify(indicatorItemFirst !== null);
238+ verify(menuContent !== null);
312
239
313− var indicatorItemCoordFirst = get_indicator_item_position(0);
240+ var indicatorArea = findChild(panel, "indicatorArea");
314− var indicatorItemCoordNext = get_indicator_item_position(indicatorRow.row.count - 1);
241+ verify(indicatorArea !== null);
315
242
316− touchPress(panel,
243+ // Wait for the indicators to get into position.
317− indicatorItemCoordFirst.x, panel.indicators.panelHeight / 2);
244+ // (switches between normal and fullscreen modes are animated)
245+ tryCompareFunction(function() { return indicatorArea.y }, data.indicatorY);
246+
247+ var startXPosition = root.mapFromItem(panel.indicators, panel.indicators.width / 2, 0).x;
248+ var startYPosition = root.mapFromItem(panel, 0, panel.height).y;
249+
250+ panel.indicators.show();
251+ tryCompare(panel.indicators.showAnimation, "running", false);
252+ tryCompare(panel.indicators, "unitProgress", 1);
253+
254+
255+ touchFlick(panel.indicators,
256+ startXPosition, startYPosition,
257+ startXPosition, 0,
258+ true /* beginTouch */, false /* endTouch */, units.gu(5), 15);
318
259
319− // 1) Drag the mouse down to hint a bit
260+ // Indicators height should follow the drag, and therefore increase accordingly.
320− touchFlick(panel,
261+ // They should be at least half-way through the screen
321− indicatorItemCoordFirst.x, panel.indicators.panelHeight / 2,
262+ tryCompareFunction(
322− indicatorItemCoordFirst.x, panel.indicators.panelHeight * 2,
263+ function() {return panel.indicators.height <= panel.height * 0.5},
323− false /* beginTouch */, false /* endTouch */);
264+ true);
324
265
325− tryCompare(indicatorRow, "currentItem", indicatorItemFirst)
266+ touchRelease(panel.indicators, startXPosition, 0);
326
267
327− // 1) Flick mouse down to bottom
268+ tryCompare(panel.indicators.hideAnimation, "running", true);
328− touchFlick(panel,
269+ tryCompare(panel.indicators.hideAnimation, "running", false);
329− indicatorItemCoordFirst.x, panel.indicators.panelHeight * 2,
270+ tryCompare(panel.indicators, "state", "initial");
330− indicatorItemCoordNext.x, panel.height,
271+ }
331− false /* beginTouch */, true /* endTouch */,
272+
332− units.gu(10) /* speed */, 30 /* iterations */); // more samples needed for accurate velocity
273+ function test_hint_data() {
274+ return [
275+ { tag: "normal", fullscreen: false, call: null, hintExpected: true},
276+ { tag: "fullscreen", fullscreen: true, call: null, hintExpected: false},
277+ { tag: "call hint", fullscreen: false, call: phoneCall, hintExpected: false},
278+ ];
279+ }
280+
281+ function test_hint(data) {
282+ panel.fullscreenMode = data.fullscreen;
283+ callManager.foregroundCall = data.call;
284+
285+ if (data.fullscreen) {
286+ // Wait for the indicators to get into position.
287+ // (switches between normal and fullscreen modes are animated)
288+ var indicatorArea = findChild(panel, "indicatorArea");
289+ tryCompare(indicatorArea, "y", -panel.panelHeight);
290+ }
291+
292+ var indicatorItem = get_indicator_item(0);
293+ var mappedPosition = root.mapFromItem(indicatorItem, indicatorItem.width / 2, indicatorItem.height / 2);
294+
295+ touchPress(panel, mappedPosition.x, panel.indicators.minimizedPanelHeight / 2);
296+
297+ // Give some time for a hint animation to change things, if any
298+ wait(500);
299+
300+ // no hint animation when fullscreen
301+ compare(panel.indicators.fullyClosed, !data.hintExpected, "Indicator should be fully closed");
302+ compare(panel.indicators.partiallyOpened, data.hintExpected, "Indicator should be partialy opened");
303+ compare(panel.indicators.fullyOpened, false, "Indicator should not be fully opened");
333
304
334− compare(indicatorRow.currentItem, indicatorItemFirst, "First indicator should still be the current item");
305+ touchRelease(panel, mappedPosition.x, panel.minimizedPanelHeight / 2);
335 }
306 }
336 }
307 }
337}
308}
tests/qmltests/Panel/tst_SearchIndicator.qml−1
@@ -16,7 +16,6 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19−import ".."
20import "../../../qml/Panel"
19import "../../../qml/Panel"
21import Ubuntu.Components 0.1
20import Ubuntu.Components 0.1
22import Unity.Test 0.1 as UT
21import Unity.Test 0.1 as UT
tests/qmltests/tst_Shell.qml+4−2
@@ -65,6 +65,8 @@ Row {
65 property bool itemDestroyed: false
65 property bool itemDestroyed: false
66 sourceComponent: Component {
66 sourceComponent: Component {
67 Shell {
67 Shell {
68+ property string indicatorProfile: "phone"
69+
68 Component.onDestruction: {
70 Component.onDestruction: {
69 shellLoader.itemDestroyed = true;
71 shellLoader.itemDestroyed = true;
70 }
72 }
@@ -345,7 +347,7 @@ Row {
345 var touchX = shell.width / 2;
347 var touchX = shell.width / 2;
346 var indicators = findChild(shell, "indicators");
348 var indicators = findChild(shell, "indicators");
347 touchFlick(indicators,
349 touchFlick(indicators,
348− touchX /* fromX */, indicators.panelHeight * 0.5 /* fromY */,
350+ touchX /* fromX */, indicators.minimizedPanelHeight * 0.5 /* fromY */,
349 touchX /* toX */, shell.height * 0.5 /* toY */,
351 touchX /* toX */, shell.height * 0.5 /* toY */,
350 true /* beginTouch */, false /* endTouch */);
352 true /* beginTouch */, false /* endTouch */);
351 verify(indicators.partiallyOpened);
353 verify(indicators.partiallyOpened);
@@ -382,7 +384,7 @@ Row {
382 var indicators = findChild(shell, "indicators");
384 var indicators = findChild(shell, "indicators");
383
385
384 var touchStartX = shell.width / 2;
386 var touchStartX = shell.width / 2;
385− var touchStartY = shell.height - (indicators.panelHeight * 0.5);
387+ var touchStartY = shell.height - (indicators.minimizedPanelHeight * 0.5);
386 touchFlick(shell,
388 touchFlick(shell,
387 touchStartX, touchStartY,
389 touchStartX, touchStartY,
388 touchStartX, shell.height * 0.1);
390 touchStartX, shell.height * 0.1);
tests/qmltests/tst_ShellWithPin.qml+2
@@ -61,6 +61,8 @@ Item {
61 property bool itemDestroyed: false
61 property bool itemDestroyed: false
62 sourceComponent: Component {
62 sourceComponent: Component {
63 Shell {
63 Shell {
64+ property string indicatorProfile: "phone"
65+
64 Component.onDestruction: {
66 Component.onDestruction: {
65 shellLoader.itemDestroyed = true
67 shellLoader.itemDestroyed = true
66 }
68 }
tests/qmltests/tst_TabletShell.qml+2
@@ -57,6 +57,8 @@ Row {
57 property bool itemDestroyed: false
57 property bool itemDestroyed: false
58 sourceComponent: Component {
58 sourceComponent: Component {
59 Shell {
59 Shell {
60+ property string indicatorProfile: "phone"
61+
60 Component.onDestruction: {
62 Component.onDestruction: {
61 shellLoader.itemDestroyed = true
63 shellLoader.itemDestroyed = true
62 }
64 }