on xenial help sign in
Merge lp:~unity-team/unity8/scope-settings
commite6808a58395c6e29e143c6d85c1a03d9310c606e authorMichał Sawicz <michal.sawicz@canonical.com> dateTue 26 Aug 2014, 21:25 (12 years ago) parent4b6ce0419bfd15
41 files changed+1064−405
plugins/Dash/CardAttributes.qml+1 plugins/Dash/CardCreator.js+6−5 plugins/Dash/ScopeStyle.qml+38−40 plugins/Utils/Style.js+37 plugins/Utils/qmldir+1 qml/Components/Background.qml+10 qml/Dash/DashNavigation.qml+58−157 qml/Dash/DashNavigationButton.qml+226 qml/Dash/DashNavigationList.qml+5−4 qml/Dash/GenericScopeView.qml+66−12 qml/Dash/PageHeader.qml+60−21 qml/Dash/PreviewListView.qml+2 qml/Dash/Previews/PreviewActionButton.qml+1−4 qml/Dash/Previews/PreviewActionCombo.qml+4−2 qml/Dash/Previews/PreviewActions.qml+6 qml/Dash/Previews/PreviewExpandable.qml+3−2 qml/Dash/Previews/PreviewTable.qml+80 qml/Dash/Previews/PreviewWidgetFactory.qml+2−1 qml/Dash/ScopesOverview.qml+7−1 qml/Dash/graphics/PageHeaderBaseDivider@18.png qml/Dash/graphics/PageHeaderBaseDivider@18.sci−5 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+15−2 tests/plugins/Dash/cardcreator/2.res+3−3 tests/plugins/Dash/cardcreator/5.res+3−3 tests/plugins/Dash/cardcreator/6.res+4−4 tests/plugins/Dash/cardcreator/7.res+4−4 tests/plugins/Dash/tst_ScopeStyle.qml+73−68 tests/plugins/Utils/CMakeLists.txt+5 tests/plugins/Utils/tst_UtilsStyle.qml+47 tests/qmltests/CMakeLists.txt+1 tests/qmltests/Components/tst_Background.qml+5−4 tests/qmltests/Dash/Previews/tst_PreviewActions.qml+8−8 tests/qmltests/Dash/Previews/tst_PreviewExpandable.qml+5−1 tests/qmltests/Dash/Previews/tst_PreviewTable.qml+114 tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qml+1 tests/qmltests/Dash/artwork/background.png tests/qmltests/Dash/tst_DashContent.qml+146−46 tests/qmltests/Dash/tst_GenericScopeView.qml+11−7
plugins/Dash/CardAttributes.qml+1
@@ -47,6 +47,7 @@ GridLayout {
4747 height: units.gu(2)
4848 sets: ["actions", "status", "apps"]
4949 source: "icon" in modelData ? modelData["icon"] : ""
50+ color: grid.color
5051 }
5152 Label {
5253 id: label
plugins/Dash/CardCreator.js+6−5
@@ -29,7 +29,7 @@ var kBackgroundLoaderCode = 'Loader {\n\
2929 gradientColor: getColor(1) || color; \n\
3030 anchors.fill: parent; \n\
3131 image: backgroundImage.source ? backgroundImage : null; \n\
32− property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b; \n\
32+ property real luminance: Style.luminance(color); \n\
3333 property Image backgroundImage: Image { \n\
3434 objectName: "backgroundImage"; \n\
3535 source: { \n\
@@ -108,7 +108,7 @@ var kOverlayLoaderCode = 'Loader { \n\
108108 sourceComponent: ShaderEffect { \n\
109109 id: overlay; \n\
110110 height: (fixedHeaderHeight > 0 ? fixedHeaderHeight : headerHeight) + units.gu(2); \n\
111− property real luminance: 0.2126 * overlayColor.r + 0.7152 * overlayColor.g + 0.0722 * overlayColor.b; \n\
111+ property real luminance: Style.luminance(overlayColor); \n\
112112 property color overlayColor: cardData && cardData["overlayColor"] || "#99000000"; \n\
113113 property var source: ShaderEffectSource { \n\
114114 id: shaderSource; \n\
@@ -439,14 +439,14 @@ function cardString(template, components) {
439439 mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible);
440440 }
441441
442− var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText)';
442+ var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white")';
443443
444444 var hasTitleContainer = hasTitle && (hasEmblem || (hasMascot && (hasSubtitle || hasAttributes)));
445445 var titleSubtitleCode = '';
446446 if (hasTitle) {
447447 var titleColor;
448448 if (headerAsOverlay) {
449− titleColor = 'overlayLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText)';
449+ titleColor = 'root.scopeStyle ? root.scopeStyle.getTextColor(overlayLoader.item.luminance) : (overlayLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white")';
450450 } else if (hasSummary) {
451451 titleColor = 'summary.color';
452452 } else if (hasBackground) {
@@ -652,7 +652,8 @@ function cardString(template, components) {
652652function createCardComponent(parent, template, components) {
653653 var imports = 'import QtQuick 2.2; \n\
654654 import Ubuntu.Components 1.1; \n\
655− import Dash 0.1;\n';
655+ import Dash 0.1;\n\
656+ import Utils 0.1;\n';
656657 var card = cardString(template, components);
657658 var code = imports + 'Component {\n' + card + '}\n';
658659
plugins/Dash/ScopeStyle.qml+38−40
@@ -15,6 +15,8 @@
1515 */
1616
1717import QtQuick 2.2
18+import Utils 0.1
19+import Ubuntu.Components 1.1
1820
1921/*! \brief Helper for processing scope customization options.
2022
@@ -27,69 +29,65 @@ QtObject {
2729 /// Style object passed from the scope
2830 property var style: Object()
2931
30− /*! \brief Calculate luminance of the passed color
31−
32− \note If not fully opaque, luminance is dependant on blending.
33− */
34− function luminance(color) {
35− return 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
36− }
37−
3832 /// Color used for text and symbolic icons
3933 readonly property color foreground: "foreground-color" in style ? style["foreground-color"] : d.defaultDark
4034
4135 /// Luminance of the foreground color
42− readonly property real foregroundLuminance: foreground ? luminance(foreground) : d.defaultForegroundLuminance
36+ readonly property real foregroundLuminance: foreground ? Style.luminance(foreground) : Style.luminance(d.defaultDark)
4337
4438 /// Color used for the overall background
45− readonly property color background: "background-color" in style ? style["background-color"] : "transparent"
46−
47− /// Luminance of the foreground color
48− readonly property real backgroundLuminance: background ? luminance(background) : d.defaultBackgroundLuminance
49−
50− /*! \brief Luminance threshold for switching between fore and background color
51−
52− \note If background colour is not fully opaque, the defaultLightLuminance it's taken into account instead of it.
53− */
54− readonly property real threshold: background.a !== 1.0 ? (foregroundLuminance + d.defaultLightLuminance) / 2
55− : (foregroundLuminance + backgroundLuminance) / 2
56−
57− /*! \brief The lighter of foreground and background colors
58−
59− \note If background color is not fully opaque, it's not taken into account
60− and defaults to the theme-provided light color.
61− */
62− readonly property color light: {
63− if (background.a !== 1.0) return foregroundLuminance > d.defaultLightLuminance ? foreground : d.defaultLight;
64− return foregroundLuminance > backgroundLuminance ? foreground : background;
65− }
39+ readonly property color background: "background-color" in style ? style["background-color"] : "#00f5f5f5"
6640
67− /*! \brief The darker of foreground and background colors
41+ /// Luminance of the background color
42+ readonly property real backgroundLuminance: background ? Style.luminance(background) : Style.luminance(d.defaultLight)
6843
69− \note If background color is not fully opaque, it's not taken into account
70− and defaults to the theme-provided dark color.
44+ /*! \brief Get the most contrasting available color based on luminance
45+ *
46+ * If background color is transparent, theme provided colors are taken into account
7147 */
72− readonly property color dark: {
73− if (background.a !== 1.0) return foregroundLuminance < d.defaultDarkLuminance ? foreground : d.defaultDark;
74− return foregroundLuminance < backgroundLuminance ? foreground : background;
48+ function getTextColor(luminance) {
49+ if (Math.abs(foregroundLuminance - luminance) >
50+ Math.abs(d.opaqueBackgroundLuminance - luminance)) {
51+ return foreground;
52+ } else {
53+ return d.opaqueBackground;
54+ }
7555 }
7656
7757 /// Source of the logo image for the header
7858 readonly property url headerLogo: "logo" in d.headerStyle ? d.headerStyle["logo"] : ""
7959
8060 /// Background style for the header
81− readonly property url headerBackground: "background" in d.headerStyle ? d.headerStyle["background"] : ""
61+ readonly property url headerBackground: "background" in d.headerStyle ? d.headerStyle["background"] : "color:///#f5f5f5"
8262
8363 /// Foreground color for the header
8464 readonly property color headerForeground: "foreground-color" in d.headerStyle ? d.headerStyle["foreground-color"] : foreground
8565
66+ /// Color of the header divider
67+ readonly property color headerDividerColor: "divider-color" in d.headerStyle ? d.headerStyle["divider-color"] : "#e0e0e0"
68+
69+ /// Background style for the navigation
70+ readonly property url navigationBackground: "navigation-background" in d.headerStyle ? d.headerStyle["navigation-background"] : "color:///#f5f5f5"
71+
72+ /// Color of the primary preview button
73+ readonly property color previewButtonColor: "preview-button-color" in style ? style["preview-button-color"] : Theme.palette.selected.foreground
74+
8675 //! @cond
8776 property var d: QtObject {
8877 // FIXME: should be taken from the theme
8978 readonly property color defaultLight: "white"
90− readonly property color defaultDark: Theme.palette.normal.baseText
91− readonly property real defaultLightLuminance: luminance(defaultLight)
92− readonly property real defaultDarkLuminance: luminance(defaultDark)
79+ readonly property color defaultDark: UbuntuColors.darkGrey
80+ readonly property real defaultLightLuminance: Style.luminance(defaultLight)
81+ readonly property real defaultDarkLuminance: Style.luminance(defaultDark)
82+
83+ readonly property color opaqueBackground: {
84+ background.a > 0 ?
85+ background :
86+ (Math.abs(foregroundLuminance - defaultLightLuminance) >
87+ Math.abs(foregroundLuminance - defaultDarkLuminance)) ?
88+ defaultLight : defaultDark
89+ }
90+ readonly property real opaqueBackgroundLuminance: Style.luminance(opaqueBackground)
9391
9492 readonly property var headerStyle: "page-header" in style ? style["page-header"] : { }
9593 }
plugins/Utils/Style.jsnew file+37
@@ -0,0 +1,37 @@
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+.pragma library
18+
19+/*! \brief Calculate average luminance of the passed colors
20+
21+ \note If not fully opaque, luminance is dependant on blending.
22+ */
23+function luminance() {
24+ var sum = 0;
25+ // TODO this was originally
26+ // for (var k in arguments) {
27+ // but for some unkown reason was causing crashes in testDash/testDashContent
28+ // investigate when we have some time
29+ for (var k = 0; k < arguments.length; ++k) {
30+ // only way to convert string to color
31+ var c = Qt.lighter(arguments[k], 1.0);
32+
33+ sum += 0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b;
34+ }
35+
36+ return sum / arguments.length;
37+}
plugins/Utils/qmldir+1
@@ -1,3 +1,4 @@
11module Utils
22plugin Utils-qml
33typeinfo Utils.qmltypes
4+Style 0.1 Style.js
qml/Components/Background.qml+10
@@ -15,6 +15,8 @@
1515 */
1616
1717import QtQuick 2.0
18+import Ubuntu.Components 1.1
19+import Utils 0.1 as Utils
1820
1921Loader {
2022 id: root
@@ -23,6 +25,14 @@ Loader {
2325 property url style
2426 readonly property var parsedStyle: String(style)
2527 .match(/^(color|gradient):\/\/\/(?:(#(?:[0-9a-f]{3,4}){1,2}|[a-z]{3,}))(?:\/(#(?:[0-9a-f]{3,4}){1,2}|[a-z]{3,}))?\/?$/i)
28+ readonly property var luminance: {
29+ if (!parsedStyle) return 0.5;
30+ if (parsedStyle[1] === "color") return Utils.Style.luminance(parsedStyle[2]);
31+ else if (parsedStyle[1] === "gradient") return Utils.Style.luminance(parsedStyle[2], parsedStyle[3]);
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
2636
2737 sourceComponent: {
2838 if (style == "") return null;
qml/Dash/DashNavigation.qml+58−157
@@ -16,195 +16,96 @@
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
2527
26− property bool showList: false
27−
28− readonly property var currentNavigation: scope && scope.hasNavigation ? scope.getNavigation(scope.currentNavigationId) : null
29−
3028 property alias windowWidth: blackRect.width
3129 property alias windowHeight: blackRect.height
3230 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: scopeStyle.getTextColor(backgroundItem.luminance)
47+ readonly property bool bothVisible: altNavigationButton.visible && navigationButton.visible
48+ readonly property real navigationWidth: root.width >= units.gu(60) ? units.gu(40) : root.width
49+ readonly property real buttonWidth: navigationWidth / (bothVisible ? 2 : 1)
4450 }
4551
4652 Rectangle {
4753 id: blackRect
4854 color: "black"
49− opacity: navigationListView.currentItem && navigationListView.currentItem.visible ? 0.3 : 0
55+ opacity: openList && openList.currentItem && openList.currentItem.visible ? 0.5 : 0
5056 Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
51− anchors.top: navigationListView.top
52− anchors.right: parent.right
57+ anchors { left: parent.left; right: parent.right }
5358 visible: opacity != 0
5459 }
5560
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
61+ Background {
62+ id: backgroundItem
63+ anchors.fill: parent
64+ style: scopeStyle ? scopeStyle.navigationBackground : "color:///f5f5f5"
6565 }
6666
6767 Image {
68− anchors {
69− bottom: parent.bottom
70− left: parent.left
71− right: parent.right
72− }
7368 fillMode: Image.Stretch
74− source: "graphics/dash_divider_top_darkgrad.png"
75− z: -1
69+ source: scopeStyle.backgroundLuminance > 0.2 ? "graphics/navigation_shadow.png" : "graphics/navigation_shadow_light.png"
70+ anchors { top: parent.bottom; left: parent.left; right: parent.right }
7671 }
7772
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
73+ DashNavigationButton {
74+ id: altNavigationButton
75+ objectName: "altNavigationButton"
76+ height: root.height
77+ width: d.buttonWidth
78+ scope: root.scope
79+ scopeStyle: root.scopeStyle
80+ foregroundColor: d.foregroundColor
81+ listView.width: d.navigationWidth
82+ isAltNavigation: true
83+ showDivider: navigationButton.visible || root.width > d.navigationWidth
84+ // needed so that InverseMouseArea is above navigationButton
85+ z: listView.height > 0 ? 1 : 0
8486 }
8587
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
88+ DashNavigationButton {
89+ id: navigationButton
90+ objectName: "navigationButton"
91+ height: root.height
92+ width: altNavigationButton.visible ? d.buttonWidth : d.navigationWidth
93+ x: altNavigationButton.visible ? d.buttonWidth : 0
94+ scope: root.scope
95+ scopeStyle: root.scopeStyle
96+ foregroundColor: d.foregroundColor
97+ listView.width: d.navigationWidth
98+ listView.x: -x
99+ showDivider: root.width > d.navigationWidth
94100 }
95101
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− }
102+ Image {
103+ fillMode: Image.Stretch
104+ source: backgroundItem.luminance > 0.7 ? "graphics/navigation_shadow.png" : "graphics/navigation_shadow_light.png"
105+ x: navigationButton.listView.height > 0 ? altNavigationButton.x : navigationButton.x
106+ width: d.buttonWidth
107+ rotation: 180
108+ anchors.bottom: parent.bottom
109+ visible: d.bothVisible && (navigationButton.listView.height > 0 || altNavigationButton.listView.height > 0)
209110 }
210111}
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+ onClicked: 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(1)
221+ }
222+ width: units.dp(2)
223+ color: root.foregroundColor
224+ opacity: 0.3
225+ }
226+}
qml/Dash/DashNavigationList.qml+5−4
@@ -16,22 +16,23 @@
1616
1717import QtQuick 2.2
1818import Ubuntu.Components 1.1
19+import "../Components"
1920
2021Item {
2122 id: root
2223 property var navigation: null
2324 property var currentNavigation: null
2425 property var scopeStyle: null
26+ property color foregroundColor: Theme.palette.normal.baseText
2527 signal enterNavigation(var newNavigationId, bool hasChildren)
2628 signal goBackToParentClicked()
2729 signal allNavigationClicked()
2830
2931 readonly property int itemHeight: units.gu(5)
30− readonly property color foregroundColor: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
3132 implicitHeight: flickable.contentHeight
3233
33− Rectangle {
34− color: !root.scopeStyle || Qt.colorEqual(root.scopeStyle.background, "transparent") ? "white" : root.scopeStyle.background
34+ Background {
35+ style: root.scopeStyle ? root.scopeStyle.navigationBackground : "color:///#f5f5f5"
3536 anchors.fill: parent
3637 }
3738
@@ -110,7 +111,7 @@ Item {
110111 id: allButton
111112 objectName: "allButton"
112113 width: parent.width
113− visible: navigation && (!navigation.isRoot || (root.currentNavigation && !root.currentNavigation.isRoot && root.currentNavigation.parentNavigationId == navigation.navigationId)) || false
114+ visible: navigation && (!navigation.isRoot || (!navigation.hidden && root.currentNavigation && !root.currentNavigation.isRoot && root.currentNavigation.parentNavigationId == navigation.navigationId)) || false
114115 height: itemHeight
115116
116117 Label {
qml/Dash/GenericScopeView.qml+66−12
@@ -144,14 +144,16 @@ FocusScope {
144144 x: subPageLoader.open ? -width : 0
145145 Behavior on x { UbuntuNumberAnimation { } }
146146 width: parent.width
147− height: parent.height
147+ height: floatingSeeLess.visible ? parent.height - floatingSeeLess.height + floatingSeeLess.yOffset
148+ : parent.height
149+ clip: height != parent.height
148150
149151 model: scopeView.categories
150152 forceNoClip: subPageLoader.open
151153 pixelAligned: true
152154 interactive: !navigationShown
153155
154− property string expandedCategoryId: ""
156+ property Item expandedCategoryItem: null
155157
156158 readonly property bool pageHeaderTotallyVisible: scopeView.showPageHeader &&
157159 ((headerItemShownHeight == 0 && categoryView.contentY <= categoryView.originY) || (headerItemShownHeight == pageHeaderLoader.item.height))
@@ -162,6 +164,8 @@ FocusScope {
162164 highlightWhenPressed: false
163165 showDivider: false
164166
167+ property Item seeAllButton: seeAll
168+
165169 readonly property bool expandable: {
166170 if (categoryView.model.count === 1) return false;
167171 if (cardTool.template && cardTool.template["collapsed-rows"] === 0) return false;
@@ -191,7 +195,7 @@ FocusScope {
191195 // This can happen with the VJ that doesn't know how height it will be on creation
192196 // so doesn't set expandable until a bit too late for onLoaded
193197 if (expandable) {
194− var shouldExpand = baseItem.category === categoryView.expandedCategoryId;
198+ var shouldExpand = baseItem === categoryView.expandedCategoryItem;
195199 baseItem.expand(shouldExpand, false /*animate*/);
196200 }
197201 }
@@ -242,7 +246,7 @@ FocusScope {
242246 item.objectName = Qt.binding(function() { return categoryId })
243247 item.scopeStyle = scopeView.scopeStyle;
244248 if (baseItem.expandable) {
245− var shouldExpand = categoryId === categoryView.expandedCategoryId;
249+ var shouldExpand = baseItem === categoryView.expandedCategoryItem;
246250 baseItem.expand(shouldExpand, false /*animate*/);
247251 }
248252 updateDelegateCreationRange();
@@ -279,13 +283,13 @@ FocusScope {
279283 }
280284 Connections {
281285 target: categoryView
282− onExpandedCategoryIdChanged: {
286+ onExpandedCategoryItemChanged: {
283287 collapseAllButExpandedCategory();
284288 }
285289 function collapseAllButExpandedCategory() {
286290 var item = rendererLoader.item;
287291 if (baseItem.expandable) {
288− var shouldExpand = categoryId === categoryView.expandedCategoryId;
292+ var shouldExpand = baseItem === categoryView.expandedCategoryItem;
289293 if (shouldExpand != baseItem.expanded) {
290294 // If the filter animation will be seen start it, otherwise, just flip the switch
291295 var shrinkingVisible = !shouldExpand && y + item.collapsedHeight + seeAll.height < categoryView.height;
@@ -346,13 +350,13 @@ FocusScope {
346350 left: parent.left
347351 right: parent.right
348352 }
349− height: seeAllLabel.visible ? seeAllLabel.font.pixelSize + units.gu(6) : 0
353+ height: seeAllLabel.visible ? seeAllLabel.font.pixelSize + units.gu(4) : 0
350354
351355 onClicked: {
352− if (categoryView.expandedCategoryId != baseItem.category) {
353− categoryView.expandedCategoryId = baseItem.category;
356+ if (categoryView.expandedCategoryItem !== baseItem) {
357+ categoryView.expandedCategoryItem = baseItem;
354358 } else {
355− categoryView.expandedCategoryId = "";
359+ categoryView.expandedCategoryItem = null;
356360 }
357361 }
358362
@@ -433,8 +437,7 @@ FocusScope {
433437
434438 bottomItem: DashNavigation {
435439 scope: scopeView.scope
436− width: parent.width <= units.gu(60) ? parent.width : units.gu(40)
437− anchors.right: parent.right
440+ anchors { left: parent.left; right: parent.right }
438441 windowHeight: scopeView.height
439442 windowWidth: scopeView.width
440443 scopeStyle: scopeView.scopeStyle
@@ -448,6 +451,57 @@ FocusScope {
448451 }
449452 }
450453
454+ AbstractButton {
455+ id: floatingSeeLess
456+ objectName: "floatingSeeLess"
457+
458+ property Item companionTo: companionBase ? companionBase.seeAllButton : null
459+ property Item companionBase: categoryView.expandedCategoryItem
460+ property bool showBecausePosition: false
461+ property real yOffset: 0
462+
463+ anchors {
464+ left: categoryView.left
465+ right: categoryView.right
466+ }
467+ y: parent.height - height + yOffset
468+ height: seeLessLabel.font.pixelSize + units.gu(4)
469+ visible: companionTo && showBecausePosition
470+
471+ onClicked: categoryView.expandedCategoryItem = null;
472+
473+ function updateVisibility() {
474+ var companionPos = companionTo.mapToItem(floatingSeeLess, 0, 0);
475+ showBecausePosition = companionPos.y > 0;
476+
477+ var posToBase = floatingSeeLess.mapToItem(companionBase, 0, -yOffset).y;
478+ yOffset = Math.max(0, companionBase.item.collapsedHeight - posToBase);
479+ yOffset = Math.min(yOffset, height);
480+ }
481+
482+ Label {
483+ id: seeLessLabel
484+ text: i18n.tr("See less")
485+ anchors {
486+ centerIn: parent
487+ verticalCenterOffset: units.gu(-0.5)
488+ }
489+ fontSize: "small"
490+ font.weight: Font.Bold
491+ color: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.baseText
492+ }
493+
494+ Connections {
495+ target: floatingSeeLess.companionTo ? categoryView : null
496+ onContentYChanged: floatingSeeLess.updateVisibility();
497+ }
498+
499+ Connections {
500+ target: floatingSeeLess.companionTo
501+ onYChanged: floatingSeeLess.updateVisibility();
502+ }
503+ }
504+
451505 LimitProxyModel {
452506 id: previewLimitModel
453507 }
qml/Dash/PageHeader.qml+60−21
@@ -242,6 +242,7 @@ Item {
242242 contentHeight: height
243243 opacity: headerContainer.clip || !headerContainer.showSearch ? 1 : 0 // setting visible false cause column to relayout
244244 separatorSource: ""
245+ separatorBottomSource: ""
245246 property var styledItem: header
246247 property string title: root.title
247248 property var config: PageHeadConfiguration {
@@ -303,25 +304,6 @@ Item {
303304 }
304305 }
305306
306− Row {
307− spacing: units.gu(.5)
308− Repeater {
309− objectName: "paginationRepeater"
310− model: root.paginationCount
311− Image {
312− objectName: "paginationDots_" + index
313− height: units.gu(1)
314− width: height
315− source: (index == root.paginationIndex) ? "graphics/pagination_dot_on.png" : "graphics/pagination_dot_off.png"
316− }
317− }
318− anchors {
319− top: headerContainer.bottom
320− horizontalCenter: headerContainer.horizontalCenter
321− topMargin: units.gu(.5)
322− }
323− }
324−
325307 Component {
326308 id: popoverComponent
327309 Popover {
@@ -359,7 +341,7 @@ Item {
359341 }
360342 }
361343
362− BorderImage {
344+ Rectangle {
363345 id: bottomBorder
364346 anchors {
365347 top: headerContainer.bottom
@@ -368,7 +350,64 @@ Item {
368350 bottom: bottomContainer.top
369351 }
370352
371− source: "graphics/PageHeaderBaseDivider.sci"
353+ color: scopeStyle ? scopeStyle.headerDividerColor : "#e0e0e0"
354+
355+ Rectangle {
356+ anchors {
357+ top: parent.top
358+ left: parent.left
359+ right: parent.right
360+ }
361+ height: units.dp(1)
362+ color: Qt.darker(parent.color, 1.1)
363+ }
364+ }
365+
366+ Row {
367+ visible: bottomBorder.visible
368+ spacing: units.gu(.5)
369+ Repeater {
370+ objectName: "paginationRepeater"
371+ model: root.paginationCount
372+ Image {
373+ objectName: "paginationDots_" + index
374+ height: units.gu(1)
375+ width: height
376+ source: (index == root.paginationIndex) ? "graphics/pagination_dot_on.png" : "graphics/pagination_dot_off.png"
377+ }
378+ }
379+ anchors {
380+ top: headerContainer.bottom
381+ horizontalCenter: headerContainer.horizontalCenter
382+ topMargin: units.gu(.5)
383+ }
384+ }
385+
386+ // FIXME this doesn't work with solid scope backgrounds due to z-ordering
387+ Item {
388+ id: bottomHighlight
389+ visible: bottomBorder.visible
390+ anchors {
391+ top: bottomContainer.top
392+ left: parent.left
393+ right: parent.right
394+ }
395+ z: 1
396+ height: units.dp(1)
397+ opacity: 0.6
398+
399+ // FIXME this should be a shader when bottomItem exists
400+ // to support image backgrounds
401+ Rectangle {
402+ anchors.fill: parent
403+ color: if (bottomItem && bottomItem.background) {
404+ Qt.lighter(Qt.rgba(bottomItem.background.topColor.r,
405+ bottomItem.background.topColor.g,
406+ bottomItem.background.topColor.b, 1.0), 1.2);
407+ } else if (!bottomItem && scopeStyle) {
408+ Qt.lighter(Qt.rgba(scopeStyle.background.r, scopeStyle.background.g, scopeStyle.background.b, 1.0), 1.2);
409+ } else "#CCFFFFFF"
410+ }
372411 }
373412
374413 Item {
qml/Dash/PreviewListView.qml+2
@@ -27,6 +27,8 @@ Item {
2727 property var scope: null
2828 property var scopeStyle: null
2929
30+ property alias showSignatureLine: header.showSignatureLine
31+
3032 property alias open: previewListView.open
3133 property alias model: previewListView.model
3234 property alias currentIndex: previewListView.currentIndex
qml/Dash/Previews/PreviewActionButton.qml+1−4
@@ -15,16 +15,13 @@
1515 */
1616
1717import QtQuick 2.0
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
1919
2020Button {
2121 signal triggeredAction(var actionData)
2222
2323 property var data: null
2424 objectName: "button" + (data && data.id || "")
25− color: Theme.palette.selected.foreground
2625 text: data && data.label || ""
27− iconSource: data && data.icon || ""
28− iconPosition: "left"
2926 onClicked: triggeredAction(data)
3027}
qml/Dash/Previews/PreviewActionCombo.qml+4−2
@@ -15,7 +15,7 @@
1515 */
1616
1717import QtQuick 2.0
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
1919
2020Item {
2121 id: root
@@ -25,6 +25,7 @@ Item {
2525 signal triggeredAction(var actionData)
2626
2727 property alias model: actionRepeater.model
28+ property alias strokeColor: moreButton.strokeColor
2829
2930 Button {
3031 id: moreButton
@@ -32,7 +33,7 @@ Item {
3233
3334 objectName: "moreLessButton"
3435 text: !expanded ? i18n.tr("More...") : i18n.tr("Less...")
35− color: Theme.palette.selected.foreground
36+ strokeColor: Theme.palette.normal.baseText
3637 onClicked: expanded = !expanded
3738 width: parent.width
3839 }
@@ -60,6 +61,7 @@ Item {
6061 data: modelData
6162 width: implicitWidth < parent.width ? parent.width : implicitWidth
6263 onClicked: root.triggeredAction(modelData)
64+ strokeColor: moreButton.strokeColor
6365 }
6466 }
6567 }
qml/Dash/Previews/PreviewActions.qml+6
@@ -46,6 +46,11 @@ PreviewWidget {
4646 item.model = row.actions.slice(1);
4747 }
4848 }
49+ Binding {
50+ target: loader.item
51+ property: "strokeColor"
52+ value: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
53+ }
4954 Connections {
5055 target: loader.item
5156 onTriggeredAction: {
@@ -59,6 +64,7 @@ PreviewWidget {
5964 visible: row.actions && row.actions.length > 0
6065 onTriggeredAction: root.triggered(root.widgetId, actionData.id, actionData)
6166 width: (root.width - units.gu(1)) / 2
67+ color: root.scopeStyle ? root.scopeStyle.previewButtonColor : Theme.palette.selected.foreground
6268 }
6369 }
6470}
qml/Dash/Previews/PreviewExpandable.qml+3−2
@@ -59,9 +59,10 @@ PreviewWidget {
5959 }
6060 Icon {
6161 anchors.fill: parent
62− width: 64
63− height: 64
62+ width: units.gu(3)
63+ height: units.gu(3)
6464 name: root.expanded ? "view-collapse" : "view-expand"
65+ color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
6566 }
6667 }
6768
qml/Dash/Previews/PreviewTable.qmlnew file+80
@@ -0,0 +1,80 @@
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.0
18+import QtQuick.Layouts 1.1
19+import Ubuntu.Components 0.1
20+import "../../Components"
21+
22+/*! \brief Preview widget for table.
23+
24+ This widget shows two columns contained in widgetData["values"]
25+ as arrays of label,value along with a title that comes from widgetData["title"].
26+
27+ In case the widget is collapsed it only shows 3 lines of values.
28+ */
29+
30+PreviewWidget {
31+ id: root
32+ implicitHeight: column.implicitHeight
33+
34+ readonly property int maximumCollapsedRowCount: 3
35+
36+ Column {
37+ id: column
38+ objectName: "column"
39+ spacing: units.gu(1)
40+
41+ Label {
42+ id: titleLabel
43+ objectName: "titleLabel"
44+ anchors {
45+ left: parent.left
46+ right: parent.right
47+ }
48+ height: visible ? implicitHeight : 0
49+ fontSize: "large"
50+ color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
51+ visible: text !== ""
52+ opacity: .8
53+ text: widgetData["title"] || ""
54+ }
55+
56+ GridLayout {
57+ objectName: "gridLayout"
58+ columns: 2
59+ columnSpacing: units.gu(2)
60+ Repeater {
61+ id: rowsRepeater
62+ model: widgetData["values"]
63+
64+ delegate: Repeater {
65+ id: perRowRepeater
66+ readonly property int rowIndex: index
67+ model: widgetData["values"][index]
68+ delegate: Label {
69+ objectName: "label"+rowIndex+index
70+ fontSize: "small"
71+ text: perRowRepeater.model[index]
72+ visible: root.expanded || rowIndex < maximumCollapsedRowCount
73+ color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
74+ font.bold: index == 0
75+ }
76+ }
77+ }
78+ }
79+ }
80+}
qml/Dash/Previews/PreviewWidgetFactory.qml+2−1
@@ -58,6 +58,7 @@ Loader {
5858 case "payments": return "PreviewPayments.qml";
5959 case "rating-input": return "PreviewRatingInput.qml";
6060 case "reviews": return "PreviewRatingDisplay.qml";
61+ case "table": return "PreviewTable.qml";
6162 case "text": return "PreviewTextSummary.qml";
6263 case "video": return "PreviewVideoPlayback.qml";
6364 default: return "";
@@ -70,7 +71,7 @@ Loader {
7071 item.widgetData = Qt.binding(function() { return root.widgetData } )
7172 item.isCurrentPreview = Qt.binding(function() { return root.isCurrentPreview } )
7273 item.expanded = Qt.binding(function() { return root.expanded } )
73− if (item.hasOwnProperty("scopeStyle")) item.scopeStyle = root.scopeStyle;
74+ item.scopeStyle = Qt.binding(function() { return root.scopeStyle } )
7475 }
7576
7677 Connections {
qml/Dash/ScopesOverview.qml+7−1
@@ -148,7 +148,12 @@ Item {
148148
149149 ScopeStyle {
150150 id: overviewScopeStyle
151− style: { "foreground-color" : "white", "background-color" : "transparent" }
151+ style: { "foreground-color" : "white",
152+ "background-color" : "transparent",
153+ "page-header": {
154+ "background": "color:///transparent"
155+ }
156+ }
152157 }
153158
154159 DashBackground {
@@ -458,6 +463,7 @@ Item {
458463 objectName: "scopesOverviewPreviewListView"
459464 scope: root.scope
460465 scopeStyle: overviewScopeStyle
466+ showSignatureLine: false
461467 visible: x != width
462468 width: parent.width
463469 height: parent.height
qml/Dash/graphics/PageHeaderBaseDivider@18.pngdeleted

Binary file not shown.

qml/Dash/graphics/PageHeaderBaseDivider@18.scideleted−5
@@ -1,5 +0,0 @@
1−border.top: 4
2−border.bottom: 4
3−border.left: 0
4−border.right: 0
5−source: PageHeaderBaseDivider@18.png
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+15−2
@@ -226,9 +226,17 @@ QVariantMap Scope::customizations() const
226226 m["background-color"] = "red";
227227 m["foreground-color"] = "blue";
228228 m["page-header"] = h;
229+ } else if (m_id == "MockScope4") {
230+ h["navigation-background"] = QUrl("../../../tests/qmltests/Dash/artwork/background.png");
231+ m["page-header"] = h;
229232 } else if (m_id == "MockScope5") {
230233 h["background"] = "gradient:///lightgrey/grey";
231234 h["logo"] = QUrl("../../../tests/qmltests/Dash/tst_PageHeader/logo-ubuntu-orange.svg");
235+ h["divider-color"] = "red";
236+ h["navigation-background"] = "color:///black";
237+ m["page-header"] = h;
238+ } else if (m_id == "MockScope6") {
239+ h["navigation-background"] = "gradient:///blue/red";
232240 m["page-header"] = h;
233241 }
234242 return m;
@@ -263,11 +271,16 @@ unity::shell::scopes::NavigationInterface* Scope::getAltNavigation(QString const
263271
264272 QString parentId;
265273 QString parentLabel;
266− if (id.startsWith("altmiddle")) {
274+ if (id != "altroot") {
267275 parentId = "altroot";
268276 parentLabel = "altroot";
269277 }
270− return new Navigation(id, id, "all"+id, parentId, parentLabel, this);
278+ auto result = new Navigation(id, id, "all"+id, parentId, parentLabel, this);
279+ if (id == "altroot") {
280+ m_currentAltNavigationId = "altrootChild1";
281+ Q_EMIT currentAltNavigationIdChanged();
282+ }
283+ return result;
271284}
272285
273286void Scope::setNavigationState(const QString &navigationId, bool isAltNavigation)
tests/plugins/Dash/cardcreator/2.res+3−3
@@ -26,7 +26,7 @@ Loader {
2626 gradientColor: getColor(1) || color;
2727 anchors.fill: parent;
2828 image: backgroundImage.source ? backgroundImage : null;
29− property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
29+ property real luminance: Style.luminance(color);
3030 property Image backgroundImage: Image {
3131 objectName: "backgroundImage";
3232 source: {
@@ -94,7 +94,7 @@ Image {
9494 wrapMode: Text.Wrap;
9595 maximumLineCount: 2;
9696 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
97− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
97+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
9898 visible: showHeader ;
9999 text: root.title;
100100 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -112,7 +112,7 @@ Image {
112112 elide: Text.ElideRight;
113113 fontSize: "small";
114114 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
115− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
115+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
116116 visible: titleLabel.visible && titleLabel.text;
117117 text: cardData && cardData["subtitle"] || "";
118118 font.weight: Font.Light;
tests/plugins/Dash/cardcreator/5.res+3−3
@@ -69,7 +69,7 @@ Loader {
6969 sourceComponent: ShaderEffect {
7070 id: overlay;
7171 height: (fixedHeaderHeight > 0 ? fixedHeaderHeight : headerHeight) + units.gu(2);
72− property real luminance: 0.2126 * overlayColor.r + 0.7152 * overlayColor.g + 0.0722 * overlayColor.b;
72+ property real luminance: Style.luminance(overlayColor);
7373 property color overlayColor: cardData && cardData["overlayColor"] || "#99000000";
7474 property var source: ShaderEffectSource {
7575 id: shaderSource;
@@ -113,7 +113,7 @@ Label {
113113 wrapMode: Text.Wrap;
114114 maximumLineCount: 2;
115115 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
116− color: overlayLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
116+ color: root.scopeStyle ? root.scopeStyle.getTextColor(overlayLoader.item.luminance) : (overlayLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
117117 visible: showHeader && overlayLoader.active;
118118 text: root.title;
119119 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -131,7 +131,7 @@ Label {
131131 elide: Text.ElideRight;
132132 fontSize: "small";
133133 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
134− color: overlayLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
134+ color: root.scopeStyle ? root.scopeStyle.getTextColor(overlayLoader.item.luminance) : (overlayLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
135135 visible: titleLabel.visible && titleLabel.text;
136136 text: cardData && cardData["subtitle"] || "";
137137 font.weight: Font.Light;
tests/plugins/Dash/cardcreator/6.res+4−4
@@ -26,7 +26,7 @@ Loader {
2626 gradientColor: getColor(1) || color;
2727 anchors.fill: parent;
2828 image: backgroundImage.source ? backgroundImage : null;
29− property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
29+ property real luminance: Style.luminance(color);
3030 property Image backgroundImage: Image {
3131 objectName: "backgroundImage";
3232 source: {
@@ -70,7 +70,7 @@ Item {
7070 wrapMode: Text.Wrap;
7171 maximumLineCount: 2;
7272 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
73− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
73+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
7474 visible: showHeader ;
7575 text: root.title;
7676 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -88,7 +88,7 @@ Item {
8888 elide: Text.ElideRight;
8989 fontSize: "small";
9090 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
91− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
91+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
9292 visible: titleLabel.visible && titleLabel.text;
9393 text: cardData && cardData["subtitle"] || "";
9494 font.weight: Font.Light;
@@ -103,7 +103,7 @@ Item {
103103 rightMargin: units.gu(1);
104104 }
105105 source: cardData && cardData["emblem"] || "";
106− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
106+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
107107 width: height;
108108 height: source != "" ? titleLabel.font.pixelSize : 0;
109109 }
tests/plugins/Dash/cardcreator/7.res+4−4
@@ -26,7 +26,7 @@ Loader {
2626 gradientColor: getColor(1) || color;
2727 anchors.fill: parent;
2828 image: backgroundImage.source ? backgroundImage : null;
29− property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
29+ property real luminance: Style.luminance(color);
3030 property Image backgroundImage: Image {
3131 objectName: "backgroundImage";
3232 source: {
@@ -95,7 +95,7 @@ Image {
9595 wrapMode: Text.Wrap;
9696 maximumLineCount: 2;
9797 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
98− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
98+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
9999 visible: showHeader ;
100100 text: root.title;
101101 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -113,7 +113,7 @@ Image {
113113 elide: Text.ElideRight;
114114 fontSize: "small";
115115 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
116− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
116+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
117117 visible: titleLabel.visible && titleLabel.text;
118118 text: cardData && cardData["subtitle"] || "";
119119 font.weight: Font.Light;
@@ -127,7 +127,7 @@ Image {
127127 rightMargin: units.gu(1);
128128 top: subtitleLabel.bottom;
129129 }
130− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
130+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
131131 model: cardData && cardData["attributes"];
132132 }
133133
tests/plugins/Dash/tst_ScopeStyle.qml+73−68
@@ -18,6 +18,7 @@ import QtQuick 2.2
1818import QtTest 1.0
1919import Dash 0.1
2020import Ubuntu.Components 1.1
21+import Utils 0.1
2122
2223Rectangle {
2324 width: units.gu(40)
@@ -35,39 +36,23 @@ Rectangle {
3536 property color color
3637 property var styles: [
3738 {},
38− { "foreground-color": "red", "background-color": "black", "page-header": { "logo": "/foo/bar" } },
39− { "foreground-color": "green", "background-color": "white", "page-header": { "foreground-color": "black" } },
40− { "foreground-color": "blue", "background-color": "darkgrey", "page-header": { "background": "gradient:///white/blue" } },
39+ { "foreground-color": "red", "background-color": "black", "page-header": { "logo": "/foo/bar" },
40+ "preview-button-color": "red"},
41+ { "foreground-color": "green", "background-color": "white",
42+ "page-header": { "foreground-color": "black",
43+ "divider-color": "blue" } },
44+ { "foreground-color": "blue", "background-color": "darkgrey",
45+ "page-header": { "background": "gradient:///white/blue",
46+ "navigation-background": "gradient:///white/blue" } },
4147 ]
4248
4349 function cleanup() {
4450 testCase.color = "transparent";
4551 }
4652
47− function test_luminance_data() {
48− return [
49− { tag: "#F00", luminance: 0.2126 },
50− { tag: "#0F0", luminance: 0.7152 },
51− { tag: "#00F", luminance: 0.0722 },
52− { tag: "white", luminance: 1.0 },
53− { tag: "black", luminance: 0.0 },
54− { tag: "lightgrey", luminance: 0.8275 },
55− { tag: "grey", luminance: 0.5020 },
56− { tag: "darkgrey", luminance: 0.6627 },
57− { tag: "red", luminance: 0.2126 },
58− { tag: "green", luminance: 0.3590 },
59− { tag: "blue", luminance: 0.0722 },
60− ];
61− }
62−
63− function test_luminance(data) {
64− testCase.color = data.tag;
65− compare(tool.luminance(testCase.color).toFixed(4), data.luminance.toFixed(4));
66− }
67−
6853 function test_foreground_data() {
6954 return [
70− { tag: "default", index: 0, foreground: Theme.palette.normal.foreground, luminance: 0.5725 },
55+ { tag: "default", index: 0, foreground: UbuntuColors.darkGrey, luminance: 0.3647 },
7156 { tag: "red on black", index: 1, foreground: "red", luminance: 0.2126 },
7257 { tag: "green on white", index: 2, foreground: "green", luminance: 0.3590 },
7358 { tag: "blue on darkgrey", index: 3, foreground: "blue", luminance: 0.0722 },
@@ -83,7 +68,7 @@ Rectangle {
8368
8469 function test_background_data() {
8570 return [
86− { tag: "default", index: 0, background: "transparent" },
71+ { tag: "default", index: 0, background: "#00f5f5f5", luminance: 0.9608 },
8772 { tag: "red on black", index: 1, background: "black", luminance: 0 },
8873 { tag: "green on white", index: 2, background: "white", luminance: 1 },
8974 { tag: "blue on darkgrey", index: 3, background: "darkgrey", luminance: 0.6627 },
@@ -94,53 +79,35 @@ Rectangle {
9479 tool.style = testCase.styles[data.index];
9580 verify(Qt.colorEqual(tool.background, data.background),
9681 "Background color not equal: %1 != %2".arg(tool.background).arg(data.background));
97− if (data.hasOwnProperty("luminance")) {
98− compare(tool.backgroundLuminance.toFixed(4), data.luminance.toFixed(4));
99− }
82+ compare(tool.backgroundLuminance.toFixed(4), data.luminance.toFixed(4));
10083 }
10184
102− function test_threshold_data() {
85+ function test_getTextColor_data() {
10386 return [
104− { tag: "default", index: 0, threshold: 0.7863 },
105− { tag: "red on black", index: 1, threshold: 0.1063 },
106− { tag: "green on white", index: 2, threshold: 0.6795 },
107− { tag: "blue on darkgrey", index: 3, threshold: 0.3675 },
87+ { tag: "default on black", background: "black", index: 0, textColor: "white" },
88+ { tag: "default on lightgrey", background: "lightgrey", index: 0, textColor: UbuntuColors.darkGrey },
89+ { tag: "default on white", background: "white", index: 0, textColor: UbuntuColors.darkGrey },
90+ { tag: "default on yellow", background: "yellow", index: 0, textColor: UbuntuColors.darkGrey },
91+ { tag: "red/black on black", background: "black", index: 1, textColor: "red" },
92+ { tag: "red/black on lightgrey", background: "lightgrey", index: 1, textColor: "black" },
93+ { tag: "red/black on white", background: "white", index: 1, textColor: "black" },
94+ { tag: "red/black on yellow", background: "yellow", index: 1, textColor: "black" },
95+ { tag: "green/white on black", background: "black", index: 2, textColor: "white" },
96+ { tag: "green/white on lightgrey", background: "lightgrey", index: 2, textColor: "green" },
97+ { tag: "green/white on white", background: "white", index: 2, textColor: "green" },
98+ { tag: "green/white on yellow", background: "yellow", index: 2, textColor: "green" },
99+ { tag: "blue/darkgrey on black", background: "black", index: 3, textColor: "darkgrey" },
100+ { tag: "blue/darkgrey on lightgrey", background: "lightgrey", index: 3, textColor: "blue" },
101+ { tag: "blue/darkgrey on white", background: "white", index: 3, textColor: "blue" },
102+ { tag: "blue/darkgrey on yellow", background: "yellow", index: 3, textColor: "blue" },
108103 ];
109104 }
110105
111− function test_threshold(data) {
106+ function test_getTextColor(data) {
112107 tool.style = testCase.styles[data.index];
113− compare(tool.threshold.toFixed(4), data.threshold.toFixed(4), "Luminance threshold was incorrect.");
114− }
115−
116− function test_light_data() {
117− return [
118− { tag: "default", index: 0, light: "white" },
119− { tag: "red on black", index: 1, light: "red" },
120− { tag: "green on white", index: 2, light: "white" },
121− { tag: "blue on darkgrey", index: 3, light: "darkgrey" },
122− ];
123− }
124−
125− function test_light(data) {
126− tool.style = testCase.styles[data.index];
127− verify(Qt.colorEqual(tool.light, data.light),
128− "Light color not equal: %1 != %2".arg(tool.light).arg(data.light));
129− }
130−
131− function test_dark_data() {
132− return [
133− { tag: "default", index: 0, dark: Theme.palette.normal.baseText },
134− { tag: "red on black", index: 1, dark: "black" },
135− { tag: "green on white", index: 2, dark: "green" },
136− { tag: "blue on darkgrey", index: 3, dark: "blue" },
137− ];
138− }
139−
140− function test_dark(data) {
141− tool.style = testCase.styles[data.index];
142− verify(Qt.colorEqual(tool.dark, data.dark),
143− "Dark color not equal: %1 != %2".arg(tool.dark).arg(data.dark));
108+ var textColor = tool.getTextColor(Style.luminance(data.background));
109+ verify(Qt.colorEqual(textColor, data.textColor),
110+ "TextColor not equal: %1 != %2".arg(textColor).arg(data.textColor));
144111 }
145112
146113 function test_headerLogo_data() {
@@ -157,7 +124,7 @@ Rectangle {
157124
158125 function test_headerForeground_data() {
159126 return [
160− { tag: "default", index: 0, headerForeground: Theme.palette.normal.baseText },
127+ { tag: "default", index: 0, headerForeground: UbuntuColors.darkGrey },
161128 { tag: "black", index: 2, headerForeground: "black" },
162129 ];
163130 }
@@ -170,7 +137,7 @@ Rectangle {
170137
171138 function test_headerBackground_data() {
172139 return [
173− { tag: "default", index: 0, headerBackground: "" },
140+ { tag: "default", index: 0, headerBackground: "color:///#f5f5f5" },
174141 { tag: "black", index: 3, headerBackground: "gradient:///white/blue" },
175142 ];
176143 }
@@ -179,5 +146,43 @@ Rectangle {
179146 tool.style = testCase.styles[data.index];
180147 compare(tool.headerBackground, data.headerBackground, "Header background was incorrect.");
181148 }
149+
150+ function test_headerDividerColor_data() {
151+ return [
152+ { tag: "default", index: 0, headerDividerColor: "#e0e0e0" },
153+ { tag: "blue", index: 2, headerDividerColor: "blue" },
154+ ];
155+ }
156+
157+ function test_headerDividerColor(data) {
158+ tool.style = testCase.styles[data.index];
159+ verify(Qt.colorEqual(tool.headerDividerColor, data.headerDividerColor),
160+ "Header divider color not equal: %1 != %2".arg(tool.headerDividerColor).arg(data.headerDividerColor));
161+ }
162+
163+ function test_navigationBackground_data() {
164+ return [
165+ { tag: "default", index: 0, navigationBackground: "color:///#f5f5f5" },
166+ { tag: "black", index: 3, navigationBackground: "gradient:///white/blue" },
167+ ];
168+ }
169+
170+ function test_navigationBackground(data) {
171+ tool.style = testCase.styles[data.index];
172+ compare(tool.navigationBackground, data.navigationBackground, "Navigation background was incorrect.");
173+ }
174+
175+ function test_previewButtonColor_data() {
176+ return [
177+ { tag: "default", index: 0, previewButtonColor: Theme.palette.selected.foreground },
178+ { tag: "red", index: 1, previewButtonColor: "red" },
179+ ];
180+ }
181+
182+ function test_previewButtonColor(data) {
183+ tool.style = testCase.styles[data.index];
184+ verify(Qt.colorEqual(tool.previewButtonColor, data.previewButtonColor),
185+ "Preview button color not equal: %1 != %2".arg(tool.previewButtonColor).arg(data.previewButtonColor));
186+ }
182187 }
183188}
tests/plugins/Utils/CMakeLists.txt+5
@@ -27,3 +27,8 @@ run_tests(
2727 qsortfilterproxymodeltest
2828 timeformattertest
2929 )
30+
31+# plain qml test
32+set(qmltest_DEFAULT_TARGETS qmltests)
33+set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal")
34+add_qml_test(. UtilsStyle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
tests/plugins/Utils/tst_UtilsStyle.qmlnew file+47
@@ -0,0 +1,47 @@
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 QtTest 1.0
19+import Utils 0.1
20+
21+TestCase {
22+ id: testCase
23+ name: "UtilsStyle"
24+
25+ property color color
26+
27+ function test_luminance_data() {
28+ return [
29+ { tag: "#F00", luminance: 0.2126 },
30+ { tag: "#0F0", luminance: 0.7152 },
31+ { tag: "#00F", luminance: 0.0722 },
32+ { tag: "white", luminance: 1.0 },
33+ { tag: "black", luminance: 0.0 },
34+ { tag: "lightgrey", luminance: 0.8275 },
35+ { tag: "grey", luminance: 0.5020 },
36+ { tag: "darkgrey", luminance: 0.6627 },
37+ { tag: "red", luminance: 0.2126 },
38+ { tag: "green", luminance: 0.3590 },
39+ { tag: "blue", luminance: 0.0722 },
40+ ];
41+ }
42+
43+ function test_luminance(data) {
44+ testCase.color = data.tag;
45+ compare(Style.luminance(testCase.color).toFixed(4), data.luminance.toFixed(4));
46+ }
47+}
tests/qmltests/CMakeLists.txt+1
@@ -50,6 +50,7 @@ add_qml_test(Dash/Previews PreviewPayments)
5050add_qml_test(Dash/Previews PreviewProgress)
5151add_qml_test(Dash/Previews PreviewRatingDisplay)
5252add_qml_test(Dash/Previews PreviewRatingInput)
53+add_qml_test(Dash/Previews PreviewTable)
5354add_qml_test(Dash/Previews PreviewTextSummary)
5455add_qml_test(Dash/Previews PreviewVideoPlayback)
5556add_qml_test(Dash/Previews PreviewWidgetFactory)
tests/qmltests/Components/tst_Background.qml+5−4
@@ -46,10 +46,10 @@ Rectangle {
4646
4747 function test_style_data() {
4848 return [
49− { tag: "empty", style: "" },
50− { tag: "solid", style: "color:///black" },
51− { tag: "gradient", style: "gradient:///black/red" },
52− { tag: "image", style: "/some/path" },
49+ { tag: "empty", style: "", luminance: 0.5 },
50+ { tag: "solid", style: "color:///black", luminance: 0 },
51+ { tag: "gradient", style: "gradient:///black/red", luminance: 0.1063 },
52+ { tag: "image", style: "/some/path", luminance: 0.5 },
5353 ];
5454 }
5555
@@ -58,6 +58,7 @@ Rectangle {
5858 expectFail("empty", "Empty style should not create a background.");
5959 loadedSpy.wait();
6060 compare(background.item.objectName, data.tag, "Background should be %1".arg(data.style));
61+ compare(background.luminance, data.luminance);
6162 }
6263
6364 function test_solid() {
tests/qmltests/Dash/Previews/tst_PreviewActions.qml+8−8
@@ -27,21 +27,21 @@ Rectangle {
2727 height: units.gu(80)
2828
2929 property var actionDataOneAction: {
30− "actions": [{"label": "Some Label", "icon": "../graphics/play_button.png", "id": "someid"}]
30+ "actions": [{"label": "Some Label", "id": "someid"}]
3131 }
3232
3333 property var actionDataTwoActions: {
34− "actions": [{"label": "Some Label A", "icon": "../graphics/icon_clear.png", "id": "someid1"},
35− {"label": "Some Label B", "icon": "../graphics/play_button.png", "id": "someid2"}
34+ "actions": [{"label": "Some Label A", "id": "someid1"},
35+ {"label": "Some Label B", "id": "someid2"}
3636 ]
3737 }
3838
3939 property var actionDataFiveActions: {
40− "actions": [{"label": "Some Label C", "icon": "../graphics/play_button.png", "id": "someid3"},
41− {"label": "Some Label D", "icon": "../graphics/icon_clear.png", "id": "someid4"},
42− {"label": "Some Label E", "icon": "../graphics/play_button.png", "id": "someid5"},
43− {"label": "Some Label F", "icon": "../graphics/icon_clear.png", "id": "someid6"},
44− {"label": "Some Label G", "icon": "../graphics/play_button.png", "id": "someid7"}
40+ "actions": [{"label": "Some Label C", "id": "someid3"},
41+ {"label": "Some Label D", "id": "someid4"},
42+ {"label": "Some Label E", "id": "someid5"},
43+ {"label": "Some Label F", "id": "someid6"},
44+ {"label": "Some Label G", "id": "someid7"}
4545 ]
4646 }
4747
tests/qmltests/Dash/Previews/tst_PreviewExpandable.qml+5−1
@@ -30,6 +30,10 @@ Rectangle {
3030 property string longText2: "This is a very very very long text. 1 This is a very very very long text. 2 This is a very very very long text. 3 This is a very very very long text. 4 This is a very very very long text. 5 This is a very very very long text. 6 This is a very very very long text. 7 This is a very very very long text. 8 This is a very very very long text. 9 This is a very very very long text. 10 This is a very very very long text. 11 This is a very very very long text."
3131 property string shortText: "This is a short text :)"
3232
33+ property var tableData: {
34+ "values": [ [ "Long Label 1", "Value 1"], [ "Label 2", "Long Value 2"], [ "Label 3", "Value 3"], [ "Label 4", "Value 4"], [ "Label 5", "Value 5"] ]
35+ }
36+
3337 ListModel {
3438 id: widgetsModel
3539 }
@@ -42,7 +46,7 @@ Rectangle {
4246
4347 Component.onCompleted: {
4448 widgetsModel.append({"type": "text", "widgetId": "text1", "properties": { "text": longText }});
45− widgetsModel.append({"type": "text", "widgetId": "text2", "properties": { "text": longText2 }});
49+ widgetsModel.append({"type": "text", "widgetId": "table1", "properties": { "text": tableData }});
4650 widgetsModel.append({"type": "text", "widgetId": "text3", "properties": { "text": shortText }});
4751 widgetsModel.append({"type": "text", "widgetId": "text4", "properties": { "text": longText }});
4852 }
tests/qmltests/Dash/Previews/tst_PreviewTable.qmlnew file+114
@@ -0,0 +1,114 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtTest 1.0
19+import Ubuntu.Components 0.1
20+import "../../../../qml/Dash/Previews"
21+import Unity.Test 0.1 as UT
22+
23+Rectangle {
24+ id: root
25+ width: units.gu(40)
26+ height: units.gu(80)
27+ color: Theme.palette.selected.background
28+
29+ property var widgetDataComplete: {
30+ "title": "Title here",
31+ "values": [ [ "Long Label 1", "Value 1"], [ "Label 2", "Long Value 2"], [ "Label 3", "Value 3"], [ "Label 4", "Value 4"], [ "Label 5", "Value 5"] ]
32+ }
33+
34+ property var widgetDataNoTitle: {
35+ "values": [ [ "Long Label 1", "Value 1"], [ "Label 2", "Long Value 2"], [ "Label 3", "Value 3"], [ "Label 4", "Value 4"], [ "Label 5", "Value 5"] ]
36+ }
37+
38+ PreviewTable {
39+ id: previewTable
40+ anchors { left: parent.left; right: parent.right }
41+ widgetData: widgetDataComplete
42+
43+ Rectangle {
44+ color: "red"
45+ anchors.fill: parent
46+ opacity: 0.5
47+ }
48+ }
49+
50+ UT.UnityTestCase {
51+ name: "PreviewTableTest"
52+ when: windowShown
53+
54+ function init() {
55+ previewTable.widgetData = widgetDataComplete;
56+ }
57+
58+ function test_values() {
59+ compare(findChild(previewTable, "label00").text, "Long Label 1");
60+ compare(findChild(previewTable, "label01").text, "Value 1");
61+ compare(findChild(previewTable, "label10").text, "Label 2");
62+ compare(findChild(previewTable, "label11").text, "Long Value 2");
63+ compare(findChild(previewTable, "label20").text, "Label 3");
64+ compare(findChild(previewTable, "label21").text, "Value 3");
65+ compare(findChild(previewTable, "label30").text, "Label 4");
66+ compare(findChild(previewTable, "label31").text, "Value 4");
67+ compare(findChild(previewTable, "label40").text, "Label 5");
68+ compare(findChild(previewTable, "label41").text, "Value 5");
69+ }
70+
71+ function test_optional_title() {
72+ var titleLabel = findChild(previewTable, "titleLabel");
73+ compare(titleLabel.visible, true);
74+ var titleHeight = titleLabel.height;
75+ var prevHeight = previewTable.height;
76+
77+ previewTable.widgetData = widgetDataNoTitle;
78+ var column = findChild(previewTable, "column");
79+ tryCompare(previewTable, "height", prevHeight - titleHeight - column.spacing );
80+ }
81+
82+ function test_show_collapsed() {
83+ verify(findChild(previewTable, "label00").visible);
84+ verify(findChild(previewTable, "label01").visible);
85+ verify(findChild(previewTable, "label10").visible);
86+ verify(findChild(previewTable, "label11").visible);
87+ verify(findChild(previewTable, "label20").visible);
88+ verify(findChild(previewTable, "label21").visible);
89+ verify(findChild(previewTable, "label30").visible);
90+ verify(findChild(previewTable, "label31").visible);
91+ verify(findChild(previewTable, "label40").visible);
92+ verify(findChild(previewTable, "label41").visible);
93+
94+ waitForRendering(previewTable);
95+ var prevHeight = previewTable.height;
96+ previewTable.expanded = false;
97+
98+ verify(findChild(previewTable, "label00").visible);
99+ verify(findChild(previewTable, "label01").visible);
100+ verify(findChild(previewTable, "label10").visible);
101+ verify(findChild(previewTable, "label11").visible);
102+ verify(findChild(previewTable, "label20").visible);
103+ verify(findChild(previewTable, "label21").visible);
104+ verify(!findChild(previewTable, "label30").visible);
105+ verify(!findChild(previewTable, "label31").visible);
106+ verify(!findChild(previewTable, "label40").visible);
107+ verify(!findChild(previewTable, "label41").visible);
108+
109+ var labelHeight = findChild(previewTable, "label00").height;
110+ var gridLayout = findChild(previewTable, "gridLayout");
111+ tryCompare(previewTable, "height", prevHeight - labelHeight * 2 - gridLayout.rowSpacing * 2);
112+ }
113+ }
114+}
tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qml+1
@@ -84,6 +84,7 @@ Rectangle {
8484 { tag: "Progress", type: "progress", source: "PreviewProgress.qml" },
8585 { tag: "Rating Input", type: "rating-input", source: "PreviewRatingInput.qml" },
8686 { tag: "Rating Display", type: "reviews", source: "PreviewRatingDisplay.qml" },
87+ { tag: "Table", type: "table", source: "PreviewTable.qml" },
8788 { tag: "Text", type: "text", source: "PreviewTextSummary.qml" },
8889 { tag: "Video", type: "video", source: "PreviewVideoPlayback.qml" },
8990 ];
tests/qmltests/Dash/artwork/background.pngnew file

Binary file not shown.

tests/qmltests/Dash/tst_DashContent.qml+146−46
@@ -266,56 +266,57 @@ Item {
266266 verify(carouselLV.tileWidth / carouselLV.tileHeight == cardTool.components["art"]["aspect-ratio"]);
267267 }
268268
269− function test_navigations() {
269+ function test_mainNavigation() {
270270 var dashContentList = findChild(dashContent, "dashContentList");
271271 tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
272272 var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
273273 tryCompare(dashNavigation, "visible", true);
274− compare(dashNavigation.showList, false);
275− waitForRendering(dashNavigation);
276− mouseClick(dashNavigation, 0, 0);
277− compare(dashNavigation.showList, true);
274+ var dashNavigationButton = findChild(dashContentList.currentItem, "navigationButton");
275+ compare(dashNavigationButton.showList, false);
276+ waitForRendering(dashNavigationButton);
277+ mouseClick(dashNavigationButton, 0, 0);
278+ compare(dashNavigationButton.showList, true);
278279
279− var navigationListView = findChild(dashNavigation, "navigationListView");
280+ var navigationListView = findChild(dashNavigationButton, "navigationListView");
280281 compare(navigationListView.count, 1);
281282 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
282283
283284 waitForRendering(navigationListView);
284285 waitForRendering(navigationListView.currentItem);
285286
286− var allButton = findChild(dashNavigation, "allButton");
287+ var allButton = findChild(dashNavigationButton, "allButton");
287288 compare(allButton.visible, false);
288289
289− var navigation = findChild(dashNavigation, "navigation0child3");
290+ var navigation = findChild(dashNavigationButton, "navigation0child3");
290291 mouseClick(navigation, 0, 0);
291− compare(dashNavigation.showList, false);
292− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle3");
292+ compare(dashNavigationButton.showList, false);
293+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle3");
293294 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
294295
295− mouseClick(dashNavigation, 0, 0);
296− compare(dashNavigation.showList, true);
296+ mouseClick(dashNavigationButton, 0, 0);
297+ compare(dashNavigationButton.showList, true);
297298 waitForRendering(navigationListView);
298299 waitForRendering(navigationListView.currentItem);
299300 compare(allButton.visible, true);
300301
301302 mouseClick(allButton, 0, 0);
302− compare(dashNavigation.showList, false);
303− tryCompare(dashNavigation.currentNavigation, "navigationId", "root");
303+ compare(dashNavigationButton.showList, false);
304+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "root");
304305 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
305306
306− mouseClick(dashNavigation, 0, 0);
307− compare(dashNavigation.showList, true);
307+ mouseClick(dashNavigationButton, 0, 0);
308+ compare(dashNavigationButton.showList, true);
308309 waitForRendering(navigationListView);
309310 waitForRendering(navigationListView.currentItem);
310311 compare(allButton.visible, false);
311312
312− navigation = findChild(dashNavigation, "navigation0child2");
313+ navigation = findChild(dashNavigationButton, "navigation0child2");
313314 mouseClick(navigation, 0, 0);
314− compare(dashNavigation.showList, true);
315− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
315+ compare(dashNavigationButton.showList, true);
316+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
316317 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
317318
318− var navigationList1 = findChild(dashNavigation, "navigation1");
319+ var navigationList1 = findChild(dashNavigationButton, "navigation1");
319320 allButton = findChild(navigationList1, "allButton");
320321 var backButton = findChild(findChild(navigationList1, "navigation1"), "backButton");
321322 compare(allButton.visible, true);
@@ -324,61 +325,160 @@ Item {
324325 tryCompare(navigationListView, "contentX", navigationList1.x);
325326 waitForRendering(navigationListView);
326327 mouseClick(allButton, 0, 0);
327− compare(dashNavigation.showList, false);
328− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
328+ compare(dashNavigationButton.showList, false);
329+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
329330 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
330331
331− mouseClick(dashNavigation, 0, 0);
332− compare(dashNavigation.showList, true);
332+ mouseClick(dashNavigationButton, 0, 0);
333+ compare(dashNavigationButton.showList, true);
333334 waitForRendering(navigationListView);
334335 waitForRendering(navigationListView.currentItem);
335336 compare(allButton.visible, true);
336337 compare(backButton.visible, true);
337338
338339 tryCompare(navigationList1.navigation, "loaded", true);
339− navigation = findChild(dashNavigation, "navigation1child2");
340+ navigation = findChild(dashNavigationButton, "navigation1child2");
340341 mouseClick(navigation, 0, 0);
341− compare(dashNavigation.showList, false);
342− tryCompare(dashNavigation.currentNavigation, "navigationId", "childmiddle22");
342+ compare(dashNavigationButton.showList, false);
343+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "childmiddle22");
343344 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
344345
345− mouseClick(dashNavigation, 0, 0);
346− compare(dashNavigation.showList, true);
346+ mouseClick(dashNavigationButton, 0, 0);
347+ compare(dashNavigationButton.showList, true);
347348 waitForRendering(navigationListView);
348349 waitForRendering(navigationListView.currentItem);
349350
350351 tryCompare(navigationList1.navigation, "loaded", true);
351− navigation = findChild(dashNavigation, "navigation1child3");
352+ navigation = findChild(dashNavigationButton, "navigation1child3");
352353 mouseClick(navigation, 0, 0);
353− compare(dashNavigation.showList, false);
354− tryCompare(dashNavigation.currentNavigation, "navigationId", "childmiddle23");
354+ compare(dashNavigationButton.showList, false);
355+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "childmiddle23");
355356 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
356357
357− mouseClick(dashNavigation, 0, 0);
358− compare(dashNavigation.showList, true);
358+ mouseClick(dashNavigationButton, 0, 0);
359+ compare(dashNavigationButton.showList, true);
359360 waitForRendering(navigationListView);
360361 waitForRendering(navigationListView.currentItem);
361362 mouseClick(backButton, 0, 0);
362363
363− tryCompare(dashNavigation.currentNavigation, "navigationId", "root");
364+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "root");
364365 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
365− compare(dashNavigation.showList, true);
366− mouseClick(dashNavigation, 0, 0);
367− compare(dashNavigation.showList, false);
366+ compare(dashNavigationButton.showList, true);
367+ mouseClick(dashNavigationButton, 0, 0);
368+ compare(dashNavigationButton.showList, false);
368369
369− mouseClick(dashNavigation, 0, 0);
370− compare(dashNavigation.showList, true);
370+ mouseClick(dashNavigationButton, 0, 0);
371+ compare(dashNavigationButton.showList, true);
371372 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
372− navigation = findChild(dashNavigation, "navigation0child2");
373+ navigation = findChild(dashNavigationButton, "navigation0child2");
373374 mouseClick(navigation, 0, 0);
374− compare(dashNavigation.showList, true);
375− navigationList1 = findChild(dashNavigation, "navigation1");
375+ compare(dashNavigationButton.showList, true);
376+ navigationList1 = findChild(dashNavigationButton, "navigation1");
376377 compare(navigationList1.navigation.loaded, false);
377− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
378+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
378379 allButton = findChild(navigationList1, "allButton");
379− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
380+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
380381 mouseClick(allButton, 0, 0);
381− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
382+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
383+ }
384+
385+ function test_altNavigation() {
386+ var dashContentList = findChild(dashContent, "dashContentList");
387+ tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
388+ var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
389+ tryCompare(dashNavigation, "visible", true);
390+ var dashNavigationButton = findChild(dashContentList.currentItem, "altNavigationButton");
391+ verify(dashNavigationButton, "Can't find navigation button");
392+
393+ compare(dashNavigationButton.showList, false);
394+ waitForRendering(dashNavigationButton);
395+ mouseClick(dashNavigationButton, 0, 0);
396+ compare(dashNavigationButton.showList, true);
397+
398+ var navigationListView = findChild(dashNavigationButton, "navigationListView");
399+ compare(navigationListView.count, 1);
400+ tryCompare(navigationListView.currentItem.navigation, "loaded", true);
401+
402+ waitForRendering(navigationListView);
403+ waitForRendering(navigationListView.currentItem);
404+
405+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "altrootChild1");
406+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "altroot");
407+
408+ var allButton = findChild(dashNavigationButton, "allButton");
409+ compare(allButton.visible, false);
410+ var backButton = findChild(navigationListView, "backButton");
411+ compare(backButton.visible, false);
412+
413+ var navigation = findChild(dashNavigationButton, "navigation0child3");
414+ mouseClick(navigation, 0, 0);
415+ compare(dashNavigationButton.showList, false);
416+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "altrootChild3");
417+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "altroot");
418+
419+ mouseClick(dashNavigationButton, 0, 0);
420+ compare(dashNavigationButton.showList, true);
421+ waitForRendering(navigationListView);
422+ waitForRendering(navigationListView.currentItem);
423+ compare(allButton.visible, false);
424+
425+ navigation = findChild(dashNavigationButton, "navigation0child2");
426+ mouseClick(navigation, 0, 0);
427+ compare(dashNavigationButton.showList, false);
428+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "altrootChild2");
429+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "altroot");
430+
431+ mouseClick(dashNavigationButton, 0, 0);
432+ compare(dashNavigationButton.showList, true);
433+ waitForRendering(navigationListView);
434+ waitForRendering(navigationListView.currentItem);
435+ verify(!backButton.visible, "Back button should not be visible");
436+
437+ compare(navigationListView.count, 1, "There should be no second-level navigation");
438+
439+ mouseClick(dashNavigationButton, 0, 0);
440+ compare(dashNavigationButton.showList, false);
441+ }
442+
443+ function test_navigationsClosing() {
444+ var dashContentList = findChild(dashContent, "dashContentList");
445+ tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
446+ var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
447+ tryCompare(dashNavigation, "visible", true);
448+ var dashNavigationButton = findChild(dashContentList.currentItem, "navigationButton");
449+ verify(dashNavigationButton, "Can't find navigation button");
450+ var dashAltNavigationButton = findChild(dashContentList.currentItem, "altNavigationButton");
451+ verify(dashAltNavigationButton, "Can't find alternative navigation button");
452+
453+ waitForRendering(dashNavigationButton);
454+ waitForRendering(dashAltNavigationButton);
455+
456+ compare(dashNavigationButton.showList, false);
457+ compare(dashAltNavigationButton.showList, false);
458+
459+ mouseClick(dashNavigation, 0, 0);
460+ compare(dashNavigationButton.showList, false);
461+ compare(dashAltNavigationButton.showList, true);
462+
463+ var navigationListView = findChild(dashAltNavigationButton, "navigationListView");
464+ tryCompare(navigationListView.currentItem.navigation, "loaded", true);
465+
466+ mouseClick(dashNavigation, dashNavigationButton.x, 0);
467+ compare(dashNavigationButton.showList, false);
468+ compare(dashAltNavigationButton.showList, false);
469+
470+ tryCompare(navigationListView, "visible", false);
471+
472+ mouseClick(dashNavigation, dashNavigationButton.x, 0);
473+ compare(dashNavigationButton.showList, true);
474+ compare(dashAltNavigationButton.showList, false);
475+
476+ navigationListView = findChild(dashNavigationButton, "navigationListView");
477+ tryCompare(navigationListView.currentItem.navigation, "loaded", true);
478+
479+ mouseClick(dashNavigation, 0, 0);
480+ compare(dashNavigationButton.showList, false);
481+ compare(dashAltNavigationButton.showList, false);
382482 }
383483
384484 function test_searchHint() {
tests/qmltests/Dash/tst_GenericScopeView.qml+11−7
@@ -20,7 +20,7 @@ import Unity 0.2
2020import ".."
2121import "../../../qml/Dash"
2222import "../../../qml/Components"
23−import Ubuntu.Components 0.1
23+import Ubuntu.Components 1.1
2424import Unity.Test 0.1 as UT
2525
2626Item {
@@ -229,11 +229,15 @@ Item {
229229 shell.width = units.gu(20)
230230 var categoryListView = findChild(genericScopeView, "categoryListView");
231231 categoryListView.contentY = units.gu(20);
232− var seeAll = findChild(category, "seeAll")
232+ var seeAll = findChild(category, "seeAll");
233+ var floatingSeeLess = findChild(genericScopeView, "floatingSeeLess");
233234 mouseClick(seeAll, seeAll.width / 2, seeAll.height / 2);
234235 tryCompare(category, "expanded", true);
235− tryCompareFunction(function() { return category.item.height == genericScopeView.height - category.item.displayMarginBeginning - category.item.displayMarginEnd; }, true);
236− mouseClick(seeAll, seeAll.width / 2, seeAll.height / 2);
236+ tryCompareFunction(function() {
237+ return category.item.height + floatingSeeLess.height ==
238+ genericScopeView.height - category.item.displayMarginBeginning - category.item.displayMarginEnd;
239+ }, true);
240+ mouseClick(floatingSeeLess, floatingSeeLess.width / 2, floatingSeeLess.height / 2);
237241 tryCompare(category, "expanded", false);
238242 }
239243
@@ -395,9 +399,9 @@ Item {
395399
396400 function test_header_style_data() {
397401 return [
398− { tag: "Default", index: 0, foreground: Theme.palette.normal.baseText, background: "", logo: "" },
399− { tag: "Foreground", index: 1, foreground: "yellow", background: "", logo: "" },
400− { tag: "Logo+Background", index: 2, foreground: Theme.palette.normal.baseText, background: "gradient:///lightgrey/grey",
402+ { tag: "Default", index: 0, foreground: UbuntuColors.darkGrey, background: "color:///#f5f5f5", logo: "" },
403+ { tag: "Foreground", index: 1, foreground: "yellow", background: "color:///#f5f5f5", logo: "" },
404+ { tag: "Logo+Background", index: 2, foreground: UbuntuColors.darkGrey, background: "gradient:///lightgrey/grey",
401405 logo: Qt.resolvedUrl("../Dash/tst_PageHeader/logo-ubuntu-orange.svg") },
402406 ];
403407 }