on xenial help sign in
Add dash PageHeader styling.
0df783eMichał Sawicz
plugins/Dash/ScopeStyle.qml+6
@@ -70,6 +70,12 @@ QtObject {
70 /// Source of the logo image for the header
70 /// Source of the logo image for the header
71 readonly property url headerLogo: "logo" in d.headerStyle ? d.headerStyle["logo"] : ""
71 readonly property url headerLogo: "logo" in d.headerStyle ? d.headerStyle["logo"] : ""
72
72
73+ /// Background style for the header
74+ readonly property url headerBackground: "background" in d.headerStyle ? d.headerStyle["background"] : ""
75+
76+ /// Foreground color for the header
77+ readonly property color headerForeground: "foreground-color" in d.headerStyle ? d.headerStyle["foreground-color"] : foreground
78+
73 //! @cond
79 //! @cond
74 property var d: QtObject {
80 property var d: QtObject {
75 readonly property real foregroundLuminance: luminance(foreground)
81 readonly property real foregroundLuminance: luminance(foreground)
qml/Components/Background.qmlnew file+77
@@ -0,0 +1,77 @@
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+
19+Loader {
20+ id: root
21+ anchors.fill: parent
22+
23+ property url style
24+ 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)
26+
27+ sourceComponent: {
28+ if (style == "") return null;
29+ if (!parsedStyle) return image;
30+ if (parsedStyle[1] === "color") return solid
31+ if (parsedStyle[1] === "gradient") return gradient
32+ }
33+
34+ onLoaded: if (item.hasOwnProperty("parsedStyle")) {
35+ item.parsedStyle = Qt.binding(function() { return root.parsedStyle } );
36+ }
37+
38+ Component {
39+ id: solid
40+
41+ Rectangle {
42+ objectName: "solid"
43+
44+ property var parsedStyle
45+
46+ color: parsedStyle ? parsedStyle[2] : "#ffffff"
47+ }
48+ }
49+
50+ Component {
51+ id: gradient
52+
53+ Rectangle {
54+ objectName: "gradient"
55+
56+ property var parsedStyle
57+
58+ gradient: Gradient {
59+ GradientStop { position: 0; color: parsedStyle ? parsedStyle[2] : "#000000" }
60+ GradientStop { position: 1; color: parsedStyle ? parsedStyle[3] : "#000000" }
61+ }
62+ }
63+ }
64+
65+ Component {
66+ id: image
67+
68+ Image {
69+ objectName: "image"
70+
71+ source: width > 0 && height > 0 && root.style
72+
73+ sourceSize.width: width
74+ sourceSize.height: height
75+ }
76+ }
77+}
qml/Components/PageHeader.qml+6−1
@@ -127,6 +127,11 @@ Item {
127 property bool showSearch: false
127 property bool showSearch: false
128 property var popover: null
128 property var popover: null
129
129
130+ Background {
131+ objectName: "headerBackground"
132+ style: scopeStyle.headerBackground
133+ }
134+
130 Behavior on contentY {
135 Behavior on contentY {
131 UbuntuNumberAnimation {
136 UbuntuNumberAnimation {
132 id: openSearchAnimation
137 id: openSearchAnimation
@@ -225,7 +230,7 @@ Item {
225 height: headerContainer.height
230 height: headerContainer.height
226 contentHeight: height
231 contentHeight: height
227 separatorSource: ""
232 separatorSource: ""
228− textColor: root.scopeStyle ? root.scopeStyle.foreground : "grey"
233+ textColor: root.scopeStyle ? root.scopeStyle.headerForeground : "grey"
229 property var styledItem: header
234 property var styledItem: header
230 property string title: root.title
235 property string title: root.title
231 property var config: PageHeadConfiguration {
236 property var config: PageHeadConfiguration {
tests/mocks/Unity/fake_scope.cpp+6−4
@@ -182,14 +182,16 @@ bool Scope::hasDepartments() const
182
182
183QVariantMap Scope::customizations() const
183QVariantMap Scope::customizations() const
184{
184{
185− QVariantMap m;
185+ QVariantMap m, h;
186 if (m_id == "clickscope") {
186 if (m_id == "clickscope") {
187+ h["foreground-color"] = "yellow";
187 m["background-color"] = "red";
188 m["background-color"] = "red";
188 m["foreground-color"] = "blue";
189 m["foreground-color"] = "blue";
190+ m["page-header"] = h;
189 } else if (m_id == "MockScope5") {
191 } else if (m_id == "MockScope5") {
190− QVariantMap pageHeader;
192+ h["background"] = "gradient:///lightgrey/grey";
191− pageHeader["logo"] = QUrl("../../../tests/qmltests/Components/tst_PageHeader/logo-ubuntu-orange.svg");
193+ h["logo"] = QUrl("../../../tests/qmltests/Components/tst_PageHeader/logo-ubuntu-orange.svg");
192− m["page-header"] = pageHeader;
194+ m["page-header"] = h;
193 }
195 }
194 return m;
196 return m;
195}
197}
tests/plugins/Dash/tst_ScopeStyle.qml+27−2
@@ -36,8 +36,8 @@ Rectangle {
36 property var styles: [
36 property var styles: [
37 {},
37 {},
38 { "foreground-color": "red", "background-color": "black", "page-header": { "logo": "/foo/bar" } },
38 { "foreground-color": "red", "background-color": "black", "page-header": { "logo": "/foo/bar" } },
39− { "foreground-color": "green", "background-color": "white" },
39+ { "foreground-color": "green", "background-color": "white", "page-header": { "foreground-color": "black" } },
40− { "foreground-color": "blue", "background-color": "darkgrey" },
40+ { "foreground-color": "blue", "background-color": "darkgrey", "page-header": { "background": "gradient:///white/blue" } },
41 ]
41 ]
42
42
43 function cleanup() {
43 function cleanup() {
@@ -147,5 +147,30 @@ Rectangle {
147 tool.style = testCase.styles[data.index];
147 tool.style = testCase.styles[data.index];
148 compare(tool.headerLogo, data.headerLogo, "Header logo was incorrect.");
148 compare(tool.headerLogo, data.headerLogo, "Header logo was incorrect.");
149 }
149 }
150+
151+ function test_headerForeground_data() {
152+ return [
153+ { tag: "default", index: 0, headerForeground: "grey" },
154+ { tag: "black", index: 2, headerForeground: "black" },
155+ ];
156+ }
157+
158+ function test_headerForeground(data) {
159+ tool.style = testCase.styles[data.index];
160+ verify(Qt.colorEqual(tool.headerForeground, data.headerForeground),
161+ "Header foreground not equal: %1 != %2".arg(tool.headerForeground).arg(data.headerForeground));
162+ }
163+
164+ function test_headerBackground_data() {
165+ return [
166+ { tag: "default", index: 0, headerBackground: "" },
167+ { tag: "black", index: 3, headerBackground: "gradient:///white/blue" },
168+ ];
169+ }
170+
171+ function test_headerBackground(data) {
172+ tool.style = testCase.styles[data.index];
173+ compare(tool.headerBackground, data.headerBackground, "Header background was incorrect.");
174+ }
150 }
175 }
151}
176}
tests/qmltests/CMakeLists.txt+1
@@ -21,6 +21,7 @@ set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
21
21
22add_qml_test(. Shell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
22add_qml_test(. Shell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
23add_qml_test(. ShellWithPin ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single-pin")
23add_qml_test(. ShellWithPin ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single-pin")
24+add_qml_test(Components Background)
24add_qml_test(Components Carousel)
25add_qml_test(Components Carousel)
25add_qml_test(Components DraggingArea)
26add_qml_test(Components DraggingArea)
26add_qml_test(Components EdgeDemoOverlay)
27add_qml_test(Components EdgeDemoOverlay)
tests/qmltests/Components/tst_Background.qmlnew file+90
@@ -0,0 +1,90 @@
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 Unity.Test 0.1
20+
21+import "../../../qml/Components"
22+
23+Rectangle {
24+ width: units.gu(40)
25+ height: units.gu(71)
26+
27+ Background {
28+ id: background
29+ }
30+
31+ UnityTestCase {
32+ id: testCase
33+ name: "Background"
34+ when: windowShown
35+
36+ SignalSpy {
37+ id: loadedSpy
38+ target: background
39+ signalName: "loaded"
40+ }
41+
42+ function cleanup() {
43+ background.style = "";
44+ loadedSpy.clear();
45+ }
46+
47+ function test_style_data() {
48+ return [
49+ { tag: "empty", style: "" },
50+ { tag: "solid", style: "color:///black" },
51+ { tag: "gradient", style: "gradient:///black/red" },
52+ { tag: "image", style: "/some/path" },
53+ ];
54+ }
55+
56+ function test_style(data) {
57+ background.style = data.style;
58+ expectFail("empty", "Empty style should not create a background.");
59+ loadedSpy.wait();
60+ compare(background.item.objectName, data.tag, "Background should be %1".arg(data.style));
61+ }
62+
63+ function test_solid() {
64+ background.style = "color:///black";
65+ loadedSpy.wait();
66+
67+ verify(Qt.colorEqual(background.item.color, "black"),
68+ "Solid color not equal: %1 != black".arg(background.item.color));
69+ }
70+
71+ function test_gradient() {
72+ background.style = "gradient:///black/red";
73+ loadedSpy.wait();
74+
75+ var stops = background.item.gradient.stops;
76+
77+ verify(Qt.colorEqual(stops[0].color, "black"),
78+ "Top gradient color not equal: %1 != black".arg(stops[0].color));
79+ verify(Qt.colorEqual(stops[1].color, "red"),
80+ "Bottom gradient color not equal: %1 != black".arg(stops[1].color));
81+ }
82+
83+ function test_image() {
84+ background.style = "/some/path";
85+ loadedSpy.wait();
86+
87+ compare(background.item.source, Qt.resolvedUrl("/some/path"), "Image path is incorrect.");
88+ }
89+ }
90+}
tests/qmltests/Dash/tst_GenericScopeView.qml+26−5
@@ -58,6 +58,7 @@ Item {
58 when: scopes.loaded && windowShown
58 when: scopes.loaded && windowShown
59
59
60 property Item previewListView: findChild(genericScopeView, "previewListView")
60 property Item previewListView: findChild(genericScopeView, "previewListView")
61+ property Item header: findChild(genericScopeView, "scopePageHeader")
61
62
62 function init() {
63 function init() {
63 genericScopeView.scope = scopes.getScope(1)
64 genericScopeView.scope = scopes.getScope(1)
@@ -324,13 +325,33 @@ Item {
324 closePreview();
325 closePreview();
325 }
326 }
326
327
327− function test_header_logo() {
328+ function test_header_style_data() {
328− genericScopeView.scope = scopes.getScope(3);
329+ return [
330+ { tag: "Default", index: 0, foreground: "grey", background: "", logo: "" },
331+ { tag: "Foreground", index: 2, foreground: "yellow", background: "", logo: "" },
332+ { tag: "Logo+Background", index: 3, foreground: "grey", background: "gradient:///lightgrey/grey",
333+ logo: Qt.resolvedUrl("../Components/tst_PageHeader/logo-ubuntu-orange.svg") },
334+ ];
335+ }
329
336
330− var image = findChild(genericScopeView, "titleImage");
337+ function test_header_style(data) {
338+ genericScopeView.scope = scopes.getScope(data.index);
339+ waitForRendering(genericScopeView);
340+ verify(header, "Could not find the header.");
341+
342+ var innerHeader = findChild(header, "innerPageHeader");
331− verify(image, "Could not find the title image");
343+ verify(innerHeader, "Could not find the inner header");
332− compare(image.source, Qt.resolvedUrl("../Components/tst_PageHeader/logo-ubuntu-orange.svg"), "Title image has the wrong source");
344+ verify(Qt.colorEqual(innerHeader.textColor, data.foreground),
345+ "Foreground color not equal: %1 != %2".arg(innerHeader.textColor).arg(data.foreground));
333
346
347+ var background = findChild(header, "headerBackground");
348+ verify(background, "Could not find the background");
349+ compare(background.style, data.background);
350+
351+ var image = findChild(genericScopeView, "titleImage");
352+ if (data.logo == "") expectFail(data.tag, "Title image should not exist.");
353+ verify(image, "Could not find the title image.");
354+ compare(image.source, data.logo, "Title image has the wrong source");
334 }
355 }
335 }
356 }
336 }
357 }