on xenial help sign in
Implementation of expandable panel design Fixes: 1368856, 1382078, 1383693
a33614bNick Dedekind
plugins/Unity/Indicators/Indicators.qmltypes+2−4
@@ -99,10 +99,8 @@ Module {
99 values: {
99 values: {
100 "Identifier": 0,
100 "Identifier": 0,
101 "Position": 1,
101 "Position": 1,
102− "WidgetSource": 2,
102+ "IndicatorProperties": 2,
103− "PageSource": 3,
103+ "IsVisible": 3
104− "IndicatorProperties": 4,
105− "IsVisible": 5
106 }
104 }
107 }
105 }
108 }
106 }
plugins/Unity/Indicators/indicators.h−2
@@ -74,8 +74,6 @@ public:
74 enum Roles {
74 enum Roles {
75 Identifier = 0,
75 Identifier = 0,
76 Position,
76 Position,
77− WidgetSource,
78− PageSource,
79 IndicatorProperties,
77 IndicatorProperties,
80 IsVisible
78 IsVisible
81 };
79 };
plugins/Unity/Indicators/indicatorsmodel.cpp−6
@@ -202,8 +202,6 @@ QHash<int, QByteArray> IndicatorsModel::roleNames() const
202 {
202 {
203 roles[IndicatorsModelRole::Identifier] = "identifier";
203 roles[IndicatorsModelRole::Identifier] = "identifier";
204 roles[IndicatorsModelRole::Position] = "position";
204 roles[IndicatorsModelRole::Position] = "position";
205− roles[IndicatorsModelRole::WidgetSource] = "widgetSource";
206− roles[IndicatorsModelRole::PageSource] = "pageSource";
207 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
205 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
208 }
206 }
209 return roles;
207 return roles;
@@ -248,10 +246,6 @@ QVariant IndicatorsModel::data(const QModelIndex &index, int role) const
248 return QVariant(indicator->indicatorProperties());
246 return QVariant(indicator->indicatorProperties());
249 }
247 }
250 break;
248 break;
251− case IndicatorsModelRole::WidgetSource:
252− return qmlDirectory()+"/Panel/Indicators/DefaultIndicatorWidget.qml";
253− case IndicatorsModelRole::PageSource:
254− return qmlDirectory()+"/Panel/Indicators/DefaultIndicatorPage.qml";
255 default:
249 default:
256 break;
250 break;
257 }
251 }
plugins/Unity/Indicators/rootactionstate.cpp+1−1
@@ -156,7 +156,7 @@ QString RootActionState::accessibleName() const
156 return m_cachedState.value("accessible-desc", QVariant::fromValue(QString())).toString();
156 return m_cachedState.value("accessible-desc", QVariant::fromValue(QString())).toString();
157}
157}
158
158
159−bool RootActionState::isVisible() const
159+bool RootActionState::indicatorVisible() const
160{
160{
161 if (!isValid()) return false;
161 if (!isValid()) return false;
162
162
plugins/Unity/Indicators/rootactionstate.h+4−4
@@ -37,7 +37,7 @@ class UNITYINDICATORS_EXPORT RootActionState : public ActionStateParser
37 Q_PROPERTY(QString rightLabel READ rightLabel NOTIFY rightLabelChanged)
37 Q_PROPERTY(QString rightLabel READ rightLabel NOTIFY rightLabelChanged)
38 Q_PROPERTY(QStringList icons READ icons NOTIFY iconsChanged)
38 Q_PROPERTY(QStringList icons READ icons NOTIFY iconsChanged)
39 Q_PROPERTY(QString accessibleName READ accessibleName NOTIFY accessibleNameChanged)
39 Q_PROPERTY(QString accessibleName READ accessibleName NOTIFY accessibleNameChanged)
40− Q_PROPERTY(bool visible READ isVisible NOTIFY visibleChanged)
40+ Q_PROPERTY(bool indicatorVisible READ indicatorVisible NOTIFY indicatorVisibleChanged)
41public:
41public:
42 RootActionState(QObject *parent = 0);
42 RootActionState(QObject *parent = 0);
43 virtual ~RootActionState();
43 virtual ~RootActionState();
@@ -54,10 +54,10 @@ public:
54 QString rightLabel() const;
54 QString rightLabel() const;
55 QStringList icons() const;
55 QStringList icons() const;
56 QString accessibleName() const;
56 QString accessibleName() const;
57− bool isVisible() const;
57+ bool indicatorVisible() const;
58
58
59 // from ActionStateParser
59 // from ActionStateParser
60− virtual QVariant toQVariant(GVariant* state) const;
60+ virtual QVariant toQVariant(GVariant* state) const override;
61
61
62Q_SIGNALS:
62Q_SIGNALS:
63 void updated();
63 void updated();
@@ -71,7 +71,7 @@ Q_SIGNALS:
71 void rightLabelChanged();
71 void rightLabelChanged();
72 void iconsChanged();
72 void iconsChanged();
73 void accessibleNameChanged();
73 void accessibleNameChanged();
74− void visibleChanged();
74+ void indicatorVisibleChanged();
75
75
76private Q_SLOTS:
76private Q_SLOTS:
77 void onModelRowsAdded(const QModelIndex& parent, int start, int end);
77 void onModelRowsAdded(const QModelIndex& parent, int start, int end);
plugins/Unity/Indicators/sharedunitymenumodel.cpp+6−1
@@ -18,6 +18,8 @@
18#include "sharedunitymenumodel.h"
18#include "sharedunitymenumodel.h"
19#include "unitymenumodelcache.h"
19#include "unitymenumodelcache.h"
20
20
21+#include <unitymenumodel.h>
22+
21SharedUnityMenuModel::SharedUnityMenuModel(QObject* parent)
23SharedUnityMenuModel::SharedUnityMenuModel(QObject* parent)
22 : QObject(parent)
24 : QObject(parent)
23{
25{
@@ -78,8 +80,11 @@ void SharedUnityMenuModel::initialize()
78 Q_EMIT modelChanged();
80 Q_EMIT modelChanged();
79 }
81 }
80 } else {
82 } else {
81− QSharedPointer<UnityMenuModel> model = UnityMenuModelCache::singleton()->model(m_busName, m_menuObjectPath, m_actions);
83+ QSharedPointer<UnityMenuModel> model = UnityMenuModelCache::singleton()->model(m_menuObjectPath);
82 if (model != m_model) {
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+
83 m_model = model;
88 m_model = model;
84 Q_EMIT modelChanged();
89 Q_EMIT modelChanged();
85 }
90 }
plugins/Unity/Indicators/unitymenumodelcache.cpp+1−5
@@ -37,9 +37,7 @@ UnityMenuModelCache::UnityMenuModelCache(QObject* parent)
37{
37{
38}
38}
39
39
40−QSharedPointer<UnityMenuModel> UnityMenuModelCache::model(const QByteArray& bus,
40+QSharedPointer<UnityMenuModel> UnityMenuModelCache::model(const QByteArray& path)
41− const QByteArray& path,
42− const QVariantMap& actions)
43{
41{
44 if (m_registry.contains(path))
42 if (m_registry.contains(path))
45 return m_registry[path];
43 return m_registry[path];
@@ -60,9 +58,7 @@ QSharedPointer<UnityMenuModel> UnityMenuModelCache::model(const QByteArray& bus,
60 });
58 });
61 m_registry[path] = menuModel.toWeakRef();
59 m_registry[path] = menuModel.toWeakRef();
62
60
63− menuModel->setBusName(bus);
64 menuModel->setMenuObjectPath(path);
61 menuModel->setMenuObjectPath(path);
65− menuModel->setActions(actions);
66 return menuModel;
62 return menuModel;
67}
63}
68
64
plugins/Unity/Indicators/unitymenumodelcache.h+1−3
@@ -37,9 +37,7 @@ public:
37
37
38 static UnityMenuModelCache* singleton();
38 static UnityMenuModelCache* singleton();
39
39
40− virtual QSharedPointer<UnityMenuModel> model(const QByteArray& bus,
40+ virtual QSharedPointer<UnityMenuModel> model(const QByteArray& path);
41− const QByteArray& path,
42− const QVariantMap& actions);
43
41
44 // for tests use
42 // for tests use
45 Q_INVOKABLE virtual bool contains(const QByteArray& path);
43 Q_INVOKABLE virtual bool contains(const QByteArray& path);
plugins/Unity/Indicators/visibleindicatorsmodel.cpp−2
@@ -33,8 +33,6 @@ QHash<int, QByteArray> VisibleIndicatorsModel::roleNames() const
33 {
33 {
34 roles[IndicatorsModelRole::Identifier] = "identifier";
34 roles[IndicatorsModelRole::Identifier] = "identifier";
35 roles[IndicatorsModelRole::Position] = "position";
35 roles[IndicatorsModelRole::Position] = "position";
36− roles[IndicatorsModelRole::WidgetSource] = "widgetSource";
37− roles[IndicatorsModelRole::PageSource] = "pageSource";
38 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
36 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
39 roles[IndicatorsModelRole::IsVisible] = "isVisible";
37 roles[IndicatorsModelRole::IsVisible] = "isVisible";
40 }
38 }
qml/Components/DragHandle.qml+6−1
@@ -68,6 +68,7 @@ EdgeDragArea {
68 }
68 }
69
69
70 property real hintDisplacement: 0
70 property real hintDisplacement: 0
71+ property var overrideStartValue: undefined
71 SmoothedAnimation {
72 SmoothedAnimation {
72 id: hintingAnimation
73 id: hintingAnimation
73 target: hintingAnimation
74 target: hintingAnimation
@@ -196,7 +197,11 @@ EdgeDragArea {
196 } else /* Undecided || Recognized */ {
197 } else /* Undecided || Recognized */ {
197 if (d.previousStatus === DirectionalDragArea.WaitingForTouch) {
198 if (d.previousStatus === DirectionalDragArea.WaitingForTouch) {
198 dragEvaluator.reset();
199 dragEvaluator.reset();
200+ if (overrideStartValue !== undefined) {
201+ d.startValue = overrideStartValue;
202+ } else {
199− d.startValue = parent[d.targetProp];
203+ d.startValue = parent[d.targetProp];
204+ }
200
205
201 if (hintDisplacement > 0) {
206 if (hintDisplacement > 0) {
202 hintingAnimation.targetValue = d.startValue;
207 hintingAnimation.targetValue = d.startValue;
qml/Components/EdgeDemo.qml+13−10
@@ -23,7 +23,7 @@ Item {
23
23
24 property Item greeter
24 property Item greeter
25 property Item launcher
25 property Item launcher
26− property Item indicators
26+ property Item panel
27 property Item stages
27 property Item stages
28
28
29 property bool launcherEnabled: true
29 property bool launcherEnabled: true
@@ -139,11 +139,11 @@ Item {
139 function startTopEdgeDemo() {
139 function startTopEdgeDemo() {
140 demo.panelEnabled = true;
140 demo.panelEnabled = true;
141 if (demo.stages) {
141 if (demo.stages) {
142− d.topEdgeDemo = d.overlay.createObject(demo.stages, {
142+ d.topEdgeDemo = d.overlay.createObject(demo.panel, {
143 "edge": "top",
143 "edge": "top",
144 "title": i18n.tr("Top edge"),
144 "title": i18n.tr("Top edge"),
145 "text": i18n.tr("Try swiping from the top edge to access the indicators"),
145 "text": i18n.tr("Try swiping from the top edge to access the indicators"),
146− "anchors.fill": demo.stages,
146+ "anchors.fill": demo.panel,
147 });
147 });
148 }
148 }
149 if (d.topEdgeDemo) {
149 if (d.topEdgeDemo) {
@@ -154,9 +154,9 @@ Item {
154 }
154 }
155
155
156 Connections {
156 Connections {
157− target: demo.indicators
157+ target: demo.panel.indicators
158 onFullyOpenedChanged: {
158 onFullyOpenedChanged: {
159− if (d.topEdgeDemo && d.topEdgeDemo.available && demo.indicators.fullyOpened) {
159+ if (d.topEdgeDemo && d.topEdgeDemo.available && demo.panel.indicators.fullyOpened) {
160 d.topEdgeDemo.hideNow()
160 d.topEdgeDemo.hideNow()
161 startBottomEdgeDemo()
161 startBottomEdgeDemo()
162 }
162 }
@@ -164,12 +164,12 @@ Item {
164 }
164 }
165
165
166 function startBottomEdgeDemo() {
166 function startBottomEdgeDemo() {
167− if (demo.indicators) {
167+ if (demo.panel.indicators) {
168− d.bottomEdgeDemo = d.overlay.createObject(demo.indicators, {
168+ d.bottomEdgeDemo = d.overlay.createObject(demo.panel.indicators, {
169 "edge": "bottom",
169 "edge": "bottom",
170 "title": i18n.tr("Close"),
170 "title": i18n.tr("Close"),
171 "text": i18n.tr("Swipe up again to close the settings screen"),
171 "text": i18n.tr("Swipe up again to close the settings screen"),
172− "anchors.fill": demo.indicators.content,
172+ "anchors.fill": demo.panel.indicators,
173 });
173 });
174 }
174 }
175 if (d.bottomEdgeDemo) {
175 if (d.bottomEdgeDemo) {
@@ -180,9 +180,12 @@ Item {
180 }
180 }
181
181
182 Connections {
182 Connections {
183− target: demo.indicators
183+ target: demo.panel.indicators
184 onPartiallyOpenedChanged: {
184 onPartiallyOpenedChanged: {
185+ if (d.bottomEdgeDemo &&
186+ d.bottomEdgeDemo.available &&
185− if (d.bottomEdgeDemo && d.bottomEdgeDemo.available && !demo.indicators.partiallyOpened && !demo.indicators.fullyOpened) {
187+ !demo.panel.indicators.partiallyOpened &&
188+ !demo.panel.indicators.fullyOpened) {
186 d.bottomEdgeDemo.hideNow()
189 d.bottomEdgeDemo.hideNow()
187 startLeftEdgeDemo()
190 startLeftEdgeDemo()
188 }
191 }
qml/Components/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/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 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
5+ * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
6 * the Free Software Foundation; version 3.
7 *
7 *
8 * This program is distributed in the hope that it will be useful,
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
11+ * GNU Lesser General Public License for more details.
12 *
12 *
13− * You should have received a copy of the GNU General Public License
13+ * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.2
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
19−import Unity.Indicators 0.1 as Indicators
19+import Ubuntu.Settings.Components 0.1
20−import "../Components"
20+import "Indicators"
21
21
22−Loader {
22+IndicatorDelegate {
23 id: root
23 id: root
24
24
25− property alias widgetSource: root.source
26− property bool dimmed: false
27− property var indicatorProperties: undefined
28− property bool indicatorVisible: item ? item.enabled : false
29 property string identifier
25 property string identifier
26+ property string title: indicatorName.text
27+ property alias leftLabel: leftLabelItem.text
28+ property alias rightLabel: rightLabelItem.text
29+ property var icons: undefined
30+ property bool expanded: false
31+ property bool selected: false
32+ property real iconHeight: units.gu(2)
33+ readonly property color color: {
34+ if (!expanded) return "#ededed";
35+ if (!selected) return "#4c4c4c";
36+ return "#ededed";
37+ }
38+
39+ signal clicked()
40+
41+ implicitWidth: mainItems.width
30
42
31− opacity: dimmed ? 0.4 : 1
43+ MouseArea {
32− Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.BriskDuration } }
44+ anchors.fill: parent
45+ onClicked: parent.clicked()
46+ }
47+
48+ // FIXME: For now we will enable led indicator support only for messaging indicator
49+ // in the future we should export a led API insted of doing that,
50+ Loader {
51+ id: indicatorLed
52+ // only load source Component if the icons contains the new message icon
53+ source: (root.icons && (String(root.icons).indexOf("indicator-messages-new") != -1)) ? Qt.resolvedUrl("Indicators/IndicatorsLight.qml") : ""
54+ }
33
55
34− onLoaded: {
56+ Item {
35− for(var pName in indicatorProperties) {
57+ id: mainItems
36− if (item.hasOwnProperty(pName)) {
58+ anchors.centerIn: parent
37− item[pName] = indicatorProperties[pName];
59+
60+ width: leftLabelItem.width + iconsItem.width + rightLabelItem.width
61+ implicitHeight: units.gu(2)
62+
63+ Label {
64+ id: leftLabelItem
65+ objectName: "leftLabel"
66+
67+ anchors {
68+ left: mainItems.left
69+ verticalCenter: parent.verticalCenter
38 }
70 }
71+ width: contentWidth > 0 ? contentWidth + units.gu(1) : 0
72+ horizontalAlignment: Text.AlignHCenter
73+
74+ opacity: 1.0
75+ font.family: "Ubuntu"
76+ fontSize: "medium"
77+ color: root.color
78+ Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
39 }
79 }
80+
81+ Item {
82+ id: iconsItem
83+ objectName: "icons"
84+
85+ width: iconRow.width > 0 ? iconRow.width + units.gu(1) : 0
86+ anchors {
87+ left: leftLabelItem.right
88+ verticalCenter: parent.verticalCenter
89+ }
90+
91+ Row {
92+ id: iconRow
93+ anchors.centerIn: iconsItem
94+ spacing: units.gu(1)
95+
96+ Repeater {
97+ id: iconRepeater
98+ objectName: "iconRepeater"
99+
100+ model: d.useFallbackIcon ? [ "image://theme/settings" ] : root.icons
101+
102+ StatusIcon {
103+ id: itemImage
104+ objectName: "icon"+index
105+ height: iconHeight
106+ source: modelData
107+ sets: ["status", "actions"]
108+ color: root.color
109+ Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
110+
111+ opacity: {
112+ if (!expanded) return 1.0;
113+ if (!selected) return 0.6;
114+ return 1.0;
115+ }
116+ Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
117+ }
118+ }
119+ }
120+ }
121+
122+ Label {
123+ id: rightLabelItem
124+ objectName: "rightLabel"
125+
126+ anchors {
127+ left: iconsItem.right
128+ verticalCenter: parent.verticalCenter
129+ }
130+ width: contentWidth > 0 ? contentWidth + units.gu(1) : 0
131+ horizontalAlignment: Text.AlignHCenter
132+
133+ opacity: 1.0
134+ font.family: "Ubuntu"
135+ fontSize: "medium"
136+ color: root.color
137+ Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
138+ }
139+ }
140+
141+ Label {
142+ id: indicatorName
143+ objectName: "indicatorName"
144+
145+ anchors.top: mainItems.bottom
146+ anchors.topMargin: units.gu(0.5)
147+ anchors.horizontalCenter: parent.horizontalCenter
148+ width: contentWidth > 0 ? contentWidth + units.gu(1) : 0
149+
150+ text: title !== "" ? title : identifier
151+ fontSize: "x-small"
152+ horizontalAlignment: Text.AlignHCenter
153+ opacity: 0
154+ color: root.color
155+ Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
40 }
156 }
41
157
42− Binding {
158+ StateGroup {
43− target: item
159+ id: d
44− property: "identifier"
160+ property bool useFallbackIcon: false
45− value: identifier
161+
162+ states: [
163+ State {
164+ name: "minimised"
165+ when: !expanded && ((icons && icons.length > 0) || leftLabel !== "" || rightLabel !== "")
166+ PropertyChanges { target: indicatorName; opacity: 0}
167+ },
168+
169+ State {
170+ name: "minimised_fallback"
171+ when: !expanded && (!icons || icons.length === 0) && leftLabel == "" && rightLabel == ""
172+ PropertyChanges { target: indicatorName; opacity: 0}
173+ PropertyChanges { target: d; useFallbackIcon: true }
174+ },
175+
176+ State {
177+ name: "expanded"
178+ PropertyChanges { target: indicatorName; visible: true; opacity: 1}
179+ PropertyChanges { target: mainItems; anchors.verticalCenterOffset: -units.gu(1) }
180+ },
181+
182+ State {
183+ name: "expanded_icon"
184+ extend: "expanded"
185+ when: expanded && (icons && icons.length > 0)
186+ AnchorChanges { target: iconsItem; anchors.left: undefined; anchors.horizontalCenter: parent.horizontalCenter }
187+ AnchorChanges { target: leftLabelItem; anchors.left: undefined; anchors.right: iconsItem.left }
188+ PropertyChanges { target: leftLabelItem; opacity: 0 }
189+ PropertyChanges { target: leftLabelItem; opacity: 0 }
190+ PropertyChanges { target: rightLabelItem; opacity: 0 }
191+ PropertyChanges { target: root; width: Math.max(units.gu(10), Math.max(iconsItem.width, indicatorName.width)) }
192+ },
193+
194+ State {
195+ name: "expanded_fallback"
196+ extend: "expanded"
197+ when: expanded && (!icons || icons.length === 0) && leftLabel == "" && rightLabel == ""
198+ PropertyChanges { target: d; useFallbackIcon: true }
199+ AnchorChanges { target: iconsItem; anchors.left: undefined; anchors.horizontalCenter: parent.horizontalCenter }
200+ AnchorChanges { target: leftLabelItem; anchors.left: undefined; anchors.right: iconsItem.left }
201+ PropertyChanges { target: leftLabelItem; opacity: 0 }
202+ PropertyChanges { target: leftLabelItem; opacity: 0 }
203+ PropertyChanges { target: rightLabelItem; opacity: 0 }
204+ PropertyChanges { target: root; width: Math.max(units.gu(10), Math.max(iconsItem.width, indicatorName.width)) }
205+ },
206+
207+ State {
208+ name: "expanded_rightLabel"
209+ extend: "expanded"
210+ when: expanded && (!icons || icons.length === 0) && rightLabel !== ""
211+ AnchorChanges { target: rightLabelItem; anchors.left: undefined; anchors.horizontalCenter: parent.horizontalCenter }
212+ PropertyChanges { target: iconsItem; opacity: 0 }
213+ PropertyChanges { target: leftLabelItem; opacity: 0 }
214+ PropertyChanges { target: root; width: Math.max(units.gu(10), Math.max(rightLabelItem.width, indicatorName.width)) }
215+ },
216+
217+ State {
218+ name: "expanded_leftLabel"
219+ extend: "expanded"
220+ when: expanded && (!icons || icons.length === 0) && leftLabel !== ""
221+ AnchorChanges { target: leftLabelItem; anchors.left: undefined; anchors.horizontalCenter: parent.horizontalCenter }
222+ PropertyChanges { target: iconsItem; opacity: 0 }
223+ PropertyChanges { target: rightLabelItem; opacity: 0 }
224+ PropertyChanges { target: root; width: Math.max(units.gu(10), Math.max(leftLabelItem.width, indicatorName.width)) }
225+ }
226+ ]
227+
228+ transitions: [
229+ Transition {
230+ PropertyAction { target: d; property: "useFallbackIcon" }
231+ AnchorAnimation {
232+ targets: [ mainItems, iconsItem, leftLabelItem, rightLabelItem ]
233+ duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing
234+ }
235+ PropertyAnimation {
236+ targets: [ root, mainItems, iconsItem, leftLabelItem, rightLabelItem, indicatorName ]
237+ properties: "width, opacity, anchors.verticalCenterOffset";
238+ duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing
239+ }
240+ }
241+ ]
46 }
242 }
47
243
48− Binding {
244+ onRootActionStateChanged: {
49− target: item
245+ if (rootActionState == undefined) {
50− property: "objectName"
246+ title = "";
51− value: identifier + "-widget"
247+ leftLabel = "";
248+ rightLabel = "";
249+ icons = undefined;
250+ return;
251+ }
252+
253+ title = rootActionState.title ? rootActionState.title : "";
254+ leftLabel = rootActionState.leftLabel ? rootActionState.leftLabel : "";
255+ rightLabel = rootActionState.rightLabel ? rootActionState.rightLabel : "";
256+ icons = rootActionState.icons;
52 }
257 }
53}
258}
qml/Panel/IndicatorItemRow.qmlnew file+297
@@ -0,0 +1,297 @@
1+/*
2+ * Copyright (C) 2013-2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+
20+Item {
21+ id: root
22+ width: row.width
23+ height: units.gu(3)
24+
25+ property QtObject indicatorsModel: null
26+ property real overFlowWidth: width
27+ property bool expanded: false
28+ property var currentItem
29+ readonly property int currentItemIndex: currentItem ? currentItem.ownIndex : -1
30+
31+ property real unitProgress: 0.0
32+ property real selectionChangeBuffer: units.gu(2)
33+ property bool enableLateralChanges: false
34+ property color hightlightColor: "#ededed"
35+
36+ property real lateralPosition: -1
37+ onLateralPositionChanged: {
38+ updateItemFromLateralPosition();
39+ }
40+
41+ onEnableLateralChangesChanged: {
42+ updateItemFromLateralPosition();
43+ }
44+
45+ function updateItemFromLateralPosition() {
46+ if (currentItem && !enableLateralChanges) return;
47+ if (lateralPosition === -1) return;
48+
49+ if (!currentItem) {
50+ selectItemAt(lateralPosition);
51+ return;
52+ }
53+
54+ var maximumBufferOffset = selectionChangeBuffer * unitProgress;
55+ var proposedItem = indicatorAt(lateralPosition, 0);
56+ if (proposedItem) {
57+ var bufferExceeded = false;
58+
59+ if (proposedItem !== currentItem) {
60+ // Proposed item is not directly adjacent to current?
61+ if (Math.abs(proposedItem.ownIndex - currentItem.ownIndex) > 1) {
62+ bufferExceeded = true;
63+ } else { // no
64+ var currentItemLateralPosition = root.mapToItem(proposedItem, lateralPosition, 0).x;
65+
66+ // Is the distance into proposed item greater than max buffer?
67+ // Proposed item is before current item
68+ if (proposedItem.x < currentItem.x) {
69+ bufferExceeded = (proposedItem.width - currentItemLateralPosition) > maximumBufferOffset;
70+ } else { // After
71+ bufferExceeded = currentItemLateralPosition > maximumBufferOffset;
72+ }
73+ }
74+ if (bufferExceeded) {
75+ selectItemAt(lateralPosition);
76+ }
77+ }
78+ } else {
79+ selectItemAt(lateralPosition);
80+ }
81+ }
82+
83+ function indicatorAt(x, y) {
84+ var item = row.childAt(x, y);
85+ return item && item.hasOwnProperty("ownIndex") ? item : null;
86+ }
87+
88+ function resetCurrentItem() {
89+ d.firstItemSwitch = true;
90+ d.previousItem = undefined;
91+ currentItem = undefined;
92+ }
93+
94+ function setCurrentItemIndex(index) {
95+ for (var i = 0; i < row.children.length; i++) {
96+ var item = row.children[i];
97+ if (item.hasOwnProperty("ownIndex") && item.ownIndex === index) {
98+ if (currentItem !== item) currentItem = item;
99+ break;
100+ }
101+ }
102+ }
103+
104+ function selectItemAt(lateralPosition) {
105+ var item = indicatorAt(lateralPosition, 0);
106+ if (item && item.opacity > 0) {
107+ currentItem = item;
108+ } else {
109+ // Select default item.
110+ var searchIndex = lateralPosition > width ? repeater.count - 1 : 0;
111+
112+ for (var i = 0; i < row.children.length; i++) {
113+ if (row.children[i].hasOwnProperty("ownIndex") && row.children[i].ownIndex === searchIndex) {
114+ item = row.children[i];
115+ break;
116+ }
117+ }
118+ if (currentItem !== item) currentItem = item;
119+ }
120+ }
121+
122+ QtObject {
123+ id: d
124+ property bool firstItemSwitch: true
125+ property var previousItem
126+ property bool forceAlignmentAnimationDisabled: false
127+ }
128+
129+ onCurrentItemChanged: {
130+ if (d.previousItem) {
131+ d.firstItemSwitch = false;
132+ }
133+ d.previousItem = currentItem;
134+ }
135+
136+ Row {
137+ id: row
138+ anchors {
139+ top: parent.top
140+ bottom: parent.bottom
141+ }
142+
143+ // TODO: make this better
144+ // when the width changes, the highlight will lag behind due to animation, so we need to disable the animation
145+ // and adjust the highlight X immediately.
146+ width: implicitWidth
147+ Behavior on width {
148+ SequentialAnimation {
149+ ScriptAction {
150+ script: {
151+ d.forceAlignmentAnimationDisabled = true;
152+ highlight.currentItemX = Qt.binding(function() { return currentItem ? currentItem.x : 0 });
153+ d.forceAlignmentAnimationDisabled = false;
154+ }
155+ }
156+ }
157+ }
158+
159+ Repeater {
160+ id: repeater
161+ model: indicatorsModel
162+ visible: false
163+
164+ onItemRemoved: {
165+ // current item removed.
166+ if (currentItem === item) {
167+ var i = 0;
168+ while (i < row.children.length) {
169+ var childItem = row.children[i];
170+ if (childItem !== item) {
171+ setCurrentItemIndex(i);
172+ break;
173+ }
174+ i++;
175+ }
176+ }
177+ }
178+
179+
180+ delegate: IndicatorItem {
181+ id: indicatorItem
182+ objectName: identifier+"-panelItem"
183+
184+ property int ownIndex: index
185+ property bool overflow: row.width - x > overFlowWidth
186+ property bool hidden: !expanded && (overflow || !indicatorVisible)
187+
188+ height: row.height
189+ expanded: root.expanded
190+ selected: currentItem === this
191+
192+ identifier: model.identifier
193+ busName: indicatorProperties.busName
194+ actionsObjectPath: indicatorProperties.actionsObjectPath
195+ menuObjectPath: indicatorProperties.menuObjectPath
196+
197+ opacity: hidden ? 0.0 : 1.0
198+ Behavior on opacity {
199+ NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing }
200+ }
201+
202+ width: (expanded || indicatorVisible) ? implicitWidth : 0
203+
204+ Behavior on width {
205+ NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing }
206+ }
207+
208+ Component.onDestruction: {
209+ // current item removed.
210+ if (currentItem === this) {
211+ var i = 0;
212+ while (i < row.children.length) {
213+ var childItem = row.children[i];
214+ if (childItem !== this) {
215+ setCurrentItemIndex(i);
216+ break;
217+ }
218+ i++;
219+ }
220+ }
221+ }
222+ }
223+ }
224+ }
225+
226+ Rectangle {
227+ id: highlight
228+ objectName: "highlight"
229+
230+ anchors.bottom: row.bottom
231+ height: units.dp(2)
232+ color: root.hightlightColor
233+ visible: currentItem !== undefined
234+ opacity: 0.0
235+
236+ width: currentItem ? currentItem.width : 0
237+ Behavior on width {
238+ enabled: !d.firstItemSwitch && expanded
239+ UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
240+ }
241+
242+ // micromovements of the highlight line when user moves the finger across the items while pulling
243+ // the handle downwards.
244+ property real highlightCenterOffset: {
245+ if (!currentItem || lateralPosition == -1 || !enableLateralChanges) return 0;
246+
247+ var itemMapped = root.mapToItem(currentItem, lateralPosition, 0);
248+
249+ var distanceFromCenter = itemMapped.x - currentItem.width / 2;
250+ if (distanceFromCenter > 0) {
251+ distanceFromCenter = Math.max(0, distanceFromCenter - currentItem.width / 8);
252+ } else {
253+ distanceFromCenter = Math.min(0, distanceFromCenter + currentItem.width / 8);
254+ }
255+
256+ if (currentItem && currentItem.ownIndex === 0 && distanceFromCenter < 0) {
257+ return 0;
258+ } else if (currentItem && currentItem.ownIndex === repeater.count-1 & distanceFromCenter > 0) {
259+ return 0;
260+ }
261+ return (distanceFromCenter / (currentItem.width / 4)) * units.gu(1);
262+ }
263+ Behavior on highlightCenterOffset {
264+ NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
265+ }
266+
267+ property real currentItemX: currentItem ? currentItem.x : 0
268+ Behavior on currentItemX {
269+ id: currentItemXBehavior
270+ enabled: !d.firstItemSwitch && expanded && !d.forceAlignmentAnimationDisabled
271+ NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
272+ }
273+ x: currentItemX + highlightCenterOffset
274+ }
275+
276+ states: [
277+ State {
278+ name: "minimised"
279+ when: !expanded
280+ },
281+ State {
282+ name: "expanded"
283+ when: expanded
284+ PropertyChanges { target: highlight; opacity: 0.9 }
285+ }
286+ ]
287+
288+ transitions: [
289+ Transition {
290+ PropertyAnimation {
291+ properties: "opacity";
292+ duration: UbuntuAnimation.SnapDuration
293+ easing: UbuntuAnimation.StandardEasing
294+ }
295+ }
296+ ]
297+}
qml/Panel/Indicators/DefaultIndicatorPage.qml → qml/Panel/IndicatorPage.qmlrenamed+10−16
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
5 * it under the terms of the GNU Lesser General Public License as published by
@@ -12,16 +12,13 @@
12 *
12 *
13 * You should have received a copy of the GNU Lesser General Public License
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors:
17− * Renato Araujo Oliveira Filho <renato@canonical.com>
18− * Nick Dedekind <nick.dedekind@canonical.com>
19 */
15 */
20
16
21import QtQuick 2.0
17import QtQuick 2.0
22import Ubuntu.Components 0.1 as Components
18import Ubuntu.Components 0.1 as Components
23import Unity.Indicators 0.1 as Indicators
19import Unity.Indicators 0.1 as Indicators
20+import "Indicators"
24−import "../../Components/Flickables" as Flickables
21+import "../Components/Flickables" as Flickables
25
22
26IndicatorBase {
23IndicatorBase {
27 id: main
24 id: main
@@ -52,13 +49,13 @@ IndicatorBase {
52
49
53 Connections {
50 Connections {
54 target: menuStack.tail
51 target: menuStack.tail
52+
53+ // fix async creation with signal from model before it's finished.
54+ Component.onCompleted: update();
55− onRowsInserted: {
55+ onRowsInserted: update();
56− if (menuStack.rootMenu !== menuStack.tail && menuStack.tail.get(0, "type") === rootMenuType) {
57− menuStack.rootMenu = menuStack.tail.submenu(0);
58− menuStack.push(menuStack.rootMenu, 0);
59− }
60− }
61− onModelReset: {
56+ onModelReset: update();
57+
58+ function update() {
62 if (menuStack.rootMenu !== menuStack.tail && menuStack.tail.get(0, "type") === rootMenuType) {
59 if (menuStack.rootMenu !== menuStack.tail && menuStack.tail.get(0, "type") === rootMenuType) {
63 menuStack.rootMenu = menuStack.tail.submenu(0);
60 menuStack.rootMenu = menuStack.tail.submenu(0);
64 menuStack.push(menuStack.rootMenu, 0);
61 menuStack.push(menuStack.rootMenu, 0);
@@ -92,8 +89,6 @@ IndicatorBase {
92
89
93 // Only allow flicking if the content doesn't fit on the page
90 // Only allow flicking if the content doesn't fit on the page
94 interactive: contentHeight > height
91 interactive: contentHeight > height
95− // FIXME - https://bugreports.qt-project.org/browse/QTBUG-41207
96− boundsBehavior: Flickable.StopAtBounds
97
92
98 property int selectedIndex: -1
93 property int selectedIndex: -1
99 property bool blockCurrentIndexChange: false
94 property bool blockCurrentIndexChange: false
@@ -126,7 +121,6 @@ IndicatorBase {
126 delegate: Loader {
121 delegate: Loader {
127 id: loader
122 id: loader
128 objectName: "menuItem" + index
123 objectName: "menuItem" + index
129− asynchronous: true
130 width: ListView.view.width
124 width: ListView.view.width
131 visible: status == Loader.Ready
125 visible: status == Loader.Ready
132
126
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/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+1−5
@@ -23,8 +23,6 @@ import Unity.Indicators 0.1
23Item {
23Item {
24 id: indicatorItem
24 id: indicatorItem
25
25
26− enabled: menuObjectPath != ""
27−
28 //const
26 //const
29 property string identifier
27 property string identifier
30 property string busName
28 property string busName
@@ -32,8 +30,6 @@ Item {
32 property string menuObjectPath
30 property string menuObjectPath
33 property string rootMenuType: "com.canonical.indicator.root"
31 property string rootMenuType: "com.canonical.indicator.root"
34
32
35− property string deviceMenuObjectPath: menuObjectPath
36−
37 property alias menuModel: cachedModel.model
33 property alias menuModel: cachedModel.model
38 property alias rootActionState: rootAction
34 property alias rootActionState: rootAction
39
35
@@ -41,7 +37,7 @@ Item {
41 id: cachedModel
37 id: cachedModel
42 busName: indicatorItem.busName
38 busName: indicatorItem.busName
43 actions: { "indicator": indicatorItem.actionsObjectPath }
39 actions: { "indicator": indicatorItem.actionsObjectPath }
44− menuObjectPath: indicatorItem.deviceMenuObjectPath
40+ menuObjectPath: indicatorItem.menuObjectPath
45 }
41 }
46
42
47 RootActionState {
43 RootActionState {
qml/Panel/Indicators/IndicatorDelegate.qml+2−2
@@ -20,6 +20,6 @@
20import QtQuick 2.0
20import QtQuick 2.0
21
21
22IndicatorBase {
22IndicatorBase {
23− enabled: rootActionState.visible
23+ readonly property bool indicatorVisible: rootActionState.indicatorVisible
24− property string title: rootActionState.title
24+ readonly property string title: rootActionState.title
25}
25}
qml/Panel/Indicators/VisibleIndicators.qml+4−5
@@ -24,6 +24,10 @@ import Utils 0.1
24Item {
24Item {
25 property SortFilterProxyModel model: filterModel
25 property SortFilterProxyModel model: filterModel
26
26
27+ function initialise(profile) {
28+ indicatorsModel.load(profile);
29+ }
30+
27 SortFilterProxyModel {
31 SortFilterProxyModel {
28 id: filterModel
32 id: filterModel
29 filterRole: Indicators.IndicatorsModelRole.IsVisible
33 filterRole: Indicators.IndicatorsModelRole.IsVisible
@@ -36,7 +40,6 @@ Item {
36 }
40 }
37 }
41 }
38
42
39−
40 Indicators.IndicatorsModel {
43 Indicators.IndicatorsModel {
41 id: indicatorsModel
44 id: indicatorsModel
42 }
45 }
@@ -78,8 +81,4 @@ Item {
78 }
81 }
79 }
82 }
80 }
83 }
81−
82− function load(profile) {
83− indicatorsModel.load(profile);
84− }
85}
84}
qml/Panel/Indicators/client/IndicatorsPage.qml → qml/Panel/Indicators/client/IndicatorRepresentation.qmlrenamed+21−10
@@ -21,13 +21,13 @@
21import QtQuick 2.0
21import QtQuick 2.0
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23import Ubuntu.Components.ListItems 0.1 as ListItem
23import Ubuntu.Components.ListItems 0.1 as ListItem
24+import "../.."
24
25
25Page {
26Page {
26− id: page
27+ id: root
27
28
28 title: indicatorProperties && indicatorProperties.title ? indicatorProperties.title : ""
29 title: indicatorProperties && indicatorProperties.title ? indicatorProperties.title : ""
29 property variant indicatorProperties
30 property variant indicatorProperties
30− property string pageSource : pageLoader.source
31
31
32 anchors.fill: parent
32 anchors.fill: parent
33
33
@@ -44,6 +44,7 @@ Page {
44 id: pageLoader
44 id: pageLoader
45 objectName: "pageLoader"
45 objectName: "pageLoader"
46 clip:true
46 clip:true
47+ asynchronous: true
47
48
48 Rectangle {
49 Rectangle {
49 anchors.fill: pageLoader
50 anchors.fill: pageLoader
@@ -58,15 +59,25 @@ Page {
58 topMargin: units.gu(2)
59 topMargin: units.gu(2)
59 bottomMargin: units.gu(2)
60 bottomMargin: units.gu(2)
60 }
61 }
61− source : visualCheck.checked ? page.pageSource : "IndicatorsTree.qml"
62+ sourceComponent: visualCheck.checked ? page : tree
62
63
63− onLoaded: {
64+ Component {
65+ id: page
66+ IndicatorPage {
67+ identifier: model.identifier
64− for(var pName in indicatorProperties) {
68+ busName: indicatorProperties.busName
65− if (item.hasOwnProperty(pName)) {
69+ actionsObjectPath: indicatorProperties.actionsObjectPath
70+ menuObjectPath: indicatorProperties.menuObjectPath
71+ }
72+ }
73+ Component {
74+ id: tree
75+ IndicatorsTree {
76+ identifier: model.identifier
77+ busName: indicatorProperties.busName
66− item[pName] = indicatorProperties[pName];
78+ actionsObjectPath: indicatorProperties.actionsObjectPath
67− }
79+ menuObjectPath: indicatorProperties.menuObjectPath
68 }
80 }
69− item.start();
70 }
81 }
71 }
82 }
72
83
@@ -85,7 +96,7 @@ Page {
85 left: parent.left
96 left: parent.left
86 }
97 }
87 text: "Back"
98 text: "Back"
88− onClicked: page.pageStack.reset()
99+ onClicked: root.pageStack.reset()
89 }
100 }
90 }
101 }
91}
102}
qml/Panel/Indicators/client/IndicatorsList.qml+2−2
@@ -58,8 +58,8 @@ Page {
58 text: identifier
58 text: identifier
59
59
60 onClicked: {
60 onClicked: {
61− var new_page = Qt.createComponent("IndicatorsPage.qml");
61+ var new_page = Qt.createComponent("IndicatorRepresentation.qml");
62− page.pageStack.push(new_page.createObject(pages), {"indicatorProperties" : model.indicatorProperties, "pageSource" : model.pageSource});
62+ page.pageStack.push(new_page.createObject(pages), {"indicatorProperties" : model.indicatorProperties });
63 }
63 }
64 }
64 }
65 }
65 }
qml/Panel/Indicators/client/IndicatorsTree.qml+4−26
@@ -20,37 +20,15 @@
20import QtQuick 2.0
20import QtQuick 2.0
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22import Unity.Indicators 0.1 as Indicators
22import Unity.Indicators 0.1 as Indicators
23−import QMenuModel 0.1
23+import ".."
24−import Ubuntu.Components.ListItems 0.1 as ListItem
25import "../../../Components/Flickables" as Flickables
24import "../../../Components/Flickables" as Flickables
26
25
27−Page {
26+IndicatorBase {
28− id: page
27+ id: root
29− anchors.fill: parent
30−
31− property string busName: unityModel.busName
32− property string actionsObjectPath
33− property string menuObjectPath
34−
35− property string deviceMenuObjectPath: menuObjectPath
36−
37− function start() {
38− }
39−
40− UnityMenuModel {
41− id: unityModel
42− busName: page.busName
43− actions: { "indicator": page.actionsObjectPath }
44− menuObjectPath: page.deviceMenuObjectPath
45− }
46−
47− Indicators.RootActionState {
48− menu: unityModel
49− }
50
28
51 Indicators.ModelPrinter {
29 Indicators.ModelPrinter {
52 id: printer
30 id: printer
53− model: unityModel
31+ model: root.menuModel
54 }
32 }
55
33
56 Flickables.Flickable {
34 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 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2014 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -14,126 +14,69 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.2
18import QtQuick.Layouts 1.1
18import QtQuick.Layouts 1.1
19import Ubuntu.Components 1.1
19import Ubuntu.Components 1.1
20import Unity.Indicators 0.1 as Indicators
20import Unity.Indicators 0.1 as Indicators
21import Utils 0.1
21import Utils 0.1
22import "../Components"
22import "../Components"
23import "../Components/Flickables" as Flickables
23import "../Components/Flickables" as Flickables
24−import "Indicators"
25
24
26Rectangle {
25Rectangle {
27 id: content
26 id: content
28
27
29 property QtObject indicatorsModel: null
28 property QtObject indicatorsModel: null
30− readonly property alias currentMenuIndex: listViewHeader.currentIndex
29+ property int currentMenuIndex: -1
31 color: "#221e1c" // FIXME not in palette yet
30 color: "#221e1c" // FIXME not in palette yet
32− property real headerHeight: listViewHeader.height
33
31
34 width: units.gu(40)
32 width: units.gu(40)
35 height: units.gu(42)
33 height: units.gu(42)
36
34
37− function setCurrentMenuIndex(index, animate) {
35+ onCurrentMenuIndexChanged: {
38− // FIXME - https://bugreports.qt-project.org/browse/QTBUG-41229
36+ listViewContent.currentIndex = currentMenuIndex;
39− listViewHeader.currentIndex = -1;
40− listViewHeader.currentIndex = index;
41− }
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− }
82 }
37 }
83
38
84 Flickables.ListView {
39 Flickables.ListView {
85 id: listViewContent
40 id: listViewContent
86 objectName: "indicatorsContentListView"
41 objectName: "indicatorsContentListView"
87− anchors {
42+ anchors.fill: parent
88− left: parent.left
89− right: parent.right
90− top: listViewHeader.bottom
91− bottom: parent.bottom
92− }
93 model: content.indicatorsModel
43 model: content.indicatorsModel
94− clip: true
95
44
96− currentIndex: listViewHeader.currentIndex
45+ highlightFollowsCurrentItem: true
97 interactive: false
46 interactive: false
98 highlightMoveDuration: 0
47 highlightMoveDuration: 0
99 orientation: ListView.Horizontal
48 orientation: ListView.Horizontal
100 // Load all the indicator menus (a big number)
49 // Load all the indicator menus (a big number)
101 cacheBuffer: 1073741823
50 cacheBuffer: 1073741823
102
51
52+ // for additions/removals.
53+ onCountChanged: {
54+ listViewContent.currentIndex = content.currentMenuIndex;
55+ }
56+
103 delegate: Loader {
57 delegate: Loader {
104 id: loader
58 id: loader
59+
105 width: ListView.view.width
60 width: ListView.view.width
106 height: ListView.view.height
61 height: ListView.view.height
107 objectName: identifier
62 objectName: identifier
108−
109− source: pageSource
110 asynchronous: true
63 asynchronous: true
64+ visible: ListView.isCurrentItem
111
65
112− onVisibleChanged: {
66+ sourceComponent: IndicatorPage {
113− // Reset the indicator states
67+ objectName: identifier + "-page"
114− if (!visible && item && item["reset"]) {
115− item.reset()
116− }
117− }
118−
119− onLoaded: {
120− for(var pName in indicatorProperties) {
121− if (item.hasOwnProperty(pName)) {
122− item[pName] = indicatorProperties[pName]
123− }
124− }
125− }
126
68
127− Binding {
69+ identifier: model.identifier
128− target: loader.item
70+ busName: indicatorProperties.busName
129− property: "identifier"
71+ actionsObjectPath: indicatorProperties.actionsObjectPath
130− value: identifier
72+ menuObjectPath: indicatorProperties.menuObjectPath
131 }
73 }
132
74
133− Binding {
75+ onVisibleChanged: {
134− target: loader.item
76+ // Reset the indicator states
135− property: "objectName"
77+ if (!visible && status == Loader.Ready) {
136− value: identifier + "-page"
78+ item.reset();
79+ }
137 }
80 }
138 }
81 }
139 }
82 }
qml/Panel/Panel.qml+48−41
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2014 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -14,11 +14,11 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.2
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
19import Unity.Application 0.1
19import Unity.Application 0.1
20import "../Components"
20import "../Components"
21−import "../Components/ListItems"
21+import ".."
22
22
23Item {
23Item {
24 id: root
24 id: root
@@ -53,7 +53,9 @@ Item {
53
53
54 anchors.fill: parent
54 anchors.fill: parent
55
55
56− Behavior on anchors.topMargin { StandardAnimation {} }
56+ Behavior on anchors.topMargin {
57+ NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
58+ }
57
59
58 BorderImage {
60 BorderImage {
59 id: dropShadow
61 id: dropShadow
@@ -62,24 +64,10 @@ Item {
62 leftMargin: -units.gu(1)
64 leftMargin: -units.gu(1)
63 bottomMargin: -units.gu(1)
65 bottomMargin: -units.gu(1)
64 }
66 }
65− visible: indicators.height > indicators.panelHeight
67+ visible: !indicators.fullyClosed
66 source: "graphics/rectangular_dropshadow.sci"
68 source: "graphics/rectangular_dropshadow.sci"
67 }
69 }
68
70
69− VerticalThinDivider {
70− id: indicatorDividor
71− anchors {
72− top: indicators.top
73− bottom: indicators.bottom
74− right: indicators.left
75−
76− topMargin: indicatorArea.anchors.topMargin + indicators.panelHeight
77− }
78−
79− width: units.dp(2)
80− source: "graphics/VerticalDivider.png"
81− }
82−
83 Rectangle {
71 Rectangle {
84 id: indicatorAreaBackground
72 id: indicatorAreaBackground
85 color: callHint.visible ? "green" : "black"
73 color: callHint.visible ? "green" : "black"
@@ -88,13 +76,13 @@ Item {
88 left: parent.left
76 left: parent.left
89 right: parent.right
77 right: parent.right
90 }
78 }
91− height: indicators.panelHeight
79+ height: indicators.minimizedPanelHeight
92
80
93 Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration } }
81 Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration } }
94 }
82 }
95
83
96 PanelSeparatorLine {
84 PanelSeparatorLine {
97− id: nonIndicatorAreaSeparatorLine
85+ id: orangeLine
98 anchors {
86 anchors {
99 top: indicatorAreaBackground.bottom
87 top: indicatorAreaBackground.bottom
100 left: parent.left
88 left: parent.left
@@ -103,18 +91,29 @@ Item {
103 saturation: 1 - indicators.unitProgress
91 saturation: 1 - indicators.unitProgress
104 }
92 }
105
93
94+ Image {
95+ anchors {
96+ top: indicators.top
97+ bottom: indicators.bottom
98+ right: indicators.left
99+ topMargin: indicatorArea.anchors.topMargin + indicators.minimizedPanelHeight
100+ }
101+ width: units.dp(2)
102+ source: "graphics/VerticalDivider.png"
103+ }
104+
106 MouseArea {
105 MouseArea {
107 anchors {
106 anchors {
108 top: parent.top
107 top: parent.top
109 left: parent.left
108 left: parent.left
110 right: indicators.left
109 right: indicators.left
111 }
110 }
112− height: indicators.panelHeight
111+ height: indicators.minimizedPanelHeight
113 enabled: callHint.visible
112 enabled: callHint.visible
114 onClicked: callHint.showLiveCall()
113 onClicked: callHint.showLiveCall()
115 }
114 }
116
115
117− Indicators {
116+ IndicatorsMenu {
118 id: __indicators
117 id: __indicators
119 objectName: "indicators"
118 objectName: "indicators"
120
119
@@ -123,51 +122,55 @@ Item {
123 right: parent.right
122 right: parent.right
124 }
123 }
125
124
126− width: root.width
127 shown: false
125 shown: false
128− panelHeight: units.gu(3)
126+ width: root.width
127+ minimizedPanelHeight: units.gu(3)
128+ expandedPanelHeight: units.gu(7)
129− openedHeight: root.height
129+ openedHeight: root.height - indicatorOrangeLine.height
130+
130 overFlowWidth: {
131 overFlowWidth: {
131 if (callHint.visible) {
132 if (callHint.visible) {
132 return Math.max(root.width - (callHint.width + units.gu(2)), 0)
133 return Math.max(root.width - (callHint.width + units.gu(2)), 0)
133 }
134 }
134 return root.width
135 return root.width
135 }
136 }
136−
137 enableHint: !callHint.active && !fullscreenMode
137 enableHint: !callHint.active && !fullscreenMode
138− showHintBottomMargin: fullscreenMode ? -panelHeight : 0
138+ panelColor: indicatorAreaBackground.color
139
139
140 onShowTapped: {
140 onShowTapped: {
141 if (callHint.active) {
141 if (callHint.active) {
142 callHint.showLiveCall();
142 callHint.showLiveCall();
143 }
143 }
144 }
144 }
145− }
146
145
147− ActiveCallHint {
146+ hideDragHandle {
148− id: __callHint
147+ anchors.bottomMargin: -indicatorOrangeLine.height
149− anchors {
150− top: parent.top
151− left: parent.left
152 }
148 }
153− height: indicators.panelHeight
154− visible: active && indicators.state == "initial"
155 }
149 }
156
150
157 PanelSeparatorLine {
151 PanelSeparatorLine {
158− id: indicatorsSeparatorLine
152+ id: indicatorOrangeLine
159− visible: true
160 anchors {
153 anchors {
161 top: indicators.bottom
154 top: indicators.bottom
162− left: indicatorDividor.left
155+ left: indicators.left
163 right: indicators.right
156 right: indicators.right
164 }
157 }
165 }
158 }
159+
160+ ActiveCallHint {
161+ id: __callHint
162+ anchors {
163+ top: parent.top
164+ left: parent.left
165+ }
166+ height: indicators.minimizedPanelHeight
167+ visible: active && indicators.state == "initial"
168+ }
166 }
169 }
167
170
168 QtObject {
171 QtObject {
169 id: d
172 id: d
170− readonly property real indicatorHeight: indicators.panelHeight + indicatorsSeparatorLine.height
173+ readonly property real indicatorHeight: indicators.minimizedPanelHeight + indicatorOrangeLine.height
171 }
174 }
172
175
173 states: [
176 states: [
@@ -186,6 +189,10 @@ Item {
186 target: indicatorArea;
189 target: indicatorArea;
187 anchors.topMargin: indicators.state === "initial" ? -d.indicatorHeight : 0
190 anchors.topMargin: indicators.state === "initial" ? -d.indicatorHeight : 0
188 }
191 }
192+ PropertyChanges {
193+ target: indicators.showDragHandle;
194+ anchors.bottomMargin: -units.gu(1)
195+ }
189 }
196 }
190 ]
197 ]
191}
198}
qml/Panel/PanelVelocityCalculator.qmlnew file+54
@@ -0,0 +1,54 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.2
18+import Ubuntu.Gestures 0.1
19+
20+Item {
21+ id: root
22+ property real trackedValue: 0
23+ property bool velocityAboveThreshold: false
24+ property real velocityThreshold: 0.4
25+
26+ function reset() {
27+ velocityTimer.stop();
28+ velocityAboveThreshold = false;
29+ calc.reset();
30+ }
31+
32+ function update() {
33+ calc.trackedPosition = trackedValue;
34+ velocityAboveThreshold = Math.abs(calc.calculate()) > velocityThreshold;
35+
36+ if (velocityAboveThreshold) { // only start timer if we're above the threshold.
37+ velocityTimer.start();
38+ } else {
39+ velocityTimer.stop();
40+ }
41+ }
42+
43+ onTrackedValueChanged: update();
44+
45+ AxisVelocityCalculator {
46+ id: calc
47+ }
48+
49+ Timer {
50+ id: velocityTimer
51+ interval: 50
52+ onTriggered: update();
53+ }
54+}
qml/Shell.qml+16−5
@@ -36,6 +36,7 @@ import "Panel"
36import "Components"
36import "Components"
37import "Notifications"
37import "Notifications"
38import "Stages"
38import "Stages"
39+import "Panel/Indicators"
39import Unity.Notifications 1.0 as NotificationBackend
40import Unity.Notifications 1.0 as NotificationBackend
40import Unity.Session 0.1
41import Unity.Session 0.1
41import Unity.DashCommunicator 0.1
42import Unity.DashCommunicator 0.1
@@ -243,7 +244,7 @@ Item {
243 target: applicationsDisplayLoader.item
244 target: applicationsDisplayLoader.item
244 property: "maximizedAppTopMargin"
245 property: "maximizedAppTopMargin"
245 // Not just using panel.panelHeight as that changes depending on the focused app.
246 // Not just using panel.panelHeight as that changes depending on the focused app.
246− value: panel.indicators.panelHeight
247+ value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
247 }
248 }
248 Binding {
249 Binding {
249 target: applicationsDisplayLoader.item
250 target: applicationsDisplayLoader.item
@@ -645,7 +646,17 @@ Item {
645 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
646 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
646 contentEnabled: edgeDemo.panelContentEnabled
647 contentEnabled: edgeDemo.panelContentEnabled
647 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
648 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
648− panelHeight: units.gu(3)
649+
650+ minimizedPanelHeight: units.gu(3)
651+ expandedPanelHeight: units.gu(7)
652+
653+ indicatorsModel: visibleIndicators.model
654+ }
655+
656+ VisibleIndicators {
657+ id: visibleIndicators
658+ // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
659+ Component.onCompleted: initialise(indicatorProfile)
649 }
660 }
650
661
651 property bool topmostApplicationIsFullscreen:
662 property bool topmostApplicationIsFullscreen:
@@ -708,9 +719,9 @@ Item {
708 model: NotificationBackend.Model
719 model: NotificationBackend.Model
709 margin: units.gu(1)
720 margin: units.gu(1)
710
721
711− y: panel.panelHeight
722+ y: topmostIsFullscreen ? 0 : panel.panelHeight
712 width: parent.width
723 width: parent.width
713− height: parent.height - panel.panelHeight
724+ height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
714
725
715 states: [
726 states: [
716 State {
727 State {
@@ -762,7 +773,7 @@ Item {
762 paused: Powerd.status === Powerd.Off // Saves power
773 paused: Powerd.status === Powerd.Off // Saves power
763 greeter: greeter
774 greeter: greeter
764 launcher: launcher
775 launcher: launcher
765− indicators: panel.indicators
776+ panel: panel
766 stages: stages
777 stages: stages
767 }
778 }
768
779
tests/autopilot/unity8/indicators/tests/test_indicators.py+1−1
@@ -61,7 +61,7 @@ class IndicatorExistsTestCase(IndicatorTestCase):
61 self.skipTest('Nexus 10 does not have bluetooth at the moment.')
61 self.skipTest('Nexus 10 does not have bluetooth at the moment.')
62
62
63 def test_indicator_exists(self):
63 def test_indicator_exists(self):
64− self.main_window._get_indicator_widget(
64+ self.main_window._get_indicator_panel_item(
65 self.indicator_name
65 self.indicator_name
66 )
66 )
67
67
tests/autopilot/unity8/shell/emulators/main_window.py+6−6
@@ -75,15 +75,15 @@ class QQuickView(emulators.UnityEmulatorBase):
75 def get_pinentryField(self):
75 def get_pinentryField(self):
76 return self.select_single(objectName="pinentryField")
76 return self.select_single(objectName="pinentryField")
77
77
78− def _get_indicator_widget(self, indicator_name):
78+ def _get_indicator_panel_item(self, indicator_name):
79 return self.select_single(
79 return self.select_single(
80− 'DefaultIndicatorWidget',
80+ 'IndicatorItem',
81− objectName=indicator_name+'-widget'
81+ objectName=indicator_name+'-panelItem'
82 )
82 )
83
83
84 def _get_indicator_page(self, indicator_name):
84 def _get_indicator_page(self, indicator_name):
85 return self.select_single(
85 return self.select_single(
86− 'DefaultIndicatorPage',
86+ 'IndicatorPage',
87 objectName=indicator_name+'-page'
87 objectName=indicator_name+'-page'
88 )
88 )
89
89
@@ -93,12 +93,12 @@ class QQuickView(emulators.UnityEmulatorBase):
93
93
94 :returns: The indicator page.
94 :returns: The indicator page.
95 """
95 """
96− widget = self._get_indicator_widget(indicator_name)
96+ widget = self._get_indicator_panel_item(indicator_name)
97 start_x, start_y = input.get_center_point(widget)
97 start_x, start_y = input.get_center_point(widget)
98 end_x = start_x
98 end_x = start_x
99 end_y = self.height
99 end_y = self.height
100 self.pointing_device.drag(start_x, start_y, end_x, end_y)
100 self.pointing_device.drag(start_x, start_y, end_x, end_y)
101− self.wait_select_single('Indicators', fullyOpened=True)
101+ self.wait_select_single('IndicatorsMenu', fullyOpened=True)
102 return self._get_indicator_page(indicator_name)
102 return self._get_indicator_page(indicator_name)
103
103
104 @autopilot_logging.log_action(logger.info)
104 @autopilot_logging.log_action(logger.info)
tests/mocks/Unity/Indicators/Indicators.qmltypes+2−4
@@ -105,10 +105,8 @@ Module {
105 values: {
105 values: {
106 "Identifier": 0,
106 "Identifier": 0,
107 "Position": 1,
107 "Position": 1,
108− "WidgetSource": 2,
108+ "IndicatorProperties": 2,
109− "PageSource": 3,
109+ "IsVisible": 3
110− "IndicatorProperties": 4,
111− "IsVisible": 5
112 }
110 }
113 }
111 }
114 }
112 }
tests/mocks/Unity/Indicators/IndicatorsModel.qml+89−79
@@ -20,42 +20,97 @@ import Unity.Indicators 0.1 as Indicators
20Indicators.FakeIndicatorsModel {
20Indicators.FakeIndicatorsModel {
21 id: root
21 id: root
22
22
23− Component.onCompleted: {
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+ ]
79+
80+ function load(profile) {
81+ unload();
82+ root.modelData = originalModelData;
83+
24− Indicators.UnityMenuModelCache.setCachedModelData("com.canonical.indicators.fake1",
84+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake1",
25− "/com/canonical/indicators/fake1",
26− "/com/canonical/indicators/fake1",
27 getUnityMenuModelData("fake-indicator-bluetooth",
85 getUnityMenuModelData("fake-indicator-bluetooth",
28 "Bluetooth (F)",
86 "Bluetooth (F)",
29 "",
87 "",
30 [ "image://theme/bluetooth-active" ]));
88 [ "image://theme/bluetooth-active" ]));
31− Indicators.UnityMenuModelCache.setCachedModelData("com.canonical.indicators.fake2",
89+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake2",
32− "/com/canonical/indicators/fake2",
33− "/com/canonical/indicators/fake2",
34 getUnityMenuModelData("fake-indicator-network",
90 getUnityMenuModelData("fake-indicator-network",
35 "Network (F)",
91 "Network (F)",
36 "",
92 "",
37 [ "image://theme/simcard-error", "image://theme/wifi-high" ]));
93 [ "image://theme/simcard-error", "image://theme/wifi-high" ]));
38− Indicators.UnityMenuModelCache.setCachedModelData("com.canonical.indicators.fake3",
94+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake3",
39− "/com/canonical/indicators/fake3",
40− "/com/canonical/indicators/fake3",
41− getUnityMenuModelData("fake-indicator-sound",
95+ getUnityMenuModelData("fake-indicator-messages",
42 "Messages (F)",
96 "Messages (F)",
43 "",
97 "",
44 [ "image://theme/messages-new" ]));
98 [ "image://theme/messages-new" ]));
45− Indicators.UnityMenuModelCache.setCachedModelData("com.canonical.indicators.fake4",
99+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake4",
46− "/com/canonical/indicators/fake4",
47− "/com/canonical/indicators/fake4",
48− getUnityMenuModelData("fake-indicator-power",
100+ getUnityMenuModelData("fake-indicator-sound",
49 "Sound (F)",
101 "Sound (F)",
50 "",
102 "",
51 [ "image://theme/audio-volume-high" ]));
103 [ "image://theme/audio-volume-high" ]));
52− Indicators.UnityMenuModelCache.setCachedModelData("com.canonical.indicators.fake5",
104+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake5",
53− "/com/canonical/indicators/fake5",
54− "/com/canonical/indicators/fake5",
55 getUnityMenuModelData("fake-indicator-power",
105 getUnityMenuModelData("fake-indicator-power",
56 "Battery (F)",
106 "Battery (F)",
57 "",
107 "",
58 [ "image://theme/battery-020" ]));
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+ []));
59 }
114 }
60
115
61 function getUnityMenuModelData(identifier, title, label, icons) {
116 function getUnityMenuModelData(identifier, title, label, icons) {
@@ -71,11 +126,12 @@ Indicators.FakeIndicatorsModel {
71 "actionState": {
126 "actionState": {
72 "title": title,
127 "title": title,
73 "label": label,
128 "label": label,
74− "icons": icons
129+ "icons": icons,
130+ "visible": true
75 },
131 },
76 "isCheck": false,
132 "isCheck": false,
77 "isRadio": false,
133 "isRadio": false,
78− "isToggled": false,
134+ "isToggled": false
79 },
135 },
80 "submenu": []
136 "submenu": []
81 }];
137 }];
@@ -94,7 +150,7 @@ Indicators.FakeIndicatorsModel {
94 "actionState": {},
150 "actionState": {},
95 "isCheck": false,
151 "isCheck": false,
96 "isRadio": false,
152 "isRadio": false,
97− "isToggled": false,
153+ "isToggled": false
98 }};
154 }};
99 submenus.push(submenu);
155 submenus.push(submenu);
100 }
156 }
@@ -103,66 +159,20 @@ Indicators.FakeIndicatorsModel {
103 return root;
159 return root;
104 }
160 }
105
161
162+ function setIndicatorVisible(identifier, visible) {
106− property var originalModelData: [
163+ for (var i = 0; i < originalModelData.length; i++) {
107− {
164+ if (originalModelData[i]["identifier"] === identifier) {
108− "identifier": "indicator-fake1",
165+ var data = Indicators.UnityMenuModelCache.getCachedModelData(
109− "widgetSource": "Indicators/DefaultIndicatorWidget.qml",
166+ originalModelData[i]["indicatorProperties"]["menuObjectPath"]);
110− "pageSource": "Indicators/DefaultIndicatorPage.qml",
167+
111− "indicatorProperties": {
168+ data[0]["rowData"]["actionState"]["visible"] = visible;
112− "enabled": true,
169+
113− "busName": "com.canonical.indicators.fake1",
170+ Indicators.UnityMenuModelCache.setCachedModelData(
114− "menuObjectPath": "/com/canonical/indicators/fake1",
115− "actionsObjectPath": "/com/canonical/indicators/fake1"
116− }
117− },
118− {
119− "identifier": "indicator-fake2",
120− "widgetSource": "Indicators/DefaultIndicatorWidget.qml",
121− "pageSource": "Indicators/DefaultIndicatorPage.qml",
122− "indicatorProperties": {
171+ originalModelData[i]["indicatorProperties"]["menuObjectPath"],
123− "enabled": true,
172+ data);
124− "busName": "com.canonical.indicators.fake2",
173+ break;
125− "menuObjectPath": "/com/canonical/indicators/fake2",
126− "actionsObjectPath": "/com/canonical/indicators/fake2"
127− }
128− },
129− {
130− "identifier": "indicator-fake3",
131− "widgetSource": "Indicators/DefaultIndicatorWidget.qml",
132− "pageSource": "Indicators/DefaultIndicatorPage.qml",
133− "indicatorProperties": {
134− "enabled": true,
135− "busName": "com.canonical.indicators.fake3",
136− "menuObjectPath": "/com/canonical/indicators/fake3",
137− "actionsObjectPath": "/com/canonical/indicators/fake3"
138− }
139− },
140− {
141− "identifier": "indicator-fake4",
142− "widgetSource": "Indicators/DefaultIndicatorWidget.qml",
143− "pageSource": "Indicators/DefaultIndicatorPage.qml",
144− "indicatorProperties": {
145− "enabled": true,
146− "busName": "com.canonical.indicators.fake4",
147− "menuObjectPath": "/com/canonical/indicators/fake4",
148− "actionsObjectPath": "/com/canonical/indicators/fake4"
149− }
150− },
151− {
152− "identifier": "indicator-fake5",
153− "widgetSource": "Indicators/DefaultIndicatorWidget.qml",
154− "pageSource": "Indicators/DefaultIndicatorPage.qml",
155− "indicatorProperties": {
156− "enabled": true,
157− "busName": "com.canonical.indicators.fake5",
158− "menuObjectPath": "/com/canonical/indicators/fake5",
159− "actionsObjectPath": "/com/canonical/indicators/fake5"
160 }
174 }
161 }
175 }
162− ]
163
176
164− function load(profile) {
165− unload();
166− root.modelData = originalModelData;
167 }
177 }
168}
178}
tests/mocks/Unity/Indicators/RootActionState.qml+2−2
@@ -27,7 +27,7 @@ Item {
27 property string rightLabel: cachedState && cachedState.hasOwnProperty("label") ? cachedState["label"] : ""
27 property string rightLabel: cachedState && cachedState.hasOwnProperty("label") ? cachedState["label"] : ""
28 property var icons: cachedState && cachedState.hasOwnProperty("icons") ? cachedState["icons"] : []
28 property var icons: cachedState && cachedState.hasOwnProperty("icons") ? cachedState["icons"] : []
29 property string accessibleName: cachedState && cachedState.hasOwnProperty("accessible-desc") ? cachedState["accessible-desc"] : ""
29 property string accessibleName: cachedState && cachedState.hasOwnProperty("accessible-desc") ? cachedState["accessible-desc"] : ""
30− visible: cachedState && cachedState.hasOwnProperty("visible") ? cachedState["visible"] : true
30+ property bool indicatorVisible: cachedState && cachedState.hasOwnProperty("visible") ? cachedState["visible"] : true
31
31
32 property var cachedState: menu ? menu.get(0, "actionState") : undefined
32 property var cachedState: menu ? menu.get(0, "actionState") : undefined
33 Connections {
33 Connections {
@@ -45,5 +45,5 @@ Item {
45 onRightLabelChanged: updated()
45 onRightLabelChanged: updated()
46 onIconsChanged: updated()
46 onIconsChanged: updated()
47 onAccessibleNameChanged: updated()
47 onAccessibleNameChanged: updated()
48− onVisibleChanged: updated()
48+ onIndicatorVisibleChanged: updated()
49}
49}
tests/mocks/Unity/Indicators/fakeindicatorsmodel.cpp−2
@@ -110,8 +110,6 @@ QHash<int, QByteArray> FakeIndicatorsModel::roleNames() const
110 {
110 {
111 roles[IndicatorsModelRole::Identifier] = "identifier";
111 roles[IndicatorsModelRole::Identifier] = "identifier";
112 roles[IndicatorsModelRole::Position] = "position";
112 roles[IndicatorsModelRole::Position] = "position";
113− roles[IndicatorsModelRole::WidgetSource] = "widgetSource";
114− roles[IndicatorsModelRole::PageSource] = "pageSource";
115 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
113 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
116 }
114 }
117 return roles;
115 return roles;
tests/mocks/Unity/Indicators/fakeunitymenumodelcache.cpp+10−8
@@ -32,11 +32,9 @@ FakeUnityMenuModelCache::FakeUnityMenuModelCache(QObject* parent)
32{
32{
33}
33}
34
34
35−QSharedPointer<UnityMenuModel> FakeUnityMenuModelCache::model(const QByteArray& bus,
35+QSharedPointer<UnityMenuModel> FakeUnityMenuModelCache::model(const QByteArray& path)
36− const QByteArray& path,
37− const QVariantMap& actions)
38{
36{
39− return UnityMenuModelCache::singleton()->model(bus, path, actions);
37+ return UnityMenuModelCache::singleton()->model(path);
40}
38}
41
39
42bool FakeUnityMenuModelCache::contains(const QByteArray& path)
40bool FakeUnityMenuModelCache::contains(const QByteArray& path)
@@ -44,14 +42,18 @@ bool FakeUnityMenuModelCache::contains(const QByteArray& path)
44 return UnityMenuModelCache::singleton()->contains(path);
42 return UnityMenuModelCache::singleton()->contains(path);
45}
43}
46
44
47−void FakeUnityMenuModelCache::setCachedModelData(const QByteArray& bus,
45+void FakeUnityMenuModelCache::setCachedModelData(const QByteArray& path,
48− const QByteArray& path,
49− const QVariantMap& actions,
50 const QVariant& data)
46 const QVariant& data)
51{
47{
52 // keep a ref forever!
48 // keep a ref forever!
53 if (!m_models.contains(path)) {
49 if (!m_models.contains(path)) {
54− m_models[path] = model(bus, path, actions);
50+ m_models[path] = model(path);
55 }
51 }
56 m_models[path]->setModelData(data);
52 m_models[path]->setModelData(data);
57}
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.h+4−8
@@ -29,18 +29,14 @@ public:
29
29
30 static FakeUnityMenuModelCache* singleton();
30 static FakeUnityMenuModelCache* singleton();
31
31
32− QSharedPointer<UnityMenuModel> model(const QByteArray& bus,
32+ QSharedPointer<UnityMenuModel> model(const QByteArray& path) override;
33− const QByteArray& path,
34− const QVariantMap& actions) override;
35 bool contains(const QByteArray& path) override;
33 bool contains(const QByteArray& path) override;
36
34
37−
38−
39− Q_INVOKABLE void setCachedModelData(const QByteArray& bus,
35+ Q_INVOKABLE void setCachedModelData(const QByteArray& path,
40− const QByteArray& path,
41− const QVariantMap& actions,
42 const QVariant& data = QVariant());
36 const QVariant& data = QVariant());
43
37
38+ Q_INVOKABLE QVariant getCachedModelData(const QByteArray& path);
39+
44private:
40private:
45 static QPointer<FakeUnityMenuModelCache> theFakeCache;
41 static QPointer<FakeUnityMenuModelCache> theFakeCache;
46 QHash<QByteArray, QSharedPointer<UnityMenuModel>> m_models;
42 QHash<QByteArray, QSharedPointer<UnityMenuModel>> m_models;
tests/qmltests/CMakeLists.txt+5−5
@@ -12,7 +12,6 @@ set(qmltest_DEFAULT_IMPORT_PATHS
12set(qmltest_DEFAULT_TARGETS qmlunittests)
12set(qmltest_DEFAULT_TARGETS qmlunittests)
13set(qmltest_DEFAULT_NO_ADD_TEST FALSE)
13set(qmltest_DEFAULT_NO_ADD_TEST FALSE)
14set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal")
14set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal")
15−add_qml_test(Panel IndicatorItem)
16add_qml_test(utils/Unity/Test UnityTest)
15add_qml_test(utils/Unity/Test UnityTest)
17
16
18set(qmltest_DEFAULT_TARGETS qmluitests)
17set(qmltest_DEFAULT_TARGETS qmluitests)
@@ -72,13 +71,14 @@ add_qml_test(Launcher Launcher)
72add_qml_test(Notifications Notifications)
71add_qml_test(Notifications Notifications)
73add_qml_test(Notifications VisualSnapDecisionsQueue)
72add_qml_test(Notifications VisualSnapDecisionsQueue)
74add_qml_test(Panel ActiveCallHint)
73add_qml_test(Panel ActiveCallHint)
74+add_qml_test(Panel IndicatorItem)
75−add_qml_test(Panel IndicatorRow ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
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")
76−add_qml_test(Panel Indicators 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")
77add_qml_test(Panel MenuContent ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
79add_qml_test(Panel MenuContent ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
78add_qml_test(Panel Panel ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
80add_qml_test(Panel Panel ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
79add_qml_test(Panel SearchIndicator)
81add_qml_test(Panel SearchIndicator)
80−add_qml_test(Panel/Indicators DefaultIndicatorWidget ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
81−add_qml_test(Panel/Indicators DefaultIndicatorPage ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
82# These MenuItemFactory tests need the test/mocks/ to come before plugins/
82# These MenuItemFactory tests need the test/mocks/ to come before plugins/
83add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
83add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
84add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
84add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
tests/qmltests/Greeter/tst_Clock.qml+1−3
@@ -37,9 +37,7 @@ Rectangle {
37 }
37 }
38
38
39 function updateDatetimeModelTime(label) {
39 function updateDatetimeModelTime(label) {
40− Indicators.UnityMenuModelCache.setCachedModelData("com.canonical.indicator.datetime",
40+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicator/datetime/phone",
41− "/com/canonical/indicator/datetime/phone",
42− "/com/canonical/indicator/datetime",
43 [{
41 [{
44 "rowData": {
42 "rowData": {
45 "actionState": { "label": label }
43 "actionState": { "label": label }
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_DefaultIndicatorWidget.qmldeleted−52
@@ -1,52 +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− busName: "test"
37− actionsObjectPath: "test"
38− deviceMenuObjectPath: "test"
39−
40− rootMenuType: ""
41−
42− iconSize: units.gu(3.2)
43− height: units.gu(3)
44− }
45−
46− UT.UnityTestCase {
47− name: "DefaultIndicatorWidget"
48− when: windowShown
49−
50− // FIXME: add tests
51− }
52−}
tests/qmltests/Panel/tst_ActiveCallHint.qml−1
@@ -19,7 +19,6 @@ import QtTest 1.0
19import Unity.Test 0.1 as UT
19import Unity.Test 0.1 as UT
20import Ubuntu.Telephony 0.1 as Telephony
20import Ubuntu.Telephony 0.1 as Telephony
21import Unity.Application 0.1
21import Unity.Application 0.1
22−import ".."
23import "../../../qml/Panel"
22import "../../../qml/Panel"
24
23
25Item {
24Item {
tests/qmltests/Panel/tst_IndicatorItem.qml+173−18
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -14,37 +14,192 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.1
18+import QtQuick.Layouts 1.1
18import QtTest 1.0
19import QtTest 1.0
19−import ".."
20−import "../../../qml/Panel"
21import Ubuntu.Components 0.1
20import Ubuntu.Components 0.1
22import Unity.Test 0.1 as UT
21import Unity.Test 0.1 as UT
22+import "../../../qml/Panel"
23
23
24Rectangle {
24Rectangle {
25− width: units.gu(10)
25+ width: units.gu(80)
26− height: units.gu(5)
26+ height: units.gu(30)
27− color: "black"
27+ color: "white"
28
28
29− IndicatorItem {
29+ RowLayout {
30− id: indicatorItem
31 anchors.fill: parent
30 anchors.fill: parent
31+ anchors.margins: units.gu(1)
32+
33+ Rectangle {
34+ id: itemArea
35+ color: "blue"
36+ Layout.fillWidth: true
37+ Layout.fillHeight: true
38+
39+ Rectangle {
40+ color: "black"
41+ anchors.fill: indicatorItem
42+ }
43+
44+ IndicatorItem {
45+ id: indicatorItem
46+ height: expanded ? units.gu(7) : units.gu(3)
47+ anchors.centerIn: parent
48+ identifier: "indicator-test"
49+
50+ rootActionState {
51+ title: titleLabel.text
52+ leftLabel : leftLabel.text
53+ rightLabel : rightLabel.text
54+ icons : {
55+ var icons = [];
56+ var i = 0;
57+ if (iconEnabled.checked) {
58+ for (i = 0; i < String(iconCount.text); i++) {
59+ icons.push("image://theme/audio-volume-high");
60+ }
61+ }
62+ return icons;
63+ }
64+ }
65+
66+ Behavior on height {
67+ NumberAnimation {
68+ id: heightAnimation
69+ duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing
70+ }
71+ }
72+ }
73+ }
74+
75+ ColumnLayout {
76+ Layout.alignment: Qt.AlignTop
77+ Layout.fillWidth: false
78+
79+ Button {
80+ id: expandButton
81+ Layout.fillWidth: true
82+ text: indicatorItem.expanded ? "Collapse" : "Expand"
83+ onClicked: indicatorItem.expanded = !indicatorItem.expanded
84+ }
85+
86+ Button {
87+ id: selectButton
88+ Layout.fillWidth: true
89+ text: indicatorItem.selected ? "Unselect" : "Select"
90+ onClicked: indicatorItem.selected = !indicatorItem.selected
91+ }
92+
93+ Rectangle {
94+ Layout.preferredHeight: units.dp(1);
95+ Layout.fillWidth: true;
96+ color: "black"
97+ }
98+
99+ RowLayout {
100+ CheckBox { id: iconEnabled; checked: true }
101+ Label { text: "icons Count:" }
102+ TextField { id: iconCount; text: "1"; enabled: iconEnabled.checked }
103+ }
104+
105+ RowLayout {
106+ Label { text: "Left Label:" }
107+ TextField { id: leftLabel; text: "Left"}
108+ }
109+
110+ RowLayout {
111+ Label { text: "Right Label:" }
112+ TextField { id: rightLabel; text: "Right"}
113+ }
114+
115+ RowLayout {
116+ Label { text: "Title:" }
117+ TextField { id: titleLabel; text: "Title"}
118+ }
119+ }
32 }
120 }
33
121
34 UT.UnityTestCase {
122 UT.UnityTestCase {
35 name: "IndicatorItem"
123 name: "IndicatorItem"
124+ when: windowShown
125+
126+ function init() {
127+ indicatorItem.selected = false;
128+ indicatorItem.expanded = false;
129+ indicatorItem.rootActionState.title = "Test Title";
130+ indicatorItem.rootActionState.leftLabel = "TestLeftLabel";
131+ indicatorItem.rootActionState.rightLabel = "TestRightLabel";
132+ indicatorItem.rootActionState.icons = [ "image://theme/audio-volume-high" ];
36
133
37− function test_dimmed() {
134+ tryCompare(heightAnimation, "running", false);
38− indicatorItem.dimmed = false;
39− tryCompareFunction(function(){return indicatorItem.opacity}, 1.0);
40− indicatorItem.dimmed = true;
41− tryCompareFunction(function(){return indicatorItem.opacity < 1.0}, true);
42 }
135 }
43
136
44− function test_empty() {
137+ function test_expand() {
45− compare(indicatorItem.indicatorVisible, false, "IndicatorItem should not be visible.");
138+ indicatorItem.expanded = true;
46− indicatorItem.widgetSource = "../../../qml/Panel/Indicators/DefaultIndicatorWidget.qml";
139+ tryCompare(indicatorItem, "height", units.gu(7));
140+ indicatorItem.expanded = false;
141+ tryCompare(indicatorItem, "height", units.gu(3));
142+ }
143+
144+ function test_minimizedVisibility() {
145+ compare(findChild(indicatorItem, "leftLabel").opacity, 1.0);
146+ compare(findChild(indicatorItem, "rightLabel").opacity, 1.0);
147+ compare(findChild(indicatorItem, "icons").opacity, 1.0);
148+ compare(findChild(indicatorItem, "indicatorName").opacity, 0.0);
149+ }
150+
151+ function test_expandIcon() {
152+ indicatorItem.expanded = true;
153+
154+ tryCompare(findChild(indicatorItem, "leftLabel"), "opacity", 0.0);
155+ tryCompare(findChild(indicatorItem, "rightLabel"), "opacity", 0.0);
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);
47− tryCompare(indicatorItem, "indicatorVisible", true);
166+ tryCompare(findChild(indicatorItem, "rightLabel"), "opacity", 1.0);
167+ tryCompare(findChild(indicatorItem, "icons"), "opacity", 0.0);
168+ tryCompare(findChild(indicatorItem, "indicatorName"), "opacity", 1.0);
169+ }
170+
171+ function test_expandLeftLabel() {
172+ indicatorItem.expanded = true;
173+
174+ indicatorItem.rootActionState.rightLabel = "";
175+ indicatorItem.rootActionState.icons = [];
176+
177+ tryCompare(findChild(indicatorItem, "rightLabel"), "opacity", 0.0);
178+ tryCompare(findChild(indicatorItem, "leftLabel"), "opacity", 1.0);
179+ tryCompare(findChild(indicatorItem, "icons"), "opacity", 0.0);
180+ tryCompare(findChild(indicatorItem, "indicatorName"), "opacity", 1.0);
181+ }
182+
183+ function test_select() {
184+ tryCompare(findChild(indicatorItem, "icon0"), "color", "#ededed");
185+ tryCompare(findChild(indicatorItem, "icon0"), "opacity", 1.0);
186+ tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#ededed");
187+ tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#ededed");
188+ tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#ededed");
189+
190+ indicatorItem.expanded = true;
191+ tryCompare(findChild(indicatorItem, "icon0"), "color", "#4c4c4c");
192+ tryCompare(findChild(indicatorItem, "icon0"), "opacity", 0.6);
193+ tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#4c4c4c");
194+ tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#4c4c4c");
195+ tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#4c4c4c");
196+
197+ indicatorItem.selected = true;
198+ tryCompare(findChild(indicatorItem, "icon0"), "color", "#ededed");
199+ tryCompare(findChild(indicatorItem, "icon0"), "opacity", 1.0);
200+ tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#ededed");
201+ tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#ededed");
202+ tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#ededed");
48 }
203 }
49 }
204 }
50}
205}
tests/qmltests/Panel/tst_IndicatorItemRow.qmlnew file+290
@@ -0,0 +1,290 @@
1+/*
2+ * Copyright 2013-2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.1
18+import QtQuick.Layouts 1.1
19+import QtTest 1.0
20+import "../../../qml/Panel"
21+import Ubuntu.Components 0.1
22+import Unity.Test 0.1 as UT
23+import Unity.Indicators 0.1 as Indicators
24+
25+IndicatorTest {
26+ id: root
27+ width: units.gu(120)
28+ height: units.gu(40)
29+ color: "white"
30+
31+ RowLayout {
32+ anchors.fill: parent
33+ anchors.margins: units.gu(1)
34+
35+ Rectangle {
36+ Layout.fillWidth: true
37+ Layout.fillHeight: true
38+
39+ id: itemArea
40+ color: "blue"
41+
42+ Rectangle {
43+ color: "black"
44+ anchors.fill: indicatorsRow
45+ }
46+
47+ IndicatorItemRow {
48+ id: indicatorsRow
49+ height: expanded ? units.gu(7) : units.gu(3)
50+ anchors.centerIn: parent
51+ indicatorsModel: root.indicatorsModel
52+ enableLateralChanges: ma.pressed
53+
54+ Behavior on height {
55+ NumberAnimation {
56+ id: heightAnimation
57+ duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing
58+ }
59+ }
60+
61+ MouseArea {
62+ id: ma
63+ anchors.fill: parent
64+ onPositionChanged: {
65+ indicatorsRow.lateralPosition = mouse.x;
66+ }
67+ onPressed: {
68+ if (pressed) {
69+ indicatorsRow.lateralPosition = mouse.x;
70+ indicatorsRow.selectItemAt(mouse.x);
71+ }
72+ }
73+ }
74+ }
75+ }
76+
77+ ColumnLayout {
78+ Layout.alignment: Qt.AlignTop
79+ Layout.fillWidth: false
80+
81+ Button {
82+ Layout.fillWidth: true
83+ text: indicatorsRow.expanded ? "Collapse" : "Expand"
84+ onClicked: indicatorsRow.expanded = !indicatorsRow.expanded
85+ }
86+
87+ Rectangle {
88+ Layout.preferredHeight: units.dp(1);
89+ Layout.fillWidth: true;
90+ color: "black"
91+ }
92+
93+ Repeater {
94+ model: root.originalModelData
95+ RowLayout {
96+ CheckBox {
97+ checked: true
98+ onCheckedChanged: checked ? insertIndicator(index) : removeIndicator(index);
99+ }
100+ Label {
101+ Layout.fillWidth: true
102+ text: modelData["identifier"]
103+ }
104+
105+ CheckBox {
106+ checked: true
107+ onCheckedChanged: setIndicatorVisible(index, checked);
108+ }
109+ Label {
110+ text: "visible"
111+ }
112+ }
113+ }
114+ }
115+ }
116+
117+ UT.UnityTestCase {
118+ name: "IndicatorItemRow"
119+ when: windowShown
120+
121+ function init() {
122+ root.resetData();
123+
124+ indicatorsRow.resetCurrentItem();
125+ indicatorsRow.lateralPosition = -1;
126+
127+ indicatorsRow.expanded = false;
128+ tryCompare(heightAnimation, "running", false);
129+ tryCompare(findChild(indicatorsRow, "highlight"), "highlightCenterOffset", 0);
130+ wait(1); // row seems to take a bit of time for item x values to update.
131+ }
132+
133+ function wait_for_expansion_to_settle() {
134+ tryCompare(heightAnimation, "running", false);
135+ wait(200); // put a little extra wait in for things to settle
136+ }
137+
138+ function test_indicatorRowChanges_data() {
139+ return [
140+ { remove: [0, 2] },
141+ { remove: [0, 1, 2, 3, 4] },
142+ ];
143+ }
144+
145+ // test the changes in the available indicators updates the
146+ // indicators that are visible.
147+ function test_indicatorRowChanges(data) {
148+ var i;
149+ var item;
150+ var itemsToRemove = [0, 2];
151+
152+ verify(root.originalModelData.length > 0);
153+ for (i = 0; i < root.originalModelData.length; i++) {
154+ item = findChild(indicatorsRow, root.originalModelData[i]["identifier"] + "-panelItem");
155+ verify(item);
156+
157+ compare(item.ownIndex, i, "Item at incorrect index");
158+ }
159+
160+ for (i = data.remove.length-1; i >= 0; i--) {
161+ removeIndicator(data.remove[i]);
162+ }
163+
164+ // test removals
165+ for (i = 0; i < root.originalModelData.length; i++) {
166+ item = findChild(indicatorsRow, root.originalModelData[i]["identifier"] + "-panelItem");
167+
168+ verify(data.remove.indexOf(i) !== -1 ? (item === null) : (item !== null));
169+ }
170+
171+ // test insertion
172+ for (i = 0; i < data.remove.length; i++) {
173+ insertIndicator(data.remove[i]);
174+ }
175+
176+ for (i = 0; i < root.originalModelData.length; i++) {
177+ item = findChild(indicatorsRow, root.originalModelData[i]["identifier"] + "-panelItem");
178+ verify(item);
179+
180+ compare(item.ownIndex, i, "Item at incorrect index");
181+ }
182+ }
183+
184+ function test_validCurrentItem_data() {
185+ return [
186+ { index: 0 },
187+ { index: 2 },
188+ { index: 4 }
189+ ];
190+ }
191+
192+ // test selecting the item at it's position sets the current item of the row.
193+ function test_validCurrentItem(data) {
194+ var dataItem = findChild(indicatorsRow, root.originalModelData[data.index]["identifier"] + "-panelItem");
195+ verify(dataItem !== null);
196+
197+ indicatorsRow.selectItemAt(dataItem.x + dataItem.width/2);
198+ compare(indicatorsRow.currentItem, dataItem);
199+ }
200+
201+ // tests item default selection (no item at position X)
202+ function test_invalidCurrentItem() {
203+ indicatorsRow.selectItemAt(-100);
204+ var item = findChild(indicatorsRow, root.originalModelData[0]["identifier"] + "-panelItem");
205+ compare(indicatorsRow.currentItem, item);
206+ }
207+
208+ // testing that changing the lateral position offset of the row changes the current item.
209+ function test_lateralPositionChangesCurrentItem_data() {
210+ return [
211+ { tag: "0 -> 4", from: 0, to: 4 },
212+ { tag: "3 -> 1", from: 3, to: 1 }
213+ ];
214+ }
215+
216+ function test_lateralPositionChangesCurrentItem(data) {
217+ indicatorsRow.expanded = true;
218+ wait_for_expansion_to_settle();
219+
220+ var fromItem = findChild(indicatorsRow, root.originalModelData[data.from]["identifier"] + "-panelItem");
221+ verify(fromItem !== null);
222+
223+ var toItem = findChild(indicatorsRow, root.originalModelData[data.to]["identifier"] + "-panelItem");
224+ verify(toItem !== null);
225+
226+ var fromPosition = indicatorsRow.mapFromItem(fromItem, fromItem.width/2, fromItem.height/2);
227+ var toPosition = indicatorsRow.mapFromItem(toItem, toItem.width/2, toItem.height/2);
228+
229+ mousePress(indicatorsRow, fromPosition.x, fromPosition.y);
230+ compare(indicatorsRow.currentItem, fromItem, "Initial item not selected");
231+
232+ // this uses the MouseArea above to change the indicatorRow lateralPosition
233+ mouseFlick(indicatorsRow, fromPosition.x, fromPosition.y, toPosition.x, toPosition.y, false, false, units.gu(5), 30);
234+
235+ mouseRelease(indicatorsRow, fromPosition.x, fromPosition.y);
236+ compare(indicatorsRow.currentItem, toItem, "Current item did not change to expected item");
237+ }
238+
239+ // testing that positive changes to the lateral position offset shifts the highlight offset to the right
240+ function test_positiveLateralPositionChangesHighlightOffset() {
241+ indicatorsRow.expanded = true;
242+ wait_for_expansion_to_settle();
243+
244+ var highlight = findChild(indicatorsRow, "highlight");
245+ var item = findChild(indicatorsRow, root.originalModelData[2]["identifier"] + "-panelItem");
246+ verify(item !== null);
247+ var mappedPosition = indicatorsRow.mapFromItem(item, item.width/2, item.height/2);
248+
249+ mousePress(indicatorsRow, mappedPosition.x, mappedPosition.y);
250+ var originalHightlightX = highlight.x;
251+ var offset = 1;
252+ while((highlight.x - originalHightlightX) <= units.gu(0.5) && offset < units.gu(10)) {
253+ mouseMove(indicatorsRow, mappedPosition.x + offset, mappedPosition.y, 10);
254+ offset = offset + 2;
255+ }
256+ // verify that we hit the offset
257+ verify((highlight.x - originalHightlightX) >= units.gu(0.5));
258+ mouseRelease(indicatorsRow);
259+
260+ // should go back to 0
261+ tryCompare(highlight, "highlightCenterOffset", 0);
262+ }
263+
264+ // testing that negative changes to the lateral position offset shifts the highlight offset to the left
265+ function test_negativeLateralPositionChangesHighlightOffset() {
266+ indicatorsRow.expanded = true;
267+ wait_for_expansion_to_settle();
268+
269+ var highlight = findChild(indicatorsRow, "highlight");
270+ var item = findChild(indicatorsRow, root.originalModelData[2]["identifier"] + "-panelItem");
271+ verify(item !== null);
272+ var mappedPosition = indicatorsRow.mapFromItem(item, item.width/2, item.height/2);
273+
274+ mousePress(indicatorsRow, mappedPosition.x, mappedPosition.y);
275+ var originalHightlightX = highlight.x;
276+ var offset = 1;
277+ while((highlight.x - originalHightlightX) >= -units.gu(0.5) && offset < units.gu(10)) {
278+ mouseMove(indicatorsRow, mappedPosition.x - offset, mappedPosition.y, 10);
279+ offset = offset + 2;
280+ }
281+
282+ // verify that we hit the offset
283+ verify((highlight.x - originalHightlightX) <= -units.gu(0.5));
284+ mouseRelease(indicatorsRow);
285+
286+ // should go back to 0
287+ tryCompare(findChild(indicatorsRow, "highlight"), "highlightCenterOffset", 0);
288+ }
289+ }
290+}
tests/qmltests/Panel/Indicators/tst_DefaultIndicatorPage.qml → tests/qmltests/Panel/tst_IndicatorPage.qmlrenamed+5−7
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -18,14 +18,14 @@ import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19import Unity.Test 0.1 as UT
19import Unity.Test 0.1 as UT
20import Unity.Indicators 0.1 as Indicators
20import Unity.Indicators 0.1 as Indicators
21−import "../../../../qml/Panel/Indicators"
21+import "../../../qml/Panel"
22
22
23Item {
23Item {
24 id: testView
24 id: testView
25 width: units.gu(40)
25 width: units.gu(40)
26 height: units.gu(70)
26 height: units.gu(70)
27
27
28− DefaultIndicatorPage {
28+ IndicatorPage {
29 id: page
29 id: page
30 anchors.fill: parent
30 anchors.fill: parent
31
31
@@ -110,14 +110,12 @@ Item {
110 }]; // end row 1
110 }]; // end row 1
111
111
112 function initializeMenuData(data) {
112 function initializeMenuData(data) {
113− Indicators.UnityMenuModelCache.setCachedModelData("com.canonical.indicator.test",
113+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicator/test",
114− "/com/canonical/indicator/test",
115− "/com/canonical/indicator/test",
116 data);
114 data);
117 }
115 }
118
116
119 UT.UnityTestCase {
117 UT.UnityTestCase {
120− name: "DefaultIndicatorPage"
118+ name: "IndicatorPage"
121
119
122 function init() {
120 function init() {
123 initializeMenuData([]);
121 initializeMenuData([]);
tests/qmltests/Panel/tst_IndicatorRow.qmldeleted−158
@@ -1,158 +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− PanelBackground {
34− anchors.fill: indicatorRow
35− }
36−
37− IndicatorRow {
38− id: indicatorRow
39− anchors {
40− left: parent.left
41− right: parent.right
42− }
43−
44− indicatorsModel: indicatorModel
45−
46− Component.onCompleted: indicatorModel.load("test1")
47− }
48−
49− Indicators.IndicatorsModel {
50− id: indicatorModel
51− }
52−
53− UT.UnityTestCase {
54− name: "IndicatorRow"
55− when: windowShown
56−
57− function init() {
58− indicatorModel.load("test1");
59−
60− indicatorRow.state = "initial";
61− indicatorRow.setCurrentItemIndex(-1);
62− indicatorRow.unitProgress = 0.0;
63− }
64−
65− function get_indicator_item(index) {
66− return findChild(indicatorRow.row, "item" + index);
67− }
68−
69− function test_set_current_item() {
70− indicatorRow.setCurrentItemIndex(0);
71− compare(indicatorRow.indicatorsModel.data(indicatorRow.currentItemIndex, Indicators.IndicatorsModelRole.Identifier),
72− "indicator-fake1",
73− "Incorrect item at position 0");
74−
75− indicatorRow.setCurrentItemIndex(1);
76− compare(indicatorRow.indicatorsModel.data(indicatorRow.currentItemIndex, Indicators.IndicatorsModelRole.Identifier),
77− "indicator-fake2",
78− "Incorrect item at position 1");
79−
80− indicatorRow.setCurrentItemIndex(2);
81− compare(indicatorRow.indicatorsModel.data(indicatorRow.currentItemIndex, Indicators.IndicatorsModelRole.Identifier),
82− "indicator-fake3",
83− "Incorrect item at position 2");
84− }
85−
86− function test_highlight_data() {
87− return [
88− { index: 0, progress: 0.0, current: false, other: false },
89− { index: 0, progress: 0.1, current: true, other: false },
90− { index: 0, progress: 0.5, current: true, other: false },
91− { index: 0, progress: 1.0, current: true, other: false },
92− { index: 2, progress: 0.0, current: false, other: false },
93− { index: 2, progress: 0.1, current: true, other: false },
94− { index: 2, progress: 0.5, current: true, other: false },
95− { index: 2, progress: 1.0, current: true, other: false }
96− ];
97− }
98−
99− function test_highlight(data) {
100− indicatorRow.unitProgress = data.progress;
101− indicatorRow.setCurrentItemIndex(data.index);
102−
103− compare(indicatorRow.currentItem.highlighted, data.current, "Indicator hightlight did not match for current item");
104−
105− for (var i = 0; i < indicatorRow.row.count; i++) {
106− compare(get_indicator_item(i).highlighted, i === data.index ? data.current: data.other, "Indicator hightlight did not match for item iter");
107− }
108− }
109−
110− function test_opacity_data() {
111− return [
112− { index: 0, progress: 0.0, current: 1.0, other: 1.0 },
113− { index: 0, progress: 0.1, current: 1.0, other: 0.9 },
114− { index: 0, progress: 0.5, current: 1.0, other: 0.5 },
115− { index: 0, progress: 1.0, current: 1.0, other: 0.0 },
116− { index: 2, progress: 0.0, current: 1.0, other: 1.0 },
117− { index: 2, progress: 0.1, current: 1.0, other: 0.9 },
118− { index: 2, progress: 0.5, current: 1.0, other: 0.5 },
119− { index: 2, progress: 1.0, current: 1.0, other: 0.0 }
120− ];
121− }
122−
123− function test_opacity(data) {
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− indicatorRow.unitProgress = data.progress;
149− indicatorRow.setCurrentItemIndex(data.index);
150−
151− compare(indicatorRow.currentItem.dimmed, data.current, "Indicator dim did not match for current item");
152−
153− for (var i = 0; i < indicatorRow.row.count; i++) {
154− compare(get_indicator_item(i).dimmed, i === data.index ? data.current: data.other, "Indicator dim did not match for item iter");
155− }
156− }
157− }
158−}
tests/qmltests/Panel/tst_Indicators.qmldeleted−231
@@ -1,231 +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− verify(item !== null);
162−
163− item.enabled = false;
164− }
165−
166− function test_row_visible_menuContent_visible_data() { return [
167− {tag: "first", visible: [false, true, true, true, true] },
168− {tag: "adjacent", visible: [true, false, false, true, true] },
169− {tag: "bounds", visible: [false, true, true, true, false] },
170− {tag: "disjoint", visible: [true, false, true, false, true] },
171− {tag: "last", visible: [true, true, true, true, false] }];
172− }
173−
174− function test_row_visible_menuContent_visible(data) {
175− indicators.show();
176−
177− var contentListView = findChild(indicators, "indicatorsContentListView");
178− var indicatorRowItems = findChild(indicators, "indicatorRowItems");
179−
180− var count = data.visible.length
181− for (var i = 0; i< data.visible.length; i++) {
182− if (data.visible[i] === false) {
183− init_invisible_indicator("indicator-fake" + (i + 1));
184− count--;
185− }
186− }
187−
188− tryCompare(indicatorRowItems, "count", count);
189−
190− for (i = 0; i < data.visible.length; i++) {
191− var widgetName = "indicator-fake" + (i + 1 + "-widget");
192− var pageName = "indicator-fake" + (i + 1 + "-page");
193−
194− // check for item
195− tryCompareFunction(function() { return findChild(indicatorRowItems, widgetName) !== null }, data.visible[i]);
196−
197− // check for tab
198− tryCompareFunction(function() { return findChild(contentListView, pageName) !== null }, data.visible[i]);
199− }
200− }
201−
202− function test_indicator_visible_correct_menu_data() { return [
203− {tag: "current-first", currentIndex: 0, visible: [false, true, true, true, true], expectedIndex: 0, expextedMenu: "indicator-fake2" },
204− {tag: "current-last", currentIndex: 4, visible: [true, true, true, true, false], expectedIndex: 3, expextedMenu: "indicator-fake4" },
205− {tag: "after", currentIndex: 0, visible: [true, false, true, true, true], expectedIndex: 0, expextedMenu: "indicator-fake1" },
206− {tag: "before", currentIndex: 1, visible: [false, true, true, true, true], expectedIndex: 1, expextedMenu: "indicator-fake2" }];
207− }
208−
209− function test_indicator_visible_correct_menu(data) {
210− var contentListView = findChild(indicators, "indicatorsContentListView");
211− var indicatorRow = findChild(indicators, "indicatorRow");
212−
213− indicators.show();
214− indicatorRow.setCurrentItemIndex(data.currentIndex);
215− tryCompare(indicators, "fullyOpened", true);
216−
217− for (var i = 0; i< data.visible.length; i++) {
218− if (data.visible[i] === false) {
219− init_invisible_indicator("indicator-fake" + (i + 1));
220− }
221− }
222−
223− // check for current selected item
224− tryCompare(indicatorRow, "currentItemIndex", data.expectedIndex);
225−
226− // check for current selected tab
227− tryCompareFunction(function() { return findChild(contentListView, data.expextedMenu) === contentListView.currentItem }, true);
228−
229− }
230− }
231−}
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−17
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -17,12 +17,11 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19import Unity.Test 0.1 as UT
19import Unity.Test 0.1 as UT
20−import ".."
21import "../../../qml/Panel"
20import "../../../qml/Panel"
22import Unity.Indicators 0.1 as Indicators
21import Unity.Indicators 0.1 as Indicators
23
22
24−Item {
23+IndicatorTest {
25− id: shell
24+ id: root
26 width: units.gu(40)
25 width: units.gu(40)
27 height: units.gu(70)
26 height: units.gu(70)
28
27
@@ -30,15 +29,9 @@ Item {
30 Item { id: greeter }
29 Item { id: greeter }
31 Item { id: handle }
30 Item { id: handle }
32
31
33−
34− Indicators.IndicatorsModel {
35− id: indicatorsModel
36− Component.onCompleted: load("test1")
37− }
38−
39 MenuContent {
32 MenuContent {
40 id: menuContent
33 id: menuContent
41− indicatorsModel: indicatorsModel
34+ indicatorsModel: root.indicatorsModel
42 height: parent.height - 50
35 height: parent.height - 50
43 }
36 }
44
37
@@ -70,12 +63,12 @@ Item {
70 if (menuContent.currentMenuIndex == -1)
63 if (menuContent.currentMenuIndex == -1)
71 activate_content(0);
64 activate_content(0);
72 else
65 else
73− activate_content((menuContent.currentMenuIndex + 1) % indicatorsModel.count)
66+ activate_content((menuContent.currentMenuIndex + 1) % root.originalModelData.length)
74 }
67 }
75
68
76 function activate_content(index)
69 function activate_content(index)
77 {
70 {
78− menuContent.setCurrentMenuIndex(index)
71+ menuContent.currentMenuIndex = index;
79 }
72 }
80
73
81 function get_test_menu_objecName(index) {
74 function get_test_menu_objecName(index) {
@@ -107,7 +100,7 @@ Item {
107
100
108 // Check that the correct menus are displayed for the requested item.
101 // Check that the correct menus are displayed for the requested item.
109 function test_show_menu() {
102 function test_show_menu() {
110− var menuCount = indicatorsModel.count;
103+ var menuCount = root.originalModelData.length;
111 verify(menuCount > 0, "Menu count should be greater than zero");
104 verify(menuCount > 0, "Menu count should be greater than zero");
112
105
113 var listView = menu_content_test.findChild(menuContent, "indicatorsContentListView")
106 var listView = menu_content_test.findChild(menuContent, "indicatorsContentListView")
@@ -118,7 +111,7 @@ Item {
118 var menuIndex = i%menuCount;
111 var menuIndex = i%menuCount;
119
112
120 activate_content(menuIndex);
113 activate_content(menuIndex);
121− testItemObjectName = indicatorsModel.data(menuIndex, Indicators.IndicatorsModelRole.Identifier);
114+ testItemObjectName = indicatorsModel.model.data(menuIndex, Indicators.IndicatorsModelRole.Identifier);
122 compare(listView.currentIndex, menuIndex, "Current tab index does not match selected tab index");
115 compare(listView.currentIndex, menuIndex, "Current tab index does not match selected tab index");
123 tryCompareFunction(current_item_equals_test_item, true);
116 tryCompareFunction(current_item_equals_test_item, true);
124 }
117 }
@@ -126,14 +119,13 @@ Item {
126
119
127 // Tests QTBUG-30632 - asynchronous loader crashes when changing index quickly.
120 // Tests QTBUG-30632 - asynchronous loader crashes when changing index quickly.
128 function test_multi_activate() {
121 function test_multi_activate() {
129− var menuCount = indicatorsModel.count;
122+ var menuCount = root.originalModelData.length;
130 verify(menuCount > 0, "Menu count should be greater than zero");
123 verify(menuCount > 0, "Menu count should be greater than zero");
131
124
132 for (var i = 0; i < 100; i++) {
125 for (var i = 0; i < 100; i++) {
133 activate_content(i % menuCount);
126 activate_content(i % menuCount);
134 compare(menuContent.currentMenuIndex, i%menuCount);
127 compare(menuContent.currentMenuIndex, i%menuCount);
135 }
128 }
136− wait(100);
137 }
129 }
138 }
130 }
139}
131}
tests/qmltests/Panel/tst_Panel.qml+185−214
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -14,95 +14,106 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.1
18+import QtQuick.Layouts 1.1
18import QtTest 1.0
19import QtTest 1.0
19import Unity.Test 0.1 as UT
20import Unity.Test 0.1 as UT
20−import Ubuntu.Components 0.1 as UC
21+import Ubuntu.Components 0.1
22+import Unity.Indicators 0.1 as Indicators
21import Ubuntu.Telephony 0.1 as Telephony
23import Ubuntu.Telephony 0.1 as Telephony
22−import ".."
23import "../../../qml/Panel"
24import "../../../qml/Panel"
24
25
25−/*
26+IndicatorTest {
26− This tests the Panel component using a fake model to stage data in the indicators
27+ id: root
27− A view will show with indicators at the top, as does in the shell. This can be controlled
28− as in the shell.
29−*/
30−Item {
31− id: shell
32− width: units.gu(40)
28+ width: units.gu(100)
33− height: units.gu(80)
29+ height: units.gu(71)
34−
30+ color: "white"
35− Panel {
36− id: panel
37− anchors {
38− left: parent.left
39− right: parent.right
40− }
41− height: parent.height - row.height
42− fullscreenMode: false
43
31
44− indicators {
32+ property string indicatorProfile: "phone"
45− profile: "test1"
46− panelHeight: units.gu(5)
47− }
48− callHint {
49− height: units.gu(4)
50− }
51
33
52− property real panelAndSeparatorHeight: panel.indicators.panelHeight + units.dp(2)
34+ RowLayout {
53− }
35+ anchors.fill: parent
36+ anchors.margins: units.gu(1)
37+ clip: true
54
38
39+ Rectangle {
40+ Layout.fillWidth: true
41+ Layout.fillHeight: true
55
42
56− Row {
43+ id: itemArea
57− id: row
44+ color: "blue"
58− anchors {
59− bottom: shell.bottom
60− left: parent.left
61− right: parent.right
62− }
63− height: 50
64
45
65− UC.Button {
46+ Panel {
66− text: panel.indicators.shown ? "Hide" : "Show"
47+ id: panel
67− anchors {
48+ anchors.fill: parent
68− top: parent.top
49+ indicators {
69− bottom: parent.bottom
50+ width: parent.width > units.gu(60) ? units.gu(40) : parent.width
51+ indicatorsModel: root.indicatorsModel
52+ }
53+
54+ property real panelAndSeparatorHeight: panel.indicators.minimizedPanelHeight + units.dp(2)
70 }
55 }
71− width: parent.width/3
56+ }
72
57
58+ ColumnLayout {
59+ Layout.alignment: Qt.AlignTop
60+ Layout.fillWidth: false
61+
62+ Button {
63+ Layout.fillWidth: true
64+ text: panel.indicators.shown ? "Hide" : "Show"
73− onClicked: {
65+ onClicked: {
74− if (panel.indicators.shown) {
66+ if (panel.indicators.shown) {
75− panel.indicators.hide();
67+ panel.indicators.hide();
76− } else {
68+ } else {
77− panel.indicators.show();
69+ panel.indicators.show();
70+ }
78 }
71 }
79 }
72 }
80− }
81
73
82− UC.Button {
74+ Button {
83− text: panel.fullscreenMode ? "Maximize" : "FullScreen"
75+ text: panel.fullscreenMode ? "Maximize" : "FullScreen"
84− anchors {
76+ Layout.fillWidth: true
85− top: parent.top
77+ onClicked: panel.fullscreenMode = !panel.fullscreenMode
86− bottom: parent.bottom
87 }
78 }
88− width: parent.width/3
89
79
90− onClicked: panel.fullscreenMode = !panel.fullscreenMode
80+ Button {
81+ Layout.fillWidth: true
82+ text: callManager.hasCalls ? "Called" : "No Calls"
83+ onClicked: {
84+ if (callManager.foregroundCall) {
85+ callManager.foregroundCall = null;
86+ } else {
87+ callManager.foregroundCall = phoneCall;
88+ }
89+ }
91− }
90+ }
92
91
93− UC.Button {
92+ Rectangle {
94− text: callManager.hasCalls ? "Called" : "No Calls"
93+ Layout.preferredHeight: units.dp(1);
95− anchors {
94+ Layout.fillWidth: true;
96− top: parent.top
95+ color: "black"
97− bottom: parent.bottom
98 }
96 }
99− width: parent.width/3
100
97
101− onClicked: {
98+ Repeater {
102− if (callManager.foregroundCall) {
99+ model: root.originalModelData
103− callManager.foregroundCall = null;
100+ RowLayout {
104− } else {
101+ CheckBox {
105− callManager.foregroundCall = phoneCall;
102+ checked: true
103+ onCheckedChanged: checked ? insertIndicator(index) : removeIndicator(index);
104+ }
105+ Label {
106+ Layout.fillWidth: true
107+ text: modelData["identifier"]
108+ }
109+
110+ CheckBox {
111+ checked: true
112+ onCheckedChanged: setIndicatorVisible(index, checked);
113+ }
114+ Label {
115+ text: "visible"
116+ }
106 }
117 }
107 }
118 }
108 }
119 }
@@ -118,13 +129,12 @@ Item {
118 when: windowShown
129 when: windowShown
119
130
120 function init() {
131 function init() {
121− panel.indicators.initialise();
122 panel.fullscreenMode = false;
132 panel.fullscreenMode = false;
133+ callManager.foregroundCall = null;
123
134
124 panel.indicators.hide();
135 panel.indicators.hide();
125 // Wait for animation to complete
136 // Wait for animation to complete
126 tryCompare(panel.indicators.hideAnimation, "running", false);
137 tryCompare(panel.indicators.hideAnimation, "running", false);
127− callManager.foregroundCall = null;
128
138
129 // Wait for the indicators to get into position.
139 // Wait for the indicators to get into position.
130 // (switches between normal and fullscreen modes are animated)
140 // (switches between normal and fullscreen modes are animated)
@@ -133,100 +143,21 @@ Item {
133 }
143 }
134
144
135 function get_indicator_item(index) {
145 function get_indicator_item(index) {
136− var indicatorRow = findChild(panel.indicators, "indicatorRow");
146+ var indicatorItem = findChild(panel, root.originalModelData[index]["identifier"]+"-panelItem");
137− verify(indicatorRow !== null);
138−
139− return findChild(indicatorRow.row, "item" + index);
140− }
141−
142− function get_indicator_item_position(index) {
143− var indicatorRow = findChild(panel.indicators, "indicatorRow");
144− verify(indicatorRow !== null);
145−
146− var indicatorItem = get_indicator_item(index);
147 verify(indicatorItem !== null);
147 verify(indicatorItem !== null);
148
148
149− return panel.mapFromItem(indicatorItem, indicatorItem.width/2, indicatorItem.height/2);
149+ return indicatorItem;
150− }
151−
152− // Pressing on the indicator panel should activate the indicator hints
153− // and expose a portion of the conent.
154− function test_hint() {
155− var indicatorItemCoord = get_indicator_item_position(0);
156−
157− touchPress(panel, indicatorItemCoord.x, panel.indicators.panelHeight / 2);
158−
159− // hint animation should be run, meaning that indicators will move downwards
160− // by hintValue pixels without any drag taking place
161− tryCompareFunction(function() { return panel.indicators.height },
162− panel.indicators.panelHeight + panel.indicators.hintValue);
163− tryCompare(panel.indicators, "partiallyOpened", true);
164− tryCompare(panel.indicators, "fullyOpened", false);
165−
166− touchRelease(panel, indicatorItemCoord.x, panel.indicators.panelHeight/2);
167− }
168−
169− // Pressing on the top edge of the screen should have no effect if the panel
170− // is hidden (fullscreen), which is the case when a fullscreen app is being shown
171− function test_noHintOnFullscreenMode() {
172− panel.fullscreenMode = true;
173− // Wait for the indicators to get into position.
174− // (switches between normal and fullscreen modes are animated)
175− var indicatorArea = findChild(panel, "indicatorArea");
176− tryCompare(indicatorArea, "y", -panel.panelHeight);
177−
178− var indicatorItemCoord = get_indicator_item_position(0);
179−
180− touchPress(panel, indicatorItemCoord.x, panel.indicators.panelHeight / 2);
181−
182− // Give some time for a hint animation to change things, if any
183− wait(500);
184−
185− // no hint animation when fullscreen
186− compare(panel.indicators.partiallyOpened, false,
187− "Indicator should not be partially opened when panel is pressed in" +
188− " fullscreenmode");
189− compare(panel.indicators.fullyOpened, false, "Indicator should not be partially" +
190− " opened when panel is pressed in fullscreenmode");
191−
192− touchRelease(panel, indicatorItemCoord.x, panel.panelHeight/2);
193− }
194−
195− // Pressing on the top edge of the indicator should have no effect if the panel
196− // has an active call
197− function test_noHintOnActiveCall() {
198− callManager.foregroundCall = phoneCall;
199−
200− var indicatorItemCoord = get_indicator_item_position(0);
201−
202− touchPress(panel, indicatorItemCoord.x, panel.callHint.height + panel.indicators.panelHeight / 2);
203−
204− // Give some time for a hint animation to change things, if any
205− wait(500);
206−
207− // no hint animation when fullscreen
208− compare(panel.indicators.partiallyOpened, false,
209− "Indicator should not be partially opened when panel is pressed in" +
210− " fullscreenmode");
211− compare(panel.indicators.fullyOpened, false, "Indicator should not be partially" +
212− " opened when panel is pressed in fullscreenmode");
213−
214− touchRelease(panel, indicatorItemCoord.x, panel.panelHeight/2);
215 }
150 }
216
151
217 function test_drag_show_data() {
152 function test_drag_show_data() {
218 return [
153 return [
219− { tag: "pinned", fullscreenFlag: false, alreadyOpen: false, call: null,
154+ { tag: "pinned", fullscreen: false, call: null,
220 indicatorY: 0 },
155 indicatorY: 0 },
221− { tag: "fullscreen", fullscreenFlag: true, alreadyOpen: false, call: null,
156+ { tag: "fullscreen", fullscreen: true, call: null,
222 indicatorY: -panel.panelAndSeparatorHeight },
157 indicatorY: -panel.panelAndSeparatorHeight },
223− { tag: "pinned-alreadyOpen", fullscreenFlag: false, alreadyOpen: true, call: null,
224− indicatorY: 0 },
225− { tag: "fullscreen-alreadyOpen", fullscreenFlag: true, alreadyOpen: true, call: null,
226− indicatorY: 0 },
227− { tag: "pinned-callActive", fullscreenFlag: false, alreadyOpen: false, call: phoneCall,
158+ { tag: "pinned-callActive", fullscreen: false, call: phoneCall,
228 indicatorY: 0},
159 indicatorY: 0},
229− { tag: "fullscreen-callActive", fullscreenFlag: true, alreadyOpen: false, call: phoneCall,
160+ { tag: "fullscreen-callActive", fullscreen: true, call: phoneCall,
230 indicatorY: -panel.panelAndSeparatorHeight }
161 indicatorY: -panel.panelAndSeparatorHeight }
231 ];
162 ];
232 }
163 }
@@ -234,16 +165,12 @@ Item {
234 // Dragging from a indicator item in the panel will gradually expose the
165 // Dragging from a indicator item in the panel will gradually expose the
235 // indicators, first by running the hint animation, then after dragging down will
166 // indicators, first by running the hint animation, then after dragging down will
236 // expose more of the panel, binding it to the selected indicator and opening it's menu.
167 // expose more of the panel, binding it to the selected indicator and opening it's menu.
168+ // Tested from first Y pixel to check for swipe from offscreen.
237 function test_drag_show(data) {
169 function test_drag_show(data) {
238− panel.fullscreenMode = data.fullscreenFlag;
170+ panel.fullscreenMode = data.fullscreen;
239 callManager.foregroundCall = data.call;
171 callManager.foregroundCall = data.call;
240
172
241− if (data.alreadyOpen) {
242− panel.indicators.show();
243− tryCompare(panel.indicators, "fullyOpened", true);
244− }
245−
246− var indicatorRow = findChild(panel.indicators, "indicatorRow");
173+ var indicatorRow = findChild(panel.indicators, "indicatorItemRow");
247 verify(indicatorRow !== null);
174 verify(indicatorRow !== null);
248
175
249 var menuContent = findChild(panel.indicators, "menuContent");
176 var menuContent = findChild(panel.indicators, "menuContent");
@@ -256,25 +183,16 @@ Item {
256 // (switches between normal and fullscreen modes are animated)
183 // (switches between normal and fullscreen modes are animated)
257 tryCompareFunction(function() { return indicatorArea.y }, data.indicatorY);
184 tryCompareFunction(function() { return indicatorArea.y }, data.indicatorY);
258
185
259− // do this for each indicator item
186+ for (var i = 0; i < root.originalModelData.length; i++) {
260− for (var i = 0; i < indicatorRow.row.count; i++) {
261−
262 var indicatorItem = get_indicator_item(i);
187 var indicatorItem = get_indicator_item(i);
263− verify(indicatorItem !== null);
264
188
265− if (!indicatorItem.visible)
189+ var startXPosition = root.mapFromItem(indicatorItem, indicatorItem.width / 2, 0).x;
266− continue;
190+ var startYPosition = root.mapFromItem(panel, 0, 0).y;
267
191
268− var indicatorItemCoord = get_indicator_item_position(i);
269−
270− touchPress(panel,
271− indicatorItemCoord.x, panel.indicators.panelHeight / 2);
272−
273− // 1) Drag the mouse down
274 touchFlick(panel,
192 touchFlick(panel,
275− indicatorItemCoord.x, panel.indicators.panelHeight / 2,
193+ startXPosition, startYPosition,
276− indicatorItemCoord.x, panel.height,
194+ startXPosition, panel.height,
277− false /* beginTouch */, false /* endTouch */);
195+ true /* beginTouch */, false /* endTouch */, units.gu(5), 15);
278
196
279 // Indicators height should follow the drag, and therefore increase accordingly.
197 // Indicators height should follow the drag, and therefore increase accordingly.
280 // They should be at least half-way through the screen
198 // They should be at least half-way through the screen
@@ -282,56 +200,109 @@ Item {
282 function() {return panel.indicators.height >= panel.height * 0.5},
200 function() {return panel.indicators.height >= panel.height * 0.5},
283 true);
201 true);
284
202
285− touchRelease(panel, indicatorItemCoord.x, panel.height);
203+ touchRelease(panel, startXPosition, panel.height);
286
204
287− compare(indicatorRow.currentItem, indicatorItem,
288− "Incorrect item activated at position " + i);
205+ compare(indicatorRow.currentItemIndex, i, "Indicator item should be activated at position " + i);
289− compare(menuContent.currentMenuIndex, i, "Menu conetent should be enabled for item at position " + i);
206+ compare(menuContent.currentMenuIndex, i, "Menu conetent should be activated for item at position " + i);
290
207
291 // init for next indicatorItem
208 // init for next indicatorItem
292− if (!data.alreadyOpen) {
293− panel.indicators.hide();
209+ panel.indicators.hide();
294− tryCompare(panel.indicators.hideAnimation, "running", false);
210+ tryCompare(panel.indicators.hideAnimation, "running", false);
295− tryCompare(panel.indicators, "state", "initial");
211+ tryCompare(panel.indicators, "state", "initial");
296− }
297 }
212 }
298 }
213 }
299
214
300− // Test the vertical velocity check when flicking the indicators open at an angle.
215+ function test_drag_hide_data() {
301− // If the vertical velocity is above a specific point, we shouldnt change active indicators
216+ return [
302− // if the x position changes
217+ { tag: "pinned", fullscreen: false, call: null,
303− function test_vertical_velocity_detector() {
218+ indicatorY: 0 },
219+ { tag: "fullscreen", fullscreen: true, call: null,
220+ indicatorY: -panel.panelAndSeparatorHeight },
221+ { tag: "pinned-callActive", fullscreen: false, call: phoneCall,
222+ indicatorY: 0},
223+ { tag: "fullscreen-callActive", fullscreen: true, call: phoneCall,
224+ indicatorY: -panel.panelAndSeparatorHeight }
225+ ];
226+ }
227+
228+ // Dragging the shown indicators up from bottom of panel will hide the indicators
229+ // Tested from last Y pixel to check for swipe from offscreen.
230+ function test_drag_hide(data) {
304− panel.fullscreenMode = false;
231+ panel.fullscreenMode = data.fullscreen;
232+ callManager.foregroundCall = data.call;
305
233
306− var indicatorRow = findChild(panel.indicators, "indicatorRow");
234+ var indicatorRow = findChild(panel.indicators, "indicatorItemRow");
307 verify(indicatorRow !== null);
235 verify(indicatorRow !== null);
308
236
309− // Get the first indicator
237+ var menuContent = findChild(panel.indicators, "menuContent");
310− var indicatorItemFirst = get_indicator_item(0);
311− verify(indicatorItemFirst !== null);
238+ verify(menuContent !== null);
312
239
313− var indicatorItemCoordFirst = get_indicator_item_position(0);
240+ var indicatorArea = findChild(panel, "indicatorArea");
314− var indicatorItemCoordNext = get_indicator_item_position(indicatorRow.row.count - 1);
241+ verify(indicatorArea !== null);
315
242
316− touchPress(panel,
243+ // Wait for the indicators to get into position.
317− indicatorItemCoordFirst.x, panel.indicators.panelHeight / 2);
244+ // (switches between normal and fullscreen modes are animated)
245+ tryCompareFunction(function() { return indicatorArea.y }, data.indicatorY);
246+
247+ var startXPosition = root.mapFromItem(panel.indicators, panel.indicators.width / 2, 0).x;
248+ var startYPosition = root.mapFromItem(panel, 0, panel.height).y;
249+
250+ panel.indicators.show();
251+ tryCompare(panel.indicators.showAnimation, "running", false);
252+ tryCompare(panel.indicators, "unitProgress", 1);
253+
254+
255+ touchFlick(panel.indicators,
256+ startXPosition, startYPosition,
257+ startXPosition, 0,
258+ true /* beginTouch */, false /* endTouch */, units.gu(5), 15);
318
259
319− // 1) Drag the mouse down to hint a bit
260+ // Indicators height should follow the drag, and therefore increase accordingly.
320− touchFlick(panel,
261+ // They should be at least half-way through the screen
321− indicatorItemCoordFirst.x, panel.indicators.panelHeight / 2,
262+ tryCompareFunction(
322− indicatorItemCoordFirst.x, panel.indicators.panelHeight * 2,
263+ function() {return panel.indicators.height <= panel.height * 0.5},
323− false /* beginTouch */, false /* endTouch */);
264+ true);
324
265
325− tryCompare(indicatorRow, "currentItem", indicatorItemFirst)
266+ touchRelease(panel.indicators, startXPosition, 0);
326
267
327− // 1) Flick mouse down to bottom
268+ tryCompare(panel.indicators.hideAnimation, "running", true);
328− touchFlick(panel,
269+ tryCompare(panel.indicators.hideAnimation, "running", false);
329− indicatorItemCoordFirst.x, panel.indicators.panelHeight * 2,
270+ tryCompare(panel.indicators, "state", "initial");
330− indicatorItemCoordNext.x, panel.height,
271+ }
331− false /* beginTouch */, true /* endTouch */,
272+
332− units.gu(10) /* speed */, 30 /* iterations */); // more samples needed for accurate velocity
273+ function test_hint_data() {
274+ return [
275+ { tag: "normal", fullscreen: false, call: null, hintExpected: true},
276+ { tag: "fullscreen", fullscreen: true, call: null, hintExpected: false},
277+ { tag: "call hint", fullscreen: false, call: phoneCall, hintExpected: false},
278+ ];
279+ }
280+
281+ function test_hint(data) {
282+ panel.fullscreenMode = data.fullscreen;
283+ callManager.foregroundCall = data.call;
284+
285+ if (data.fullscreen) {
286+ // Wait for the indicators to get into position.
287+ // (switches between normal and fullscreen modes are animated)
288+ var indicatorArea = findChild(panel, "indicatorArea");
289+ tryCompare(indicatorArea, "y", -panel.panelHeight);
290+ }
291+
292+ var indicatorItem = get_indicator_item(0);
293+ var mappedPosition = root.mapFromItem(indicatorItem, indicatorItem.width / 2, indicatorItem.height / 2);
294+
295+ touchPress(panel, mappedPosition.x, panel.indicators.minimizedPanelHeight / 2);
296+
297+ // Give some time for a hint animation to change things, if any
298+ wait(500);
299+
300+ // no hint animation when fullscreen
301+ compare(panel.indicators.fullyClosed, !data.hintExpected, "Indicator should be fully closed");
302+ compare(panel.indicators.partiallyOpened, data.hintExpected, "Indicator should be partialy opened");
303+ compare(panel.indicators.fullyOpened, false, "Indicator should not be fully opened");
333
304
334− compare(indicatorRow.currentItem, indicatorItemFirst, "First indicator should still be the current item");
305+ touchRelease(panel, mappedPosition.x, panel.minimizedPanelHeight / 2);
335 }
306 }
336 }
307 }
337}
308}
tests/qmltests/Panel/tst_SearchIndicator.qml−1
@@ -16,7 +16,6 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19−import ".."
20import "../../../qml/Panel"
19import "../../../qml/Panel"
21import Ubuntu.Components 0.1
20import Ubuntu.Components 0.1
22import Unity.Test 0.1 as UT
21import Unity.Test 0.1 as UT
tests/qmltests/tst_Shell.qml+2−2
@@ -433,7 +433,7 @@ Item {
433 var touchX = shell.width / 2;
433 var touchX = shell.width / 2;
434 var indicators = findChild(shell, "indicators");
434 var indicators = findChild(shell, "indicators");
435 touchFlick(indicators,
435 touchFlick(indicators,
436− touchX /* fromX */, indicators.panelHeight * 0.5 /* fromY */,
436+ touchX /* fromX */, indicators.minimizedPanelHeight * 0.5 /* fromY */,
437 touchX /* toX */, shell.height * 0.5 /* toY */,
437 touchX /* toX */, shell.height * 0.5 /* toY */,
438 true /* beginTouch */, false /* endTouch */);
438 true /* beginTouch */, false /* endTouch */);
439 verify(indicators.partiallyOpened);
439 verify(indicators.partiallyOpened);
@@ -470,7 +470,7 @@ Item {
470 var indicators = findChild(shell, "indicators");
470 var indicators = findChild(shell, "indicators");
471
471
472 var touchStartX = shell.width / 2;
472 var touchStartX = shell.width / 2;
473− var touchStartY = shell.height - (indicators.panelHeight * 0.5);
473+ var touchStartY = shell.height - (indicators.minimizedPanelHeight * 0.5);
474 touchFlick(shell,
474 touchFlick(shell,
475 touchStartX, touchStartY,
475 touchStartX, touchStartY,
476 touchStartX, shell.height * 0.1);
476 touchStartX, shell.height * 0.1);