on xenial help sign in
Support alternative navigation in the dash.
f539346Michał Sawicz
qml/Components/Background.qml+4
@@ -15,6 +15,7 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18+import Ubuntu.Components 1.1
18import Utils 0.1 as Utils
19import Utils 0.1 as Utils
19
20
20Loader {
21Loader {
@@ -30,6 +31,9 @@ Loader {
30 else if (parsedStyle[1] === "gradient") return Utils.Style.luminance(parsedStyle[2], parsedStyle[3]);
31 else if (parsedStyle[1] === "gradient") return Utils.Style.luminance(parsedStyle[2], parsedStyle[3]);
31 }
32 }
32
33
34+ // FIXME this is only here for highlight purposes (see DashNavigation.qml, too)
35+ readonly property color topColor: parsedStyle ? parsedStyle[2] : UbuntuColors.lightGrey
36+
33 sourceComponent: {
37 sourceComponent: {
34 if (style == "") return null;
38 if (style == "") return null;
35 if (!parsedStyle) return image;
39 if (!parsedStyle) return image;
qml/Dash/DashNavigation.qml+62−159
@@ -16,195 +16,98 @@
16
16
17import QtQuick 2.2
17import QtQuick 2.2
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19+import Dash 0.1
20+import "../Components"
19
21
20−AbstractButton {
22+Item {
21 id: root
23 id: root
22 objectName: "dashNavigation"
24 objectName: "dashNavigation"
23
25
24 property var scope: null
26 property var scope: null
25−
27+ property var scopeStyle: null
26− property bool showList: false
27−
28− readonly property var currentNavigation: scope && scope.hasNavigation ? scope.getNavigation(scope.currentNavigationId) : null
29
28
30 property alias windowWidth: blackRect.width
29 property alias windowWidth: blackRect.width
31 property alias windowHeight: blackRect.height
30 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+ }
33
36
34− // Are we drilling down the tree or up?
37+ // FIXME this is only here for highlight purposes (see Background.qml, too)
35− property bool isGoingBack: false
38+ readonly property var background: backgroundItem
36
39
37− visible: root.currentNavigation != null
40+ visible: navigationButton.currentNavigation || altNavigationButton.currentNavigation
38
41
39 height: visible ? units.gu(5) : 0
42 height: visible ? units.gu(5) : 0
40
43
41− onClicked: {
44+ QtObject {
42− navigationListView.updateMaxHeight();
45+ id: d
43− root.showList = !root.showList;
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)
44 }
52 }
45
53
46 Rectangle {
54 Rectangle {
47 id: blackRect
55 id: blackRect
48 color: "black"
56 color: "black"
49− opacity: navigationListView.currentItem && navigationListView.currentItem.visible ? 0.3 : 0
57+ opacity: openList && openList.currentItem && openList.currentItem.visible ? 0.5 : 0
50 Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
58 Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
51− anchors.top: navigationListView.top
52− anchors.right: parent.right
59+ anchors { left: parent.left; right: parent.right }
53− visible: opacity != 0
60+ visible: opacity > 0
54 }
61 }
55
62
56− Image {
63+ Background {
64+ id: backgroundItem
57− anchors {
65+ anchors.fill: parent
58− top: parent.top
66+ style: scopeStyle ? scopeStyle.navigationBackground : "color:///#f5f5f5"
59− left: parent.left
60− right: parent.right
61− }
62− fillMode: Image.Stretch
63− source: "graphics/dash_divider_top_lightgrad.png"
64− z: -1
65 }
67 }
66
68
67 Image {
69 Image {
68− anchors {
69− bottom: parent.bottom
70− left: parent.left
71− right: parent.right
72− }
73 fillMode: Image.Stretch
70 fillMode: Image.Stretch
74− source: "graphics/dash_divider_top_darkgrad.png"
71+ source: scopeStyle.backgroundLuminance > 0.2 ? "graphics/navigation_shadow.png" : "graphics/navigation_shadow_light.png"
75− z: -1
72+ anchors { top: parent.bottom; left: parent.left; right: parent.right }
76 }
73 }
77
74
78− Label {
75+ DashNavigationButton {
79− anchors.fill: parent
76+ id: altNavigationButton
80− anchors.margins: units.gu(2)
77+ objectName: "altNavigationButton"
81− verticalAlignment: Text.AlignVCenter
78+ height: root.height
82− text: root.currentNavigation ? root.currentNavigation.label : ""
79+ width: d.buttonWidth
83− color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
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
84 }
88 }
85
89
86− Icon {
90+ DashNavigationButton {
87− anchors.verticalCenter: parent.verticalCenter
91+ id: navigationButton
88− anchors.right: parent.right
92+ objectName: "navigationButton"
89− anchors.rightMargin: units.gu(2)
93+ height: root.height
90− name: showList ? "up" : "down"
94+ width: altNavigationButton.visible ? d.buttonWidth : d.navigationWidth
91− height: units.gu(2)
95+ x: altNavigationButton.visible ? d.buttonWidth : 0
92− width: height
96+ scope: root.scope
93− color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
97+ scopeStyle: root.scopeStyle
98+ foregroundColor: d.foregroundColor
99+ listView.width: d.navigationWidth
100+ listView.x: -x
101+ showDivider: root.width > d.navigationWidth
94 }
102 }
95
103
96− // navigationListView is outside root
104+ Image {
97− ListView {
105+ fillMode: Image.Stretch
98− id: navigationListView
106+ source: backgroundItem.luminance > 0.7 ? "graphics/navigation_shadow.png" : "graphics/navigation_shadow_light.png"
99− objectName: "navigationListView"
107+ x: navigationButton.listView.height > 0 ? altNavigationButton.x : navigationButton.x
100− orientation: ListView.Horizontal
108+ width: d.buttonWidth
101− interactive: false
109+ rotation: 180
102− clip: root.width != windowWidth
110+ anchors.bottom: parent.bottom
103− model: ListModel {
111+ visible: d.bothVisible && (navigationButton.listView.height > 0 || altNavigationButton.listView.height > 0)
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− }
209 }
112 }
210}
113}
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 {
23 property var navigation: null
23 property var navigation: null
24 property var currentNavigation: null
24 property var currentNavigation: null
25 property var scopeStyle: null
25 property var scopeStyle: null
26+ property color foregroundColor: Theme.palette.normal.baseText
26 signal enterNavigation(var newNavigationId, bool hasChildren)
27 signal enterNavigation(var newNavigationId, bool hasChildren)
27 signal goBackToParentClicked()
28 signal goBackToParentClicked()
28 signal allNavigationClicked()
29 signal allNavigationClicked()
29
30
30 readonly property int itemHeight: units.gu(5)
31 readonly property int itemHeight: units.gu(5)
31− readonly property color foregroundColor: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
32 implicitHeight: flickable.contentHeight
32 implicitHeight: flickable.contentHeight
33
33
34 Background {
34 Background {
35− style: root.scopeStyle ? root.scopeStyle.navigationBackground : "color://white"
35+ style: root.scopeStyle ? root.scopeStyle.navigationBackground : "color:///#f5f5f5"
36 anchors.fill: parent
36 anchors.fill: parent
37 }
37 }
38
38
@@ -87,10 +87,15 @@ Item {
87 anchors {
87 anchors {
88 verticalCenter: parent.verticalCenter
88 verticalCenter: parent.verticalCenter
89 left: backImage.right
89 left: backImage.right
90+ right: parent.right
90 leftMargin: units.gu(0.5)
91 leftMargin: units.gu(0.5)
92+ rightMargin: units.gu(2)
91 }
93 }
92 text: navigation ? navigation.parentLabel : ""
94 text: navigation ? navigation.parentLabel : ""
93 color: root.foregroundColor
95 color: root.foregroundColor
96+ wrapMode: Text.Wrap
97+ maximumLineCount: 2
98+ elide: Text.ElideMiddle
94 }
99 }
95
100
96 Rectangle {
101 Rectangle {
@@ -111,18 +116,23 @@ Item {
111 id: allButton
116 id: allButton
112 objectName: "allButton"
117 objectName: "allButton"
113 width: parent.width
118 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
115 height: itemHeight
120 height: itemHeight
116
121
117 Label {
122 Label {
118 anchors {
123 anchors {
119 verticalCenter: parent.verticalCenter
124 verticalCenter: parent.verticalCenter
120 left: parent.left
125 left: parent.left
126+ right: parent.right
121 leftMargin: units.gu(2)
127 leftMargin: units.gu(2)
128+ rightMargin: units.gu(2)
122 }
129 }
123 text: navigation ? (navigation.allLabel != "" ? navigation.allLabel : navigation.label) : ""
130 text: navigation ? (navigation.allLabel != "" ? navigation.allLabel : navigation.label) : ""
124 font.bold: true
131 font.bold: true
125 color: root.foregroundColor
132 color: root.foregroundColor
133+ wrapMode: Text.Wrap
134+ maximumLineCount: 2
135+ elide: Text.ElideMiddle
126 }
136 }
127
137
128 Rectangle {
138 Rectangle {
@@ -156,12 +166,18 @@ Item {
156 verticalCenter: parent.verticalCenter
166 verticalCenter: parent.verticalCenter
157 left: parent.left
167 left: parent.left
158 leftMargin: units.gu(2)
168 leftMargin: units.gu(2)
169+ right: rightIcon.visible ? rightIcon.left : parent.right
170+ rightMargin: rightIcon.visible ? units.gu(0.5) : units.gu(2)
159 }
171 }
160 text: label
172 text: label
161 color: root.foregroundColor
173 color: root.foregroundColor
174+ wrapMode: Text.Wrap
175+ maximumLineCount: 2
176+ elide: Text.ElideMiddle
162 }
177 }
163
178
164 Icon {
179 Icon {
180+ id: rightIcon
165 anchors {
181 anchors {
166 verticalCenter: parent.verticalCenter
182 verticalCenter: parent.verticalCenter
167 right: parent.right
183 right: parent.right
qml/Dash/GenericScopeView.qml+1−2
@@ -434,8 +434,7 @@ FocusScope {
434
434
435 bottomItem: DashNavigation {
435 bottomItem: DashNavigation {
436 scope: scopeView.scope
436 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 }
439 windowHeight: scopeView.height
438 windowHeight: scopeView.height
440 windowWidth: scopeView.width
439 windowWidth: scopeView.width
441 scopeStyle: scopeView.scopeStyle
440 scopeStyle: scopeView.scopeStyle
qml/Dash/PageHeader.qml+16−7
@@ -40,9 +40,6 @@ Item {
40 property int paginationCount: 0
40 property int paginationCount: 0
41 property int paginationIndex: -1
41 property int paginationIndex: -1
42
42
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 :-/
46 property var scopeStyle: null
43 property var scopeStyle: null
47
44
48 signal backClicked()
45 signal backClicked()
@@ -369,18 +366,30 @@ Item {
369 }
366 }
370
367
371 // FIXME this doesn't work with solid scope backgrounds due to z-ordering
368 // FIXME this doesn't work with solid scope backgrounds due to z-ordering
372− Rectangle {
369+ Item {
370+ id: bottomHighlight
373 visible: bottomBorder.visible
371 visible: bottomBorder.visible
374 anchors {
372 anchors {
375 top: bottomContainer.top
373 top: bottomContainer.top
376 left: parent.left
374 left: parent.left
377 right: parent.right
375 right: parent.right
378 }
376 }
377+ z: 1
379 height: units.dp(1)
378 height: units.dp(1)
380 opacity: 0.6
379 opacity: 0.6
381− color: scopeStyle ?
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) {
382− Qt.lighter(Qt.rgba(scopeStyle.background.r, scopeStyle.background.g, scopeStyle.background.b, 1.0), 1.2) :
390+ Qt.lighter(Qt.rgba(scopeStyle.background.r, scopeStyle.background.g, scopeStyle.background.b, 1.0), 1.2);
383− "#CCFFFFFF"
391+ } else "#CCFFFFFF"
392+ }
384 }
393 }
385
394
386 Item {
395 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
32{
32{
33 QTimer::singleShot(1500, this, SLOT(slotLoaded()));
33 QTimer::singleShot(1500, this, SLOT(slotLoaded()));
34 connect(scope, SIGNAL(currentNavigationIdChanged()), this, SLOT(slotCurrentNavigationChanged()));
34 connect(scope, SIGNAL(currentNavigationIdChanged()), this, SLOT(slotCurrentNavigationChanged()));
35+ connect(scope, SIGNAL(currentAltNavigationIdChanged()), this, SLOT(slotCurrentNavigationChanged()));
35}
36}
36
37
37QString Navigation::navigationId() const
38QString Navigation::navigationId() const
@@ -106,7 +107,11 @@ QVariant Navigation::data(const QModelIndex &index, int role) const
106 case RoleHasChildren:
107 case RoleHasChildren:
107 return m_navigationId == "root" && index.row() != 3;
108 return m_navigationId == "root" && index.row() != 3;
108 case RoleIsActive:
109 case RoleIsActive:
110+ if (m_navigationId.startsWith("alt")) {
111+ return m_scope->currentAltNavigationId() == data(index, RoleNavigationId);
112+ } else {
109− return m_scope->currentNavigationId() == data(index, RoleNavigationId);
113+ return m_scope->currentNavigationId() == data(index, RoleNavigationId);
114+ }
110 }
115 }
111 return QVariant();
116 return QVariant();
112}
117}
tests/mocks/Unity/fake_scope.cpp+13−2
@@ -224,12 +224,18 @@ QVariantMap Scope::customizations() const
224 m["background-color"] = "red";
224 m["background-color"] = "red";
225 m["foreground-color"] = "blue";
225 m["foreground-color"] = "blue";
226 m["page-header"] = h;
226 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;
227 } else if (m_id == "MockScope5") {
230 } else if (m_id == "MockScope5") {
228 h["background"] = "gradient:///lightgrey/grey";
231 h["background"] = "gradient:///lightgrey/grey";
229 h["logo"] = QUrl("../../../tests/qmltests/Dash/tst_PageHeader/logo-ubuntu-orange.svg");
232 h["logo"] = QUrl("../../../tests/qmltests/Dash/tst_PageHeader/logo-ubuntu-orange.svg");
230 h["divider-color"] = "red";
233 h["divider-color"] = "red";
231 h["navigation-background"] = "color:///black";
234 h["navigation-background"] = "color:///black";
232 m["page-header"] = h;
235 m["page-header"] = h;
236+ } else if (m_id == "MockScope6") {
237+ h["navigation-background"] = "gradient:///blue/red";
238+ m["page-header"] = h;
233 }
239 }
234 return m;
240 return m;
235}
241}
@@ -263,11 +269,16 @@ unity::shell::scopes::NavigationInterface* Scope::getAltNavigation(QString const
263
269
264 QString parentId;
270 QString parentId;
265 QString parentLabel;
271 QString parentLabel;
266− if (id.startsWith("altmiddle")) {
272+ if (id != "altroot") {
267 parentId = "altroot";
273 parentId = "altroot";
268 parentLabel = "altroot";
274 parentLabel = "altroot";
269 }
275 }
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;
271}
282}
272
283
273void Scope::setNavigationState(const QString &navigationId, bool isAltNavigation)
284void 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 {
200 dashContent.setCurrentScopeAtIndex(data.index, true, false);
200 dashContent.setCurrentScopeAtIndex(data.index, true, false);
201 tryCompareFunction(get_current_item_object_name, data.objectName)
201 tryCompareFunction(get_current_item_object_name, data.objectName)
202 var scopeView = findChild(dashContent, data.objectName);
202 var scopeView = findChild(dashContent, data.objectName);
203+ verify(scopeView, "Could not find the scope view.");
203− var pageHeader = findChild(scopeView, "innerPageHeader");
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.");
204− compare(pageHeader.title, scopesModel.getScope(data.index).name);
208+ compare(innerHeader.title, scopesModel.getScope(data.index).name);
205 }
209 }
206
210
207 function test_is_active_data() {
211 function test_is_active_data() {
@@ -266,56 +270,57 @@ Item {
266 verify(carouselLV.tileWidth / carouselLV.tileHeight == cardTool.components["art"]["aspect-ratio"]);
270 verify(carouselLV.tileWidth / carouselLV.tileHeight == cardTool.components["art"]["aspect-ratio"]);
267 }
271 }
268
272
269− function test_navigations() {
273+ function test_mainNavigation() {
270 var dashContentList = findChild(dashContent, "dashContentList");
274 var dashContentList = findChild(dashContent, "dashContentList");
271 tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
275 tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
272 var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
276 var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
273 tryCompare(dashNavigation, "visible", true);
277 tryCompare(dashNavigation, "visible", true);
278+ var dashNavigationButton = findChild(dashContentList.currentItem, "navigationButton");
274− compare(dashNavigation.showList, false);
279+ compare(dashNavigationButton.showList, false);
275− waitForRendering(dashNavigation);
280+ waitForRendering(dashNavigationButton);
276− mouseClick(dashNavigation, 0, 0);
281+ mouseClick(dashNavigationButton, 0, 0);
277− compare(dashNavigation.showList, true);
282+ compare(dashNavigationButton.showList, true);
278
283
279− var navigationListView = findChild(dashNavigation, "navigationListView");
284+ var navigationListView = findChild(dashNavigationButton, "navigationListView");
280 compare(navigationListView.count, 1);
285 compare(navigationListView.count, 1);
281 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
286 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
282
287
283 waitForRendering(navigationListView);
288 waitForRendering(navigationListView);
284 waitForRendering(navigationListView.currentItem);
289 waitForRendering(navigationListView.currentItem);
285
290
286− var allButton = findChild(dashNavigation, "allButton");
291+ var allButton = findChild(dashNavigationButton, "allButton");
287 compare(allButton.visible, false);
292 compare(allButton.visible, false);
288
293
289− var navigation = findChild(dashNavigation, "navigation0child3");
294+ var navigation = findChild(dashNavigationButton, "navigation0child3");
290 mouseClick(navigation, 0, 0);
295 mouseClick(navigation, 0, 0);
291− compare(dashNavigation.showList, false);
296+ compare(dashNavigationButton.showList, false);
292− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle3");
297+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle3");
293 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
298 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
294
299
295− mouseClick(dashNavigation, 0, 0);
300+ mouseClick(dashNavigationButton, 0, 0);
296− compare(dashNavigation.showList, true);
301+ compare(dashNavigationButton.showList, true);
297 waitForRendering(navigationListView);
302 waitForRendering(navigationListView);
298 waitForRendering(navigationListView.currentItem);
303 waitForRendering(navigationListView.currentItem);
299 compare(allButton.visible, true);
304 compare(allButton.visible, true);
300
305
301 mouseClick(allButton, 0, 0);
306 mouseClick(allButton, 0, 0);
302− compare(dashNavigation.showList, false);
307+ compare(dashNavigationButton.showList, false);
303− tryCompare(dashNavigation.currentNavigation, "navigationId", "root");
308+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "root");
304 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
309 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
305
310
306− mouseClick(dashNavigation, 0, 0);
311+ mouseClick(dashNavigationButton, 0, 0);
307− compare(dashNavigation.showList, true);
312+ compare(dashNavigationButton.showList, true);
308 waitForRendering(navigationListView);
313 waitForRendering(navigationListView);
309 waitForRendering(navigationListView.currentItem);
314 waitForRendering(navigationListView.currentItem);
310 compare(allButton.visible, false);
315 compare(allButton.visible, false);
311
316
312− navigation = findChild(dashNavigation, "navigation0child2");
317+ navigation = findChild(dashNavigationButton, "navigation0child2");
313 mouseClick(navigation, 0, 0);
318 mouseClick(navigation, 0, 0);
314− compare(dashNavigation.showList, true);
319+ compare(dashNavigationButton.showList, true);
315− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
320+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
316 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
321 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
317
322
318− var navigationList1 = findChild(dashNavigation, "navigation1");
323+ var navigationList1 = findChild(dashNavigationButton, "navigation1");
319 allButton = findChild(navigationList1, "allButton");
324 allButton = findChild(navigationList1, "allButton");
320 var backButton = findChild(findChild(navigationList1, "navigation1"), "backButton");
325 var backButton = findChild(findChild(navigationList1, "navigation1"), "backButton");
321 compare(allButton.visible, true);
326 compare(allButton.visible, true);
@@ -324,61 +329,160 @@ Item {
324 tryCompare(navigationListView, "contentX", navigationList1.x);
329 tryCompare(navigationListView, "contentX", navigationList1.x);
325 waitForRendering(navigationListView);
330 waitForRendering(navigationListView);
326 mouseClick(allButton, 0, 0);
331 mouseClick(allButton, 0, 0);
327− compare(dashNavigation.showList, false);
332+ compare(dashNavigationButton.showList, false);
328− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
333+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
329 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
334 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
330
335
331− mouseClick(dashNavigation, 0, 0);
336+ mouseClick(dashNavigationButton, 0, 0);
332− compare(dashNavigation.showList, true);
337+ compare(dashNavigationButton.showList, true);
333 waitForRendering(navigationListView);
338 waitForRendering(navigationListView);
334 waitForRendering(navigationListView.currentItem);
339 waitForRendering(navigationListView.currentItem);
335 compare(allButton.visible, true);
340 compare(allButton.visible, true);
336 compare(backButton.visible, true);
341 compare(backButton.visible, true);
337
342
338 tryCompare(navigationList1.navigation, "loaded", true);
343 tryCompare(navigationList1.navigation, "loaded", true);
339− navigation = findChild(dashNavigation, "navigation1child2");
344+ navigation = findChild(dashNavigationButton, "navigation1child2");
340 mouseClick(navigation, 0, 0);
345 mouseClick(navigation, 0, 0);
341− compare(dashNavigation.showList, false);
346+ compare(dashNavigationButton.showList, false);
342− tryCompare(dashNavigation.currentNavigation, "navigationId", "childmiddle22");
347+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "childmiddle22");
343 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
348 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
344
349
345− mouseClick(dashNavigation, 0, 0);
350+ mouseClick(dashNavigationButton, 0, 0);
346− compare(dashNavigation.showList, true);
351+ compare(dashNavigationButton.showList, true);
347 waitForRendering(navigationListView);
352 waitForRendering(navigationListView);
348 waitForRendering(navigationListView.currentItem);
353 waitForRendering(navigationListView.currentItem);
349
354
350 tryCompare(navigationList1.navigation, "loaded", true);
355 tryCompare(navigationList1.navigation, "loaded", true);
351− navigation = findChild(dashNavigation, "navigation1child3");
356+ navigation = findChild(dashNavigationButton, "navigation1child3");
352 mouseClick(navigation, 0, 0);
357 mouseClick(navigation, 0, 0);
353− compare(dashNavigation.showList, false);
358+ compare(dashNavigationButton.showList, false);
354− tryCompare(dashNavigation.currentNavigation, "navigationId", "childmiddle23");
359+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "childmiddle23");
355 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
360 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
356
361
357− mouseClick(dashNavigation, 0, 0);
362+ mouseClick(dashNavigationButton, 0, 0);
358− compare(dashNavigation.showList, true);
363+ compare(dashNavigationButton.showList, true);
359 waitForRendering(navigationListView);
364 waitForRendering(navigationListView);
360 waitForRendering(navigationListView.currentItem);
365 waitForRendering(navigationListView.currentItem);
361 mouseClick(backButton, 0, 0);
366 mouseClick(backButton, 0, 0);
362
367
363− tryCompare(dashNavigation.currentNavigation, "navigationId", "root");
368+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "root");
364 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
369 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
365− compare(dashNavigation.showList, true);
370+ compare(dashNavigationButton.showList, true);
366− mouseClick(dashNavigation, 0, 0);
371+ mouseClick(dashNavigationButton, 0, 0);
367− compare(dashNavigation.showList, false);
372+ compare(dashNavigationButton.showList, false);
368
373
369− mouseClick(dashNavigation, 0, 0);
374+ mouseClick(dashNavigationButton, 0, 0);
370− compare(dashNavigation.showList, true);
375+ compare(dashNavigationButton.showList, true);
371 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
376 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
372− navigation = findChild(dashNavigation, "navigation0child2");
377+ navigation = findChild(dashNavigationButton, "navigation0child2");
373 mouseClick(navigation, 0, 0);
378 mouseClick(navigation, 0, 0);
374− compare(dashNavigation.showList, true);
379+ compare(dashNavigationButton.showList, true);
375− navigationList1 = findChild(dashNavigation, "navigation1");
380+ navigationList1 = findChild(dashNavigationButton, "navigation1");
376 compare(navigationList1.navigation.loaded, false);
381 compare(navigationList1.navigation.loaded, false);
377− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
382+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
378 allButton = findChild(navigationList1, "allButton");
383 allButton = findChild(navigationList1, "allButton");
379− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
384+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
380 mouseClick(allButton, 0, 0);
385 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);
382 }
486 }
383
487
384 function test_searchHint() {
488 function test_searchHint() {