on xenial help sign in
Support alternative navigation in the dash.

This adds a second drop-down in the dash for secondary navigation options. Approved by: Andrea Cimitan

commitf5393468f67eec8d7061d07d00f22ba5a664b2b3 authorMichał Sawicz <michal.sawicz@canonical.com> dateWed 27 Aug 2014, 15:35 (12 years ago) parentdb4d213e6424c2
12 files changed+499−222
qml/Components/Background.qml+4 qml/Dash/DashNavigation.qml+62−159 qml/Dash/DashNavigationButton.qml+226 qml/Dash/DashNavigationList.qml+19−3 qml/Dash/GenericScopeView.qml+1−2 qml/Dash/PageHeader.qml+16−7 qml/Dash/graphics/navigation_shadow@18.png qml/Dash/graphics/navigation_shadow_light@18.png tests/mocks/Unity/fake_navigation.cpp+6−1 tests/mocks/Unity/fake_scope.cpp+13−2 tests/qmltests/Dash/artwork/background.png tests/qmltests/Dash/tst_DashContent.qml+152−48
qml/Components/Background.qml+4
@@ -15,6 +15,7 @@
1515 */
1616
1717import QtQuick 2.0
18+import Ubuntu.Components 1.1
1819import Utils 0.1 as Utils
1920
2021Loader {
@@ -30,6 +31,9 @@ Loader {
3031 else if (parsedStyle[1] === "gradient") return Utils.Style.luminance(parsedStyle[2], parsedStyle[3]);
3132 }
3233
34+ // FIXME this is only here for highlight purposes (see DashNavigation.qml, too)
35+ readonly property color topColor: parsedStyle ? parsedStyle[2] : UbuntuColors.lightGrey
36+
3337 sourceComponent: {
3438 if (style == "") return null;
3539 if (!parsedStyle) return image;
qml/Dash/DashNavigation.qml+62−159
@@ -16,195 +16,98 @@
1616
1717import QtQuick 2.2
1818import Ubuntu.Components 1.1
19+import Dash 0.1
20+import "../Components"
1921
20−AbstractButton {
22+Item {
2123 id: root
2224 objectName: "dashNavigation"
2325
2426 property var scope: null
25−
26− property bool showList: false
27−
28− readonly property var currentNavigation: scope && scope.hasNavigation ? scope.getNavigation(scope.currentNavigationId) : null
27+ property var scopeStyle: null
2928
3029 property alias windowWidth: blackRect.width
3130 property alias windowHeight: blackRect.height
32− property var scopeStyle: null
31+ readonly property var openList: {
32+ if (navigationButton.showList) return navigationButton.listView;
33+ if (altNavigationButton.showList) return altNavigationButton.listView;
34+ return null;
35+ }
3336
34− // Are we drilling down the tree or up?
35− property bool isGoingBack: false
37+ // FIXME this is only here for highlight purposes (see Background.qml, too)
38+ readonly property var background: backgroundItem
3639
37− visible: root.currentNavigation != null
40+ visible: navigationButton.currentNavigation || altNavigationButton.currentNavigation
3841
3942 height: visible ? units.gu(5) : 0
4043
41− onClicked: {
42− navigationListView.updateMaxHeight();
43− root.showList = !root.showList;
44+ QtObject {
45+ id: d
46+ readonly property color foregroundColor: root.scopeStyle
47+ ? root.scopeStyle.getTextColor(backgroundItem.luminance)
48+ : Theme.palette.normal.baseText
49+ readonly property bool bothVisible: altNavigationButton.visible && navigationButton.visible
50+ readonly property real navigationWidth: root.width >= units.gu(60) ? units.gu(40) : root.width
51+ readonly property real buttonWidth: navigationWidth / (bothVisible ? 2 : 1)
4452 }
4553
4654 Rectangle {
4755 id: blackRect
4856 color: "black"
49− opacity: navigationListView.currentItem && navigationListView.currentItem.visible ? 0.3 : 0
57+ opacity: openList && openList.currentItem && openList.currentItem.visible ? 0.5 : 0
5058 Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
51− anchors.top: navigationListView.top
52− anchors.right: parent.right
53− visible: opacity != 0
59+ anchors { left: parent.left; right: parent.right }
60+ visible: opacity > 0
5461 }
5562
56− Image {
57− anchors {
58− top: parent.top
59− left: parent.left
60− right: parent.right
61− }
62− fillMode: Image.Stretch
63− source: "graphics/dash_divider_top_lightgrad.png"
64− z: -1
63+ Background {
64+ id: backgroundItem
65+ anchors.fill: parent
66+ style: scopeStyle ? scopeStyle.navigationBackground : "color:///#f5f5f5"
6567 }
6668
6769 Image {
68− anchors {
69− bottom: parent.bottom
70− left: parent.left
71− right: parent.right
72− }
7370 fillMode: Image.Stretch
74− source: "graphics/dash_divider_top_darkgrad.png"
75− z: -1
71+ source: scopeStyle.backgroundLuminance > 0.2 ? "graphics/navigation_shadow.png" : "graphics/navigation_shadow_light.png"
72+ anchors { top: parent.bottom; left: parent.left; right: parent.right }
7673 }
7774
78− Label {
79− anchors.fill: parent
80− anchors.margins: units.gu(2)
81− verticalAlignment: Text.AlignVCenter
82− text: root.currentNavigation ? root.currentNavigation.label : ""
83− color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
75+ DashNavigationButton {
76+ id: altNavigationButton
77+ objectName: "altNavigationButton"
78+ height: root.height
79+ width: d.buttonWidth
80+ scope: root.scope
81+ scopeStyle: root.scopeStyle
82+ foregroundColor: d.foregroundColor
83+ listView.width: d.navigationWidth
84+ isAltNavigation: true
85+ showDivider: navigationButton.visible || root.width > d.navigationWidth
86+ // needed so that InverseMouseArea is above navigationButton
87+ z: listView.height > 0 ? 1 : 0
8488 }
8589
86− Icon {
87− anchors.verticalCenter: parent.verticalCenter
88− anchors.right: parent.right
89− anchors.rightMargin: units.gu(2)
90− name: showList ? "up" : "down"
91− height: units.gu(2)
92− width: height
93− color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
90+ DashNavigationButton {
91+ id: navigationButton
92+ objectName: "navigationButton"
93+ height: root.height
94+ width: altNavigationButton.visible ? d.buttonWidth : d.navigationWidth
95+ x: altNavigationButton.visible ? d.buttonWidth : 0
96+ scope: root.scope
97+ scopeStyle: root.scopeStyle
98+ foregroundColor: d.foregroundColor
99+ listView.width: d.navigationWidth
100+ listView.x: -x
101+ showDivider: root.width > d.navigationWidth
94102 }
95103
96− // navigationListView is outside root
97− ListView {
98− id: navigationListView
99− objectName: "navigationListView"
100− orientation: ListView.Horizontal
101− interactive: false
102− clip: root.width != windowWidth
103− model: ListModel {
104− id: navigationModel
105− // We have two roles
106− // navigationId: the navigation id of the navigation the list represents
107− // nullifyNavigation: overrides navigationId to be null
108− // This is used to "clear" the delegate when going "right" on the tree
109− }
110− anchors {
111− left: parent.left
112− right: parent.right
113− top: root.bottom
114− }
115− property int maxHeight: -1
116− Component.onCompleted: updateMaxHeight();
117− function updateMaxHeight()
118− {
119− maxHeight = (windowHeight - mapToItem(null, 0, 0).y) - units.gu(8);
120− }
121− property int prevHeight: maxHeight
122− height: currentItem ? currentItem.height : maxHeight
123− onHeightChanged: {
124− if (root.showList) {
125− prevHeight = currentItem.desiredHeight;
126− }
127− }
128− highlightMoveDuration: UbuntuAnimation.FastDuration
129− delegate: DashNavigationList {
130− objectName: "navigation" + index
131− visible: height != 0
132− width: navigationListView.width
133− scopeStyle: root.scopeStyle
134− property real desiredHeight: {
135− if (root.showList) {
136− if (navigation && navigation.loaded && x == navigationListView.contentX)
137− {
138− navigationListView.updateMaxHeight();
139− return Math.min(implicitHeight, navigationListView.maxHeight);
140− } else {
141− return navigationListView.prevHeight;
142− }
143− } else {
144− return 0;
145− }
146− }
147− height: desiredHeight
148− navigation: (nullifyNavigation || !scope) ? null : scope.getNavigation(navigationId)
149− currentNavigation: root.currentNavigation
150− onEnterNavigation: {
151− scope.setNavigationState(newNavigationId, false);
152− // We only need to add a new item to the model
153− // if we have children, otherwise just load it
154− if (hasChildren) {
155− isGoingBack = false;
156− navigationModel.append({"navigationId": newNavigationId, "nullifyNavigation": false});
157− navigationListView.currentIndex++;
158− } else {
159− showList = false;
160− }
161− }
162− onGoBackToParentClicked: {
163− scope.setNavigationState(navigation.parentNavigationId, false);
164− isGoingBack = true;
165− navigationModel.setProperty(navigationListView.currentIndex - 1, "nullifyNavigation", false);
166− navigationListView.currentIndex--;
167− }
168− onAllNavigationClicked: {
169− showList = false;
170− if (root.currentNavigation.parentNavigationId == navigation.navigationId) {
171− // For leaves we have to go to the parent too
172− scope.setNavigationState(root.currentNavigation.parentNavigationId, false);
173− }
174− }
175− }
176− onContentXChanged: {
177− if (contentX == width * navigationListView.currentIndex) {
178− if (isGoingBack) {
179− navigationModel.remove(navigationListView.currentIndex + 1);
180− } else {
181− navigationModel.setProperty(navigationListView.currentIndex - 1, "nullifyNavigation", true);
182− }
183− }
184− }
185− }
186−
187− InverseMouseArea {
188− anchors.fill: navigationListView
189− enabled: root.showList
190− onClicked: root.showList = false
191− }
192−
193− onScopeChanged: {
194− navigationModel.clear();
195− if (scope && scope.hasNavigation) {
196− navigationModel.append({"navigationId": scope.currentNavigationId, "nullifyNavigation": false});
197− }
198− }
199−
200− Connections {
201− target: scope
202− onHasNavigationChanged: {
203− if (scope.hasNavigation) {
204− navigationModel.append({"navigationId": scope.currentNavigationId, "nullifyNavigation": false});
205− } else {
206− navigationModel.clear();
207− }
208− }
104+ Image {
105+ fillMode: Image.Stretch
106+ source: backgroundItem.luminance > 0.7 ? "graphics/navigation_shadow.png" : "graphics/navigation_shadow_light.png"
107+ x: navigationButton.listView.height > 0 ? altNavigationButton.x : navigationButton.x
108+ width: d.buttonWidth
109+ rotation: 180
110+ anchors.bottom: parent.bottom
111+ visible: d.bothVisible && (navigationButton.listView.height > 0 || altNavigationButton.listView.height > 0)
209112 }
210113}
qml/Dash/DashNavigationButton.qmlnew file+226
@@ -0,0 +1,226 @@
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+AbstractButton {
21+ id: root
22+ objectName: "dashNavigation"
23+
24+ // Set by parent
25+ property var scope: null
26+ property var scopeStyle: null
27+ property color foregroundColor: Theme.palette.normal.baseText
28+ property bool isAltNavigation: false
29+ property bool showDivider: false
30+
31+ // Used by parent
32+ readonly property var currentNavigation: scope && scope[hasNavigation] ? getNavigation(scope[currentNavigationId]) : null
33+ readonly property alias listView: navigationListView
34+ property bool showList: false
35+
36+ // Internal
37+ // Are we drilling down the tree or up?
38+ property bool isGoingBack: false
39+ readonly property string hasNavigation: isAltNavigation ? "hasAltNavigation" : "hasNavigation"
40+ readonly property string currentNavigationId: isAltNavigation ? "currentAltNavigationId" : "currentNavigationId"
41+ function getNavigation(navId) {
42+ if (isAltNavigation) {
43+ return scope.getAltNavigation(navId);
44+ } else {
45+ return scope.getNavigation(navId);
46+ }
47+ }
48+
49+ visible: root.currentNavigation != null
50+
51+ onClicked: {
52+ navigationListView.updateMaxHeight();
53+ root.showList = !root.showList;
54+ }
55+
56+ Label {
57+ anchors.fill: parent
58+ anchors.margins: units.gu(2)
59+ anchors.rightMargin: units.gu(5)
60+ verticalAlignment: Text.AlignVCenter
61+ text: root.currentNavigation ? root.currentNavigation.label : ""
62+ color: root.foregroundColor
63+ elide: Text.ElideRight
64+ maximumLineCount: 1
65+ }
66+
67+ Icon {
68+ anchors.verticalCenter: parent.verticalCenter
69+ anchors.right: parent.right
70+ anchors.rightMargin: units.gu(2)
71+ name: showList ? "up" : "down"
72+ height: units.gu(2)
73+ width: height
74+ color: root.foregroundColor
75+ }
76+
77+ // navigationListView is outside root
78+ ListView {
79+ id: navigationListView
80+ objectName: "navigationListView"
81+ visible: height > 0
82+ orientation: ListView.Horizontal
83+ interactive: false
84+ clip: root.width != windowWidth
85+ model: ListModel {
86+ id: navigationModel
87+ // We have two roles
88+ // navigationId: the navigation id of the navigation the list represents
89+ // nullifyNavigation: overrides navigationId to be null
90+ // This is used to "clear" the delegate when going "right" on the tree
91+ }
92+ anchors.top: root.bottom
93+ property int maxHeight: -1
94+ Component.onCompleted: updateMaxHeight();
95+ function updateMaxHeight()
96+ {
97+ maxHeight = (windowHeight - mapToItem(null, 0, 0).y) - units.gu(8);
98+ }
99+ property int prevHeight: maxHeight
100+ height: currentItem ? currentItem.height : maxHeight
101+ onHeightChanged: {
102+ if (root.showList) {
103+ prevHeight = currentItem.desiredHeight;
104+ }
105+ }
106+ highlightMoveDuration: UbuntuAnimation.FastDuration
107+ delegate: DashNavigationList {
108+ objectName: "navigation" + index
109+ visible: height > 0
110+ width: navigationListView.width
111+ scopeStyle: root.scopeStyle
112+ foregroundColor: root.foregroundColor
113+ property real desiredHeight: {
114+ if (root.showList) {
115+ if (navigation && navigation.loaded && x == navigationListView.contentX)
116+ {
117+ navigationListView.updateMaxHeight();
118+ return Math.min(implicitHeight, navigationListView.maxHeight);
119+ } else {
120+ return navigationListView.prevHeight;
121+ }
122+ } else {
123+ return 0;
124+ }
125+ }
126+ height: desiredHeight
127+ navigation: (nullifyNavigation || !scope) ? null : getNavigation(navigationId)
128+ currentNavigation: root.currentNavigation
129+ onEnterNavigation: {
130+ scope.setNavigationState(newNavigationId, isAltNavigation);
131+ // We only need to add a new item to the model
132+ // if we have children, otherwise just load it
133+ if (hasChildren) {
134+ isGoingBack = false;
135+ navigationModel.append({"navigationId": newNavigationId, "nullifyNavigation": false});
136+ navigationListView.currentIndex++;
137+ } else {
138+ showList = false;
139+ }
140+ }
141+ onGoBackToParentClicked: {
142+ scope.setNavigationState(navigation.parentNavigationId, isAltNavigation);
143+ isGoingBack = true;
144+ navigationModel.setProperty(navigationListView.currentIndex - 1, "nullifyNavigation", false);
145+ navigationListView.currentIndex--;
146+ }
147+ onAllNavigationClicked: {
148+ showList = false;
149+ if (root.currentNavigation.parentNavigationId == navigation.navigationId) {
150+ // For leaves we have to go to the parent too
151+ scope.setNavigationState(root.currentNavigation.parentNavigationId, isAltNavigation);
152+ }
153+ }
154+ }
155+ onContentXChanged: {
156+ if (contentX == width * navigationListView.currentIndex) {
157+ if (isGoingBack) {
158+ navigationModel.remove(navigationListView.currentIndex + 1);
159+ } else {
160+ navigationModel.setProperty(navigationListView.currentIndex - 1, "nullifyNavigation", true);
161+ }
162+ }
163+ }
164+ }
165+
166+ Image {
167+ anchors {
168+ top: navigationListView.bottom
169+ left: navigationListView.left
170+ right: navigationListView.right
171+ }
172+ fillMode: Image.Stretch
173+ source: "graphics/navigation_shadow.png"
174+ visible: root.showList
175+ }
176+
177+ onScopeChanged: {
178+ navigationModel.clear();
179+ if (scope && scope[hasNavigation]) {
180+ navigationModel.append({"navigationId": scope[currentNavigationId], "nullifyNavigation": false});
181+ }
182+ }
183+
184+ Connections {
185+ target: scope
186+ // This is duplicated since we can't have something based on the dynamic hasNavigation string property
187+ onHasNavigationChanged: {
188+ if (!root.isAltNavigation) {
189+ if (scope.hasNavigation) {
190+ navigationModel.append({"navigationId": scope.currentNavigationId, "nullifyNavigation": false});
191+ } else {
192+ navigationModel.clear();
193+ }
194+ }
195+ }
196+ onHasAltNavigationChanged: {
197+ if (root.isAltNavigation) {
198+ if (scope.hasAltNavigation) {
199+ navigationModel.append({"navigationId": scope.currentAltNavigationId, "nullifyNavigation": false});
200+ } else {
201+ navigationModel.clear();
202+ }
203+ }
204+ }
205+ }
206+
207+ InverseMouseArea {
208+ anchors.fill: navigationListView
209+ enabled: root.showList
210+ onPressed: root.showList = false
211+ }
212+
213+ Rectangle {
214+ visible: root.showDivider
215+ anchors {
216+ top: parent.top
217+ topMargin: units.dp(1)
218+ bottom: parent.bottom
219+ left: parent.right
220+ leftMargin: -units.dp(0.5)
221+ }
222+ width: units.dp(1)
223+ color: root.foregroundColor
224+ opacity: 0.2
225+ }
226+}
qml/Dash/DashNavigationList.qml+19−3
@@ -23,16 +23,16 @@ Item {
2323 property var navigation: null
2424 property var currentNavigation: null
2525 property var scopeStyle: null
26+ property color foregroundColor: Theme.palette.normal.baseText
2627 signal enterNavigation(var newNavigationId, bool hasChildren)
2728 signal goBackToParentClicked()
2829 signal allNavigationClicked()
2930
3031 readonly property int itemHeight: units.gu(5)
31− readonly property color foregroundColor: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
3232 implicitHeight: flickable.contentHeight
3333
3434 Background {
35− style: root.scopeStyle ? root.scopeStyle.navigationBackground : "color://white"
35+ style: root.scopeStyle ? root.scopeStyle.navigationBackground : "color:///#f5f5f5"
3636 anchors.fill: parent
3737 }
3838
@@ -87,10 +87,15 @@ Item {
8787 anchors {
8888 verticalCenter: parent.verticalCenter
8989 left: backImage.right
90+ right: parent.right
9091 leftMargin: units.gu(0.5)
92+ rightMargin: units.gu(2)
9193 }
9294 text: navigation ? navigation.parentLabel : ""
9395 color: root.foregroundColor
96+ wrapMode: Text.Wrap
97+ maximumLineCount: 2
98+ elide: Text.ElideMiddle
9499 }
95100
96101 Rectangle {
@@ -111,18 +116,23 @@ Item {
111116 id: allButton
112117 objectName: "allButton"
113118 width: parent.width
114− visible: navigation && (!navigation.isRoot || (root.currentNavigation && !root.currentNavigation.isRoot && root.currentNavigation.parentNavigationId == navigation.navigationId)) || false
119+ visible: navigation && (!navigation.isRoot || (!navigation.hidden && root.currentNavigation && !root.currentNavigation.isRoot && root.currentNavigation.parentNavigationId == navigation.navigationId)) || false
115120 height: itemHeight
116121
117122 Label {
118123 anchors {
119124 verticalCenter: parent.verticalCenter
120125 left: parent.left
126+ right: parent.right
121127 leftMargin: units.gu(2)
128+ rightMargin: units.gu(2)
122129 }
123130 text: navigation ? (navigation.allLabel != "" ? navigation.allLabel : navigation.label) : ""
124131 font.bold: true
125132 color: root.foregroundColor
133+ wrapMode: Text.Wrap
134+ maximumLineCount: 2
135+ elide: Text.ElideMiddle
126136 }
127137
128138 Rectangle {
@@ -156,12 +166,18 @@ Item {
156166 verticalCenter: parent.verticalCenter
157167 left: parent.left
158168 leftMargin: units.gu(2)
169+ right: rightIcon.visible ? rightIcon.left : parent.right
170+ rightMargin: rightIcon.visible ? units.gu(0.5) : units.gu(2)
159171 }
160172 text: label
161173 color: root.foregroundColor
174+ wrapMode: Text.Wrap
175+ maximumLineCount: 2
176+ elide: Text.ElideMiddle
162177 }
163178
164179 Icon {
180+ id: rightIcon
165181 anchors {
166182 verticalCenter: parent.verticalCenter
167183 right: parent.right
qml/Dash/GenericScopeView.qml+1−2
@@ -434,8 +434,7 @@ FocusScope {
434434
435435 bottomItem: DashNavigation {
436436 scope: scopeView.scope
437− width: parent.width <= units.gu(60) ? parent.width : units.gu(40)
438− anchors.right: parent.right
437+ anchors { left: parent.left; right: parent.right }
439438 windowHeight: scopeView.height
440439 windowWidth: scopeView.width
441440 scopeStyle: scopeView.scopeStyle
qml/Dash/PageHeader.qml+16−7
@@ -40,9 +40,6 @@ Item {
4040 property int paginationCount: 0
4141 property int paginationIndex: -1
4242
43− // TODO We should use foreground for the icons
44− // of the toolbar but unfortunately Action does not have
45− // the keyColor property as Icon does :-/
4643 property var scopeStyle: null
4744
4845 signal backClicked()
@@ -369,18 +366,30 @@ Item {
369366 }
370367
371368 // FIXME this doesn't work with solid scope backgrounds due to z-ordering
372− Rectangle {
369+ Item {
370+ id: bottomHighlight
373371 visible: bottomBorder.visible
374372 anchors {
375373 top: bottomContainer.top
376374 left: parent.left
377375 right: parent.right
378376 }
377+ z: 1
379378 height: units.dp(1)
380379 opacity: 0.6
381− color: scopeStyle ?
382− Qt.lighter(Qt.rgba(scopeStyle.background.r, scopeStyle.background.g, scopeStyle.background.b, 1.0), 1.2) :
383− "#CCFFFFFF"
380+
381+ // FIXME this should be a shader when bottomItem exists
382+ // to support image backgrounds
383+ Rectangle {
384+ anchors.fill: parent
385+ color: if (bottomItem && bottomItem.background) {
386+ Qt.lighter(Qt.rgba(bottomItem.background.topColor.r,
387+ bottomItem.background.topColor.g,
388+ bottomItem.background.topColor.b, 1.0), 1.2);
389+ } else if (!bottomItem && scopeStyle) {
390+ Qt.lighter(Qt.rgba(scopeStyle.background.r, scopeStyle.background.g, scopeStyle.background.b, 1.0), 1.2);
391+ } else "#CCFFFFFF"
392+ }
384393 }
385394
386395 Item {
qml/Dash/graphics/navigation_shadow@18.pngnew file

Binary file not shown.

qml/Dash/graphics/navigation_shadow_light@18.pngnew file

Binary file not shown.

tests/mocks/Unity/fake_navigation.cpp+6−1
@@ -32,6 +32,7 @@ Navigation::Navigation(const QString& navigationId, const QString& label, const
3232{
3333 QTimer::singleShot(1500, this, SLOT(slotLoaded()));
3434 connect(scope, SIGNAL(currentNavigationIdChanged()), this, SLOT(slotCurrentNavigationChanged()));
35+ connect(scope, SIGNAL(currentAltNavigationIdChanged()), this, SLOT(slotCurrentNavigationChanged()));
3536}
3637
3738QString Navigation::navigationId() const
@@ -106,7 +107,11 @@ QVariant Navigation::data(const QModelIndex &index, int role) const
106107 case RoleHasChildren:
107108 return m_navigationId == "root" && index.row() != 3;
108109 case RoleIsActive:
109− return m_scope->currentNavigationId() == data(index, RoleNavigationId);
110+ if (m_navigationId.startsWith("alt")) {
111+ return m_scope->currentAltNavigationId() == data(index, RoleNavigationId);
112+ } else {
113+ return m_scope->currentNavigationId() == data(index, RoleNavigationId);
114+ }
110115 }
111116 return QVariant();
112117}
tests/mocks/Unity/fake_scope.cpp+13−2
@@ -224,12 +224,18 @@ QVariantMap Scope::customizations() const
224224 m["background-color"] = "red";
225225 m["foreground-color"] = "blue";
226226 m["page-header"] = h;
227+ } else if (m_id == "MockScope4") {
228+ h["navigation-background"] = QUrl("../../../tests/qmltests/Dash/artwork/background.png");
229+ m["page-header"] = h;
227230 } else if (m_id == "MockScope5") {
228231 h["background"] = "gradient:///lightgrey/grey";
229232 h["logo"] = QUrl("../../../tests/qmltests/Dash/tst_PageHeader/logo-ubuntu-orange.svg");
230233 h["divider-color"] = "red";
231234 h["navigation-background"] = "color:///black";
232235 m["page-header"] = h;
236+ } else if (m_id == "MockScope6") {
237+ h["navigation-background"] = "gradient:///blue/red";
238+ m["page-header"] = h;
233239 }
234240 return m;
235241}
@@ -263,11 +269,16 @@ unity::shell::scopes::NavigationInterface* Scope::getAltNavigation(QString const
263269
264270 QString parentId;
265271 QString parentLabel;
266− if (id.startsWith("altmiddle")) {
272+ if (id != "altroot") {
267273 parentId = "altroot";
268274 parentLabel = "altroot";
269275 }
270− return new Navigation(id, id, "all"+id, parentId, parentLabel, this);
276+ auto result = new Navigation(id, id, "all"+id, parentId, parentLabel, this);
277+ if (id == "altroot") {
278+ m_currentAltNavigationId = "altrootChild1";
279+ Q_EMIT currentAltNavigationIdChanged();
280+ }
281+ return result;
271282}
272283
273284void Scope::setNavigationState(const QString &navigationId, bool isAltNavigation)
tests/qmltests/Dash/artwork/background.pngnew file

Binary file not shown.

tests/qmltests/Dash/tst_DashContent.qml+152−48
@@ -200,8 +200,12 @@ Item {
200200 dashContent.setCurrentScopeAtIndex(data.index, true, false);
201201 tryCompareFunction(get_current_item_object_name, data.objectName)
202202 var scopeView = findChild(dashContent, data.objectName);
203− var pageHeader = findChild(scopeView, "innerPageHeader");
204− compare(pageHeader.title, scopesModel.getScope(data.index).name);
203+ verify(scopeView, "Could not find the scope view.");
204+ var pageHeader = findChild(scopeView, "scopePageHeader");
205+ verify(pageHeader, "Could not find the scope page header.");
206+ var innerHeader = findChild(pageHeader, "innerPageHeader");
207+ verify(innerHeader, "Could not find the scope page header.");
208+ compare(innerHeader.title, scopesModel.getScope(data.index).name);
205209 }
206210
207211 function test_is_active_data() {
@@ -266,56 +270,57 @@ Item {
266270 verify(carouselLV.tileWidth / carouselLV.tileHeight == cardTool.components["art"]["aspect-ratio"]);
267271 }
268272
269− function test_navigations() {
273+ function test_mainNavigation() {
270274 var dashContentList = findChild(dashContent, "dashContentList");
271275 tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
272276 var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
273277 tryCompare(dashNavigation, "visible", true);
274− compare(dashNavigation.showList, false);
275− waitForRendering(dashNavigation);
276− mouseClick(dashNavigation, 0, 0);
277− compare(dashNavigation.showList, true);
278+ var dashNavigationButton = findChild(dashContentList.currentItem, "navigationButton");
279+ compare(dashNavigationButton.showList, false);
280+ waitForRendering(dashNavigationButton);
281+ mouseClick(dashNavigationButton, 0, 0);
282+ compare(dashNavigationButton.showList, true);
278283
279− var navigationListView = findChild(dashNavigation, "navigationListView");
284+ var navigationListView = findChild(dashNavigationButton, "navigationListView");
280285 compare(navigationListView.count, 1);
281286 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
282287
283288 waitForRendering(navigationListView);
284289 waitForRendering(navigationListView.currentItem);
285290
286− var allButton = findChild(dashNavigation, "allButton");
291+ var allButton = findChild(dashNavigationButton, "allButton");
287292 compare(allButton.visible, false);
288293
289− var navigation = findChild(dashNavigation, "navigation0child3");
294+ var navigation = findChild(dashNavigationButton, "navigation0child3");
290295 mouseClick(navigation, 0, 0);
291− compare(dashNavigation.showList, false);
292− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle3");
296+ compare(dashNavigationButton.showList, false);
297+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle3");
293298 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
294299
295− mouseClick(dashNavigation, 0, 0);
296− compare(dashNavigation.showList, true);
300+ mouseClick(dashNavigationButton, 0, 0);
301+ compare(dashNavigationButton.showList, true);
297302 waitForRendering(navigationListView);
298303 waitForRendering(navigationListView.currentItem);
299304 compare(allButton.visible, true);
300305
301306 mouseClick(allButton, 0, 0);
302− compare(dashNavigation.showList, false);
303− tryCompare(dashNavigation.currentNavigation, "navigationId", "root");
307+ compare(dashNavigationButton.showList, false);
308+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "root");
304309 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
305310
306− mouseClick(dashNavigation, 0, 0);
307− compare(dashNavigation.showList, true);
311+ mouseClick(dashNavigationButton, 0, 0);
312+ compare(dashNavigationButton.showList, true);
308313 waitForRendering(navigationListView);
309314 waitForRendering(navigationListView.currentItem);
310315 compare(allButton.visible, false);
311316
312− navigation = findChild(dashNavigation, "navigation0child2");
317+ navigation = findChild(dashNavigationButton, "navigation0child2");
313318 mouseClick(navigation, 0, 0);
314− compare(dashNavigation.showList, true);
315− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
319+ compare(dashNavigationButton.showList, true);
320+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
316321 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
317322
318− var navigationList1 = findChild(dashNavigation, "navigation1");
323+ var navigationList1 = findChild(dashNavigationButton, "navigation1");
319324 allButton = findChild(navigationList1, "allButton");
320325 var backButton = findChild(findChild(navigationList1, "navigation1"), "backButton");
321326 compare(allButton.visible, true);
@@ -324,61 +329,160 @@ Item {
324329 tryCompare(navigationListView, "contentX", navigationList1.x);
325330 waitForRendering(navigationListView);
326331 mouseClick(allButton, 0, 0);
327− compare(dashNavigation.showList, false);
328− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
332+ compare(dashNavigationButton.showList, false);
333+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
329334 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
330335
331− mouseClick(dashNavigation, 0, 0);
332− compare(dashNavigation.showList, true);
336+ mouseClick(dashNavigationButton, 0, 0);
337+ compare(dashNavigationButton.showList, true);
333338 waitForRendering(navigationListView);
334339 waitForRendering(navigationListView.currentItem);
335340 compare(allButton.visible, true);
336341 compare(backButton.visible, true);
337342
338343 tryCompare(navigationList1.navigation, "loaded", true);
339− navigation = findChild(dashNavigation, "navigation1child2");
344+ navigation = findChild(dashNavigationButton, "navigation1child2");
340345 mouseClick(navigation, 0, 0);
341− compare(dashNavigation.showList, false);
342− tryCompare(dashNavigation.currentNavigation, "navigationId", "childmiddle22");
346+ compare(dashNavigationButton.showList, false);
347+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "childmiddle22");
343348 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
344349
345− mouseClick(dashNavigation, 0, 0);
346− compare(dashNavigation.showList, true);
350+ mouseClick(dashNavigationButton, 0, 0);
351+ compare(dashNavigationButton.showList, true);
347352 waitForRendering(navigationListView);
348353 waitForRendering(navigationListView.currentItem);
349354
350355 tryCompare(navigationList1.navigation, "loaded", true);
351− navigation = findChild(dashNavigation, "navigation1child3");
356+ navigation = findChild(dashNavigationButton, "navigation1child3");
352357 mouseClick(navigation, 0, 0);
353− compare(dashNavigation.showList, false);
354− tryCompare(dashNavigation.currentNavigation, "navigationId", "childmiddle23");
358+ compare(dashNavigationButton.showList, false);
359+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "childmiddle23");
355360 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
356361
357− mouseClick(dashNavigation, 0, 0);
358− compare(dashNavigation.showList, true);
362+ mouseClick(dashNavigationButton, 0, 0);
363+ compare(dashNavigationButton.showList, true);
359364 waitForRendering(navigationListView);
360365 waitForRendering(navigationListView.currentItem);
361366 mouseClick(backButton, 0, 0);
362367
363− tryCompare(dashNavigation.currentNavigation, "navigationId", "root");
368+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "root");
364369 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
365− compare(dashNavigation.showList, true);
366− mouseClick(dashNavigation, 0, 0);
367− compare(dashNavigation.showList, false);
370+ compare(dashNavigationButton.showList, true);
371+ mouseClick(dashNavigationButton, 0, 0);
372+ compare(dashNavigationButton.showList, false);
368373
369− mouseClick(dashNavigation, 0, 0);
370− compare(dashNavigation.showList, true);
374+ mouseClick(dashNavigationButton, 0, 0);
375+ compare(dashNavigationButton.showList, true);
371376 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
372− navigation = findChild(dashNavigation, "navigation0child2");
377+ navigation = findChild(dashNavigationButton, "navigation0child2");
373378 mouseClick(navigation, 0, 0);
374− compare(dashNavigation.showList, true);
375− navigationList1 = findChild(dashNavigation, "navigation1");
379+ compare(dashNavigationButton.showList, true);
380+ navigationList1 = findChild(dashNavigationButton, "navigation1");
376381 compare(navigationList1.navigation.loaded, false);
377− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
382+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
378383 allButton = findChild(navigationList1, "allButton");
379− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
384+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
380385 mouseClick(allButton, 0, 0);
381− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
386+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
387+ }
388+
389+ function test_altNavigation() {
390+ var dashContentList = findChild(dashContent, "dashContentList");
391+ tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
392+ var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
393+ tryCompare(dashNavigation, "visible", true);
394+ var dashNavigationButton = findChild(dashContentList.currentItem, "altNavigationButton");
395+ verify(dashNavigationButton, "Can't find navigation button");
396+
397+ compare(dashNavigationButton.showList, false);
398+ waitForRendering(dashNavigationButton);
399+ mouseClick(dashNavigationButton, 0, 0);
400+ compare(dashNavigationButton.showList, true);
401+
402+ var navigationListView = findChild(dashNavigationButton, "navigationListView");
403+ compare(navigationListView.count, 1);
404+ tryCompare(navigationListView.currentItem.navigation, "loaded", true);
405+
406+ waitForRendering(navigationListView);
407+ waitForRendering(navigationListView.currentItem);
408+
409+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "altrootChild1");
410+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "altroot");
411+
412+ var allButton = findChild(dashNavigationButton, "allButton");
413+ compare(allButton.visible, false);
414+ var backButton = findChild(navigationListView, "backButton");
415+ compare(backButton.visible, false);
416+
417+ var navigation = findChild(dashNavigationButton, "navigation0child3");
418+ mouseClick(navigation, 0, 0);
419+ compare(dashNavigationButton.showList, false);
420+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "altrootChild3");
421+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "altroot");
422+
423+ mouseClick(dashNavigationButton, 0, 0);
424+ compare(dashNavigationButton.showList, true);
425+ waitForRendering(navigationListView);
426+ waitForRendering(navigationListView.currentItem);
427+ compare(allButton.visible, false);
428+
429+ navigation = findChild(dashNavigationButton, "navigation0child2");
430+ mouseClick(navigation, 0, 0);
431+ compare(dashNavigationButton.showList, false);
432+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "altrootChild2");
433+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "altroot");
434+
435+ mouseClick(dashNavigationButton, 0, 0);
436+ compare(dashNavigationButton.showList, true);
437+ waitForRendering(navigationListView);
438+ waitForRendering(navigationListView.currentItem);
439+ verify(!backButton.visible, "Back button should not be visible");
440+
441+ compare(navigationListView.count, 1, "There should be no second-level navigation");
442+
443+ mouseClick(dashNavigationButton, 0, 0);
444+ compare(dashNavigationButton.showList, false);
445+ }
446+
447+ function test_navigationsClosing() {
448+ var dashContentList = findChild(dashContent, "dashContentList");
449+ tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
450+ var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
451+ tryCompare(dashNavigation, "visible", true);
452+ var dashNavigationButton = findChild(dashContentList.currentItem, "navigationButton");
453+ verify(dashNavigationButton, "Can't find navigation button");
454+ var dashAltNavigationButton = findChild(dashContentList.currentItem, "altNavigationButton");
455+ verify(dashAltNavigationButton, "Can't find alternative navigation button");
456+
457+ waitForRendering(dashNavigationButton);
458+ waitForRendering(dashAltNavigationButton);
459+
460+ compare(dashNavigationButton.showList, false);
461+ compare(dashAltNavigationButton.showList, false);
462+
463+ mouseClick(dashNavigation, 0, 0);
464+ compare(dashNavigationButton.showList, false);
465+ compare(dashAltNavigationButton.showList, true);
466+
467+ var navigationListView = findChild(dashAltNavigationButton, "navigationListView");
468+ tryCompare(navigationListView.currentItem.navigation, "loaded", true);
469+
470+ mouseClick(dashNavigation, dashNavigationButton.x, 0);
471+ compare(dashNavigationButton.showList, false);
472+ compare(dashAltNavigationButton.showList, false);
473+
474+ tryCompare(navigationListView, "visible", false);
475+
476+ mouseClick(dashNavigation, dashNavigationButton.x, 0);
477+ compare(dashNavigationButton.showList, true);
478+ compare(dashAltNavigationButton.showList, false);
479+
480+ navigationListView = findChild(dashNavigationButton, "navigationListView");
481+ tryCompare(navigationListView.currentItem.navigation, "loaded", true);
482+
483+ mouseClick(dashNavigation, 0, 0);
484+ compare(dashNavigationButton.showList, false);
485+ compare(dashAltNavigationButton.showList, false);
382486 }
383487
384488 function test_searchHint() {