on xenial help sign in
Merge lp:~nick-dedekind/unity8/expanded-panel-design
commit6b2c36c8fe3bbe7034536c9301d357a475b6c64f authorDaniel d'Andrada <daniel.dandrada@canonical.com> dateTue 21 Oct 2014, 15:20 (12 years ago) parent1ff2631c5a6fd2
103 files changed+3608−2426
plugins/AccountsService/AccountsService.qmltypes+2 plugins/Dash/Dash.qmltypes+3 plugins/LightDM/LightDM.qmltypes+1 plugins/Ubuntu/Payments/Payments.qmltypes+1 plugins/Unity/DashCommunicator/DashCommunicator.qmltypes+16−3 plugins/Unity/Indicators/CMakeLists.txt+1 plugins/Unity/Indicators/Indicators.qmltypes+12−11 plugins/Unity/Indicators/indicators.h−2 plugins/Unity/Indicators/indicatorsmodel.cpp−6 plugins/Unity/Indicators/plugin.cpp+8−2 plugins/Unity/Indicators/plugin.h+1−1 plugins/Unity/Indicators/rootactionstate.cpp+1−1 plugins/Unity/Indicators/rootactionstate.h+4−4 plugins/Unity/Indicators/sharedunitymenumodel.cpp+92 plugins/Unity/Indicators/sharedunitymenumodel.h+66 plugins/Unity/Indicators/unitymenumodelcache.cpp+25−14 plugins/Unity/Indicators/unitymenumodelcache.h+11−7 plugins/Unity/Indicators/visibleindicatorsmodel.cpp−2 plugins/Unity/Launcher/Launcher.qmltypes+17−12 plugins/Unity/Session/Session.qmltypes+10 plugins/Utils/Utils.qmltypes+6 qml/Components/DragHandle.qml+6−1 qml/Components/EdgeDemo.qml+13−10 qml/Components/ListItems/VerticalThinDivider.qml−26 qml/Components/ListItems/graphics/ListItemDividerVertical@18.png qml/Components/ScrollCalculator.qml+81 qml/Greeter/Clock.qml+9−9 qml/Panel/Handle.qml+43 qml/Panel/IndicatorItem.qml+232−27 qml/Panel/IndicatorItemRow.qml+297 qml/Panel/Indicators/DefaultIndicatorPage.qml → qml/Panel/IndicatorPage.qml+10−14 qml/Panel/IndicatorRow.qml−163 qml/Panel/Indicators.qml−415 qml/Panel/Indicators/CachedUnityMenuModel.qml−39 qml/Panel/Indicators/DefaultIndicatorWidget.qml−119 qml/Panel/Indicators/IndicatorBase.qml+3−7 qml/Panel/Indicators/IndicatorDelegate.qml+2−2 qml/Panel/Indicators/VisibleIndicators.qml+4−5 qml/Panel/Indicators/client/IndicatorsPage.qml → qml/Panel/Indicators/client/IndicatorRepresentation.qml+21−10 qml/Panel/Indicators/client/IndicatorsList.qml+2−2 qml/Panel/Indicators/client/IndicatorsTree.qml+4−26 qml/Panel/IndicatorsBar.qml+269 qml/Panel/IndicatorsMenu.qml+318 qml/Panel/MenuContent.qml+25−82 qml/Panel/Panel.qml+48−41 qml/Panel/PanelVelocityCalculator.qml+54 qml/Shell.qml+16−5 tests/autopilot/unity8/indicators/tests/test_indicators.py+1−1 tests/autopilot/unity8/shell/emulators/main_window.py+6−6 tests/mocks/AccountsService/AccountsService.qmltypes+2 tests/mocks/LightDM/LightDM.qmltypes+1 tests/mocks/QMenuModel/QMenuModel.qmltypes+1−1 tests/mocks/QMenuModel/unitymenumodel.cpp+1−2 tests/mocks/QMenuModel/unitymenumodel.h+6−2 tests/mocks/Ubuntu/Payments/Payments.qmltypes+2 tests/mocks/Ubuntu/Telephony/Telephony.qmltypes+2−1 tests/mocks/Unity/Application/Application.qmltypes+13 tests/mocks/Unity/DashCommunicator/DashCommunicator.qmltypes+2−2 tests/mocks/Unity/Indicators/CMakeLists.txt+3−10 tests/mocks/Unity/Indicators/FakeMenuPage.qml−27 tests/mocks/Unity/Indicators/Indicators.qmltypes+43−15 tests/mocks/Unity/Indicators/IndicatorsModel.qml+152−34 tests/mocks/Unity/Indicators/RootActionState.qml+18−10 tests/mocks/Unity/Indicators/fakeindicatorsmodel.cpp+50−21 tests/mocks/Unity/Indicators/fakeindicatorsmodel.h+8−2 tests/mocks/Unity/Indicators/fakeplugin.cpp+8−4 tests/mocks/Unity/Indicators/fakeunitymenumodelcache.cpp+59 tests/mocks/Unity/Indicators/fakeunitymenumodelcache.h+45 tests/mocks/Unity/Indicators/indicators_fake.qrc−14 tests/mocks/Unity/Indicators/qml/fake_menu_page1.qml−35 tests/mocks/Unity/Indicators/qml/fake_menu_page2.qml−35 tests/mocks/Unity/Indicators/qml/fake_menu_page3.qml−35 tests/mocks/Unity/Indicators/qml/fake_menu_page4.qml−35 tests/mocks/Unity/Indicators/qml/fake_menu_page5.qml−35 tests/mocks/Unity/Indicators/qml/fake_menu_widget1.qml−23 tests/mocks/Unity/Indicators/qml/fake_menu_widget2.qml−23 tests/mocks/Unity/Indicators/qml/fake_menu_widget3.qml−23 tests/mocks/Unity/Indicators/qml/fake_menu_widget4.qml−23 tests/mocks/Unity/Indicators/qml/fake_menu_widget5.qml−23 tests/mocks/Unity/Indicators/qmldir−1 tests/mocks/Unity/Launcher/Launcher.qmltypes+13 tests/mocks/Unity/Unity.qmltypes+32 tests/plugins/Unity/Indicators/CMakeLists.txt+1 tests/plugins/Unity/Indicators/sharedunitymenumodeltest.cpp+102 tests/qmltests/CMakeLists.txt+10−11 tests/qmltests/Greeter/tst_Clock.qml+49−34 tests/qmltests/Panel/IndicatorTest.qml+80 tests/qmltests/Panel/Indicators/tst_CachedUnityMenuModel.qml−137 tests/qmltests/Panel/Indicators/tst_DefaultIndicatorWidget.qml−60 tests/qmltests/Panel/tst_ActiveCallHint.qml−1 tests/qmltests/Panel/tst_IndicatorItem.qml+173−18 tests/qmltests/Panel/tst_IndicatorItemRow.qml+290 tests/qmltests/Panel/Indicators/tst_DefaultIndicatorPage.qml → tests/qmltests/Panel/tst_IndicatorPage.qml+17−22 tests/qmltests/Panel/tst_IndicatorRow.qml−160 tests/qmltests/Panel/tst_Indicators.qml−230 tests/qmltests/Panel/tst_IndicatorsBar.qml+224 tests/qmltests/Panel/tst_IndicatorsMenu.qml+258 tests/qmltests/Panel/tst_MenuContent.qml+9−25 tests/qmltests/Panel/tst_Panel.qml+185−214 tests/qmltests/Panel/tst_SearchIndicator.qml−1 tests/qmltests/tst_Shell.qml+4−2 tests/qmltests/tst_ShellWithPin.qml+2 tests/qmltests/tst_TabletShell.qml+2
plugins/AccountsService/AccountsService.qmltypes+2
@@ -23,6 +23,8 @@ Module {
2323 }
2424 Property { name: "user"; type: "string" }
2525 Property { name: "demoEdges"; type: "bool" }
26+ Property { name: "enableLauncherWhileLocked"; type: "bool"; isReadonly: true }
27+ Property { name: "enableIndicatorsWhileLocked"; type: "bool"; isReadonly: true }
2628 Property { name: "backgroundFile"; type: "string"; isReadonly: true }
2729 Property { name: "statsWelcomeScreen"; type: "bool"; isReadonly: true }
2830 Property { name: "passwordDisplayHint"; type: "PasswordDisplayHint"; isReadonly: true }
plugins/Dash/Dash.qmltypes+3
@@ -40,9 +40,12 @@ Module {
4040 Property { name: "forceNoClip"; type: "bool" }
4141 Property { name: "stickyHeaderHeight"; type: "int"; isReadonly: true }
4242 Property { name: "headerItemShownHeight"; type: "double"; isReadonly: true }
43+ Property { name: "cacheBuffer"; type: "double" }
4344 Signal { name: "headerChanged" }
4445 Method { name: "positionAtBeginning" }
4546 Method { name: "showHeader" }
47+ Method { name: "firstCreatedIndex"; type: "int" }
48+ Method { name: "createdItemCount"; type: "int" }
4649 Method {
4750 name: "item"
4851 type: "QQuickItem*"
plugins/LightDM/LightDM.qmltypes+1
@@ -37,6 +37,7 @@ Module {
3737 Signal { name: "isActiveChanged" }
3838 Signal { name: "isAuthenticatedChanged" }
3939 Signal { name: "showGreeter" }
40+ Signal { name: "hideGreeter" }
4041 Signal {
4142 name: "requestAuthenticationUser"
4243 Parameter { name: "user"; type: "string" }
plugins/Ubuntu/Payments/Payments.qmltypes+1
@@ -37,6 +37,7 @@ Module {
3737 Parameter { name: "error"; type: "string" }
3838 }
3939 Signal { name: "purchaseCompleted" }
40+ Signal { name: "purchaseCancelled" }
4041 Method { name: "start" }
4142 }
4243}
plugins/Unity/DashCommunicator/DashCommunicator.qmltypes+16−3
@@ -9,14 +9,14 @@ import QtQuick.tooling 1.1
99Module {
1010 Component {
1111 name: "DashCommunicator"
12− prototype: "QObject"
12+ prototype: "QThread"
1313 exports: ["Unity.DashCommunicator/DashCommunicator 0.1"]
1414 exportMetaObjectRevisions: [0]
1515 Method {
1616 name: "setCurrentScope"
1717 Parameter { name: "scopeId"; type: "string" }
1818 Parameter { name: "animate"; type: "bool" }
19− Parameter { name: "reset"; type: "bool" }
19+ Parameter { name: "isSwipe"; type: "bool" }
2020 }
2121 }
2222 Component {
@@ -28,7 +28,20 @@ Module {
2828 name: "setCurrentScopeRequested"
2929 Parameter { name: "scopeId"; type: "string" }
3030 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" }
3242 }
43+ Method { name: "start" }
44+ Method { name: "terminate" }
45+ Method { name: "quit" }
3346 }
3447}
plugins/Unity/Indicators/CMakeLists.txt+1
@@ -24,6 +24,7 @@ set(IndicatorsQML_SOURCES
2424 modelprinter.cpp
2525 plugin.cpp
2626 rootactionstate.cpp
27+ sharedunitymenumodel.cpp
2728 unitymenumodelcache.cpp
2829 unitymenumodelstack.cpp
2930 visibleindicatorsmodel.cpp
plugins/Unity/Indicators/Indicators.qmltypes+12−11
@@ -99,10 +99,8 @@ Module {
9999 values: {
100100 "Identifier": 0,
101101 "Position": 1,
102− "WidgetSource": 2,
103− "PageSource": 3,
104− "IndicatorProperties": 4,
105− "IsVisible": 5
102+ "IndicatorProperties": 2,
103+ "IsVisible": 3
106104 }
107105 }
108106 }
@@ -209,6 +207,16 @@ Module {
209207 Signal { name: "updated" }
210208 Signal { name: "indexChanged" }
211209 }
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+ }
212220 Component {
213221 name: "UnityMenuModelCache"
214222 prototype: "QObject"
@@ -216,13 +224,6 @@ Module {
216224 isCreatable: false
217225 isSingleton: true
218226 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− }
226227 Method {
227228 name: "contains"
228229 type: "bool"
plugins/Unity/Indicators/indicators.h−2
@@ -74,8 +74,6 @@ public:
7474 enum Roles {
7575 Identifier = 0,
7676 Position,
77− WidgetSource,
78− PageSource,
7977 IndicatorProperties,
8078 IsVisible
8179 };
plugins/Unity/Indicators/indicatorsmodel.cpp−6
@@ -202,8 +202,6 @@ QHash<int, QByteArray> IndicatorsModel::roleNames() const
202202 {
203203 roles[IndicatorsModelRole::Identifier] = "identifier";
204204 roles[IndicatorsModelRole::Position] = "position";
205− roles[IndicatorsModelRole::WidgetSource] = "widgetSource";
206− roles[IndicatorsModelRole::PageSource] = "pageSource";
207205 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
208206 }
209207 return roles;
@@ -248,10 +246,6 @@ QVariant IndicatorsModel::data(const QModelIndex &index, int role) const
248246 return QVariant(indicator->indicatorProperties());
249247 }
250248 break;
251− case IndicatorsModelRole::WidgetSource:
252− return qmlDirectory()+"/Panel/Indicators/DefaultIndicatorWidget.qml";
253− case IndicatorsModelRole::PageSource:
254− return qmlDirectory()+"/Panel/Indicators/DefaultIndicatorPage.qml";
255249 default:
256250 break;
257251 }
plugins/Unity/Indicators/plugin.cpp+8−2
@@ -30,18 +30,23 @@
3030#include "menucontentactivator.h"
3131#include "modelprinter.h"
3232#include "rootactionstate.h"
33+#include "sharedunitymenumodel.h"
3334#include "unitymenumodelcache.h"
3435#include "unitymenumodelstack.h"
3536#include "visibleindicatorsmodel.h"
3637
38+#include <unitymenumodel.h>
39+
3740static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
3841 Q_UNUSED(engine);
3942 Q_UNUSED(scriptEngine);
40− return new UnityMenuModelCache;
43+ return UnityMenuModelCache::singleton();
4144}
4245
43−void Indicators2Plugin::registerTypes(const char *uri)
46+void IndicatorsPlugin::registerTypes(const char *uri)
4447{
48+ qRegisterMetaType<UnityMenuModel*>("UnityMenuModel*");
49+
4550 qmlRegisterType<IndicatorsManager>(uri, 0, 1, "IndicatorsManager");
4651 qmlRegisterType<IndicatorsModel>(uri, 0, 1, "IndicatorsModel");
4752 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
@@ -49,6 +54,7 @@ void Indicators2Plugin::registerTypes(const char *uri)
4954 qmlRegisterType<RootActionState>(uri, 0, 1, "RootActionState");
5055 qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
5156 qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
57+ qmlRegisterType<SharedUnityMenuModel>(uri, 0, 1, "SharedUnityMenuModel");
5258
5359 qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
5460
plugins/Unity/Indicators/plugin.h+1−1
@@ -22,7 +22,7 @@
2222
2323#include <QtQml/QQmlExtensionPlugin>
2424
25−class Indicators2Plugin : public QQmlExtensionPlugin
25+class IndicatorsPlugin : public QQmlExtensionPlugin
2626{
2727 Q_OBJECT
2828 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
plugins/Unity/Indicators/rootactionstate.cpp+1−1
@@ -156,7 +156,7 @@ QString RootActionState::accessibleName() const
156156 return m_cachedState.value("accessible-desc", QVariant::fromValue(QString())).toString();
157157}
158158
159−bool RootActionState::isVisible() const
159+bool RootActionState::indicatorVisible() const
160160{
161161 if (!isValid()) return false;
162162
plugins/Unity/Indicators/rootactionstate.h+4−4
@@ -37,7 +37,7 @@ class UNITYINDICATORS_EXPORT RootActionState : public ActionStateParser
3737 Q_PROPERTY(QString rightLabel READ rightLabel NOTIFY rightLabelChanged)
3838 Q_PROPERTY(QStringList icons READ icons NOTIFY iconsChanged)
3939 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)
4141public:
4242 RootActionState(QObject *parent = 0);
4343 virtual ~RootActionState();
@@ -54,10 +54,10 @@ public:
5454 QString rightLabel() const;
5555 QStringList icons() const;
5656 QString accessibleName() const;
57− bool isVisible() const;
57+ bool indicatorVisible() const;
5858
5959 // from ActionStateParser
60− virtual QVariant toQVariant(GVariant* state) const;
60+ virtual QVariant toQVariant(GVariant* state) const override;
6161
6262Q_SIGNALS:
6363 void updated();
@@ -71,7 +71,7 @@ Q_SIGNALS:
7171 void rightLabelChanged();
7272 void iconsChanged();
7373 void accessibleNameChanged();
74− void visibleChanged();
74+ void indicatorVisibleChanged();
7575
7676private Q_SLOTS:
7777 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 @@
2020#include "unitymenumodelcache.h"
2121#include <unitymenumodel.h>
2222
23−UnityMenuModelCache::UnityMenuModelCache(QObject* parent)
24− : QObject(parent)
23+#include <QQmlEngine>
24+
25+QPointer<UnityMenuModelCache> UnityMenuModelCache::theCache = nullptr;
26+
27+UnityMenuModelCache* UnityMenuModelCache::singleton()
2528{
29+ if (theCache.isNull()) {
30+ theCache = new UnityMenuModelCache();
31+ }
32+ return theCache.data();
2633}
2734
28−UnityMenuModelCache::~UnityMenuModelCache()
35+UnityMenuModelCache::UnityMenuModelCache(QObject* parent)
36+ : QObject(parent)
2937{
3038}
3139
32−UnityMenuModel* UnityMenuModelCache::model(const QByteArray& bus,
33− const QByteArray& path,
34− const QVariantMap& actions)
40+QSharedPointer<UnityMenuModel> UnityMenuModelCache::model(const QByteArray& path)
3541{
3642 if (m_registry.contains(path))
3743 return m_registry[path];
3844
39− UnityMenuModel* menuModel = new UnityMenuModel;
40− connect(menuModel, &QObject::destroyed, this, [menuModel, this](QObject*) {
41− QList<QByteArray> keys = m_registry.keys(menuModel);
42− Q_FOREACH(const QByteArray& key, keys) {
43− m_registry.remove(key);
45+ UnityMenuModel* model = new UnityMenuModel;
46+ QQmlEngine::setObjectOwnership(model, QQmlEngine::CppOwnership);
47+
48+ QSharedPointer<UnityMenuModel> menuModel(model);
49+ connect(model, &QObject::destroyed, this, [this] {
50+ QMutableHashIterator<QByteArray, QWeakPointer<UnityMenuModel>> iter(m_registry);
51+ while(iter.hasNext()) {
52+ auto keyVal = iter.next();
53+ if (keyVal.value().isNull()) {
54+ iter.remove();
55+ break;
56+ }
4457 }
4558 });
46− m_registry[path] = menuModel;
59+ m_registry[path] = menuModel.toWeakRef();
4760
48− menuModel->setBusName(bus);
4961 menuModel->setMenuObjectPath(path);
50− menuModel->setActions(actions);
5162 return menuModel;
5263}
5364
plugins/Unity/Indicators/unitymenumodelcache.h+11−7
@@ -24,6 +24,8 @@
2424
2525#include <QObject>
2626#include <QHash>
27+#include <QPointer>
28+#include <QWeakPointer>
2729
2830class UnityMenuModel;
2931
@@ -32,15 +34,17 @@ class UNITYINDICATORS_EXPORT UnityMenuModelCache : public QObject
3234 Q_OBJECT
3335public:
3436 UnityMenuModelCache(QObject*parent=nullptr);
35− ~UnityMenuModelCache();
3637
37− Q_INVOKABLE UnityMenuModel* model(const QByteArray& bus,
38− const QByteArray& path,
39− const QVariantMap& actions);
40− Q_INVOKABLE bool contains(const QByteArray& path);
38+ static UnityMenuModelCache* singleton();
4139
42−private:
43− QHash<QByteArray, UnityMenuModel*> m_registry;
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:
46+ QHash<QByteArray, QWeakPointer<UnityMenuModel>> m_registry;
47+ static QPointer<UnityMenuModelCache> theCache;
4448};
4549
4650#endif // UNITYMENUMODELCACHE_H
plugins/Unity/Indicators/visibleindicatorsmodel.cpp−2
@@ -33,8 +33,6 @@ QHash<int, QByteArray> VisibleIndicatorsModel::roleNames() const
3333 {
3434 roles[IndicatorsModelRole::Identifier] = "identifier";
3535 roles[IndicatorsModelRole::Position] = "position";
36− roles[IndicatorsModelRole::WidgetSource] = "widgetSource";
37− roles[IndicatorsModelRole::PageSource] = "pageSource";
3836 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
3937 roles[IndicatorsModelRole::IsVisible] = "isVisible";
4038 }
plugins/Unity/Launcher/Launcher.qmltypes+17−12
@@ -13,14 +13,6 @@ Module {
1313 exports: ["Unity.Launcher/LauncherItem 0.1"]
1414 isCreatable: false
1515 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− }
2416 }
2517 Component {
2618 name: "LauncherModel"
@@ -29,6 +21,10 @@ Module {
2921 isCreatable: false
3022 isSingleton: true
3123 exportMetaObjectRevisions: [0]
24+ Method {
25+ name: "requestRemove"
26+ Parameter { name: "appId"; type: "string" }
27+ }
3228 Method {
3329 name: "get"
3430 type: "unity::shell::launcher::LauncherItemInterface*"
@@ -48,10 +44,6 @@ Module {
4844 name: "pin"
4945 Parameter { name: "appId"; type: "string" }
5046 }
51− Method {
52− name: "requestRemove"
53− Parameter { name: "appId"; type: "string" }
54− }
5547 Method {
5648 name: "quickListActionInvoked"
5749 Parameter { name: "appId"; type: "string" }
@@ -88,6 +80,7 @@ Module {
8880 Property { name: "recent"; type: "bool"; isReadonly: true }
8981 Property { name: "progress"; type: "int"; isReadonly: true }
9082 Property { name: "count"; type: "int"; isReadonly: true }
83+ Property { name: "countVisible"; type: "bool"; isReadonly: true }
9184 Property { name: "focused"; type: "bool"; isReadonly: true }
9285 Property {
9386 name: "quickList"
@@ -95,6 +88,14 @@ Module {
9588 isReadonly: true
9689 isPointer: true
9790 }
91+ Signal {
92+ name: "nameChanged"
93+ Parameter { name: "name"; type: "string" }
94+ }
95+ Signal {
96+ name: "iconChanged"
97+ Parameter { name: "icon"; type: "string" }
98+ }
9899 Signal {
99100 name: "pinnedChanged"
100101 Parameter { name: "pinned"; type: "bool" }
@@ -115,6 +116,10 @@ Module {
115116 name: "countChanged"
116117 Parameter { name: "count"; type: "int" }
117118 }
119+ Signal {
120+ name: "countVisibleChanged"
121+ Parameter { name: "countVisible"; type: "bool" }
122+ }
118123 Signal {
119124 name: "focusedChanged"
120125 Parameter { name: "focused"; type: "bool" }
plugins/Unity/Session/Session.qmltypes+10
@@ -34,4 +34,14 @@ Module {
3434 Method { name: "RequestReboot" }
3535 Method { name: "RequestShutdown" }
3636 }
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+ }
3747}
plugins/Utils/Utils.qmltypes+6
@@ -106,6 +106,12 @@ Module {
106106 Parameter { name: "row"; type: "int" }
107107 }
108108 }
109+ Component {
110+ name: "RelativeTimeFormatter"
111+ prototype: "TimeFormatter"
112+ exports: ["Utils/RelativeTimeFormatter 0.1"]
113+ exportMetaObjectRevisions: [0]
114+ }
109115 Component {
110116 name: "TimeFormatter"
111117 prototype: "QObject"
qml/Components/DragHandle.qml+6−1
@@ -68,6 +68,7 @@ EdgeDragArea {
6868 }
6969
7070 property real hintDisplacement: 0
71+ property var overrideStartValue: undefined
7172 SmoothedAnimation {
7273 id: hintingAnimation
7374 target: hintingAnimation
@@ -196,7 +197,11 @@ EdgeDragArea {
196197 } else /* Undecided || Recognized */ {
197198 if (d.previousStatus === DirectionalDragArea.WaitingForTouch) {
198199 dragEvaluator.reset();
199− d.startValue = parent[d.targetProp];
200+ if (overrideStartValue !== undefined) {
201+ d.startValue = overrideStartValue;
202+ } else {
203+ d.startValue = parent[d.targetProp];
204+ }
200205
201206 if (hintDisplacement > 0) {
202207 hintingAnimation.targetValue = d.startValue;
qml/Components/EdgeDemo.qml+13−10
@@ -23,7 +23,7 @@ Item {
2323
2424 property Item greeter
2525 property Item launcher
26− property Item indicators
26+ property Item panel
2727 property Item stages
2828
2929 property bool launcherEnabled: true
@@ -139,11 +139,11 @@ Item {
139139 function startTopEdgeDemo() {
140140 demo.panelEnabled = true;
141141 if (demo.stages) {
142− d.topEdgeDemo = d.overlay.createObject(demo.stages, {
142+ d.topEdgeDemo = d.overlay.createObject(demo.panel, {
143143 "edge": "top",
144144 "title": i18n.tr("Top edge"),
145145 "text": i18n.tr("Try swiping from the top edge to access the indicators"),
146− "anchors.fill": demo.stages,
146+ "anchors.fill": demo.panel,
147147 });
148148 }
149149 if (d.topEdgeDemo) {
@@ -154,9 +154,9 @@ Item {
154154 }
155155
156156 Connections {
157− target: demo.indicators
157+ target: demo.panel.indicators
158158 onFullyOpenedChanged: {
159− if (d.topEdgeDemo && d.topEdgeDemo.available && demo.indicators.fullyOpened) {
159+ if (d.topEdgeDemo && d.topEdgeDemo.available && demo.panel.indicators.fullyOpened) {
160160 d.topEdgeDemo.hideNow()
161161 startBottomEdgeDemo()
162162 }
@@ -164,12 +164,12 @@ Item {
164164 }
165165
166166 function startBottomEdgeDemo() {
167− if (demo.indicators) {
168− d.bottomEdgeDemo = d.overlay.createObject(demo.indicators, {
167+ if (demo.panel.indicators) {
168+ d.bottomEdgeDemo = d.overlay.createObject(demo.panel.indicators, {
169169 "edge": "bottom",
170170 "title": i18n.tr("Close"),
171171 "text": i18n.tr("Swipe up again to close the settings screen"),
172− "anchors.fill": demo.indicators.content,
172+ "anchors.fill": demo.panel.indicators,
173173 });
174174 }
175175 if (d.bottomEdgeDemo) {
@@ -180,9 +180,12 @@ Item {
180180 }
181181
182182 Connections {
183− target: demo.indicators
183+ target: demo.panel.indicators
184184 onPartiallyOpenedChanged: {
185− if (d.bottomEdgeDemo && d.bottomEdgeDemo.available && !demo.indicators.partiallyOpened && !demo.indicators.fullyOpened) {
185+ if (d.bottomEdgeDemo &&
186+ d.bottomEdgeDemo.available &&
187+ !demo.panel.indicators.partiallyOpened &&
188+ !demo.panel.indicators.fullyOpened) {
186189 d.bottomEdgeDemo.hideNow()
187190 startLeftEdgeDemo()
188191 }
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/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/Greeter/Clock.qml+9−9
@@ -34,21 +34,21 @@ Item {
3434 }
3535 }
3636
37− CachedUnityMenuModel {
37+ Indicators.SharedUnityMenuModel {
3838 id: timeModel
3939 objectName: "timeModel"
4040
4141 busName: "com.canonical.indicator.datetime"
42− actionsObjectPath: "/com/canonical/indicator/datetime"
42+ actions: { "indicator": "/com/canonical/indicator/datetime" }
4343 menuObjectPath: clock.visible ? "/com/canonical/indicator/datetime/phone" : ""
44+ }
4445
45− Indicators.RootActionState {
46− menu: timeModel.model
47− onUpdated: {
48− if (timeLabel.text != rightLabel) {
49− timeLabel.text = rightLabel;
50− clock.currentDate = new Date();
51− }
46+ Indicators.RootActionState {
47+ menu: timeModel.model
48+ onUpdated: {
49+ if (timeLabel.text != rightLabel) {
50+ if (rightLabel != "") timeLabel.text = rightLabel;
51+ clock.currentDate = new Date();
5252 }
5353 }
5454 }
qml/Panel/Handle.qmlnew file+43
@@ -0,0 +1,43 @@
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+Rectangle {
21+ id: handle
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+ }
43+}
qml/Panel/IndicatorItem.qml+232−27
@@ -1,53 +1,258 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
33 *
44 * 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
66 * the Free Software Foundation; version 3.
77 *
88 * This program is distributed in the hope that it will be useful,
99 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1010 * 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.
1212 *
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
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−import Unity.Indicators 0.1 as Indicators
20−import "../Components"
17+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+import Ubuntu.Settings.Components 0.1
20+import "Indicators"
2121
22−Loader {
22+IndicatorDelegate {
2323 id: root
2424
25− property alias widgetSource: root.source
26− property bool dimmed: false
27− property var indicatorProperties: undefined
28− property bool indicatorVisible: item ? item.enabled : false
2925 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
3042
31− opacity: dimmed ? 0.4 : 1
32− Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.BriskDuration } }
43+ MouseArea {
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+ }
3355
34− onLoaded: {
35− for(var pName in indicatorProperties) {
36− if (item.hasOwnProperty(pName)) {
37− item[pName] = indicatorProperties[pName];
56+ Item {
57+ id: mainItems
58+ anchors.centerIn: parent
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
3870 }
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 } }
3979 }
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 } }
40156 }
41157
42− Binding {
43− target: item
44− property: "identifier"
45− value: identifier
158+ StateGroup {
159+ id: d
160+ property bool useFallbackIcon: false
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+ ]
46242 }
47243
48− Binding {
49− target: item
50− property: "objectName"
51− value: identifier + "-widget"
244+ onRootActionStateChanged: {
245+ if (rootActionState == undefined) {
246+ title = "";
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;
52257 }
53258}
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−14
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -12,16 +12,13 @@
1212 *
1313 * You should have received a copy of the GNU Lesser General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors:
17− * Renato Araujo Oliveira Filho <renato@canonical.com>
18− * Nick Dedekind <nick.dedekind@canonical.com>
1915 */
2016
2117import QtQuick 2.0
2218import Ubuntu.Components 0.1 as Components
2319import Unity.Indicators 0.1 as Indicators
24−import "../../Components/Flickables" as Flickables
20+import "Indicators"
21+import "../Components/Flickables" as Flickables
2522
2623IndicatorBase {
2724 id: main
@@ -52,13 +49,13 @@ IndicatorBase {
5249
5350 Connections {
5451 target: menuStack.tail
55− onRowsInserted: {
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: {
52+
53+ // fix async creation with signal from model before it's finished.
54+ Component.onCompleted: update();
55+ onRowsInserted: update();
56+ onModelReset: update();
57+
58+ function update() {
6259 if (menuStack.rootMenu !== menuStack.tail && menuStack.tail.get(0, "type") === rootMenuType) {
6360 menuStack.rootMenu = menuStack.tail.submenu(0);
6461 menuStack.push(menuStack.rootMenu, 0);
@@ -126,7 +123,6 @@ IndicatorBase {
126123 delegate: Loader {
127124 id: loader
128125 objectName: "menuItem" + index
129− asynchronous: true
130126 width: ListView.view.width
131127 visible: status == Loader.Ready
132128
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
2323Item {
2424 id: indicatorItem
2525
26− enabled: menuObjectPath != ""
27−
2826 //const
2927 property string identifier
3028 property string busName
@@ -32,16 +30,14 @@ Item {
3230 property string menuObjectPath
3331 property string rootMenuType: "com.canonical.indicator.root"
3432
35− property string deviceMenuObjectPath: menuObjectPath
36−
3733 property alias menuModel: cachedModel.model
3834 property alias rootActionState: rootAction
3935
40− CachedUnityMenuModel {
36+ SharedUnityMenuModel {
4137 id: cachedModel
4238 busName: indicatorItem.busName
43− actionsObjectPath: indicatorItem.actionsObjectPath
44− menuObjectPath: indicatorItem.deviceMenuObjectPath
39+ actions: { "indicator": indicatorItem.actionsObjectPath }
40+ menuObjectPath: indicatorItem.menuObjectPath
4541 }
4642
4743 RootActionState {
qml/Panel/Indicators/IndicatorDelegate.qml+2−2
@@ -20,6 +20,6 @@
2020import QtQuick 2.0
2121
2222IndicatorBase {
23− enabled: rootActionState.visible
24− property string title: rootActionState.title
23+ readonly property bool indicatorVisible: rootActionState.indicatorVisible
24+ readonly property string title: rootActionState.title
2525}
qml/Panel/Indicators/VisibleIndicators.qml+4−5
@@ -24,6 +24,10 @@ import Utils 0.1
2424Item {
2525 property SortFilterProxyModel model: filterModel
2626
27+ function initialise(profile) {
28+ indicatorsModel.load(profile);
29+ }
30+
2731 SortFilterProxyModel {
2832 id: filterModel
2933 filterRole: Indicators.IndicatorsModelRole.IsVisible
@@ -36,7 +40,6 @@ Item {
3640 }
3741 }
3842
39−
4043 Indicators.IndicatorsModel {
4144 id: indicatorsModel
4245 }
@@ -78,8 +81,4 @@ Item {
7881 }
7982 }
8083 }
81−
82− function load(profile) {
83− indicatorsModel.load(profile);
84− }
8584}
qml/Panel/Indicators/client/IndicatorsPage.qml → qml/Panel/Indicators/client/IndicatorRepresentation.qmlrenamed+21−10
@@ -21,13 +21,13 @@
2121import QtQuick 2.0
2222import Ubuntu.Components 0.1
2323import Ubuntu.Components.ListItems 0.1 as ListItem
24+import "../.."
2425
2526Page {
26− id: page
27+ id: root
2728
2829 title: indicatorProperties && indicatorProperties.title ? indicatorProperties.title : ""
2930 property variant indicatorProperties
30− property string pageSource : pageLoader.source
3131
3232 anchors.fill: parent
3333
@@ -44,6 +44,7 @@ Page {
4444 id: pageLoader
4545 objectName: "pageLoader"
4646 clip:true
47+ asynchronous: true
4748
4849 Rectangle {
4950 anchors.fill: pageLoader
@@ -58,15 +59,25 @@ Page {
5859 topMargin: units.gu(2)
5960 bottomMargin: units.gu(2)
6061 }
61− source : visualCheck.checked ? page.pageSource : "IndicatorsTree.qml"
62+ sourceComponent: visualCheck.checked ? page : tree
6263
63− onLoaded: {
64− for(var pName in indicatorProperties) {
65− if (item.hasOwnProperty(pName)) {
66− item[pName] = indicatorProperties[pName];
67− }
64+ Component {
65+ id: page
66+ IndicatorPage {
67+ identifier: model.identifier
68+ busName: indicatorProperties.busName
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
78+ actionsObjectPath: indicatorProperties.actionsObjectPath
79+ menuObjectPath: indicatorProperties.menuObjectPath
6880 }
69− item.start();
7081 }
7182 }
7283
@@ -85,7 +96,7 @@ Page {
8596 left: parent.left
8697 }
8798 text: "Back"
88− onClicked: page.pageStack.reset()
99+ onClicked: root.pageStack.reset()
89100 }
90101 }
91102}
qml/Panel/Indicators/client/IndicatorsList.qml+2−2
@@ -58,8 +58,8 @@ Page {
5858 text: identifier
5959
6060 onClicked: {
61− var new_page = Qt.createComponent("IndicatorsPage.qml");
62− page.pageStack.push(new_page.createObject(pages), {"indicatorProperties" : model.indicatorProperties, "pageSource" : model.pageSource});
61+ var new_page = Qt.createComponent("IndicatorRepresentation.qml");
62+ page.pageStack.push(new_page.createObject(pages), {"indicatorProperties" : model.indicatorProperties });
6363 }
6464 }
6565 }
qml/Panel/Indicators/client/IndicatorsTree.qml+4−26
@@ -20,37 +20,15 @@
2020import QtQuick 2.0
2121import Ubuntu.Components 0.1
2222import Unity.Indicators 0.1 as Indicators
23−import QMenuModel 0.1
24−import Ubuntu.Components.ListItems 0.1 as ListItem
23+import ".."
2524import "../../../Components/Flickables" as Flickables
2625
27−Page {
28− id: page
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− }
26+IndicatorBase {
27+ id: root
5028
5129 Indicators.ModelPrinter {
5230 id: printer
53− model: unityModel
31+ model: root.menuModel
5432 }
5533
5634 Flickables.Flickable {
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+25−82
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2014 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -14,126 +14,69 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−import QtQuick 2.0
17+import QtQuick 2.2
1818import QtQuick.Layouts 1.1
1919import Ubuntu.Components 1.1
2020import Unity.Indicators 0.1 as Indicators
2121import Utils 0.1
2222import "../Components"
2323import "../Components/Flickables" as Flickables
24−import "Indicators"
2524
2625Rectangle {
2726 id: content
2827
2928 property QtObject indicatorsModel: null
30− readonly property alias currentMenuIndex: listViewHeader.currentIndex
29+ property int currentMenuIndex: -1
3130 color: "#221e1c" // FIXME not in palette yet
32− property real headerHeight: listViewHeader.height
3331
3432 width: units.gu(40)
3533 height: units.gu(42)
3634
37− function setCurrentMenuIndex(index, animate) {
38− // FIXME - https://bugreports.qt-project.org/browse/QTBUG-41229
39− listViewHeader.currentIndex = -1;
40− listViewHeader.currentIndex = index;
41− }
42−
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− }
35+ onCurrentMenuIndexChanged: {
36+ listViewContent.currentIndex = currentMenuIndex;
8237 }
8338
8439 Flickables.ListView {
8540 id: listViewContent
8641 objectName: "indicatorsContentListView"
87− anchors {
88− left: parent.left
89− right: parent.right
90− top: listViewHeader.bottom
91− bottom: parent.bottom
92− }
42+ anchors.fill: parent
9343 model: content.indicatorsModel
94− clip: true
9544
96− currentIndex: listViewHeader.currentIndex
45+ highlightFollowsCurrentItem: true
9746 interactive: false
9847 highlightMoveDuration: 0
9948 orientation: ListView.Horizontal
10049 // Load all the indicator menus (a big number)
10150 cacheBuffer: 1073741823
10251
52+ // for additions/removals.
53+ onCountChanged: {
54+ listViewContent.currentIndex = content.currentMenuIndex;
55+ }
56+
10357 delegate: Loader {
10458 id: loader
59+
10560 width: ListView.view.width
10661 height: ListView.view.height
10762 objectName: identifier
108−
109− source: pageSource
11063 asynchronous: true
64+ visible: ListView.isCurrentItem
11165
112− onVisibleChanged: {
113− // Reset the indicator states
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− }
66+ sourceComponent: IndicatorPage {
67+ objectName: identifier + "-page"
12668
127− Binding {
128− target: loader.item
129− property: "identifier"
130− value: identifier
69+ identifier: model.identifier
70+ busName: indicatorProperties.busName
71+ actionsObjectPath: indicatorProperties.actionsObjectPath
72+ menuObjectPath: indicatorProperties.menuObjectPath
13173 }
13274
133− Binding {
134− target: loader.item
135− property: "objectName"
136− value: identifier + "-page"
75+ onVisibleChanged: {
76+ // Reset the indicator states
77+ if (!visible && status == Loader.Ready) {
78+ item.reset();
79+ }
13780 }
13881 }
13982 }
qml/Panel/Panel.qml+48−41
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2014 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -14,11 +14,11 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
17+import QtQuick 2.2
18+import Ubuntu.Components 1.1
1919import Unity.Application 0.1
2020import "../Components"
21−import "../Components/ListItems"
21+import ".."
2222
2323Item {
2424 id: root
@@ -53,7 +53,9 @@ Item {
5353
5454 anchors.fill: parent
5555
56− Behavior on anchors.topMargin { StandardAnimation {} }
56+ Behavior on anchors.topMargin {
57+ NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
58+ }
5759
5860 BorderImage {
5961 id: dropShadow
@@ -62,24 +64,10 @@ Item {
6264 leftMargin: -units.gu(1)
6365 bottomMargin: -units.gu(1)
6466 }
65− visible: indicators.height > indicators.panelHeight
67+ visible: !indicators.fullyClosed
6668 source: "graphics/rectangular_dropshadow.sci"
6769 }
6870
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−
8371 Rectangle {
8472 id: indicatorAreaBackground
8573 color: callHint.visible ? "green" : "black"
@@ -88,13 +76,13 @@ Item {
8876 left: parent.left
8977 right: parent.right
9078 }
91− height: indicators.panelHeight
79+ height: indicators.minimizedPanelHeight
9280
9381 Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration } }
9482 }
9583
9684 PanelSeparatorLine {
97− id: nonIndicatorAreaSeparatorLine
85+ id: orangeLine
9886 anchors {
9987 top: indicatorAreaBackground.bottom
10088 left: parent.left
@@ -103,18 +91,29 @@ Item {
10391 saturation: 1 - indicators.unitProgress
10492 }
10593
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+
106105 MouseArea {
107106 anchors {
108107 top: parent.top
109108 left: parent.left
110109 right: indicators.left
111110 }
112− height: indicators.panelHeight
111+ height: indicators.minimizedPanelHeight
113112 enabled: callHint.visible
114113 onClicked: callHint.showLiveCall()
115114 }
116115
117− Indicators {
116+ IndicatorsMenu {
118117 id: __indicators
119118 objectName: "indicators"
120119
@@ -123,51 +122,55 @@ Item {
123122 right: parent.right
124123 }
125124
126− width: root.width
127125 shown: false
128− panelHeight: units.gu(3)
129− openedHeight: root.height
126+ width: root.width
127+ minimizedPanelHeight: units.gu(3)
128+ expandedPanelHeight: units.gu(7)
129+ openedHeight: root.height - indicatorOrangeLine.height
130+
130131 overFlowWidth: {
131132 if (callHint.visible) {
132133 return Math.max(root.width - (callHint.width + units.gu(2)), 0)
133134 }
134135 return root.width
135136 }
136−
137137 enableHint: !callHint.active && !fullscreenMode
138− showHintBottomMargin: fullscreenMode ? -panelHeight : 0
138+ panelColor: indicatorAreaBackground.color
139139
140140 onShowTapped: {
141141 if (callHint.active) {
142142 callHint.showLiveCall();
143143 }
144144 }
145− }
146145
147− ActiveCallHint {
148− id: __callHint
149− anchors {
150− top: parent.top
151− left: parent.left
146+ hideDragHandle {
147+ anchors.bottomMargin: -indicatorOrangeLine.height
152148 }
153− height: indicators.panelHeight
154− visible: active && indicators.state == "initial"
155149 }
156150
157151 PanelSeparatorLine {
158− id: indicatorsSeparatorLine
159− visible: true
152+ id: indicatorOrangeLine
160153 anchors {
161154 top: indicators.bottom
162− left: indicatorDividor.left
155+ left: indicators.left
163156 right: indicators.right
164157 }
165158 }
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+ }
166169 }
167170
168171 QtObject {
169172 id: d
170− readonly property real indicatorHeight: indicators.panelHeight + indicatorsSeparatorLine.height
173+ readonly property real indicatorHeight: indicators.minimizedPanelHeight + indicatorOrangeLine.height
171174 }
172175
173176 states: [
@@ -186,6 +189,10 @@ Item {
186189 target: indicatorArea;
187190 anchors.topMargin: indicators.state === "initial" ? -d.indicatorHeight : 0
188191 }
192+ PropertyChanges {
193+ target: indicators.showDragHandle;
194+ anchors.bottomMargin: -units.gu(1)
195+ }
189196 }
190197 ]
191198}
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"
3636import "Components"
3737import "Notifications"
3838import "Stages"
39+import "Panel/Indicators"
3940import Unity.Notifications 1.0 as NotificationBackend
4041import Unity.Session 0.1
4142import Unity.DashCommunicator 0.1
@@ -243,7 +244,7 @@ Item {
243244 target: applicationsDisplayLoader.item
244245 property: "maximizedAppTopMargin"
245246 // 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
247248 }
248249 Binding {
249250 target: applicationsDisplayLoader.item
@@ -646,7 +647,17 @@ Item {
646647 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
647648 contentEnabled: edgeDemo.panelContentEnabled
648649 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)
650661 }
651662
652663 property bool topmostApplicationIsFullscreen:
@@ -709,9 +720,9 @@ Item {
709720 model: NotificationBackend.Model
710721 margin: units.gu(1)
711722
712− y: panel.panelHeight
723+ y: topmostIsFullscreen ? 0 : panel.panelHeight
713724 width: parent.width
714− height: parent.height - panel.panelHeight
725+ height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
715726
716727 states: [
717728 State {
@@ -763,7 +774,7 @@ Item {
763774 paused: Powerd.status === Powerd.Off // Saves power
764775 greeter: greeter
765776 launcher: launcher
766− indicators: panel.indicators
777+ panel: panel
767778 stages: stages
768779 }
769780
tests/autopilot/unity8/indicators/tests/test_indicators.py+1−1
@@ -61,7 +61,7 @@ class IndicatorExistsTestCase(IndicatorTestCase):
6161 self.skipTest('Nexus 10 does not have bluetooth at the moment.')
6262
6363 def test_indicator_exists(self):
64− self.main_window._get_indicator_widget(
64+ self.main_window._get_indicator_panel_item(
6565 self.indicator_name
6666 )
6767
tests/autopilot/unity8/shell/emulators/main_window.py+6−6
@@ -75,15 +75,15 @@ class QQuickView(emulators.UnityEmulatorBase):
7575 def get_pinentryField(self):
7676 return self.select_single(objectName="pinentryField")
7777
78− def _get_indicator_widget(self, indicator_name):
78+ def _get_indicator_panel_item(self, indicator_name):
7979 return self.select_single(
80− 'DefaultIndicatorWidget',
81− objectName=indicator_name+'-widget'
80+ 'IndicatorItem',
81+ objectName=indicator_name+'-panelItem'
8282 )
8383
8484 def _get_indicator_page(self, indicator_name):
8585 return self.select_single(
86− 'DefaultIndicatorPage',
86+ 'IndicatorPage',
8787 objectName=indicator_name+'-page'
8888 )
8989
@@ -93,12 +93,12 @@ class QQuickView(emulators.UnityEmulatorBase):
9393
9494 :returns: The indicator page.
9595 """
96− widget = self._get_indicator_widget(indicator_name)
96+ widget = self._get_indicator_panel_item(indicator_name)
9797 start_x, start_y = input.get_center_point(widget)
9898 end_x = start_x
9999 end_y = self.height
100100 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)
102102 return self._get_indicator_page(indicator_name)
103103
104104 @autopilot_logging.log_action(logger.info)
tests/mocks/AccountsService/AccountsService.qmltypes+2
@@ -23,6 +23,8 @@ Module {
2323 }
2424 Property { name: "user"; type: "string" }
2525 Property { name: "demoEdges"; type: "bool" }
26+ Property { name: "enableLauncherWhileLocked"; type: "bool" }
27+ Property { name: "enableIndicatorsWhileLocked"; type: "bool" }
2628 Property { name: "backgroundFile"; type: "string" }
2729 Property { name: "statsWelcomeScreen"; type: "bool" }
2830 Property { name: "passwordDisplayHint"; type: "PasswordDisplayHint"; isReadonly: true }
tests/mocks/LightDM/LightDM.qmltypes+1
@@ -37,6 +37,7 @@ Module {
3737 Signal { name: "isActiveChanged" }
3838 Signal { name: "isAuthenticatedChanged" }
3939 Signal { name: "showGreeter" }
40+ Signal { name: "hideGreeter" }
4041 Signal {
4142 name: "requestAuthenticationUser"
4243 Parameter { name: "user"; type: "string" }
tests/mocks/QMenuModel/QMenuModel.qmltypes+1−1
@@ -18,12 +18,12 @@ Module {
1818 prototype: "QAbstractListModel"
1919 exports: ["QMenuModel/UnityMenuModel 0.1"]
2020 exportMetaObjectRevisions: [0]
21− Property { name: "modelData"; type: "QVariant" }
2221 Property { name: "busName"; type: "QByteArray" }
2322 Property { name: "actions"; type: "QVariantMap" }
2423 Property { name: "menuObjectPath"; type: "QByteArray" }
2524 Property { name: "actionStateParser"; type: "ActionStateParser"; isPointer: true }
2625 Property { name: "nameOwner"; type: "string"; isReadonly: true }
26+ Property { name: "modelData"; type: "QVariant" }
2727 Method {
2828 name: "insertRow"
2929 Parameter { name: "row"; type: "int" }
tests/mocks/QMenuModel/unitymenumodel.cpp+1−2
@@ -50,13 +50,12 @@ void UnityMenuModel::setModelData(const QVariant& data)
5050{
5151 beginResetModel();
5252
53− m_modelData.clear();
5453 m_modelData = data.toList();
54+ Q_EMIT modelDataChanged();
5555
5656 endResetModel();
5757}
5858
59−
6059void UnityMenuModel::insertRow(int row, const QVariant& data)
6160{
6261 row = qMin(row, rowCount());
tests/mocks/QMenuModel/unitymenumodel.h+6−2
@@ -27,13 +27,15 @@ class ActionStateParser;
2727class Q_DECL_EXPORT UnityMenuModel : public QAbstractListModel
2828{
2929 Q_OBJECT
30− Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
3130 Q_PROPERTY(QByteArray busName READ busName WRITE setBusName NOTIFY busNameChanged)
3231 Q_PROPERTY(QVariantMap actions READ actions WRITE setActions NOTIFY actionsChanged)
3332 Q_PROPERTY(QByteArray menuObjectPath READ menuObjectPath WRITE setMenuObjectPath NOTIFY menuObjectPathChanged)
3433 Q_PROPERTY(ActionStateParser* actionStateParser READ actionStateParser WRITE setActionStateParser NOTIFY actionStateParserChanged)
3534 Q_PROPERTY(QString nameOwner READ nameOwner NOTIFY nameOwnerChanged)
3635
36+ // internal mock properties
37+ Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
38+
3739public:
3840 UnityMenuModel(QObject *parent = nullptr);
3941 virtual ~UnityMenuModel();
@@ -81,9 +83,11 @@ Q_SIGNALS:
8183 void actionsChanged();
8284 void menuObjectPathChanged();
8385 void actionStateParserChanged();
84− void modelDataChanged();
8586 void nameOwnerChanged();
8687
88+ // Internal mock usage
89+ void modelDataChanged();
90+
8791private:
8892 QVariantMap rowData(int row) const;
8993 QVariant subMenuData(int row) const;
tests/mocks/Ubuntu/Payments/Payments.qmltypes+2
@@ -37,6 +37,8 @@ Module {
3737 Parameter { name: "error"; type: "string" }
3838 }
3939 Signal { name: "purchaseCompleted" }
40+ Signal { name: "purchaseCancelled" }
4041 Method { name: "start" }
42+ Method { name: "process" }
4143 }
4244}
tests/mocks/Ubuntu/Telephony/Telephony.qmltypes+2−1
@@ -22,7 +22,8 @@ Module {
2222 exportMetaObjectRevisions: [0]
2323 Property { name: "phoneNumber"; type: "string" }
2424 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" }
2627 }
2728 Component {
2829 name: "MockCallManager"
tests/mocks/Unity/Application/Application.qmltypes+13
@@ -238,6 +238,7 @@ Module {
238238 Property { name: "state"; type: "State"; isReadonly: true }
239239 Property { name: "name"; type: "string"; isReadonly: true }
240240 Property { name: "live"; type: "bool"; isReadonly: true }
241+ Property { name: "orientation"; type: "Qt::ScreenOrientation" }
241242 Signal {
242243 name: "typeChanged"
243244 Parameter { type: "Type" }
@@ -268,9 +269,14 @@ Module {
268269 exportMetaObjectRevisions: [0]
269270 Property { name: "name"; type: "string"; isReadonly: true }
270271 Property { name: "surface"; type: "MirSurfaceItem"; isReadonly: true; isPointer: true }
272+ Property { name: "application"; type: "ApplicationInfo"; isReadonly: true; isPointer: true }
271273 Property { name: "parentSession"; type: "Session"; isReadonly: true; isPointer: true }
272274 Property { name: "childSessions"; type: "SessionModel"; isReadonly: true; isPointer: true }
273275 Property { name: "live"; type: "bool"; isReadonly: true }
276+ Signal {
277+ name: "applicationChanged"
278+ Parameter { type: "ApplicationInfo"; isPointer: true }
279+ }
274280 Signal {
275281 name: "surfaceChanged"
276282 Parameter { type: "MirSurfaceItem"; isPointer: true }
@@ -368,6 +374,12 @@ Module {
368374 Property { name: "stage"; type: "Stage"; isReadonly: true }
369375 Property { name: "state"; type: "State"; isReadonly: true }
370376 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 }
371383 Signal {
372384 name: "nameChanged"
373385 Parameter { name: "name"; type: "string" }
@@ -414,6 +426,7 @@ Module {
414426 Property { name: "count"; type: "int"; isReadonly: true }
415427 Property { name: "focusedApplicationId"; type: "string"; isReadonly: true }
416428 Property { name: "suspended"; type: "bool" }
429+ Property { name: "forceDashActive"; type: "bool" }
417430 Signal {
418431 name: "focusRequested"
419432 Parameter { name: "appId"; type: "string" }
tests/mocks/Unity/DashCommunicator/DashCommunicator.qmltypes+2−2
@@ -34,13 +34,13 @@ Module {
3434 name: "setCurrentScopeRequested"
3535 Parameter { name: "scopeId"; type: "string" }
3636 Parameter { name: "animate"; type: "bool" }
37− Parameter { name: "reset"; type: "bool" }
37+ Parameter { name: "isSwipe"; type: "bool" }
3838 }
3939 Method {
4040 name: "mockSetCurrentScope"
4141 Parameter { name: "scopeId"; type: "string" }
4242 Parameter { name: "animate"; type: "bool" }
43− Parameter { name: "reset"; type: "bool" }
43+ Parameter { name: "isSwipe"; type: "bool" }
4444 }
4545 }
4646}
tests/mocks/Unity/Indicators/CMakeLists.txt+3−10
@@ -12,24 +12,17 @@ include_directories(
1212
1313set(IndicatorsFakeQml_SOURCES
1414 fakeplugin.cpp
15+ fakeindicatorsmodel.cpp
16+ fakeunitymenumodelcache.cpp
1517 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/indicators.h
1618 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/menucontentactivator.cpp
19+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/sharedunitymenumodel.cpp
1720 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/unitymenumodelcache.cpp
1821 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/unitymenumodelstack.cpp
1922 ${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}
2923)
3024
3125add_library(IndicatorsFakeQml SHARED
32− ${IndicatorsFakeQml_RESOURCES_RCC}
3326 ${IndicatorsFakeQml_SOURCES}
3427)
3528add_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 {
2929 exports: ["Unity.Indicators/FakeIndicatorsModel 0.1"]
3030 exportMetaObjectRevisions: [0]
3131 Property { name: "count"; type: "int"; isReadonly: true }
32+ Property { name: "modelData"; type: "QVariant" }
3233 Method {
3334 name: "load"
3435 Parameter { name: "profile"; type: "string" }
@@ -38,6 +39,15 @@ Module {
3839 name: "append"
3940 Parameter { name: "row"; type: "QVariantMap" }
4041 }
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+ }
4151 Method {
4252 name: "data"
4353 type: "QVariant"
@@ -45,6 +55,27 @@ Module {
4555 Parameter { name: "role"; type: "int" }
4656 }
4757 }
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+ }
4879 Component {
4980 name: "FlatMenuProxyModelRole"
5081 prototype: "QObject"
@@ -74,10 +105,8 @@ Module {
74105 values: {
75106 "Identifier": 0,
76107 "Position": 1,
77− "WidgetSource": 2,
78− "PageSource": 3,
79− "IndicatorProperties": 4,
80− "IsVisible": 5
108+ "IndicatorProperties": 2,
109+ "IsVisible": 3
81110 }
82111 }
83112 }
@@ -160,19 +189,18 @@ Module {
160189 }
161190 Component { name: "QIdentityProxyModel"; prototype: "QAbstractProxyModel" }
162191 Component {
163− name: "UnityMenuModelCache"
192+ name: "SharedUnityMenuModel"
164193 prototype: "QObject"
165− exports: ["Unity.Indicators/UnityMenuModelCache 0.1"]
166− isCreatable: false
167− isSingleton: true
194+ exports: ["Unity.Indicators/SharedUnityMenuModel 0.1"]
168195 exportMetaObjectRevisions: [0]
169− Method {
170− name: "model"
171− type: "UnityMenuModel*"
172− Parameter { name: "bus"; type: "QByteArray" }
173− Parameter { name: "path"; type: "QByteArray" }
174− Parameter { name: "actions"; type: "QVariantMap" }
175− }
196+ Property { name: "busName"; type: "QByteArray" }
197+ Property { name: "menuObjectPath"; type: "QByteArray" }
198+ Property { name: "actions"; type: "QVariantMap" }
199+ Property { name: "model"; type: "UnityMenuModel"; isReadonly: true; isPointer: true }
200+ }
201+ Component {
202+ name: "UnityMenuModelCache"
203+ prototype: "QObject"
176204 Method {
177205 name: "contains"
178206 type: "bool"
tests/mocks/Unity/Indicators/IndicatorsModel.qml+152−34
@@ -18,43 +18,161 @@ import QtQuick 2.0
1818import Unity.Indicators 0.1 as Indicators
1919
2020Indicators.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+ ]
2179
2280 function load(profile) {
2381 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+ }
24176
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− });
59177 }
60178}
tests/mocks/Unity/Indicators/RootActionState.qml+18−10
@@ -19,15 +19,23 @@
1919
2020import QtQuick 2.0
2121
22−QtObject {
23− property var menu
24− property bool valid: false
25− property string title
26− property string leftLabel
27− property string rightLabel
28− property var icons
29− property string accessibleName
30− property bool visible: true
22+Item {
23+ property var menu: null
24+ property bool valid: cachedState !== undefined
25+ property string title: cachedState && cachedState.hasOwnProperty("title") ? cachedState["title"] : ""
26+ property string leftLabel: cachedState && cachedState.hasOwnProperty("pre-label") ? cachedState["pre-label"] : ""
27+ property string rightLabel: cachedState && cachedState.hasOwnProperty("label") ? cachedState["label"] : ""
28+ property var icons: cachedState && cachedState.hasOwnProperty("icons") ? cachedState["icons"] : []
29+ property string accessibleName: cachedState && cachedState.hasOwnProperty("accessible-desc") ? cachedState["accessible-desc"] : ""
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+ }
3139
3240 signal updated
3341
@@ -37,5 +45,5 @@ QtObject {
3745 onRightLabelChanged: updated()
3846 onIconsChanged: updated()
3947 onAccessibleNameChanged: updated()
40− onVisibleChanged: updated()
48+ onIndicatorVisibleChanged: updated()
4149}
tests/mocks/Unity/Indicators/fakeindicatorsmodel.cpp+50−21
@@ -31,7 +31,6 @@ FakeIndicatorsModel::FakeIndicatorsModel(QObject *parent)
3131/*! \internal */
3232FakeIndicatorsModel::~FakeIndicatorsModel()
3333{
34− qDeleteAll(m_indicators);
3534}
3635
3736int FakeIndicatorsModel::count() const
@@ -47,31 +46,63 @@ void FakeIndicatorsModel::unload()
4746{
4847 beginResetModel();
4948
50− qDeleteAll(m_indicators);
51− m_indicators.clear();
49+ m_modelData.clear();
50+ Q_EMIT modelDataChanged();
5251
5352 endResetModel();
5453}
5554
5655
57−void FakeIndicatorsModel::append(const QVariantMap& row)
56+void FakeIndicatorsModel::append(const QVariantMap& data)
5857{
59− Indicator* new_row = new QHash<int, QVariant>();
60− for (auto iter = row.begin(); iter != row.end(); ++iter )
61− {
62− int key = roleNames().key(iter.key().toUtf8(), -1);
63− if (key != -1) {
64− new_row->insert(key, iter.value());
65− }
66− }
58+ QList<QVariant> allData = m_modelData.toList();
59+ beginInsertRows(QModelIndex(), allData.count(), allData.count());
60+
61+ allData.append(data);
62+ m_modelData = allData;
63+ Q_EMIT modelDataChanged();
6764
68− beginInsertRows(QModelIndex(), m_indicators.count(), m_indicators.count());
65+ endInsertRows();
66+}
6967
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();
7178
7279 endInsertRows();
7380}
7481
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+
75106QHash<int, QByteArray> FakeIndicatorsModel::roleNames() const
76107{
77108 static QHash<int, QByteArray> roles;
@@ -79,8 +110,6 @@ QHash<int, QByteArray> FakeIndicatorsModel::roleNames() const
79110 {
80111 roles[IndicatorsModelRole::Identifier] = "identifier";
81112 roles[IndicatorsModelRole::Position] = "position";
82− roles[IndicatorsModelRole::WidgetSource] = "widgetSource";
83− roles[IndicatorsModelRole::PageSource] = "pageSource";
84113 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
85114 }
86115 return roles;
@@ -91,18 +120,18 @@ int FakeIndicatorsModel::columnCount(const QModelIndex &) const
91120 return 1;
92121}
93122
94−Q_INVOKABLE QVariant FakeIndicatorsModel::data(int row, int role) const
123+QVariant FakeIndicatorsModel::data(int row, int role) const
95124{
96125 return data(index(row, 0), role);
97126}
98127
99128QVariant FakeIndicatorsModel::data(const QModelIndex &index, int role) const
100129{
101− if (!index.isValid() || index.row() >= m_indicators.size())
130+ QList<QVariant> dataList = m_modelData.toList();
131+ if (!index.isValid() || index.row() >= dataList.size())
102132 return QVariant();
103133
104− Indicator* indicator = m_indicators[index.row()];
105− return indicator->value(role, QVariant());
134+ return dataList[index.row()].toMap()[roleNames()[role]];
106135}
107136
108137QModelIndex FakeIndicatorsModel::parent(const QModelIndex&) const
@@ -112,5 +141,5 @@ QModelIndex FakeIndicatorsModel::parent(const QModelIndex&) const
112141
113142int FakeIndicatorsModel::rowCount(const QModelIndex&) const
114143{
115− return m_indicators.count();
144+ return m_modelData.toList().count();
116145}
tests/mocks/Unity/Indicators/fakeindicatorsmodel.h+8−2
@@ -27,6 +27,7 @@ class FakeIndicatorsModel : public QAbstractListModel
2727 Q_OBJECT
2828 Q_ENUMS(Roles)
2929 Q_PROPERTY(int count READ count NOTIFY countChanged)
30+ Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
3031public:
3132
3233 FakeIndicatorsModel(QObject *parent=0);
@@ -36,9 +37,14 @@ public:
3637 Q_INVOKABLE void unload();
3738
3839 Q_INVOKABLE void append(const QVariantMap& row);
40+ Q_INVOKABLE void insert(int row, const QVariantMap& data);
41+ Q_INVOKABLE void remove(int row);
3942
4043 Q_INVOKABLE QVariant data(int row, int role) const;
4144
45+ void setModelData(const QVariant& data);
46+ QVariant modelData() const { return m_modelData; }
47+
4248 /* QAbstractItemModel */
4349 QHash<int, QByteArray> roleNames() const;
4450 int columnCount(const QModelIndex &parent = QModelIndex()) const;
@@ -48,12 +54,12 @@ public:
4854
4955Q_SIGNALS:
5056 void countChanged();
57+ void modelDataChanged();
5158
5259private:
5360 int count() const;
5461
55− typedef QHash<int, QVariant> Indicator;
56− QList<Indicator*> m_indicators;
62+ QVariant m_modelData;
5763};
5864
5965#endif // FAKE_INDICATORSMODEL_H
tests/mocks/Unity/Indicators/fakeplugin.cpp+8−4
@@ -26,19 +26,22 @@
2626#include "fakeindicatorsmodel.h"
2727#include "indicators.h"
2828#include "menucontentactivator.h"
29−#include "unitymenumodelcache.h"
29+#include "sharedunitymenumodel.h"
30+#include "fakeunitymenumodelcache.h"
3031#include "unitymenumodelstack.h"
3132#include "visibleindicatorsmodel.h"
3233
34+#include <unitymenumodel.h>
35+
3336static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
3437 Q_UNUSED(engine);
3538 Q_UNUSED(scriptEngine);
36− return new UnityMenuModelCache;
39+ return FakeUnityMenuModelCache::singleton();
3740}
3841
3942void IndicatorsFakePlugin::registerTypes(const char * uri)
4043{
41− Q_INIT_RESOURCE(indicators_fake);
44+ qRegisterMetaType<UnityMenuModel*>("UnityMenuModel*");
4245
4346 // internal
4447 qmlRegisterType<FakeIndicatorsModel>(uri, 0, 1, "FakeIndicatorsModel");
@@ -47,8 +50,9 @@ void IndicatorsFakePlugin::registerTypes(const char * uri)
4750 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
4851 qmlRegisterType<UnityMenuModelStack>(uri, 0, 1, "UnityMenuModelStack");
4952 qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
53+ qmlRegisterType<SharedUnityMenuModel>(uri, 0, 1, "SharedUnityMenuModel");
5054
51− qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
55+ qmlRegisterSingletonType<FakeUnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
5256
5357 // external uncreatables
5458 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
33typeinfo Indicators.qmltypes
44
55IndicatorsModel 0.1 IndicatorsModel.qml
6−FakeMenuPage 0.1 FakeMenuPage.qml
76RootActionState 0.1 RootActionState.qml
tests/mocks/Unity/Launcher/Launcher.qmltypes+13
@@ -85,6 +85,7 @@ Module {
8585 Property { name: "recent"; type: "bool"; isReadonly: true }
8686 Property { name: "progress"; type: "int"; isReadonly: true }
8787 Property { name: "count"; type: "int"; isReadonly: true }
88+ Property { name: "countVisible"; type: "bool"; isReadonly: true }
8889 Property { name: "focused"; type: "bool"; isReadonly: true }
8990 Property {
9091 name: "quickList"
@@ -92,6 +93,14 @@ Module {
9293 isReadonly: true
9394 isPointer: true
9495 }
96+ Signal {
97+ name: "nameChanged"
98+ Parameter { name: "name"; type: "string" }
99+ }
100+ Signal {
101+ name: "iconChanged"
102+ Parameter { name: "icon"; type: "string" }
103+ }
95104 Signal {
96105 name: "pinnedChanged"
97106 Parameter { name: "pinned"; type: "bool" }
@@ -112,6 +121,10 @@ Module {
112121 name: "countChanged"
113122 Parameter { name: "count"; type: "int" }
114123 }
124+ Signal {
125+ name: "countVisibleChanged"
126+ Parameter { name: "countVisible"; type: "bool" }
127+ }
115128 Signal {
116129 name: "focusedChanged"
117130 Parameter { name: "focused"; type: "bool" }
tests/mocks/Unity/Unity.qmltypes+32
@@ -30,6 +30,25 @@ Module {
3030 Parameter { name: "row"; type: "int" }
3131 Parameter { name: "role"; type: "int" }
3232 }
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+ }
3352 }
3453 Component {
3554 name: "PreviewModel"
@@ -66,6 +85,10 @@ Module {
6685 exports: ["Unity/FakeResultsModel 0.2", "Unity/ResultsModel 0.2"]
6786 isCreatable: false
6887 exportMetaObjectRevisions: [0, 0]
88+ Method {
89+ name: "setResultCount"
90+ Parameter { name: "result_count"; type: "int" }
91+ }
6992 }
7093 Component {
7194 name: "Scope"
@@ -76,6 +99,15 @@ Module {
7699 name: "performQuery"
77100 Parameter { name: "query"; type: "string" }
78101 }
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+ }
79111 Method {
80112 name: "setSearchInProgress"
81113 Parameter { name: "inProg"; type: "bool" }
tests/plugins/Unity/Indicators/CMakeLists.txt+1
@@ -40,3 +40,4 @@ indicator_test(indicatorsmodeltest ADDITIONAL_LIBS IndicatorsQml)
4040indicator_test(menucontentactivatortest ADDITIONAL_CPPS ${INDICATORS_DIR}/menucontentactivator.cpp)
4141indicator_test(unitymenumodelstacktest ADDITIONAL_CPPS ${TEST_DIR}/mocks/QMenuModel/unitymenumodel.cpp ${INDICATORS_DIR}/unitymenumodelstack.cpp)
4242indicator_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
1212set(qmltest_DEFAULT_TARGETS qmlunittests)
1313set(qmltest_DEFAULT_NO_ADD_TEST FALSE)
1414set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal")
15−add_qml_test(Panel IndicatorItem)
1615add_qml_test(utils/Unity/Test UnityTest)
1716
1817set(qmltest_DEFAULT_TARGETS qmluitests)
1918set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
2019set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
2120
22−add_qml_test(. Shell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
23−add_qml_test(. ShellWithPin ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single-pin")
24−add_qml_test(. TabletShell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
21+add_qml_test(. Shell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single:${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
22+add_qml_test(. ShellWithPin ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single-pin:${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
23+add_qml_test(. TabletShell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full:${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
2524add_qml_test(Components Background)
2625add_qml_test(Components Carousel)
2726add_qml_test(Components DraggingArea)
@@ -72,14 +71,14 @@ add_qml_test(Launcher Launcher)
7271add_qml_test(Notifications Notifications)
7372add_qml_test(Notifications VisualSnapDecisionsQueue)
7473add_qml_test(Panel ActiveCallHint)
75−add_qml_test(Panel IndicatorRow)
76−add_qml_test(Panel Indicators)
77−add_qml_test(Panel MenuContent)
78−add_qml_test(Panel Panel)
74+add_qml_test(Panel IndicatorItem)
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")
79+add_qml_test(Panel MenuContent ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
80+add_qml_test(Panel Panel ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
7981add_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")
8382# These MenuItemFactory tests need the test/mocks/ to come before plugins/
8483add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
8584add_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
1919import ".."
2020import "../../../qml/Greeter"
2121import Ubuntu.Components 0.1
22−import QMenuModel 0.1
22+import Unity.Indicators 0.1 as Indicators
2323import Unity.Test 0.1 as UT
2424
2525Rectangle {
@@ -36,56 +36,71 @@ Rectangle {
3636 }
3737 }
3838
39− UnityMenuModel {
40− id: menuModel
41− modelData: [{
42− "rowData": {
43− "actionState": { "label": Qt.formatTime(new Date("October 13, 1975 11:13:00")) }
44− }
45− }]
39+ function updateDatetimeModelTime(label) {
40+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicator/datetime/phone",
41+ [{
42+ "rowData": {
43+ "actionState": { "label": label }
44+ }
45+ }]);
4646 }
4747
4848 UT.UnityTestCase {
4949 name: "Clock"
50+ when: windowShown
5051
5152 function init() {
52− var cachedModel = findChild(clock, "timeModel");
53− verify(cachedModel !== undefined);
54− cachedModel.model = menuModel;
53+ updateDatetimeModelTime(Qt.formatTime(new Date("October 13, 1975 12:14:00")));
54+ clock.visible = true;
5555 }
5656
57− function test_customDate() {
58− var dateObj = new Date("October 13, 1975 11:13:00")
59− var dateString = Qt.formatDate(dateObj, Qt.DefaultLocaleLongDate)
60− var timeString = Qt.formatTime(dateObj)
57+ // Test that the date portion of the clock updates with custom value.
58+ // Time portion is controlled by indicators
59+ function test_updateDate() {
60+ var dateLabel = findChild(clock, "dateLabel");
61+ var timeLabel = findChild(clock, "timeLabel");
6162
62− clock.currentDate = dateObj
63− var dateLabel = findChild(clock, "dateLabel")
64− compare(dateLabel.text, dateString, "Not the expected date")
65− var timeLabel = findChild(clock, "timeLabel")
66− compare(timeLabel.text, timeString, "Not the expected time")
63+ var timeString = Qt.formatTime(new Date("October 13, 1975 12:14:00"));
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+
70+ compare(dateLabel.text, dateString, "Not the expected date");
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+
78+ compare(dateLabel.text, dateString2, "Not the expected date");
79+ compare(timeLabel.text,timeString, "Time should come from indicators");
6780 }
6881
69− function test_dateUpdate() {
70− var dateObj = new Date("October 13, 1975 11:13:00")
71− var dateString = Qt.formatDate(dateObj, Qt.DefaultLocaleLongDate)
72− var timeString = Qt.formatTime(dateObj)
82+ // Test that the date portion of the clock updates with custom value.
83+ // Time portion is controlled by indicators
84+ function test_updateTime() {
85+ var timeLabel = findChild(clock, "timeLabel");
7386
74− clock.visible = false
75− var timeModel = findInvisibleChild(clock, "timeModel")
87+ var timeString1 = Qt.formatTime(new Date("October 13, 1975 11:15:00"));
88+ var timeString2 = Qt.formatTime(new Date("October 14, 1976 12:16:00"));
7689
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");
7892
79− clock.currentDate = dateObj
93+ updateDatetimeModelTime(timeString2);
94+ compare(timeLabel.text, timeString2, "Time should come from indicators");
95+ }
8096
81− var dateLabel = findChild(clock, "dateLabel")
82− compare(dateLabel.text, dateString, "Not the expected date")
83− var timeLabel = findChild(clock, "timeLabel")
84− compare(timeLabel.text, timeString, "Not the expected time")
97+ function test_indicatorDisconnect() {
98+ clock.visible = false
99+ var timeModel = findInvisibleChild(clock, "timeModel")
100+ compare(timeModel.menuObjectPath, "", "Clock shouldn't be connected to Indicators when not visible.")
85101
86102 clock.visible = true
87−
88− verify(timeModel.menuObjectPath != "", "Should be connected to Indicators.")
103+ verify(timeModel.menuObjectPath !== "", "Should be connected to Indicators.")
89104 }
90105 }
91106}
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
1919import Unity.Test 0.1 as UT
2020import Ubuntu.Telephony 0.1 as Telephony
2121import Unity.Application 0.1
22−import ".."
2322import "../../../qml/Panel"
2423
2524Item {
tests/qmltests/Panel/tst_IndicatorItem.qml+173−18
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -14,37 +14,192 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−import QtQuick 2.0
17+import QtQuick 2.1
18+import QtQuick.Layouts 1.1
1819import QtTest 1.0
19−import ".."
20−import "../../../qml/Panel"
2120import Ubuntu.Components 0.1
2221import Unity.Test 0.1 as UT
22+import "../../../qml/Panel"
2323
2424Rectangle {
25− width: units.gu(10)
26− height: units.gu(5)
27− color: "black"
25+ width: units.gu(80)
26+ height: units.gu(30)
27+ color: "white"
2828
29− IndicatorItem {
30− id: indicatorItem
29+ RowLayout {
3130 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+ }
32120 }
33121
34122 UT.UnityTestCase {
35123 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" ];
36133
37− function test_dimmed() {
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);
134+ tryCompare(heightAnimation, "running", false);
42135 }
43136
44− function test_empty() {
45− compare(indicatorItem.indicatorVisible, false, "IndicatorItem should not be visible.");
46− indicatorItem.widgetSource = "qrc:/tests/indciators/qml/fake_menu_widget1.qml";
47− tryCompare(indicatorItem, "indicatorVisible", true);
137+ function test_expand() {
138+ indicatorItem.expanded = true;
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);
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");
48203 }
49204 }
50205}
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 @@
11/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -17,26 +17,22 @@
1717import QtQuick 2.0
1818import QtTest 1.0
1919import Unity.Test 0.1 as UT
20−import QMenuModel 0.1
21−import "../../../../qml/Panel/Indicators"
20+import Unity.Indicators 0.1 as Indicators
21+import "../../../qml/Panel"
2222
2323Item {
2424 id: testView
2525 width: units.gu(40)
2626 height: units.gu(70)
2727
28− DefaultIndicatorPage {
28+ IndicatorPage {
2929 id: page
30−
3130 anchors.fill: parent
3231
33− menuModel: UnityMenuModel {}
34− busName: "test"
35− actionsObjectPath: "test"
36− menuObjectPath: "test"
37−
3832 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"
4036 }
4137
4238 property var fullMenuData: [{
@@ -113,29 +109,28 @@ Item {
113109 "submenu": []
114110 }]; // end row 1
115111
112+ function initializeMenuData(data) {
113+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicator/test",
114+ data);
115+ }
116+
116117 UT.UnityTestCase {
117− name: "DefaultIndicatorPage"
118+ name: "IndicatorPage"
118119
119120 function init() {
120− var mainMenu = findChild(page, "mainMenu");
121− page.menuModel.modelData = [];
122− verify(mainMenu.model !== null);
121+ initializeMenuData([]);
123122 }
124123
125124 function test_reloadData() {
126125 var mainMenu = findChild(page, "mainMenu");
127126
128− page.menuModel.modelData = [];
129127 tryCompare(mainMenu, "count", 0);
130128
131− page.menuModel.modelData = fullMenuData;
129+ initializeMenuData(fullMenuData);
132130 tryCompare(mainMenu, "count", 3);
133131
134− page.menuModel.modelData = [];
132+ initializeMenuData([]);
135133 tryCompare(mainMenu, "count", 0);
136−
137− page.menuModel.modelData = fullMenuData;
138− tryCompare(mainMenu, "count", 3);
139134 }
140135
141136 function test_traverse_rootMenuType_data() {
@@ -147,7 +142,7 @@ Item {
147142
148143 function test_traverse_rootMenuType(data) {
149144 page.rootMenuType = data.rootMenuType;
150− page.menuModel.modelData = fullMenuData;
145+ initializeMenuData(fullMenuData);
151146
152147 var mainMenu = findChild(page, "mainMenu");
153148 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 @@
11/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -17,36 +17,21 @@
1717import QtQuick 2.0
1818import QtTest 1.0
1919import Unity.Test 0.1 as UT
20−import ".."
2120import "../../../qml/Panel"
2221import Unity.Indicators 0.1 as Indicators
2322
24−Item {
25− id: shell
23+IndicatorTest {
24+ id: root
2625 width: units.gu(40)
2726 height: units.gu(70)
2827
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−
3728 // Dummy objects
3829 Item { id: greeter }
3930 Item { id: handle }
4031
41−
42− Indicators.IndicatorsModel {
43− id: indicatorsModel
44− Component.onCompleted: load("test1")
45− }
46−
4732 MenuContent {
4833 id: menuContent
49− indicatorsModel: indicatorsModel
34+ indicatorsModel: root.indicatorsModel
5035 height: parent.height - 50
5136 }
5237
@@ -78,12 +63,12 @@ Item {
7863 if (menuContent.currentMenuIndex == -1)
7964 activate_content(0);
8065 else
81− activate_content((menuContent.currentMenuIndex + 1) % indicatorsModel.count)
66+ activate_content((menuContent.currentMenuIndex + 1) % root.originalModelData.length)
8267 }
8368
8469 function activate_content(index)
8570 {
86− menuContent.setCurrentMenuIndex(index)
71+ menuContent.currentMenuIndex = index;
8772 }
8873
8974 function get_test_menu_objecName(index) {
@@ -115,7 +100,7 @@ Item {
115100
116101 // Check that the correct menus are displayed for the requested item.
117102 function test_show_menu() {
118− var menuCount = indicatorsModel.count;
103+ var menuCount = root.originalModelData.length;
119104 verify(menuCount > 0, "Menu count should be greater than zero");
120105
121106 var listView = menu_content_test.findChild(menuContent, "indicatorsContentListView")
@@ -126,7 +111,7 @@ Item {
126111 var menuIndex = i%menuCount;
127112
128113 activate_content(menuIndex);
129− testItemObjectName = indicatorsModel.data(menuIndex, Indicators.IndicatorsModelRole.Identifier);
114+ testItemObjectName = indicatorsModel.model.data(menuIndex, Indicators.IndicatorsModelRole.Identifier);
130115 compare(listView.currentIndex, menuIndex, "Current tab index does not match selected tab index");
131116 tryCompareFunction(current_item_equals_test_item, true);
132117 }
@@ -134,14 +119,13 @@ Item {
134119
135120 // Tests QTBUG-30632 - asynchronous loader crashes when changing index quickly.
136121 function test_multi_activate() {
137− var menuCount = indicatorsModel.count;
122+ var menuCount = root.originalModelData.length;
138123 verify(menuCount > 0, "Menu count should be greater than zero");
139124
140125 for (var i = 0; i < 100; i++) {
141126 activate_content(i % menuCount);
142127 compare(menuContent.currentMenuIndex, i%menuCount);
143128 }
144− wait(100);
145129 }
146130 }
147131}
tests/qmltests/Panel/tst_Panel.qml+185−214
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -14,95 +14,106 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−import QtQuick 2.0
17+import QtQuick 2.1
18+import QtQuick.Layouts 1.1
1819import QtTest 1.0
1920import 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
2123import Ubuntu.Telephony 0.1 as Telephony
22−import ".."
2324import "../../../qml/Panel"
2425
25−/*
26− This tests the Panel component 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. This can be controlled
28− as in the shell.
29−*/
30−Item {
31− id: shell
32− width: units.gu(40)
33− height: units.gu(80)
34−
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
26+IndicatorTest {
27+ id: root
28+ width: units.gu(100)
29+ height: units.gu(71)
30+ color: "white"
4331
44− indicators {
45− profile: "test1"
46− panelHeight: units.gu(5)
47− }
48− callHint {
49− height: units.gu(4)
50− }
32+ property string indicatorProfile: "phone"
5133
52− property real panelAndSeparatorHeight: panel.indicators.panelHeight + units.dp(2)
53− }
34+ RowLayout {
35+ anchors.fill: parent
36+ anchors.margins: units.gu(1)
37+ clip: true
5438
39+ Rectangle {
40+ Layout.fillWidth: true
41+ Layout.fillHeight: true
5542
56− Row {
57− id: row
58− anchors {
59− bottom: shell.bottom
60− left: parent.left
61− right: parent.right
62− }
63− height: 50
43+ id: itemArea
44+ color: "blue"
6445
65− UC.Button {
66− text: panel.indicators.shown ? "Hide" : "Show"
67− anchors {
68− top: parent.top
69− bottom: parent.bottom
46+ Panel {
47+ id: panel
48+ anchors.fill: parent
49+ indicators {
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)
7055 }
71− width: parent.width/3
56+ }
7257
73− onClicked: {
74− if (panel.indicators.shown) {
75− panel.indicators.hide();
76− } else {
77− panel.indicators.show();
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"
65+ onClicked: {
66+ if (panel.indicators.shown) {
67+ panel.indicators.hide();
68+ } else {
69+ panel.indicators.show();
70+ }
7871 }
7972 }
80− }
8173
82− UC.Button {
83− text: panel.fullscreenMode ? "Maximize" : "FullScreen"
84− anchors {
85− top: parent.top
86− bottom: parent.bottom
74+ Button {
75+ text: panel.fullscreenMode ? "Maximize" : "FullScreen"
76+ Layout.fillWidth: true
77+ onClicked: panel.fullscreenMode = !panel.fullscreenMode
8778 }
88− width: parent.width/3
8979
90− onClicked: panel.fullscreenMode = !panel.fullscreenMode
91− }
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+ }
90+ }
9291
93− UC.Button {
94− text: callManager.hasCalls ? "Called" : "No Calls"
95− anchors {
96− top: parent.top
97− bottom: parent.bottom
92+ Rectangle {
93+ Layout.preferredHeight: units.dp(1);
94+ Layout.fillWidth: true;
95+ color: "black"
9896 }
99− width: parent.width/3
10097
101− onClicked: {
102− if (callManager.foregroundCall) {
103− callManager.foregroundCall = null;
104− } else {
105− callManager.foregroundCall = phoneCall;
98+ Repeater {
99+ model: root.originalModelData
100+ RowLayout {
101+ CheckBox {
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+ }
106117 }
107118 }
108119 }
@@ -118,13 +129,12 @@ Item {
118129 when: windowShown
119130
120131 function init() {
121− panel.indicators.initialise();
122132 panel.fullscreenMode = false;
133+ callManager.foregroundCall = null;
123134
124135 panel.indicators.hide();
125136 // Wait for animation to complete
126137 tryCompare(panel.indicators.hideAnimation, "running", false);
127− callManager.foregroundCall = null;
128138
129139 // Wait for the indicators to get into position.
130140 // (switches between normal and fullscreen modes are animated)
@@ -133,100 +143,21 @@ Item {
133143 }
134144
135145 function get_indicator_item(index) {
136− var indicatorRow = findChild(panel.indicators, "indicatorRow");
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);
146+ var indicatorItem = findChild(panel, root.originalModelData[index]["identifier"]+"-panelItem");
147147 verify(indicatorItem !== null);
148148
149− return panel.mapFromItem(indicatorItem, indicatorItem.width/2, indicatorItem.height/2);
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);
149+ return indicatorItem;
215150 }
216151
217152 function test_drag_show_data() {
218153 return [
219− { tag: "pinned", fullscreenFlag: false, alreadyOpen: false, call: null,
154+ { tag: "pinned", fullscreen: false, call: null,
220155 indicatorY: 0 },
221− { tag: "fullscreen", fullscreenFlag: true, alreadyOpen: false, call: null,
156+ { tag: "fullscreen", fullscreen: true, call: null,
222157 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,
228159 indicatorY: 0},
229− { tag: "fullscreen-callActive", fullscreenFlag: true, alreadyOpen: false, call: phoneCall,
160+ { tag: "fullscreen-callActive", fullscreen: true, call: phoneCall,
230161 indicatorY: -panel.panelAndSeparatorHeight }
231162 ];
232163 }
@@ -234,16 +165,12 @@ Item {
234165 // Dragging from a indicator item in the panel will gradually expose the
235166 // indicators, first by running the hint animation, then after dragging down will
236167 // 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.
237169 function test_drag_show(data) {
238− panel.fullscreenMode = data.fullscreenFlag;
170+ panel.fullscreenMode = data.fullscreen;
239171 callManager.foregroundCall = data.call;
240172
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");
247174 verify(indicatorRow !== null);
248175
249176 var menuContent = findChild(panel.indicators, "menuContent");
@@ -256,25 +183,16 @@ Item {
256183 // (switches between normal and fullscreen modes are animated)
257184 tryCompareFunction(function() { return indicatorArea.y }, data.indicatorY);
258185
259− // do this for each indicator item
260− for (var i = 0; i < indicatorRow.row.count; i++) {
261−
186+ for (var i = 0; i < root.originalModelData.length; i++) {
262187 var indicatorItem = get_indicator_item(i);
263− verify(indicatorItem !== null);
264188
265− if (!indicatorItem.visible)
266− continue;
189+ var startXPosition = root.mapFromItem(indicatorItem, indicatorItem.width / 2, 0).x;
190+ var startYPosition = root.mapFromItem(panel, 0, 0).y;
267191
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
274192 touchFlick(panel,
275− indicatorItemCoord.x, panel.indicators.panelHeight / 2,
276− indicatorItemCoord.x, panel.height,
277− false /* beginTouch */, false /* endTouch */);
193+ startXPosition, startYPosition,
194+ startXPosition, panel.height,
195+ true /* beginTouch */, false /* endTouch */, units.gu(5), 15);
278196
279197 // Indicators height should follow the drag, and therefore increase accordingly.
280198 // They should be at least half-way through the screen
@@ -282,56 +200,109 @@ Item {
282200 function() {return panel.indicators.height >= panel.height * 0.5},
283201 true);
284202
285− touchRelease(panel, indicatorItemCoord.x, panel.height);
203+ touchRelease(panel, startXPosition, panel.height);
286204
287− compare(indicatorRow.currentItem, indicatorItem,
288− "Incorrect item activated at position " + i);
289− compare(menuContent.currentMenuIndex, i, "Menu conetent should be enabled for item at position " + i);
205+ compare(indicatorRow.currentItemIndex, i, "Indicator item should be activated at position " + i);
206+ compare(menuContent.currentMenuIndex, i, "Menu conetent should be activated for item at position " + i);
290207
291208 // init for next indicatorItem
292− if (!data.alreadyOpen) {
293− panel.indicators.hide();
294− tryCompare(panel.indicators.hideAnimation, "running", false);
295− tryCompare(panel.indicators, "state", "initial");
296− }
209+ panel.indicators.hide();
210+ tryCompare(panel.indicators.hideAnimation, "running", false);
211+ tryCompare(panel.indicators, "state", "initial");
297212 }
298213 }
299214
300− // Test the vertical velocity check when flicking the indicators open at an angle.
301− // If the vertical velocity is above a specific point, we shouldnt change active indicators
302− // if the x position changes
303− function test_vertical_velocity_detector() {
304− panel.fullscreenMode = false;
215+ function test_drag_hide_data() {
216+ return [
217+ { tag: "pinned", fullscreen: false, call: null,
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) {
231+ panel.fullscreenMode = data.fullscreen;
232+ callManager.foregroundCall = data.call;
305233
306− var indicatorRow = findChild(panel.indicators, "indicatorRow");
234+ var indicatorRow = findChild(panel.indicators, "indicatorItemRow");
307235 verify(indicatorRow !== null);
308236
309− // Get the first indicator
310− var indicatorItemFirst = get_indicator_item(0);
311− verify(indicatorItemFirst !== null);
237+ var menuContent = findChild(panel.indicators, "menuContent");
238+ verify(menuContent !== null);
312239
313− var indicatorItemCoordFirst = get_indicator_item_position(0);
314− var indicatorItemCoordNext = get_indicator_item_position(indicatorRow.row.count - 1);
240+ var indicatorArea = findChild(panel, "indicatorArea");
241+ verify(indicatorArea !== null);
315242
316− touchPress(panel,
317− indicatorItemCoordFirst.x, panel.indicators.panelHeight / 2);
243+ // Wait for the indicators to get into position.
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);
318259
319− // 1) Drag the mouse down to hint a bit
320− touchFlick(panel,
321− indicatorItemCoordFirst.x, panel.indicators.panelHeight / 2,
322− indicatorItemCoordFirst.x, panel.indicators.panelHeight * 2,
323− false /* beginTouch */, false /* endTouch */);
260+ // Indicators height should follow the drag, and therefore increase accordingly.
261+ // They should be at least half-way through the screen
262+ tryCompareFunction(
263+ function() {return panel.indicators.height <= panel.height * 0.5},
264+ true);
324265
325− tryCompare(indicatorRow, "currentItem", indicatorItemFirst)
266+ touchRelease(panel.indicators, startXPosition, 0);
326267
327− // 1) Flick mouse down to bottom
328− touchFlick(panel,
329− indicatorItemCoordFirst.x, panel.indicators.panelHeight * 2,
330− indicatorItemCoordNext.x, panel.height,
331− false /* beginTouch */, true /* endTouch */,
332− units.gu(10) /* speed */, 30 /* iterations */); // more samples needed for accurate velocity
268+ tryCompare(panel.indicators.hideAnimation, "running", true);
269+ tryCompare(panel.indicators.hideAnimation, "running", false);
270+ tryCompare(panel.indicators, "state", "initial");
271+ }
272+
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");
333304
334− compare(indicatorRow.currentItem, indicatorItemFirst, "First indicator should still be the current item");
305+ touchRelease(panel, mappedPosition.x, panel.minimizedPanelHeight / 2);
335306 }
336307 }
337308}
tests/qmltests/Panel/tst_SearchIndicator.qml−1
@@ -16,7 +16,6 @@
1616
1717import QtQuick 2.0
1818import QtTest 1.0
19−import ".."
2019import "../../../qml/Panel"
2120import Ubuntu.Components 0.1
2221import Unity.Test 0.1 as UT
tests/qmltests/tst_Shell.qml+4−2
@@ -65,6 +65,8 @@ Row {
6565 property bool itemDestroyed: false
6666 sourceComponent: Component {
6767 Shell {
68+ property string indicatorProfile: "phone"
69+
6870 Component.onDestruction: {
6971 shellLoader.itemDestroyed = true;
7072 }
@@ -345,7 +347,7 @@ Row {
345347 var touchX = shell.width / 2;
346348 var indicators = findChild(shell, "indicators");
347349 touchFlick(indicators,
348− touchX /* fromX */, indicators.panelHeight * 0.5 /* fromY */,
350+ touchX /* fromX */, indicators.minimizedPanelHeight * 0.5 /* fromY */,
349351 touchX /* toX */, shell.height * 0.5 /* toY */,
350352 true /* beginTouch */, false /* endTouch */);
351353 verify(indicators.partiallyOpened);
@@ -382,7 +384,7 @@ Row {
382384 var indicators = findChild(shell, "indicators");
383385
384386 var touchStartX = shell.width / 2;
385− var touchStartY = shell.height - (indicators.panelHeight * 0.5);
387+ var touchStartY = shell.height - (indicators.minimizedPanelHeight * 0.5);
386388 touchFlick(shell,
387389 touchStartX, touchStartY,
388390 touchStartX, shell.height * 0.1);
tests/qmltests/tst_ShellWithPin.qml+2
@@ -61,6 +61,8 @@ Item {
6161 property bool itemDestroyed: false
6262 sourceComponent: Component {
6363 Shell {
64+ property string indicatorProfile: "phone"
65+
6466 Component.onDestruction: {
6567 shellLoader.itemDestroyed = true
6668 }
tests/qmltests/tst_TabletShell.qml+2
@@ -57,6 +57,8 @@ Row {
5757 property bool itemDestroyed: false
5858 sourceComponent: Component {
5959 Shell {
60+ property string indicatorProfile: "phone"
61+
6062 Component.onDestruction: {
6163 shellLoader.itemDestroyed = true
6264 }