on xenial help sign in
Merge lp:~unity-team/unity8/alt_nav_support
19bfd15Michał Sawicz
plugins/Dash/CardAttributes.qml+1
@@ -47,6 +47,7 @@ GridLayout {
47 height: units.gu(2)
47 height: units.gu(2)
48 sets: ["actions", "status", "apps"]
48 sets: ["actions", "status", "apps"]
49 source: "icon" in modelData ? modelData["icon"] : ""
49 source: "icon" in modelData ? modelData["icon"] : ""
50+ color: grid.color
50 }
51 }
51 Label {
52 Label {
52 id: label
53 id: label
plugins/Dash/CardCreator.js+6−5
@@ -29,7 +29,7 @@ var kBackgroundLoaderCode = 'Loader {\n\
29 gradientColor: getColor(1) || color; \n\
29 gradientColor: getColor(1) || color; \n\
30 anchors.fill: parent; \n\
30 anchors.fill: parent; \n\
31 image: backgroundImage.source ? backgroundImage : null; \n\
31 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\
33 property Image backgroundImage: Image { \n\
33 property Image backgroundImage: Image { \n\
34 objectName: "backgroundImage"; \n\
34 objectName: "backgroundImage"; \n\
35 source: { \n\
35 source: { \n\
@@ -108,7 +108,7 @@ var kOverlayLoaderCode = 'Loader { \n\
108 sourceComponent: ShaderEffect { \n\
108 sourceComponent: ShaderEffect { \n\
109 id: overlay; \n\
109 id: overlay; \n\
110 height: (fixedHeaderHeight > 0 ? fixedHeaderHeight : headerHeight) + units.gu(2); \n\
110 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\
112 property color overlayColor: cardData && cardData["overlayColor"] || "#99000000"; \n\
112 property color overlayColor: cardData && cardData["overlayColor"] || "#99000000"; \n\
113 property var source: ShaderEffectSource { \n\
113 property var source: ShaderEffectSource { \n\
114 id: shaderSource; \n\
114 id: shaderSource; \n\
@@ -439,14 +439,14 @@ function cardString(template, components) {
439 mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible);
439 mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible);
440 }
440 }
441
441
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")';
443
443
444 var hasTitleContainer = hasTitle && (hasEmblem || (hasMascot && (hasSubtitle || hasAttributes)));
444 var hasTitleContainer = hasTitle && (hasEmblem || (hasMascot && (hasSubtitle || hasAttributes)));
445 var titleSubtitleCode = '';
445 var titleSubtitleCode = '';
446 if (hasTitle) {
446 if (hasTitle) {
447 var titleColor;
447 var titleColor;
448 if (headerAsOverlay) {
448 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")';
450 } else if (hasSummary) {
450 } else if (hasSummary) {
451 titleColor = 'summary.color';
451 titleColor = 'summary.color';
452 } else if (hasBackground) {
452 } else if (hasBackground) {
@@ -652,7 +652,8 @@ function cardString(template, components) {
652function createCardComponent(parent, template, components) {
652function createCardComponent(parent, template, components) {
653 var imports = 'import QtQuick 2.2; \n\
653 var imports = 'import QtQuick 2.2; \n\
654 import Ubuntu.Components 1.1; \n\
654 import Ubuntu.Components 1.1; \n\
655− import Dash 0.1;\n';
655+ import Dash 0.1;\n\
656+ import Utils 0.1;\n';
656 var card = cardString(template, components);
657 var card = cardString(template, components);
657 var code = imports + 'Component {\n' + card + '}\n';
658 var code = imports + 'Component {\n' + card + '}\n';
658
659
plugins/Dash/ScopeStyle.qml+38−40
@@ -15,6 +15,8 @@
15 */
15 */
16
16
17import QtQuick 2.2
17import QtQuick 2.2
18+import Utils 0.1
19+import Ubuntu.Components 1.1
18
20
19/*! \brief Helper for processing scope customization options.
21/*! \brief Helper for processing scope customization options.
20
22
@@ -27,69 +29,65 @@ QtObject {
27 /// Style object passed from the scope
29 /// Style object passed from the scope
28 property var style: Object()
30 property var style: Object()
29
31
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−
38 /// Color used for text and symbolic icons
32 /// Color used for text and symbolic icons
39 readonly property color foreground: "foreground-color" in style ? style["foreground-color"] : d.defaultDark
33 readonly property color foreground: "foreground-color" in style ? style["foreground-color"] : d.defaultDark
40
34
41 /// Luminance of the foreground color
35 /// 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)
43
37
44 /// Color used for the overall background
38 /// Color used for the overall background
45− readonly property color background: "background-color" in style ? style["background-color"] : "transparent"
39+ readonly property color background: "background-color" in style ? style["background-color"] : "#00f5f5f5"
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− }
66
40
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)
68
43
44+ /*! \brief Get the most contrasting available color based on luminance
45+ *
69− \note If background color is not fully opaque, it's not taken into account
46+ * If background color is transparent, theme provided colors are taken into account
70− and defaults to the theme-provided dark color.
71 */
47 */
72− readonly property color dark: {
48+ function getTextColor(luminance) {
73− if (background.a !== 1.0) return foregroundLuminance < d.defaultDarkLuminance ? foreground : d.defaultDark;
49+ if (Math.abs(foregroundLuminance - luminance) >
74− return foregroundLuminance < backgroundLuminance ? foreground : background;
50+ Math.abs(d.opaqueBackgroundLuminance - luminance)) {
51+ return foreground;
52+ } else {
53+ return d.opaqueBackground;
54+ }
75 }
55 }
76
56
77 /// Source of the logo image for the header
57 /// Source of the logo image for the header
78 readonly property url headerLogo: "logo" in d.headerStyle ? d.headerStyle["logo"] : ""
58 readonly property url headerLogo: "logo" in d.headerStyle ? d.headerStyle["logo"] : ""
79
59
80 /// Background style for the header
60 /// 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"
82
62
83 /// Foreground color for the header
63 /// Foreground color for the header
84 readonly property color headerForeground: "foreground-color" in d.headerStyle ? d.headerStyle["foreground-color"] : foreground
64 readonly property color headerForeground: "foreground-color" in d.headerStyle ? d.headerStyle["foreground-color"] : foreground
85
65
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+
86 //! @cond
75 //! @cond
87 property var d: QtObject {
76 property var d: QtObject {
88 // FIXME: should be taken from the theme
77 // FIXME: should be taken from the theme
89 readonly property color defaultLight: "white"
78 readonly property color defaultLight: "white"
90− readonly property color defaultDark: Theme.palette.normal.baseText
79+ readonly property color defaultDark: UbuntuColors.darkGrey
91− readonly property real defaultLightLuminance: luminance(defaultLight)
80+ readonly property real defaultLightLuminance: Style.luminance(defaultLight)
92− readonly property real defaultDarkLuminance: luminance(defaultDark)
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)
93
91
94 readonly property var headerStyle: "page-header" in style ? style["page-header"] : { }
92 readonly property var headerStyle: "page-header" in style ? style["page-header"] : { }
95 }
93 }
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 @@
1module Utils
1module Utils
2plugin Utils-qml
2plugin Utils-qml
3typeinfo Utils.qmltypes
3typeinfo Utils.qmltypes
4+Style 0.1 Style.js
qml/Components/Background.qml+10
@@ -15,6 +15,8 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18+import Ubuntu.Components 1.1
19+import Utils 0.1 as Utils
18
20
19Loader {
21Loader {
20 id: root
22 id: root
@@ -23,6 +25,14 @@ Loader {
23 property url style
25 property url style
24 readonly property var parsedStyle: String(style)
26 readonly property var parsedStyle: String(style)
25 .match(/^(color|gradient):\/\/\/(?:(#(?:[0-9a-f]{3,4}){1,2}|[a-z]{3,}))(?:\/(#(?:[0-9a-f]{3,4}){1,2}|[a-z]{3,}))?\/?$/i)
27 .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
26
36
27 sourceComponent: {
37 sourceComponent: {
28 if (style == "") return null;
38 if (style == "") return null;
qml/Dash/DashNavigation.qml+58−157
@@ -16,195 +16,96 @@
16
16
17import QtQuick 2.2
17import QtQuick 2.2
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19+import Dash 0.1
20+import "../Components"
19
21
20−AbstractButton {
22+Item {
21 id: root
23 id: root
22 objectName: "dashNavigation"
24 objectName: "dashNavigation"
23
25
24 property var scope: null
26 property var scope: null
25
27
26− property bool showList: false
27−
28− readonly property var currentNavigation: scope && scope.hasNavigation ? scope.getNavigation(scope.currentNavigationId) : null
29−
30 property alias windowWidth: blackRect.width
28 property alias windowWidth: blackRect.width
31 property alias windowHeight: blackRect.height
29 property alias windowHeight: blackRect.height
32 property var scopeStyle: null
30 property var scopeStyle: null
31+ readonly property var openList: {
32+ if (navigationButton.showList) return navigationButton.listView;
33+ if (altNavigationButton.showList) return altNavigationButton.listView;
34+ return null;
35+ }
33
36
34− // Are we drilling down the tree or up?
37+ // FIXME this is only here for highlight purposes (see Background.qml, too)
35− property bool isGoingBack: false
38+ readonly property var background: backgroundItem
36
39
37− visible: root.currentNavigation != null
40+ visible: navigationButton.currentNavigation || altNavigationButton.currentNavigation
38
41
39 height: visible ? units.gu(5) : 0
42 height: visible ? units.gu(5) : 0
40
43
41− onClicked: {
44+ QtObject {
42− navigationListView.updateMaxHeight();
45+ id: d
43− root.showList = !root.showList;
46+ readonly property color foregroundColor: 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)
44 }
50 }
45
51
46 Rectangle {
52 Rectangle {
47 id: blackRect
53 id: blackRect
48 color: "black"
54 color: "black"
49− opacity: navigationListView.currentItem && navigationListView.currentItem.visible ? 0.3 : 0
55+ opacity: openList && openList.currentItem && openList.currentItem.visible ? 0.5 : 0
50 Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
56 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 }
53 visible: opacity != 0
58 visible: opacity != 0
54 }
59 }
55
60
56− Image {
61+ Background {
62+ id: backgroundItem
57− anchors {
63+ anchors.fill: parent
58− top: parent.top
64+ style: scopeStyle ? scopeStyle.navigationBackground : "color:///f5f5f5"
59− left: parent.left
60− right: parent.right
61− }
62− fillMode: Image.Stretch
63− source: "graphics/dash_divider_top_lightgrad.png"
64− z: -1
65 }
65 }
66
66
67 Image {
67 Image {
68− anchors {
69− bottom: parent.bottom
70− left: parent.left
71− right: parent.right
72− }
73 fillMode: Image.Stretch
68 fillMode: Image.Stretch
74− source: "graphics/dash_divider_top_darkgrad.png"
69+ source: scopeStyle.backgroundLuminance > 0.2 ? "graphics/navigation_shadow.png" : "graphics/navigation_shadow_light.png"
75− z: -1
70+ anchors { top: parent.bottom; left: parent.left; right: parent.right }
76 }
71 }
77
72
78− Label {
73+ DashNavigationButton {
79− anchors.fill: parent
74+ id: altNavigationButton
80− anchors.margins: units.gu(2)
75+ objectName: "altNavigationButton"
81− verticalAlignment: Text.AlignVCenter
76+ height: root.height
82− text: root.currentNavigation ? root.currentNavigation.label : ""
77+ width: d.buttonWidth
83− color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
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
84 }
86 }
85
87
86− Icon {
88+ DashNavigationButton {
87− anchors.verticalCenter: parent.verticalCenter
89+ id: navigationButton
88− anchors.right: parent.right
90+ objectName: "navigationButton"
89− anchors.rightMargin: units.gu(2)
91+ height: root.height
90− name: showList ? "up" : "down"
92+ width: altNavigationButton.visible ? d.buttonWidth : d.navigationWidth
91− height: units.gu(2)
93+ x: altNavigationButton.visible ? d.buttonWidth : 0
92− width: height
94+ scope: root.scope
93− color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
95+ scopeStyle: root.scopeStyle
96+ foregroundColor: d.foregroundColor
97+ listView.width: d.navigationWidth
98+ listView.x: -x
99+ showDivider: root.width > d.navigationWidth
94 }
100 }
95
101
96− // navigationListView is outside root
102+ Image {
97− ListView {
103+ fillMode: Image.Stretch
98− id: navigationListView
104+ source: backgroundItem.luminance > 0.7 ? "graphics/navigation_shadow.png" : "graphics/navigation_shadow_light.png"
99− objectName: "navigationListView"
105+ x: navigationButton.listView.height > 0 ? altNavigationButton.x : navigationButton.x
100− orientation: ListView.Horizontal
106+ width: d.buttonWidth
101− interactive: false
107+ rotation: 180
102− clip: root.width != windowWidth
108+ anchors.bottom: parent.bottom
103− model: ListModel {
109+ visible: d.bothVisible && (navigationButton.listView.height > 0 || altNavigationButton.listView.height > 0)
104− id: navigationModel
105− // We have two roles
106− // navigationId: the navigation id of the navigation the list represents
107− // nullifyNavigation: overrides navigationId to be null
108− // This is used to "clear" the delegate when going "right" on the tree
109− }
110− anchors {
111− left: parent.left
112− right: parent.right
113− top: root.bottom
114− }
115− property int maxHeight: -1
116− Component.onCompleted: updateMaxHeight();
117− function updateMaxHeight()
118− {
119− maxHeight = (windowHeight - mapToItem(null, 0, 0).y) - units.gu(8);
120− }
121− property int prevHeight: maxHeight
122− height: currentItem ? currentItem.height : maxHeight
123− onHeightChanged: {
124− if (root.showList) {
125− prevHeight = currentItem.desiredHeight;
126− }
127− }
128− highlightMoveDuration: UbuntuAnimation.FastDuration
129− delegate: DashNavigationList {
130− objectName: "navigation" + index
131− visible: height != 0
132− width: navigationListView.width
133− scopeStyle: root.scopeStyle
134− property real desiredHeight: {
135− if (root.showList) {
136− if (navigation && navigation.loaded && x == navigationListView.contentX)
137− {
138− navigationListView.updateMaxHeight();
139− return Math.min(implicitHeight, navigationListView.maxHeight);
140− } else {
141− return navigationListView.prevHeight;
142− }
143− } else {
144− return 0;
145− }
146− }
147− height: desiredHeight
148− navigation: (nullifyNavigation || !scope) ? null : scope.getNavigation(navigationId)
149− currentNavigation: root.currentNavigation
150− onEnterNavigation: {
151− scope.setNavigationState(newNavigationId, false);
152− // We only need to add a new item to the model
153− // if we have children, otherwise just load it
154− if (hasChildren) {
155− isGoingBack = false;
156− navigationModel.append({"navigationId": newNavigationId, "nullifyNavigation": false});
157− navigationListView.currentIndex++;
158− } else {
159− showList = false;
160− }
161− }
162− onGoBackToParentClicked: {
163− scope.setNavigationState(navigation.parentNavigationId, false);
164− isGoingBack = true;
165− navigationModel.setProperty(navigationListView.currentIndex - 1, "nullifyNavigation", false);
166− navigationListView.currentIndex--;
167− }
168− onAllNavigationClicked: {
169− showList = false;
170− if (root.currentNavigation.parentNavigationId == navigation.navigationId) {
171− // For leaves we have to go to the parent too
172− scope.setNavigationState(root.currentNavigation.parentNavigationId, false);
173− }
174− }
175− }
176− onContentXChanged: {
177− if (contentX == width * navigationListView.currentIndex) {
178− if (isGoingBack) {
179− navigationModel.remove(navigationListView.currentIndex + 1);
180− } else {
181− navigationModel.setProperty(navigationListView.currentIndex - 1, "nullifyNavigation", true);
182− }
183− }
184− }
185− }
186−
187− InverseMouseArea {
188− anchors.fill: navigationListView
189− enabled: root.showList
190− onClicked: root.showList = false
191− }
192−
193− onScopeChanged: {
194− navigationModel.clear();
195− if (scope && scope.hasNavigation) {
196− navigationModel.append({"navigationId": scope.currentNavigationId, "nullifyNavigation": false});
197− }
198− }
199−
200− Connections {
201− target: scope
202− onHasNavigationChanged: {
203− if (scope.hasNavigation) {
204− navigationModel.append({"navigationId": scope.currentNavigationId, "nullifyNavigation": false});
205− } else {
206− navigationModel.clear();
207− }
208− }
209 }
110 }
210}
111}
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 @@
16
16
17import QtQuick 2.2
17import QtQuick 2.2
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19+import "../Components"
19
20
20Item {
21Item {
21 id: root
22 id: root
22 property var navigation: null
23 property var navigation: null
23 property var currentNavigation: null
24 property var currentNavigation: null
24 property var scopeStyle: null
25 property var scopeStyle: null
26+ property color foregroundColor: Theme.palette.normal.baseText
25 signal enterNavigation(var newNavigationId, bool hasChildren)
27 signal enterNavigation(var newNavigationId, bool hasChildren)
26 signal goBackToParentClicked()
28 signal goBackToParentClicked()
27 signal allNavigationClicked()
29 signal allNavigationClicked()
28
30
29 readonly property int itemHeight: units.gu(5)
31 readonly property int itemHeight: units.gu(5)
30− readonly property color foregroundColor: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
31 implicitHeight: flickable.contentHeight
32 implicitHeight: flickable.contentHeight
32
33
33− Rectangle {
34+ Background {
34− color: !root.scopeStyle || Qt.colorEqual(root.scopeStyle.background, "transparent") ? "white" : root.scopeStyle.background
35+ style: root.scopeStyle ? root.scopeStyle.navigationBackground : "color:///#f5f5f5"
35 anchors.fill: parent
36 anchors.fill: parent
36 }
37 }
37
38
@@ -110,7 +111,7 @@ Item {
110 id: allButton
111 id: allButton
111 objectName: "allButton"
112 objectName: "allButton"
112 width: parent.width
113 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
114 height: itemHeight
115 height: itemHeight
115
116
116 Label {
117 Label {
qml/Dash/GenericScopeView.qml+66−12
@@ -144,14 +144,16 @@ FocusScope {
144 x: subPageLoader.open ? -width : 0
144 x: subPageLoader.open ? -width : 0
145 Behavior on x { UbuntuNumberAnimation { } }
145 Behavior on x { UbuntuNumberAnimation { } }
146 width: parent.width
146 width: parent.width
147+ height: floatingSeeLess.visible ? parent.height - floatingSeeLess.height + floatingSeeLess.yOffset
148+ : parent.height
147− height: parent.height
149+ clip: height != parent.height
148
150
149 model: scopeView.categories
151 model: scopeView.categories
150 forceNoClip: subPageLoader.open
152 forceNoClip: subPageLoader.open
151 pixelAligned: true
153 pixelAligned: true
152 interactive: !navigationShown
154 interactive: !navigationShown
153
155
154− property string expandedCategoryId: ""
156+ property Item expandedCategoryItem: null
155
157
156 readonly property bool pageHeaderTotallyVisible: scopeView.showPageHeader &&
158 readonly property bool pageHeaderTotallyVisible: scopeView.showPageHeader &&
157 ((headerItemShownHeight == 0 && categoryView.contentY <= categoryView.originY) || (headerItemShownHeight == pageHeaderLoader.item.height))
159 ((headerItemShownHeight == 0 && categoryView.contentY <= categoryView.originY) || (headerItemShownHeight == pageHeaderLoader.item.height))
@@ -162,6 +164,8 @@ FocusScope {
162 highlightWhenPressed: false
164 highlightWhenPressed: false
163 showDivider: false
165 showDivider: false
164
166
167+ property Item seeAllButton: seeAll
168+
165 readonly property bool expandable: {
169 readonly property bool expandable: {
166 if (categoryView.model.count === 1) return false;
170 if (categoryView.model.count === 1) return false;
167 if (cardTool.template && cardTool.template["collapsed-rows"] === 0) return false;
171 if (cardTool.template && cardTool.template["collapsed-rows"] === 0) return false;
@@ -191,7 +195,7 @@ FocusScope {
191 // This can happen with the VJ that doesn't know how height it will be on creation
195 // This can happen with the VJ that doesn't know how height it will be on creation
192 // so doesn't set expandable until a bit too late for onLoaded
196 // so doesn't set expandable until a bit too late for onLoaded
193 if (expandable) {
197 if (expandable) {
194− var shouldExpand = baseItem.category === categoryView.expandedCategoryId;
198+ var shouldExpand = baseItem === categoryView.expandedCategoryItem;
195 baseItem.expand(shouldExpand, false /*animate*/);
199 baseItem.expand(shouldExpand, false /*animate*/);
196 }
200 }
197 }
201 }
@@ -242,7 +246,7 @@ FocusScope {
242 item.objectName = Qt.binding(function() { return categoryId })
246 item.objectName = Qt.binding(function() { return categoryId })
243 item.scopeStyle = scopeView.scopeStyle;
247 item.scopeStyle = scopeView.scopeStyle;
244 if (baseItem.expandable) {
248 if (baseItem.expandable) {
245− var shouldExpand = categoryId === categoryView.expandedCategoryId;
249+ var shouldExpand = baseItem === categoryView.expandedCategoryItem;
246 baseItem.expand(shouldExpand, false /*animate*/);
250 baseItem.expand(shouldExpand, false /*animate*/);
247 }
251 }
248 updateDelegateCreationRange();
252 updateDelegateCreationRange();
@@ -279,13 +283,13 @@ FocusScope {
279 }
283 }
280 Connections {
284 Connections {
281 target: categoryView
285 target: categoryView
282− onExpandedCategoryIdChanged: {
286+ onExpandedCategoryItemChanged: {
283 collapseAllButExpandedCategory();
287 collapseAllButExpandedCategory();
284 }
288 }
285 function collapseAllButExpandedCategory() {
289 function collapseAllButExpandedCategory() {
286 var item = rendererLoader.item;
290 var item = rendererLoader.item;
287 if (baseItem.expandable) {
291 if (baseItem.expandable) {
288− var shouldExpand = categoryId === categoryView.expandedCategoryId;
292+ var shouldExpand = baseItem === categoryView.expandedCategoryItem;
289 if (shouldExpand != baseItem.expanded) {
293 if (shouldExpand != baseItem.expanded) {
290 // If the filter animation will be seen start it, otherwise, just flip the switch
294 // If the filter animation will be seen start it, otherwise, just flip the switch
291 var shrinkingVisible = !shouldExpand && y + item.collapsedHeight + seeAll.height < categoryView.height;
295 var shrinkingVisible = !shouldExpand && y + item.collapsedHeight + seeAll.height < categoryView.height;
@@ -346,13 +350,13 @@ FocusScope {
346 left: parent.left
350 left: parent.left
347 right: parent.right
351 right: parent.right
348 }
352 }
349− height: seeAllLabel.visible ? seeAllLabel.font.pixelSize + units.gu(6) : 0
353+ height: seeAllLabel.visible ? seeAllLabel.font.pixelSize + units.gu(4) : 0
350
354
351 onClicked: {
355 onClicked: {
352− if (categoryView.expandedCategoryId != baseItem.category) {
356+ if (categoryView.expandedCategoryItem !== baseItem) {
353− categoryView.expandedCategoryId = baseItem.category;
357+ categoryView.expandedCategoryItem = baseItem;
354 } else {
358 } else {
355− categoryView.expandedCategoryId = "";
359+ categoryView.expandedCategoryItem = null;
356 }
360 }
357 }
361 }
358
362
@@ -431,8 +435,7 @@ FocusScope {
431
435
432 bottomItem: DashNavigation {
436 bottomItem: DashNavigation {
433 scope: scopeView.scope
437 scope: scopeView.scope
434− width: parent.width <= units.gu(60) ? parent.width : units.gu(40)
435− anchors.right: parent.right
438+ anchors { left: parent.left; right: parent.right }
436 windowHeight: scopeView.height
439 windowHeight: scopeView.height
437 windowWidth: scopeView.width
440 windowWidth: scopeView.width
438 scopeStyle: scopeView.scopeStyle
441 scopeStyle: scopeView.scopeStyle
@@ -445,6 +448,57 @@ FocusScope {
445 }
448 }
446 }
449 }
447
450
451+ AbstractButton {
452+ id: floatingSeeLess
453+ objectName: "floatingSeeLess"
454+
455+ property Item companionTo: companionBase ? companionBase.seeAllButton : null
456+ property Item companionBase: categoryView.expandedCategoryItem
457+ property bool showBecausePosition: false
458+ property real yOffset: 0
459+
460+ anchors {
461+ left: categoryView.left
462+ right: categoryView.right
463+ }
464+ y: parent.height - height + yOffset
465+ height: seeLessLabel.font.pixelSize + units.gu(4)
466+ visible: companionTo && showBecausePosition
467+
468+ onClicked: categoryView.expandedCategoryItem = null;
469+
470+ function updateVisibility() {
471+ var companionPos = companionTo.mapToItem(floatingSeeLess, 0, 0);
472+ showBecausePosition = companionPos.y > 0;
473+
474+ var posToBase = floatingSeeLess.mapToItem(companionBase, 0, -yOffset).y;
475+ yOffset = Math.max(0, companionBase.item.collapsedHeight - posToBase);
476+ yOffset = Math.min(yOffset, height);
477+ }
478+
479+ Label {
480+ id: seeLessLabel
481+ text: i18n.tr("See less")
482+ anchors {
483+ centerIn: parent
484+ verticalCenterOffset: units.gu(-0.5)
485+ }
486+ fontSize: "small"
487+ font.weight: Font.Bold
488+ color: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.baseText
489+ }
490+
491+ Connections {
492+ target: floatingSeeLess.companionTo ? categoryView : null
493+ onContentYChanged: floatingSeeLess.updateVisibility();
494+ }
495+
496+ Connections {
497+ target: floatingSeeLess.companionTo
498+ onYChanged: floatingSeeLess.updateVisibility();
499+ }
500+ }
501+
448 LimitProxyModel {
502 LimitProxyModel {
449 id: previewLimitModel
503 id: previewLimitModel
450 }
504 }
qml/Dash/PageHeader.qml+61−24
@@ -41,9 +41,7 @@ Item {
41 property int paginationCount: 0
41 property int paginationCount: 0
42 property int paginationIndex: -1
42 property int paginationIndex: -1
43
43
44− // TODO We should use foreground for the icons
44+ property var scope: null
45− // of the toolbar but unfortunately Action does not have
46− // the keyColor property as Icon does :-/
47 property var scopeStyle: null
45 property var scopeStyle: null
48
46
49 signal backClicked()
47 signal backClicked()
@@ -242,6 +240,7 @@ Item {
242 contentHeight: height
240 contentHeight: height
243 opacity: headerContainer.clip || !headerContainer.showSearch ? 1 : 0 // setting visible false cause column to relayout
241 opacity: headerContainer.clip || !headerContainer.showSearch ? 1 : 0 // setting visible false cause column to relayout
244 separatorSource: ""
242 separatorSource: ""
243+ separatorBottomSource: ""
245 property var styledItem: header
244 property var styledItem: header
246 property string title: root.title
245 property string title: root.title
247 property var config: PageHeadConfiguration {
246 property var config: PageHeadConfiguration {
@@ -294,25 +293,6 @@ Item {
294 }
293 }
295 }
294 }
296
295
297− Row {
298− spacing: units.gu(.5)
299− Repeater {
300− objectName: "paginationRepeater"
301− model: root.paginationCount
302− Image {
303− objectName: "paginationDots_" + index
304− height: units.gu(1)
305− width: height
306− source: (index == root.paginationIndex) ? "graphics/pagination_dot_on.png" : "graphics/pagination_dot_off.png"
307− }
308− }
309− anchors {
310− top: headerContainer.bottom
311− horizontalCenter: headerContainer.horizontalCenter
312− topMargin: units.gu(.5)
313− }
314− }
315−
316 Component {
296 Component {
317 id: popoverComponent
297 id: popoverComponent
318 Popover {
298 Popover {
@@ -350,7 +330,7 @@ Item {
350 }
330 }
351 }
331 }
352
332
353− BorderImage {
333+ Rectangle {
354 id: bottomBorder
334 id: bottomBorder
355 anchors {
335 anchors {
356 top: headerContainer.bottom
336 top: headerContainer.bottom
@@ -359,7 +339,64 @@ Item {
359 bottom: bottomContainer.top
339 bottom: bottomContainer.top
360 }
340 }
361
341
362− source: "graphics/PageHeaderBaseDivider.sci"
342+ color: scopeStyle ? scopeStyle.headerDividerColor : "#e0e0e0"
343+
344+ Rectangle {
345+ anchors {
346+ top: parent.top
347+ left: parent.left
348+ right: parent.right
349+ }
350+ height: units.dp(1)
351+ color: Qt.darker(parent.color, 1.1)
352+ }
353+ }
354+
355+ Row {
356+ visible: bottomBorder.visible
357+ spacing: units.gu(.5)
358+ Repeater {
359+ objectName: "paginationRepeater"
360+ model: root.paginationCount
361+ Image {
362+ objectName: "paginationDots_" + index
363+ height: units.gu(1)
364+ width: height
365+ source: (index == root.paginationIndex) ? "graphics/pagination_dot_on.png" : "graphics/pagination_dot_off.png"
366+ }
367+ }
368+ anchors {
369+ top: headerContainer.bottom
370+ horizontalCenter: headerContainer.horizontalCenter
371+ topMargin: units.gu(.5)
372+ }
373+ }
374+
375+ // FIXME this doesn't work with solid scope backgrounds due to z-ordering
376+ Item {
377+ id: bottomHighlight
378+ visible: bottomBorder.visible
379+ anchors {
380+ top: bottomContainer.top
381+ left: parent.left
382+ right: parent.right
383+ }
384+ z: 1
385+ height: units.dp(1)
386+ opacity: 0.6
387+
388+ // FIXME this should be a shader when bottomItem exists
389+ // to support image backgrounds
390+ Rectangle {
391+ anchors.fill: parent
392+ color: if (bottomItem && bottomItem.background) {
393+ Qt.lighter(Qt.rgba(bottomItem.background.topColor.r,
394+ bottomItem.background.topColor.g,
395+ bottomItem.background.topColor.b, 1.0), 1.2);
396+ } else if (!bottomItem && scopeStyle) {
397+ Qt.lighter(Qt.rgba(scopeStyle.background.r, scopeStyle.background.g, scopeStyle.background.b, 1.0), 1.2);
398+ } else "#CCFFFFFF"
399+ }
363 }
400 }
364
401
365 Item {
402 Item {
qml/Dash/PreviewListView.qml+2
@@ -27,6 +27,8 @@ Item {
27 property var scope: null
27 property var scope: null
28 property var scopeStyle: null
28 property var scopeStyle: null
29
29
30+ property alias showSignatureLine: header.showSignatureLine
31+
30 property alias open: previewListView.open
32 property alias open: previewListView.open
31 property alias model: previewListView.model
33 property alias model: previewListView.model
32 property alias currentIndex: previewListView.currentIndex
34 property alias currentIndex: previewListView.currentIndex
qml/Dash/Previews/PreviewActionButton.qml+1−4
@@ -15,16 +15,13 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
19
19
20Button {
20Button {
21 signal triggeredAction(var actionData)
21 signal triggeredAction(var actionData)
22
22
23 property var data: null
23 property var data: null
24 objectName: "button" + (data && data.id || "")
24 objectName: "button" + (data && data.id || "")
25− color: Theme.palette.selected.foreground
26 text: data && data.label || ""
25 text: data && data.label || ""
27− iconSource: data && data.icon || ""
28− iconPosition: "left"
29 onClicked: triggeredAction(data)
26 onClicked: triggeredAction(data)
30}
27}
qml/Dash/Previews/PreviewActionCombo.qml+4−2
@@ -15,7 +15,7 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
19
19
20Item {
20Item {
21 id: root
21 id: root
@@ -25,6 +25,7 @@ Item {
25 signal triggeredAction(var actionData)
25 signal triggeredAction(var actionData)
26
26
27 property alias model: actionRepeater.model
27 property alias model: actionRepeater.model
28+ property alias strokeColor: moreButton.strokeColor
28
29
29 Button {
30 Button {
30 id: moreButton
31 id: moreButton
@@ -32,7 +33,7 @@ Item {
32
33
33 objectName: "moreLessButton"
34 objectName: "moreLessButton"
34 text: !expanded ? i18n.tr("More...") : i18n.tr("Less...")
35 text: !expanded ? i18n.tr("More...") : i18n.tr("Less...")
35− color: Theme.palette.selected.foreground
36+ strokeColor: Theme.palette.normal.baseText
36 onClicked: expanded = !expanded
37 onClicked: expanded = !expanded
37 width: parent.width
38 width: parent.width
38 }
39 }
@@ -60,6 +61,7 @@ Item {
60 data: modelData
61 data: modelData
61 width: implicitWidth < parent.width ? parent.width : implicitWidth
62 width: implicitWidth < parent.width ? parent.width : implicitWidth
62 onClicked: root.triggeredAction(modelData)
63 onClicked: root.triggeredAction(modelData)
64+ strokeColor: moreButton.strokeColor
63 }
65 }
64 }
66 }
65 }
67 }
qml/Dash/Previews/PreviewActions.qml+6
@@ -46,6 +46,11 @@ PreviewWidget {
46 item.model = row.actions.slice(1);
46 item.model = row.actions.slice(1);
47 }
47 }
48 }
48 }
49+ Binding {
50+ target: loader.item
51+ property: "strokeColor"
52+ value: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
53+ }
49 Connections {
54 Connections {
50 target: loader.item
55 target: loader.item
51 onTriggeredAction: {
56 onTriggeredAction: {
@@ -59,6 +64,7 @@ PreviewWidget {
59 visible: row.actions && row.actions.length > 0
64 visible: row.actions && row.actions.length > 0
60 onTriggeredAction: root.triggered(root.widgetId, actionData.id, actionData)
65 onTriggeredAction: root.triggered(root.widgetId, actionData.id, actionData)
61 width: (root.width - units.gu(1)) / 2
66 width: (root.width - units.gu(1)) / 2
67+ color: root.scopeStyle ? root.scopeStyle.previewButtonColor : Theme.palette.selected.foreground
62 }
68 }
63 }
69 }
64}
70}
qml/Dash/Previews/PreviewExpandable.qml+3−2
@@ -59,9 +59,10 @@ PreviewWidget {
59 }
59 }
60 Icon {
60 Icon {
61 anchors.fill: parent
61 anchors.fill: parent
62− width: 64
62+ width: units.gu(3)
63− height: 64
63+ height: units.gu(3)
64 name: root.expanded ? "view-collapse" : "view-expand"
64 name: root.expanded ? "view-collapse" : "view-expand"
65+ color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
65 }
66 }
66 }
67 }
67
68
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 {
58 case "payments": return "PreviewPayments.qml";
58 case "payments": return "PreviewPayments.qml";
59 case "rating-input": return "PreviewRatingInput.qml";
59 case "rating-input": return "PreviewRatingInput.qml";
60 case "reviews": return "PreviewRatingDisplay.qml";
60 case "reviews": return "PreviewRatingDisplay.qml";
61+ case "table": return "PreviewTable.qml";
61 case "text": return "PreviewTextSummary.qml";
62 case "text": return "PreviewTextSummary.qml";
62 case "video": return "PreviewVideoPlayback.qml";
63 case "video": return "PreviewVideoPlayback.qml";
63 default: return "";
64 default: return "";
@@ -70,7 +71,7 @@ Loader {
70 item.widgetData = Qt.binding(function() { return root.widgetData } )
71 item.widgetData = Qt.binding(function() { return root.widgetData } )
71 item.isCurrentPreview = Qt.binding(function() { return root.isCurrentPreview } )
72 item.isCurrentPreview = Qt.binding(function() { return root.isCurrentPreview } )
72 item.expanded = Qt.binding(function() { return root.expanded } )
73 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 } )
74 }
75 }
75
76
76 Connections {
77 Connections {
qml/Dash/ScopesOverview.qml+7−1
@@ -148,7 +148,12 @@ Item {
148
148
149 ScopeStyle {
149 ScopeStyle {
150 id: overviewScopeStyle
150 id: overviewScopeStyle
151+ style: { "foreground-color" : "white",
151− style: { "foreground-color" : "white", "background-color" : "transparent" }
152+ "background-color" : "transparent",
153+ "page-header": {
154+ "background": "color:///transparent"
155+ }
156+ }
152 }
157 }
153
158
154 DashBackground {
159 DashBackground {
@@ -458,6 +463,7 @@ Item {
458 objectName: "scopesOverviewPreviewListView"
463 objectName: "scopesOverviewPreviewListView"
459 scope: root.scope
464 scope: root.scope
460 scopeStyle: overviewScopeStyle
465 scopeStyle: overviewScopeStyle
466+ showSignatureLine: false
461 visible: x != width
467 visible: x != width
462 width: parent.width
468 width: parent.width
463 height: parent.height
469 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
32{
32{
33 QTimer::singleShot(1500, this, SLOT(slotLoaded()));
33 QTimer::singleShot(1500, this, SLOT(slotLoaded()));
34 connect(scope, SIGNAL(currentNavigationIdChanged()), this, SLOT(slotCurrentNavigationChanged()));
34 connect(scope, SIGNAL(currentNavigationIdChanged()), this, SLOT(slotCurrentNavigationChanged()));
35+ connect(scope, SIGNAL(currentAltNavigationIdChanged()), this, SLOT(slotCurrentNavigationChanged()));
35}
36}
36
37
37QString Navigation::navigationId() const
38QString Navigation::navigationId() const
@@ -106,7 +107,11 @@ QVariant Navigation::data(const QModelIndex &index, int role) const
106 case RoleHasChildren:
107 case RoleHasChildren:
107 return m_navigationId == "root" && index.row() != 3;
108 return m_navigationId == "root" && index.row() != 3;
108 case RoleIsActive:
109 case RoleIsActive:
110+ if (m_navigationId.startsWith("alt")) {
111+ return m_scope->currentAltNavigationId() == data(index, RoleNavigationId);
112+ } else {
109− return m_scope->currentNavigationId() == data(index, RoleNavigationId);
113+ return m_scope->currentNavigationId() == data(index, RoleNavigationId);
114+ }
110 }
115 }
111 return QVariant();
116 return QVariant();
112}
117}
tests/mocks/Unity/fake_scope.cpp+15−2
@@ -226,9 +226,17 @@ QVariantMap Scope::customizations() const
226 m["background-color"] = "red";
226 m["background-color"] = "red";
227 m["foreground-color"] = "blue";
227 m["foreground-color"] = "blue";
228 m["page-header"] = h;
228 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;
229 } else if (m_id == "MockScope5") {
232 } else if (m_id == "MockScope5") {
230 h["background"] = "gradient:///lightgrey/grey";
233 h["background"] = "gradient:///lightgrey/grey";
231 h["logo"] = QUrl("../../../tests/qmltests/Dash/tst_PageHeader/logo-ubuntu-orange.svg");
234 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";
232 m["page-header"] = h;
240 m["page-header"] = h;
233 }
241 }
234 return m;
242 return m;
@@ -263,11 +271,16 @@ unity::shell::scopes::NavigationInterface* Scope::getAltNavigation(QString const
263
271
264 QString parentId;
272 QString parentId;
265 QString parentLabel;
273 QString parentLabel;
266− if (id.startsWith("altmiddle")) {
274+ if (id != "altroot") {
267 parentId = "altroot";
275 parentId = "altroot";
268 parentLabel = "altroot";
276 parentLabel = "altroot";
269 }
277 }
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;
271}
284}
272
285
273void Scope::setNavigationState(const QString &navigationId, bool isAltNavigation)
286void Scope::setNavigationState(const QString &navigationId, bool isAltNavigation)
tests/plugins/Dash/cardcreator/2.res+3−3
@@ -26,7 +26,7 @@ Loader {
26 gradientColor: getColor(1) || color;
26 gradientColor: getColor(1) || color;
27 anchors.fill: parent;
27 anchors.fill: parent;
28 image: backgroundImage.source ? backgroundImage : null;
28 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);
30 property Image backgroundImage: Image {
30 property Image backgroundImage: Image {
31 objectName: "backgroundImage";
31 objectName: "backgroundImage";
32 source: {
32 source: {
@@ -94,7 +94,7 @@ Image {
94 wrapMode: Text.Wrap;
94 wrapMode: Text.Wrap;
95 maximumLineCount: 2;
95 maximumLineCount: 2;
96 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
96 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");
98 visible: showHeader ;
98 visible: showHeader ;
99 text: root.title;
99 text: root.title;
100 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
100 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -112,7 +112,7 @@ Image {
112 elide: Text.ElideRight;
112 elide: Text.ElideRight;
113 fontSize: "small";
113 fontSize: "small";
114 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
114 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");
116 visible: titleLabel.visible && titleLabel.text;
116 visible: titleLabel.visible && titleLabel.text;
117 text: cardData && cardData["subtitle"] || "";
117 text: cardData && cardData["subtitle"] || "";
118 font.weight: Font.Light;
118 font.weight: Font.Light;
tests/plugins/Dash/cardcreator/5.res+3−3
@@ -69,7 +69,7 @@ Loader {
69 sourceComponent: ShaderEffect {
69 sourceComponent: ShaderEffect {
70 id: overlay;
70 id: overlay;
71 height: (fixedHeaderHeight > 0 ? fixedHeaderHeight : headerHeight) + units.gu(2);
71 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);
73 property color overlayColor: cardData && cardData["overlayColor"] || "#99000000";
73 property color overlayColor: cardData && cardData["overlayColor"] || "#99000000";
74 property var source: ShaderEffectSource {
74 property var source: ShaderEffectSource {
75 id: shaderSource;
75 id: shaderSource;
@@ -113,7 +113,7 @@ Label {
113 wrapMode: Text.Wrap;
113 wrapMode: Text.Wrap;
114 maximumLineCount: 2;
114 maximumLineCount: 2;
115 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
115 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");
117 visible: showHeader && overlayLoader.active;
117 visible: showHeader && overlayLoader.active;
118 text: root.title;
118 text: root.title;
119 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
119 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -131,7 +131,7 @@ Label {
131 elide: Text.ElideRight;
131 elide: Text.ElideRight;
132 fontSize: "small";
132 fontSize: "small";
133 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
133 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");
135 visible: titleLabel.visible && titleLabel.text;
135 visible: titleLabel.visible && titleLabel.text;
136 text: cardData && cardData["subtitle"] || "";
136 text: cardData && cardData["subtitle"] || "";
137 font.weight: Font.Light;
137 font.weight: Font.Light;
tests/plugins/Dash/cardcreator/6.res+4−4
@@ -26,7 +26,7 @@ Loader {
26 gradientColor: getColor(1) || color;
26 gradientColor: getColor(1) || color;
27 anchors.fill: parent;
27 anchors.fill: parent;
28 image: backgroundImage.source ? backgroundImage : null;
28 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);
30 property Image backgroundImage: Image {
30 property Image backgroundImage: Image {
31 objectName: "backgroundImage";
31 objectName: "backgroundImage";
32 source: {
32 source: {
@@ -70,7 +70,7 @@ Item {
70 wrapMode: Text.Wrap;
70 wrapMode: Text.Wrap;
71 maximumLineCount: 2;
71 maximumLineCount: 2;
72 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
72 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");
74 visible: showHeader ;
74 visible: showHeader ;
75 text: root.title;
75 text: root.title;
76 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
76 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -88,7 +88,7 @@ Item {
88 elide: Text.ElideRight;
88 elide: Text.ElideRight;
89 fontSize: "small";
89 fontSize: "small";
90 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
90 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");
92 visible: titleLabel.visible && titleLabel.text;
92 visible: titleLabel.visible && titleLabel.text;
93 text: cardData && cardData["subtitle"] || "";
93 text: cardData && cardData["subtitle"] || "";
94 font.weight: Font.Light;
94 font.weight: Font.Light;
@@ -103,7 +103,7 @@ Item {
103 rightMargin: units.gu(1);
103 rightMargin: units.gu(1);
104 }
104 }
105 source: cardData && cardData["emblem"] || "";
105 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");
107 width: height;
107 width: height;
108 height: source != "" ? titleLabel.font.pixelSize : 0;
108 height: source != "" ? titleLabel.font.pixelSize : 0;
109 }
109 }
tests/plugins/Dash/cardcreator/7.res+4−4
@@ -26,7 +26,7 @@ Loader {
26 gradientColor: getColor(1) || color;
26 gradientColor: getColor(1) || color;
27 anchors.fill: parent;
27 anchors.fill: parent;
28 image: backgroundImage.source ? backgroundImage : null;
28 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);
30 property Image backgroundImage: Image {
30 property Image backgroundImage: Image {
31 objectName: "backgroundImage";
31 objectName: "backgroundImage";
32 source: {
32 source: {
@@ -95,7 +95,7 @@ Image {
95 wrapMode: Text.Wrap;
95 wrapMode: Text.Wrap;
96 maximumLineCount: 2;
96 maximumLineCount: 2;
97 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
97 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");
99 visible: showHeader ;
99 visible: showHeader ;
100 text: root.title;
100 text: root.title;
101 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
101 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -113,7 +113,7 @@ Image {
113 elide: Text.ElideRight;
113 elide: Text.ElideRight;
114 fontSize: "small";
114 fontSize: "small";
115 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
115 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");
117 visible: titleLabel.visible && titleLabel.text;
117 visible: titleLabel.visible && titleLabel.text;
118 text: cardData && cardData["subtitle"] || "";
118 text: cardData && cardData["subtitle"] || "";
119 font.weight: Font.Light;
119 font.weight: Font.Light;
@@ -127,7 +127,7 @@ Image {
127 rightMargin: units.gu(1);
127 rightMargin: units.gu(1);
128 top: subtitleLabel.bottom;
128 top: subtitleLabel.bottom;
129 }
129 }
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");
131 model: cardData && cardData["attributes"];
131 model: cardData && cardData["attributes"];
132 }
132 }
133
133
tests/plugins/Dash/tst_ScopeStyle.qml+73−68
@@ -18,6 +18,7 @@ import QtQuick 2.2
18import QtTest 1.0
18import QtTest 1.0
19import Dash 0.1
19import Dash 0.1
20import Ubuntu.Components 1.1
20import Ubuntu.Components 1.1
21+import Utils 0.1
21
22
22Rectangle {
23Rectangle {
23 width: units.gu(40)
24 width: units.gu(40)
@@ -35,39 +36,23 @@ Rectangle {
35 property color color
36 property color color
36 property var styles: [
37 property var styles: [
37 {},
38 {},
38− { "foreground-color": "red", "background-color": "black", "page-header": { "logo": "/foo/bar" } },
39+ { "foreground-color": "red", "background-color": "black", "page-header": { "logo": "/foo/bar" },
40+ "preview-button-color": "red"},
39− { "foreground-color": "green", "background-color": "white", "page-header": { "foreground-color": "black" } },
41+ { "foreground-color": "green", "background-color": "white",
42+ "page-header": { "foreground-color": "black",
43+ "divider-color": "blue" } },
40− { "foreground-color": "blue", "background-color": "darkgrey", "page-header": { "background": "gradient:///white/blue" } },
44+ { "foreground-color": "blue", "background-color": "darkgrey",
45+ "page-header": { "background": "gradient:///white/blue",
46+ "navigation-background": "gradient:///white/blue" } },
41 ]
47 ]
42
48
43 function cleanup() {
49 function cleanup() {
44 testCase.color = "transparent";
50 testCase.color = "transparent";
45 }
51 }
46
52
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−
68 function test_foreground_data() {
53 function test_foreground_data() {
69 return [
54 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 },
71 { tag: "red on black", index: 1, foreground: "red", luminance: 0.2126 },
56 { tag: "red on black", index: 1, foreground: "red", luminance: 0.2126 },
72 { tag: "green on white", index: 2, foreground: "green", luminance: 0.3590 },
57 { tag: "green on white", index: 2, foreground: "green", luminance: 0.3590 },
73 { tag: "blue on darkgrey", index: 3, foreground: "blue", luminance: 0.0722 },
58 { tag: "blue on darkgrey", index: 3, foreground: "blue", luminance: 0.0722 },
@@ -83,7 +68,7 @@ Rectangle {
83
68
84 function test_background_data() {
69 function test_background_data() {
85 return [
70 return [
86− { tag: "default", index: 0, background: "transparent" },
71+ { tag: "default", index: 0, background: "#00f5f5f5", luminance: 0.9608 },
87 { tag: "red on black", index: 1, background: "black", luminance: 0 },
72 { tag: "red on black", index: 1, background: "black", luminance: 0 },
88 { tag: "green on white", index: 2, background: "white", luminance: 1 },
73 { tag: "green on white", index: 2, background: "white", luminance: 1 },
89 { tag: "blue on darkgrey", index: 3, background: "darkgrey", luminance: 0.6627 },
74 { tag: "blue on darkgrey", index: 3, background: "darkgrey", luminance: 0.6627 },
@@ -94,53 +79,35 @@ Rectangle {
94 tool.style = testCase.styles[data.index];
79 tool.style = testCase.styles[data.index];
95 verify(Qt.colorEqual(tool.background, data.background),
80 verify(Qt.colorEqual(tool.background, data.background),
96 "Background color not equal: %1 != %2".arg(tool.background).arg(data.background));
81 "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));
82+ compare(tool.backgroundLuminance.toFixed(4), data.luminance.toFixed(4));
99− }
100 }
83 }
101
84
102− function test_threshold_data() {
85+ function test_getTextColor_data() {
103 return [
86 return [
104− { tag: "default", index: 0, threshold: 0.7863 },
87+ { tag: "default on black", background: "black", index: 0, textColor: "white" },
105− { tag: "red on black", index: 1, threshold: 0.1063 },
88+ { tag: "default on lightgrey", background: "lightgrey", index: 0, textColor: UbuntuColors.darkGrey },
106− { tag: "green on white", index: 2, threshold: 0.6795 },
89+ { tag: "default on white", background: "white", index: 0, textColor: UbuntuColors.darkGrey },
107− { tag: "blue on darkgrey", index: 3, threshold: 0.3675 },
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" },
108 ];
103 ];
109 }
104 }
110
105
111− function test_threshold(data) {
106+ function test_getTextColor(data) {
112 tool.style = testCase.styles[data.index];
107 tool.style = testCase.styles[data.index];
113− compare(tool.threshold.toFixed(4), data.threshold.toFixed(4), "Luminance threshold was incorrect.");
108+ var textColor = tool.getTextColor(Style.luminance(data.background));
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),
109+ verify(Qt.colorEqual(textColor, data.textColor),
143− "Dark color not equal: %1 != %2".arg(tool.dark).arg(data.dark));
110+ "TextColor not equal: %1 != %2".arg(textColor).arg(data.textColor));
144 }
111 }
145
112
146 function test_headerLogo_data() {
113 function test_headerLogo_data() {
@@ -157,7 +124,7 @@ Rectangle {
157
124
158 function test_headerForeground_data() {
125 function test_headerForeground_data() {
159 return [
126 return [
160− { tag: "default", index: 0, headerForeground: Theme.palette.normal.baseText },
127+ { tag: "default", index: 0, headerForeground: UbuntuColors.darkGrey },
161 { tag: "black", index: 2, headerForeground: "black" },
128 { tag: "black", index: 2, headerForeground: "black" },
162 ];
129 ];
163 }
130 }
@@ -170,7 +137,7 @@ Rectangle {
170
137
171 function test_headerBackground_data() {
138 function test_headerBackground_data() {
172 return [
139 return [
173− { tag: "default", index: 0, headerBackground: "" },
140+ { tag: "default", index: 0, headerBackground: "color:///#f5f5f5" },
174 { tag: "black", index: 3, headerBackground: "gradient:///white/blue" },
141 { tag: "black", index: 3, headerBackground: "gradient:///white/blue" },
175 ];
142 ];
176 }
143 }
@@ -179,5 +146,43 @@ Rectangle {
179 tool.style = testCase.styles[data.index];
146 tool.style = testCase.styles[data.index];
180 compare(tool.headerBackground, data.headerBackground, "Header background was incorrect.");
147 compare(tool.headerBackground, data.headerBackground, "Header background was incorrect.");
181 }
148 }
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+ }
182 }
187 }
183}
188}
tests/plugins/Utils/CMakeLists.txt+5
@@ -27,3 +27,8 @@ run_tests(
27 qsortfilterproxymodeltest
27 qsortfilterproxymodeltest
28 timeformattertest
28 timeformattertest
29 )
29 )
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)
50add_qml_test(Dash/Previews PreviewProgress)
50add_qml_test(Dash/Previews PreviewProgress)
51add_qml_test(Dash/Previews PreviewRatingDisplay)
51add_qml_test(Dash/Previews PreviewRatingDisplay)
52add_qml_test(Dash/Previews PreviewRatingInput)
52add_qml_test(Dash/Previews PreviewRatingInput)
53+add_qml_test(Dash/Previews PreviewTable)
53add_qml_test(Dash/Previews PreviewTextSummary)
54add_qml_test(Dash/Previews PreviewTextSummary)
54add_qml_test(Dash/Previews PreviewVideoPlayback)
55add_qml_test(Dash/Previews PreviewVideoPlayback)
55add_qml_test(Dash/Previews PreviewWidgetFactory)
56add_qml_test(Dash/Previews PreviewWidgetFactory)
tests/qmltests/Components/tst_Background.qml+5−4
@@ -46,10 +46,10 @@ Rectangle {
46
46
47 function test_style_data() {
47 function test_style_data() {
48 return [
48 return [
49− { tag: "empty", style: "" },
49+ { tag: "empty", style: "", luminance: 0.5 },
50− { tag: "solid", style: "color:///black" },
50+ { tag: "solid", style: "color:///black", luminance: 0 },
51− { tag: "gradient", style: "gradient:///black/red" },
51+ { tag: "gradient", style: "gradient:///black/red", luminance: 0.1063 },
52− { tag: "image", style: "/some/path" },
52+ { tag: "image", style: "/some/path", luminance: 0.5 },
53 ];
53 ];
54 }
54 }
55
55
@@ -58,6 +58,7 @@ Rectangle {
58 expectFail("empty", "Empty style should not create a background.");
58 expectFail("empty", "Empty style should not create a background.");
59 loadedSpy.wait();
59 loadedSpy.wait();
60 compare(background.item.objectName, data.tag, "Background should be %1".arg(data.style));
60 compare(background.item.objectName, data.tag, "Background should be %1".arg(data.style));
61+ compare(background.luminance, data.luminance);
61 }
62 }
62
63
63 function test_solid() {
64 function test_solid() {
tests/qmltests/Dash/Previews/tst_PreviewActions.qml+8−8
@@ -27,21 +27,21 @@ Rectangle {
27 height: units.gu(80)
27 height: units.gu(80)
28
28
29 property var actionDataOneAction: {
29 property var actionDataOneAction: {
30− "actions": [{"label": "Some Label", "icon": "../graphics/play_button.png", "id": "someid"}]
30+ "actions": [{"label": "Some Label", "id": "someid"}]
31 }
31 }
32
32
33 property var actionDataTwoActions: {
33 property var actionDataTwoActions: {
34− "actions": [{"label": "Some Label A", "icon": "../graphics/icon_clear.png", "id": "someid1"},
34+ "actions": [{"label": "Some Label A", "id": "someid1"},
35− {"label": "Some Label B", "icon": "../graphics/play_button.png", "id": "someid2"}
35+ {"label": "Some Label B", "id": "someid2"}
36 ]
36 ]
37 }
37 }
38
38
39 property var actionDataFiveActions: {
39 property var actionDataFiveActions: {
40− "actions": [{"label": "Some Label C", "icon": "../graphics/play_button.png", "id": "someid3"},
40+ "actions": [{"label": "Some Label C", "id": "someid3"},
41− {"label": "Some Label D", "icon": "../graphics/icon_clear.png", "id": "someid4"},
41+ {"label": "Some Label D", "id": "someid4"},
42− {"label": "Some Label E", "icon": "../graphics/play_button.png", "id": "someid5"},
42+ {"label": "Some Label E", "id": "someid5"},
43− {"label": "Some Label F", "icon": "../graphics/icon_clear.png", "id": "someid6"},
43+ {"label": "Some Label F", "id": "someid6"},
44− {"label": "Some Label G", "icon": "../graphics/play_button.png", "id": "someid7"}
44+ {"label": "Some Label G", "id": "someid7"}
45 ]
45 ]
46 }
46 }
47
47
tests/qmltests/Dash/Previews/tst_PreviewExpandable.qml+5−1
@@ -30,6 +30,10 @@ Rectangle {
30 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."
30 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."
31 property string shortText: "This is a short text :)"
31 property string shortText: "This is a short text :)"
32
32
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+
33 ListModel {
37 ListModel {
34 id: widgetsModel
38 id: widgetsModel
35 }
39 }
@@ -42,7 +46,7 @@ Rectangle {
42
46
43 Component.onCompleted: {
47 Component.onCompleted: {
44 widgetsModel.append({"type": "text", "widgetId": "text1", "properties": { "text": longText }});
48 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 }});
46 widgetsModel.append({"type": "text", "widgetId": "text3", "properties": { "text": shortText }});
50 widgetsModel.append({"type": "text", "widgetId": "text3", "properties": { "text": shortText }});
47 widgetsModel.append({"type": "text", "widgetId": "text4", "properties": { "text": longText }});
51 widgetsModel.append({"type": "text", "widgetId": "text4", "properties": { "text": longText }});
48 }
52 }
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 {
84 { tag: "Progress", type: "progress", source: "PreviewProgress.qml" },
84 { tag: "Progress", type: "progress", source: "PreviewProgress.qml" },
85 { tag: "Rating Input", type: "rating-input", source: "PreviewRatingInput.qml" },
85 { tag: "Rating Input", type: "rating-input", source: "PreviewRatingInput.qml" },
86 { tag: "Rating Display", type: "reviews", source: "PreviewRatingDisplay.qml" },
86 { tag: "Rating Display", type: "reviews", source: "PreviewRatingDisplay.qml" },
87+ { tag: "Table", type: "table", source: "PreviewTable.qml" },
87 { tag: "Text", type: "text", source: "PreviewTextSummary.qml" },
88 { tag: "Text", type: "text", source: "PreviewTextSummary.qml" },
88 { tag: "Video", type: "video", source: "PreviewVideoPlayback.qml" },
89 { tag: "Video", type: "video", source: "PreviewVideoPlayback.qml" },
89 ];
90 ];
tests/qmltests/Dash/artwork/background.pngnew file

Binary file not shown.

tests/qmltests/Dash/tst_DashContent.qml+146−46
@@ -266,56 +266,57 @@ Item {
266 verify(carouselLV.tileWidth / carouselLV.tileHeight == cardTool.components["art"]["aspect-ratio"]);
266 verify(carouselLV.tileWidth / carouselLV.tileHeight == cardTool.components["art"]["aspect-ratio"]);
267 }
267 }
268
268
269− function test_navigations() {
269+ function test_mainNavigation() {
270 var dashContentList = findChild(dashContent, "dashContentList");
270 var dashContentList = findChild(dashContent, "dashContentList");
271 tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
271 tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
272 var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
272 var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
273 tryCompare(dashNavigation, "visible", true);
273 tryCompare(dashNavigation, "visible", true);
274+ var dashNavigationButton = findChild(dashContentList.currentItem, "navigationButton");
274− compare(dashNavigation.showList, false);
275+ compare(dashNavigationButton.showList, false);
275− waitForRendering(dashNavigation);
276+ waitForRendering(dashNavigationButton);
276− mouseClick(dashNavigation, 0, 0);
277+ mouseClick(dashNavigationButton, 0, 0);
277− compare(dashNavigation.showList, true);
278+ compare(dashNavigationButton.showList, true);
278
279
279− var navigationListView = findChild(dashNavigation, "navigationListView");
280+ var navigationListView = findChild(dashNavigationButton, "navigationListView");
280 compare(navigationListView.count, 1);
281 compare(navigationListView.count, 1);
281 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
282 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
282
283
283 waitForRendering(navigationListView);
284 waitForRendering(navigationListView);
284 waitForRendering(navigationListView.currentItem);
285 waitForRendering(navigationListView.currentItem);
285
286
286− var allButton = findChild(dashNavigation, "allButton");
287+ var allButton = findChild(dashNavigationButton, "allButton");
287 compare(allButton.visible, false);
288 compare(allButton.visible, false);
288
289
289− var navigation = findChild(dashNavigation, "navigation0child3");
290+ var navigation = findChild(dashNavigationButton, "navigation0child3");
290 mouseClick(navigation, 0, 0);
291 mouseClick(navigation, 0, 0);
291− compare(dashNavigation.showList, false);
292+ compare(dashNavigationButton.showList, false);
292− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle3");
293+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle3");
293 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
294 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
294
295
295− mouseClick(dashNavigation, 0, 0);
296+ mouseClick(dashNavigationButton, 0, 0);
296− compare(dashNavigation.showList, true);
297+ compare(dashNavigationButton.showList, true);
297 waitForRendering(navigationListView);
298 waitForRendering(navigationListView);
298 waitForRendering(navigationListView.currentItem);
299 waitForRendering(navigationListView.currentItem);
299 compare(allButton.visible, true);
300 compare(allButton.visible, true);
300
301
301 mouseClick(allButton, 0, 0);
302 mouseClick(allButton, 0, 0);
302− compare(dashNavigation.showList, false);
303+ compare(dashNavigationButton.showList, false);
303− tryCompare(dashNavigation.currentNavigation, "navigationId", "root");
304+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "root");
304 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
305 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
305
306
306− mouseClick(dashNavigation, 0, 0);
307+ mouseClick(dashNavigationButton, 0, 0);
307− compare(dashNavigation.showList, true);
308+ compare(dashNavigationButton.showList, true);
308 waitForRendering(navigationListView);
309 waitForRendering(navigationListView);
309 waitForRendering(navigationListView.currentItem);
310 waitForRendering(navigationListView.currentItem);
310 compare(allButton.visible, false);
311 compare(allButton.visible, false);
311
312
312− navigation = findChild(dashNavigation, "navigation0child2");
313+ navigation = findChild(dashNavigationButton, "navigation0child2");
313 mouseClick(navigation, 0, 0);
314 mouseClick(navigation, 0, 0);
314− compare(dashNavigation.showList, true);
315+ compare(dashNavigationButton.showList, true);
315− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
316+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
316 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
317 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
317
318
318− var navigationList1 = findChild(dashNavigation, "navigation1");
319+ var navigationList1 = findChild(dashNavigationButton, "navigation1");
319 allButton = findChild(navigationList1, "allButton");
320 allButton = findChild(navigationList1, "allButton");
320 var backButton = findChild(findChild(navigationList1, "navigation1"), "backButton");
321 var backButton = findChild(findChild(navigationList1, "navigation1"), "backButton");
321 compare(allButton.visible, true);
322 compare(allButton.visible, true);
@@ -324,61 +325,160 @@ Item {
324 tryCompare(navigationListView, "contentX", navigationList1.x);
325 tryCompare(navigationListView, "contentX", navigationList1.x);
325 waitForRendering(navigationListView);
326 waitForRendering(navigationListView);
326 mouseClick(allButton, 0, 0);
327 mouseClick(allButton, 0, 0);
327− compare(dashNavigation.showList, false);
328+ compare(dashNavigationButton.showList, false);
328− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
329+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
329 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
330 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
330
331
331− mouseClick(dashNavigation, 0, 0);
332+ mouseClick(dashNavigationButton, 0, 0);
332− compare(dashNavigation.showList, true);
333+ compare(dashNavigationButton.showList, true);
333 waitForRendering(navigationListView);
334 waitForRendering(navigationListView);
334 waitForRendering(navigationListView.currentItem);
335 waitForRendering(navigationListView.currentItem);
335 compare(allButton.visible, true);
336 compare(allButton.visible, true);
336 compare(backButton.visible, true);
337 compare(backButton.visible, true);
337
338
338 tryCompare(navigationList1.navigation, "loaded", true);
339 tryCompare(navigationList1.navigation, "loaded", true);
339− navigation = findChild(dashNavigation, "navigation1child2");
340+ navigation = findChild(dashNavigationButton, "navigation1child2");
340 mouseClick(navigation, 0, 0);
341 mouseClick(navigation, 0, 0);
341− compare(dashNavigation.showList, false);
342+ compare(dashNavigationButton.showList, false);
342− tryCompare(dashNavigation.currentNavigation, "navigationId", "childmiddle22");
343+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "childmiddle22");
343 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
344 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
344
345
345− mouseClick(dashNavigation, 0, 0);
346+ mouseClick(dashNavigationButton, 0, 0);
346− compare(dashNavigation.showList, true);
347+ compare(dashNavigationButton.showList, true);
347 waitForRendering(navigationListView);
348 waitForRendering(navigationListView);
348 waitForRendering(navigationListView.currentItem);
349 waitForRendering(navigationListView.currentItem);
349
350
350 tryCompare(navigationList1.navigation, "loaded", true);
351 tryCompare(navigationList1.navigation, "loaded", true);
351− navigation = findChild(dashNavigation, "navigation1child3");
352+ navigation = findChild(dashNavigationButton, "navigation1child3");
352 mouseClick(navigation, 0, 0);
353 mouseClick(navigation, 0, 0);
353− compare(dashNavigation.showList, false);
354+ compare(dashNavigationButton.showList, false);
354− tryCompare(dashNavigation.currentNavigation, "navigationId", "childmiddle23");
355+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "childmiddle23");
355 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
356 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
356
357
357− mouseClick(dashNavigation, 0, 0);
358+ mouseClick(dashNavigationButton, 0, 0);
358− compare(dashNavigation.showList, true);
359+ compare(dashNavigationButton.showList, true);
359 waitForRendering(navigationListView);
360 waitForRendering(navigationListView);
360 waitForRendering(navigationListView.currentItem);
361 waitForRendering(navigationListView.currentItem);
361 mouseClick(backButton, 0, 0);
362 mouseClick(backButton, 0, 0);
362
363
363− tryCompare(dashNavigation.currentNavigation, "navigationId", "root");
364+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "root");
364 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
365 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
365− compare(dashNavigation.showList, true);
366+ compare(dashNavigationButton.showList, true);
366− mouseClick(dashNavigation, 0, 0);
367+ mouseClick(dashNavigationButton, 0, 0);
367− compare(dashNavigation.showList, false);
368+ compare(dashNavigationButton.showList, false);
368
369
369− mouseClick(dashNavigation, 0, 0);
370+ mouseClick(dashNavigationButton, 0, 0);
370− compare(dashNavigation.showList, true);
371+ compare(dashNavigationButton.showList, true);
371 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
372 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
372− navigation = findChild(dashNavigation, "navigation0child2");
373+ navigation = findChild(dashNavigationButton, "navigation0child2");
373 mouseClick(navigation, 0, 0);
374 mouseClick(navigation, 0, 0);
374− compare(dashNavigation.showList, true);
375+ compare(dashNavigationButton.showList, true);
375− navigationList1 = findChild(dashNavigation, "navigation1");
376+ navigationList1 = findChild(dashNavigationButton, "navigation1");
376 compare(navigationList1.navigation.loaded, false);
377 compare(navigationList1.navigation.loaded, false);
377− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
378+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
378 allButton = findChild(navigationList1, "allButton");
379 allButton = findChild(navigationList1, "allButton");
379− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
380+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
380 mouseClick(allButton, 0, 0);
381 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);
382 }
482 }
383
483
384 function test_searchHint() {
484 function test_searchHint() {
tests/qmltests/Dash/tst_GenericScopeView.qml+11−7
@@ -20,7 +20,7 @@ import Unity 0.2
20import ".."
20import ".."
21import "../../../qml/Dash"
21import "../../../qml/Dash"
22import "../../../qml/Components"
22import "../../../qml/Components"
23−import Ubuntu.Components 0.1
23+import Ubuntu.Components 1.1
24import Unity.Test 0.1 as UT
24import Unity.Test 0.1 as UT
25
25
26Item {
26Item {
@@ -229,11 +229,15 @@ Item {
229 shell.width = units.gu(20)
229 shell.width = units.gu(20)
230 var categoryListView = findChild(genericScopeView, "categoryListView");
230 var categoryListView = findChild(genericScopeView, "categoryListView");
231 categoryListView.contentY = units.gu(20);
231 categoryListView.contentY = units.gu(20);
232− var seeAll = findChild(category, "seeAll")
232+ var seeAll = findChild(category, "seeAll");
233+ var floatingSeeLess = findChild(genericScopeView, "floatingSeeLess");
233 mouseClick(seeAll, seeAll.width / 2, seeAll.height / 2);
234 mouseClick(seeAll, seeAll.width / 2, seeAll.height / 2);
234 tryCompare(category, "expanded", true);
235 tryCompare(category, "expanded", true);
236+ tryCompareFunction(function() {
237+ return category.item.height + floatingSeeLess.height ==
235− tryCompareFunction(function() { return category.item.height == genericScopeView.height - category.item.displayMarginBeginning - category.item.displayMarginEnd; }, true);
238+ genericScopeView.height - category.item.displayMarginBeginning - category.item.displayMarginEnd;
239+ }, true);
236− mouseClick(seeAll, seeAll.width / 2, seeAll.height / 2);
240+ mouseClick(floatingSeeLess, floatingSeeLess.width / 2, floatingSeeLess.height / 2);
237 tryCompare(category, "expanded", false);
241 tryCompare(category, "expanded", false);
238 }
242 }
239
243
@@ -395,9 +399,9 @@ Item {
395
399
396 function test_header_style_data() {
400 function test_header_style_data() {
397 return [
401 return [
398− { tag: "Default", index: 0, foreground: Theme.palette.normal.baseText, background: "", logo: "" },
402+ { tag: "Default", index: 0, foreground: UbuntuColors.darkGrey, background: "color:///#f5f5f5", logo: "" },
399− { tag: "Foreground", index: 1, foreground: "yellow", background: "", logo: "" },
403+ { tag: "Foreground", index: 1, foreground: "yellow", background: "color:///#f5f5f5", logo: "" },
400− { tag: "Logo+Background", index: 2, foreground: Theme.palette.normal.baseText, background: "gradient:///lightgrey/grey",
404+ { tag: "Logo+Background", index: 2, foreground: UbuntuColors.darkGrey, background: "gradient:///lightgrey/grey",
401 logo: Qt.resolvedUrl("../Dash/tst_PageHeader/logo-ubuntu-orange.svg") },
405 logo: Qt.resolvedUrl("../Dash/tst_PageHeader/logo-ubuntu-orange.svg") },
402 ];
406 ];
403 }
407 }