on xenial help sign in
Add initial support for scope customizations.

Approved by: Michael Zanetti, Michael Zanetti

commit4c3de717badf2a6ed792ee86f6bd4718de82324a authorMichał Sawicz <michal.sawicz@canonical.com> dateWed 9 Jul 2014, 19:44 (12 years ago) parent80cb348ce4905a
33 files changed+388−81
doc/CMakeLists.txt+2 plugins/Dash/CardCreator.js+4−3 plugins/Dash/ScopeStyle.qml+87 plugins/Dash/qmldir+1 qml/Components/ListItems/Header.qml+1 qml/Components/PageHeader.qml+19−12 qml/Dash/CardCarousel.qml+1 qml/Dash/CardFilterGrid.qml+1 qml/Dash/CardVerticalJournal.qml+1 qml/Dash/DashDepartments.qml+8−7 qml/Dash/DashDepartmentsList.qml+19−17 qml/Dash/DashRenderer.qml+5−4 qml/Dash/GenericScopeView.qml+16 qml/Dash/PreviewListView.qml+3 qml/Dash/Previews/Preview.qml+4 qml/Dash/Previews/PreviewAudioPlayback.qml+3−3 qml/Dash/Previews/PreviewHeader.qml+1−1 qml/Dash/Previews/PreviewRatingDisplay.qml+2−4 qml/Dash/Previews/PreviewRatingInput.qml+2−4 qml/Dash/Previews/PreviewTextSummary.qml+2−4 qml/Dash/Previews/PreviewWidget.qml+3 qml/Dash/Previews/PreviewWidgetFactory.qml+4 qml/Dash/graphics/tick@30.png tests/mocks/Unity/fake_scope.cpp+12−1 tests/plugins/Dash/CMakeLists.txt+2 tests/plugins/Dash/cardcreator/1.res+3−2 tests/plugins/Dash/cardcreator/2.res+5−5 tests/plugins/Dash/cardcreator/3.res+6−4 tests/plugins/Dash/cardcreator/4.res+4−3 tests/plugins/Dash/cardcreator/5.res+3−2 tests/plugins/Dash/tst_ScopeStyle.qml+151 tests/qmltests/Components/tst_PageHeader.qml+4−1 tests/qmltests/Dash/tst_GenericScopeView.qml+9−4
doc/CMakeLists.txt+2
@@ -15,6 +15,8 @@ else()
1515 ${CMAKE_SOURCE_DIR}/src/*.cpp
1616 ${CMAKE_SOURCE_DIR}/plugins/*.h
1717 ${CMAKE_SOURCE_DIR}/plugins/*.cpp
18+ ${CMAKE_SOURCE_DIR}/plugins/*.qml
19+ ${CMAKE_SOURCE_DIR}/plugins/*.js
1820 ${CMAKE_SOURCE_DIR}/qml/*.qml
1921 ${CMAKE_SOURCE_DIR}/qml/*.js
2022 ${CMAKE_SOURCE_DIR}/tests/autopilot/*.py
plugins/Dash/CardCreator.js+4−3
@@ -289,6 +289,7 @@ function cardString(template, components) {
289289 property var cardData; \n\
290290 property var artShapeBorderSource: undefined; \n\
291291 property real fontScale: 1.0; \n\
292+ property var scopeStyle: null; \n\
292293 property int headerAlignment: Text.AlignLeft; \n\
293294 property int fixedHeaderHeight: -1; \n\
294295 property size fixedArtShapeSize: Qt.size(-1, -1); \n\
@@ -407,7 +408,7 @@ function cardString(template, components) {
407408 mascotCode = kMascotImageCode.arg(anchors).arg(mascotImageVisible);
408409 }
409410
410− var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : "grey"';
411+ var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey")';
411412
412413 var titleSubtitleCode = "";
413414 if (hasTitle) {
@@ -419,7 +420,7 @@ function cardString(template, components) {
419420 } else if (hasBackground) {
420421 color = summaryColorWithBackground;
421422 } else {
422− color = '"grey"';
423+ color = 'root.scopeStyle ? root.scopeStyle.foreground : "grey"';
423424 }
424425
425426 var titleAnchors;
@@ -495,7 +496,7 @@ function cardString(template, components) {
495496 if (hasBackground) {
496497 color = summaryColorWithBackground;
497498 } else {
498− color = '"grey"';
499+ color = 'root.scopeStyle ? root.scopeStyle.foreground : "grey"';
499500 }
500501
501502 var summaryTopMargin = (hasMascot || hasSubtitle ? 'anchors.margins' : '0');
plugins/Dash/ScopeStyle.qmlnew file+87
@@ -0,0 +1,87 @@
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.2
18+
19+/*! \brief Helper for processing scope customization options.
20+
21+ It will take the customizations object passed by the scope and
22+ process it to provide the scope UI with data such as colors or
23+ image paths.
24+ */
25+
26+QtObject {
27+ /// Style object passed from the scope
28+ property var style: Object()
29+
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
39+ readonly property color foreground: "foreground-color" in style ? style["foreground-color"] : d.defaultDark
40+
41+ /// Color used for the overall background
42+ readonly property color background: "background-color" in style ? style["background-color"] : "transparent"
43+
44+ /*! \brief Luminance threshold for switching between fore and background color
45+
46+ \note If background colour is not fully opaque, it's not taken into account.
47+ */
48+ readonly property real threshold: background.a !== 1.0 ? d.foregroundLuminance : (d.foregroundLuminance + d.backgroundLuminance) / 2
49+
50+ /*! \brief The lighter of foreground and background colors
51+
52+ \note If background color is not fully opaque, it's not taken into account
53+ and defaults to the theme-provided light color.
54+ */
55+ readonly property color light: {
56+ if (background.a !== 1.0) return d.foregroundLuminance > d.defaultLightLuminance ? foreground : d.defaultLight;
57+ return d.foregroundLuminance > d.backgroundLuminance ? foreground : background;
58+ }
59+
60+ /*! \brief The darker of foreground and background colors
61+
62+ \note If background color is not fully opaque, it's not taken into account
63+ and defaults to the theme-provided dark color.
64+ */
65+ readonly property color dark: {
66+ if (background.a !== 1.0) return d.foregroundLuminance < d.defaultDarkLuminance ? foreground : d.defaultDark;
67+ return d.foregroundLuminance < d.backgroundLuminance ? foreground : background;
68+ }
69+
70+ /// Source of the logo image for the header
71+ readonly property url headerLogo: "logo" in d.headerStyle ? d.headerStyle["logo"] : ""
72+
73+ //! @cond
74+ property var d: QtObject {
75+ readonly property real foregroundLuminance: luminance(foreground)
76+ readonly property real backgroundLuminance: luminance(background)
77+
78+ // FIXME: should be taken from the theme
79+ readonly property color defaultLight: "white"
80+ readonly property color defaultDark: "grey"
81+ readonly property real defaultLightLuminance: luminance(defaultLight)
82+ readonly property real defaultDarkLuminance: luminance(defaultDark)
83+
84+ readonly property var headerStyle: "page-header" in style ? style["page-header"] : { }
85+ }
86+ //! @endcond
87+}
plugins/Dash/qmldir+1
@@ -2,3 +2,4 @@ module Dash
22plugin Dash-qml
33typeinfo Dash.qmltypes
44singleton CardCreatorCache 0.1 CardCreatorCache.qml
5+ScopeStyle 0.1 ScopeStyle.qml
qml/Components/ListItems/Header.qml+1
@@ -24,6 +24,7 @@ Item {
2424 \qmlproperty string text
2525 */
2626 property alias text: label.text
27+ property alias textColor: label.color
2728 property alias image: image.source
2829
2930 signal clicked(variant mouse)
qml/Components/PageHeader.qml+19−12
@@ -28,7 +28,6 @@ Item {
2828
2929 property bool showBackButton: false
3030 property string title
31− property string imageSource
3231
3332 property bool searchEntryEnabled: false
3433 property ListModel searchHistory: SearchHistoryModel
@@ -37,8 +36,15 @@ Item {
3736
3837 property alias bottomItem: bottomContainer.children
3938
39+ // TODO We should use foreground for the icons
40+ // of the toolbar but unfortunately Action does not have
41+ // the keyColor property as Icon does :-/
42+ property var scopeStyle: null
43+
4044 signal backClicked()
4145
46+ onScopeStyleChanged: refreshLogo()
47+
4248 function triggerSearch() {
4349 if (searchEntryEnabled) {
4450 headerContainer.showSearch = true;
@@ -79,15 +85,20 @@ Item {
7985 }
8086 }
8187
82− onImageSourceChanged: {
83− if (imageSource) {
88+ function refreshLogo() {
89+ if (scopeStyle ? scopeStyle.headerLogo != "" : false) {
8490 header.contents = imageComponent.createObject();
85− } else {
91+ } else if (header.contents) {
8692 header.contents.destroy();
8793 header.contents = null;
8894 }
8995 }
9096
97+ Connections {
98+ target: root.scopeStyle
99+ onHeaderLogoChanged: root.refreshLogo()
100+ }
101+
91102 InverseMouseArea {
92103 anchors { fill: parent; margins: units.gu(1); bottomMargin: units.gu(3) + bottomContainer.height }
93104 visible: headerContainer.showSearch
@@ -214,8 +225,7 @@ Item {
214225 height: headerContainer.height
215226 contentHeight: height
216227 separatorSource: ""
217− // FIXME: Use the theme's color once we're split from the shell
218− textColor: "grey"
228+ textColor: root.scopeStyle ? root.scopeStyle.foreground : "grey"
219229 property var styledItem: header
220230 property string title: root.title
221231 property var config: PageHeadConfiguration {
@@ -240,11 +250,8 @@ Item {
240250 }
241251
242252 property var contents: null
243− Component.onCompleted: {
244− if (root.imageSource) {
245− header.contents = imageComponent.createObject();
246− }
247− }
253+ Component.onCompleted: root.refreshLogo()
254+
248255 Component {
249256 id: imageComponent
250257
@@ -254,7 +261,7 @@ Item {
254261 Image {
255262 objectName: "titleImage"
256263 anchors.fill: parent
257− source: root.imageSource
264+ source: root.scopeStyle ? root.scopeStyle.headerLogo : ""
258265 fillMode: Image.PreserveAspectFit
259266 horizontalAlignment: Image.AlignLeft
260267 sourceSize.height: height
qml/Dash/CardCarousel.qml+1
@@ -66,6 +66,7 @@ DashRenderer {
6666 item.fontScale = Qt.binding(function() { return carousel.fontScale; });
6767 item.showHeader = Qt.binding(function() { return loader.explicitlyScaled; });
6868 item.artShapeBorderSource = "none";
69+ item.scopeStyle = cardCarousel.scopeStyle;
6970 }
7071
7172 BorderImage {
qml/Dash/CardFilterGrid.qml+1
@@ -58,6 +58,7 @@ DashRenderer {
5858 item.template = Qt.binding(function() { return cardTool.template; });
5959 item.components = Qt.binding(function() { return cardTool.components; });
6060 item.headerAlignment = Qt.binding(function() { return cardTool.headerAlignment; });
61+ item.scopeStyle = genericFilterGrid.scopeStyle;
6162 }
6263 Connections {
6364 target: loader.item
qml/Dash/CardVerticalJournal.qml+1
@@ -75,6 +75,7 @@ DashRenderer {
7575 item.template = Qt.binding(function() { return cardTool.template; });
7676 item.components = Qt.binding(function() { return cardTool.components; });
7777 item.headerAlignment = Qt.binding(function() { return cardTool.headerAlignment; });
78+ item.scopeStyle = root.scopeStyle;
7879 }
7980 Connections {
8081 target: loader.item
qml/Dash/DashDepartments.qml+8−7
@@ -29,6 +29,7 @@ AbstractButton {
2929
3030 property alias windowWidth: blackRect.width
3131 property alias windowHeight: blackRect.height
32+ property var scopeStyle: null
3233
3334 // Are we drilling down the tree or up?
3435 property bool isGoingBack: false
@@ -78,18 +79,17 @@ AbstractButton {
7879 anchors.margins: units.gu(2)
7980 verticalAlignment: Text.AlignVCenter
8081 text: root.currentDepartment ? root.currentDepartment.label : ""
81− color: "gray" // TODO remove once we're a separate app
82+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey"
8283 }
8384
84− Image {
85+ Icon {
8586 anchors.verticalCenter: parent.verticalCenter
8687 anchors.right: parent.right
8788 anchors.rightMargin: units.gu(2)
88− rotation: showList ? 180 : 0
89− source: "image://theme/dropdown-menu"
90− sourceSize.height: parent.height - units.gu(2)
91− sourceSize.width: units.gu(2)
92− fillMode: Image.PreserveAspectFit
89+ name: showList ? "up" : "down"
90+ height: units.gu(2)
91+ width: height
92+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey"
9393 }
9494
9595 // departmentListView is outside root
@@ -124,6 +124,7 @@ AbstractButton {
124124 objectName: "department" + index
125125 visible: height != 0
126126 width: departmentListView.width
127+ scopeStyle: root.scopeStyle
127128 property real desiredHeight: {
128129 if (root.showList) {
129130 if (department && department.loaded && x == departmentListView.contentX)
qml/Dash/DashDepartmentsList.qml+19−17
@@ -21,15 +21,17 @@ Item {
2121 id: root
2222 property var department: null
2323 property var currentDepartment: null
24+ property var scopeStyle: null
2425 signal enterDepartment(var newDepartmentId, bool hasChildren)
2526 signal goBackToParentClicked()
2627 signal allDepartmentClicked()
2728
2829 readonly property int itemHeight: units.gu(5)
30+ readonly property color foregroundColor: root.scopeStyle ? root.scopeStyle.foreground : "grey"
2931 implicitHeight: flickable.contentHeight
3032
3133 Rectangle {
32− color: "white"
34+ color: !root.scopeStyle || Qt.colorEqual(root.scopeStyle.background, "transparent") ? "white" : root.scopeStyle.background
3335 anchors.fill: parent
3436 }
3537
@@ -71,17 +73,17 @@ Item {
7173
7274 onClicked: root.goBackToParentClicked();
7375
74− Image {
76+ Icon {
7577 id: backImage
7678 anchors {
7779 verticalCenter: parent.verticalCenter
7880 left: parent.left
7981 leftMargin: units.gu(2)
8082 }
81− source: "image://theme/back"
82− sourceSize.height: parent.height - units.gu(3)
83− sourceSize.width: units.gu(3)
84− fillMode: Image.PreserveAspectFit
83+ name: "back"
84+ height: units.gu(2)
85+ width: height
86+ color: root.foregroundColor
8587 }
8688
8789 Label {
@@ -91,7 +93,7 @@ Item {
9193 leftMargin: units.gu(0.5)
9294 }
9395 text: department ? department.parentLabel : ""
94− color: "gray" // TODO remove once we're a separate app
96+ color: root.foregroundColor
9597 }
9698
9799 Rectangle {
@@ -102,7 +104,7 @@ Item {
102104 leftMargin: units.gu(2)
103105 rightMargin: units.gu(2)
104106 }
105− color: "gray"
107+ color: root.foregroundColor
106108 opacity: 0.2
107109 height: units.dp(1)
108110 }
@@ -123,7 +125,7 @@ Item {
123125 }
124126 text: department ? (department.allLabel != "" ? department.allLabel : department.label) : ""
125127 font.bold: true
126− color: "gray" // TODO remove once we're a separate app
128+ color: root.foregroundColor
127129 }
128130
129131 Rectangle {
@@ -134,7 +136,7 @@ Item {
134136 leftMargin: units.gu(2)
135137 rightMargin: units.gu(2)
136138 }
137− color: "gray"
139+ color: root.foregroundColor
138140 opacity: 0.2
139141 height: units.dp(1)
140142 }
@@ -159,19 +161,19 @@ Item {
159161 leftMargin: units.gu(2)
160162 }
161163 text: label
162− color: "gray" // TODO remove once we're a separate app
164+ color: root.foregroundColor
163165 }
164166
165− Image {
167+ Icon {
166168 anchors {
167169 verticalCenter: parent.verticalCenter
168170 right: parent.right
169171 rightMargin: units.gu(2)
170172 }
171− source: hasChildren ? "image://theme/chevron" : "graphics/tick.png"
172− sourceSize.height: parent.height - units.gu(3)
173− sourceSize.width: units.gu(3)
174− fillMode: Image.PreserveAspectFit
173+ height: units.gu(2)
174+ width: height
175+ name: hasChildren ? "go-next" : "tick"
176+ color: root.foregroundColor
175177 visible: hasChildren || isActive
176178 }
177179
@@ -183,7 +185,7 @@ Item {
183185 leftMargin: units.gu(2)
184186 rightMargin: units.gu(2)
185187 }
186− color: "gray"
188+ color: root.foregroundColor
187189 opacity: 0.1
188190 height: units.dp(1)
189191 visible: index != department.count - 1
qml/Dash/DashRenderer.qml+5−4
@@ -36,10 +36,11 @@ Item {
3636 // The model to renderer
3737 property var model
3838
39− /*!
40− \brief CardTool component.
41− */
42− property var cardTool: undefined
39+ /// CardTool component.
40+ property var cardTool: null
41+
42+ /// ScopeStyle component.
43+ property var scopeStyle: null
4344
4445 /// Emitted when the user clicked on an item
4546 /// @param index is the index of the clicked item
qml/Dash/GenericScopeView.qml+16
@@ -19,6 +19,7 @@ import Ubuntu.Components 0.1
1919import Utils 0.1
2020import Unity 0.2
2121import Unity.Application 0.1
22+import Dash 0.1
2223import "../Components"
2324import "../Components/ListItems" as ListItems
2425
@@ -33,6 +34,10 @@ FocusScope {
3334 property bool enableHeightBehaviorOnNextCreation: false
3435 property var categoryView: categoryView
3536
37+ property var scopeStyle: ScopeStyle {
38+ style: scope ? scope.customizations : {}
39+ }
40+
3641 signal backClicked()
3742
3843 onScopeChanged: {
@@ -97,6 +102,12 @@ FocusScope {
97102 onHideDash: previewListView.open = false;
98103 }
99104
105+ Rectangle {
106+ anchors.fill: parent
107+ color: scopeView.scopeStyle ? scopeView.scopeStyle.background : "transparent"
108+ visible: color != "transparent"
109+ }
110+
100111 ScopeListView {
101112 id: categoryView
102113 objectName: "categoryListView"
@@ -164,6 +175,7 @@ FocusScope {
164175 item.model = Qt.binding(function() { return results })
165176 }
166177 item.objectName = Qt.binding(function() { return categoryId })
178+ item.scopeStyle = scopeView.scopeStyle;
167179 if (item.expandable) {
168180 var shouldFilter = categoryId != categoryView.expandedCategoryId;
169181 item.setFilter(shouldFilter, false /*animate*/);
@@ -303,6 +315,7 @@ FocusScope {
303315 property var delegate: categoryView.item(delegateIndex)
304316 width: categoryView.width
305317 text: section
318+ textColor: scopeStyle ? scopeStyle.foreground : "grey"
306319 image: {
307320 if (delegate && delegate.expandable)
308321 return delegate.filtered ? "graphics/header_handlearrow.png" : "graphics/header_handlearrow2.png"
@@ -324,6 +337,7 @@ FocusScope {
324337 showBackButton: scopeView.hasBackAction
325338 searchEntryEnabled: true
326339 searchInProgress: scopeView.scope ? scopeView.scope.searchInProgress : false
340+ scopeStyle: scopeView.scopeStyle
327341
328342 bottomItem: DashDepartments {
329343 scope: scopeView.scope
@@ -331,6 +345,7 @@ FocusScope {
331345 anchors.right: parent.right
332346 windowHeight: scopeView.height
333347 windowWidth: scopeView.width
348+ scopeStyle: scopeView.scopeStyle
334349 }
335350
336351 onBackClicked: scopeView.backClicked()
@@ -342,6 +357,7 @@ FocusScope {
342357 objectName: "previewListView"
343358 visible: x != width
344359 scope: scopeView.scope
360+ scopeStyle: scopeView.scopeStyle
345361 width: parent.width
346362 height: parent.height
347363 anchors.left: categoryView.right
qml/Dash/PreviewListView.qml+3
@@ -24,6 +24,7 @@ Item {
2424 id: root
2525
2626 property var scope: null
27+ property var scopeStyle: null
2728
2829 property alias open: previewListView.open
2930 property alias model: previewListView.model
@@ -38,6 +39,7 @@ Item {
3839 title: scope ? scope.name : ""
3940 showBackButton: true
4041 searchEntryEnabled: false
42+ scopeStyle: root.scopeStyle
4143
4244 onBackClicked: root.open = false
4345 }
@@ -101,6 +103,7 @@ Item {
101103 var previewStack = root.scope.preview(result);
102104 return previewStack.getPreviewModel(0);
103105 }
106+ scopeStyle: root.scopeStyle
104107 }
105108
106109 MouseArea {
qml/Dash/Previews/Preview.qml+4
@@ -36,6 +36,9 @@ Item {
3636 //! \brief Should be set to true if this preview is currently displayed.
3737 property bool isCurrent: false
3838
39+ //! \brief The ScopeStyle component.
40+ property var scopeStyle: null
41+
3942 clip: true
4043
4144 Binding {
@@ -77,6 +80,7 @@ Item {
7780 widgetType: model.type
7881 widgetData: model.properties
7982 isCurrentPreview: root.isCurrent
83+ scopeStyle: root.scopeStyle
8084 anchors {
8185 left: parent.left
8286 right: parent.right
qml/Dash/Previews/PreviewAudioPlayback.qml+3−3
@@ -139,7 +139,7 @@ PreviewWidget {
139139 objectName: "trackTitleLabel"
140140 anchors { top: parent.top; left: parent.left; right: parent.right }
141141 opacity: 0.9
142− color: "grey"//Theme.palette.selected.backgroundText // TODO karni: Use updated Palette.
142+ color: scopeStyle ? scopeStyle.foreground : "grey"
143143 fontSize: "small"
144144 horizontalAlignment: Text.AlignLeft
145145 text: modelData["title"]
@@ -152,7 +152,7 @@ PreviewWidget {
152152 anchors { top: trackTitleLabel.bottom; left: parent.left; right: parent.right }
153153 visible: text !== ""
154154 opacity: 0.9
155− color: "grey"//Theme.palette.selected.backgroundText // TODO karni: Use updated Palette.
155+ color: scopeStyle ? scopeStyle.foreground : "grey"
156156 font.weight: Font.Light
157157 fontSize: "small"
158158 horizontalAlignment: Text.AlignLeft
@@ -193,7 +193,7 @@ PreviewWidget {
193193 anchors.verticalCenter: parent.verticalCenter
194194 width: parent.column3Width
195195 opacity: 0.9
196− color: "grey"//Theme.palette.selected.backgroundText // TODO karni: Use updated Palette.
196+ color: scopeStyle ? scopeStyle.foreground : "grey"
197197 fontSize: "small"
198198 horizontalAlignment: Text.AlignRight
199199 text: audio.lengthToString(modelData["length"])
qml/Dash/Previews/PreviewHeader.qml+1−1
@@ -35,7 +35,7 @@ PreviewWidget {
3535 readonly property url mascot: root.widgetData["mascot"] || ""
3636 readonly property string title: root.widgetData["title"] || ""
3737 readonly property string subtitle: root.widgetData["subtitle"] || ""
38− readonly property color fontColor: "grey"
38+ readonly property color fontColor: root.scopeStyle ? root.scopeStyle.foreground : "grey"
3939
4040 implicitHeight: row.height + row.margins * 2
4141 width: parent.width
qml/Dash/Previews/PreviewRatingDisplay.qml+2−4
@@ -71,8 +71,7 @@ PreviewWidget {
7171 id: authorLabel
7272 objectName: "authorLabel"
7373 anchors { left: parent.left; right: parent.right }
74− // TODO cimi: Yet another fix requiring Palette update.
75− color: "grey" //Theme.palette.selected.backgroundText
74+ color: scopeStyle ? scopeStyle.foreground : "grey"
7675 opacity: .8
7776 text: modelData["author"] || ""
7877 visible: text !== ""
@@ -83,8 +82,7 @@ PreviewWidget {
8382 id: reviewLabel
8483 objectName: "reviewLabel"
8584 anchors { left: parent.left; right: parent.right }
86− // TODO cimi: Yet another fix requiring Palette update.
87− color: "grey" //Theme.palette.selected.backgroundText
85+ color: scopeStyle ? scopeStyle.foreground : "grey"
8886 opacity: .8
8987 text: modelData["review"] || ""
9088 visible: text !== ""
qml/Dash/Previews/PreviewRatingInput.qml+2−4
@@ -76,8 +76,7 @@ PreviewWidget {
7676 verticalCenter: parent.verticalCenter
7777 left: parent.left
7878 }
79− // TODO cimi: Yet another fix requiring Palette update.
80− color: "grey" //Theme.palette.selected.backgroundText
79+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey"
8180 opacity: .8
8281 text: widgetData["rating-label"] || i18n.tr("Rate this")
8382 }
@@ -122,8 +121,7 @@ PreviewWidget {
122121 left: parent.left
123122 right: parent.right
124123 }
125− // TODO cimi: Yet another fix requiring Palette update.
126− color: "grey" //Theme.palette.selected.backgroundText
124+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey"
127125 opacity: .8
128126 text: widgetData["review-label"] || i18n.tr("Add a review")
129127 }
qml/Dash/Previews/PreviewTextSummary.qml+2−4
@@ -39,8 +39,7 @@ PreviewWidget {
3939 }
4040 height: visible ? implicitHeight : 0
4141 fontSize: "large"
42− // TODO karni: Yet another fix requiring Palette update.
43− color: "grey" //Theme.palette.selected.backgroundText
42+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey"
4443 visible: text !== ""
4544 opacity: .8
4645 text: widgetData["title"] || ""
@@ -62,8 +61,7 @@ PreviewWidget {
6261 clip: true
6362 fontSize: "small"
6463 lineHeight: 1.2
65− // TODO karni: Yet another fix requiring Palette update.
66− color: "grey" //Theme.palette.selected.backgroundText
64+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey"
6765 opacity: .8
6866 text: widgetData["text"]
6967 wrapMode: Text.Wrap
qml/Dash/Previews/PreviewWidget.qml+3
@@ -28,6 +28,9 @@ Item {
2828 //! Variable used to contain widget's data
2929 property var widgetData
3030
31+ //! The ScopeStyle component.
32+ property var scopeStyle: null
33+
3134 /*! \brief This signal should be emitted when a preview action was triggered.
3235 *
3336 * \param widgetId, actionId Respective identifiers from widgetData.
qml/Dash/Previews/PreviewWidgetFactory.qml+4
@@ -31,6 +31,9 @@ Loader {
3131 //! Widget data, forwarded to the widget as is.
3232 property var widgetData: null
3333
34+ //! The ScopeStyle component.
35+ property var scopeStyle: null
36+
3437 //! Set to true if the parent preview is displayed.
3538 property bool isCurrentPreview: false
3639
@@ -62,6 +65,7 @@ Loader {
6265 item.widgetId = Qt.binding(function() { return root.widgetId } )
6366 item.widgetData = Qt.binding(function() { return root.widgetData } )
6467 item.isCurrentPreview = Qt.binding(function() { return root.isCurrentPreview } )
68+ if (item.hasOwnProperty("scopeStyle")) item.scopeStyle = root.scopeStyle;
6569 }
6670
6771 Connections {
qml/Dash/graphics/tick@30.pngdeleted

Binary file not shown.

tests/mocks/Unity/fake_scope.cpp+12−1
@@ -14,6 +14,8 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17+#include <QUrl>
18+
1719#include "fake_scope.h"
1820#include "fake_department.h"
1921#include "fake_resultsmodel.h"
@@ -180,7 +182,16 @@ bool Scope::hasDepartments() const
180182
181183QVariantMap Scope::customizations() const
182184{
183− return QVariantMap();
185+ QVariantMap m;
186+ if (m_id == "clickscope") {
187+ m["background-color"] = "red";
188+ m["foreground-color"] = "blue";
189+ } else if (m_id == "MockScope5") {
190+ QVariantMap pageHeader;
191+ pageHeader["logo"] = QUrl("../../../tests/qmltests/Components/tst_PageHeader/logo-ubuntu-orange.svg");
192+ m["page-header"] = pageHeader;
193+ }
194+ return m;
184195}
185196
186197unity::shell::scopes::DepartmentInterface* Scope::getDepartment(const QString& id)
tests/plugins/Dash/CMakeLists.txt+2
@@ -76,4 +76,6 @@ add_binary_qml_test(CardCreator "" "Dash-qml")
7676# plain qml test
7777set(qmltest_DEFAULT_TARGETS qmluitests)
7878set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
79+set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
80+add_qml_test(. ScopeStyle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
7981add_qml_test(. ListViewWithPageHeaderQML IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
tests/plugins/Dash/cardcreator/1.res+3−2
@@ -5,7 +5,8 @@ AbstractButton {
55 property var cardData;
66 property var artShapeBorderSource: undefined;
77 property real fontScale: 1.0;
8− property int headerAlignment: Text.AlignLeft;
8+ property var scopeStyle: null;
9+ property int headerAlignment: Text.AlignLeft;
910 property int fixedHeaderHeight: -1;
1011 property size fixedArtShapeSize: Qt.size(-1, -1);
1112 readonly property string title: cardData && cardData["title"] || "";
@@ -75,7 +76,7 @@ leftMargin: units.gu(1);
7576 wrapMode: Text.Wrap;
7677 maximumLineCount: 2;
7778 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
78− color: "grey";
79+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey";
7980 visible: showHeader ;
8081 text: root.title;
8182 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
tests/plugins/Dash/cardcreator/2.res+5−5
@@ -5,7 +5,8 @@ AbstractButton {
55 property var cardData;
66 property var artShapeBorderSource: undefined;
77 property real fontScale: 1.0;
8− property int headerAlignment: Text.AlignLeft;
8+ property var scopeStyle: null;
9+ property int headerAlignment: Text.AlignLeft;
910 property int fixedHeaderHeight: -1;
1011 property size fixedArtShapeSize: Qt.size(-1, -1);
1112 readonly property string title: cardData && cardData["title"] || "";
@@ -78,8 +79,7 @@ Column {
7879 anchors.verticalCenter: parent.verticalCenter;
7980 spacing: units.dp(2);
8081 width: parent.width - x;
81− data: [
82−Label {
82+ data: [ Label {
8383 id: titleLabel;
8484 objectName: "titleLabel";
8585 anchors { left: parent.left; right: parent.right }
@@ -88,7 +88,7 @@ Label {
8888 wrapMode: Text.Wrap;
8989 maximumLineCount: 2;
9090 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
91− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : "grey";
91+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
9292 visible: showHeader ;
9393 text: root.title;
9494 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -103,7 +103,7 @@ Label {
103103 elide: Text.ElideRight;
104104 fontSize: "small";
105105 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
106− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : "grey";
106+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
107107 visible: titleLabel.visible && titleLabel.text;
108108 text: cardData && cardData["subtitle"] || "";
109109 font.weight: Font.Light;
tests/plugins/Dash/cardcreator/3.res+6−4
@@ -5,7 +5,8 @@ AbstractButton {
55 property var cardData;
66 property var artShapeBorderSource: undefined;
77 property real fontScale: 1.0;
8− property int headerAlignment: Text.AlignLeft;
8+ property var scopeStyle: null;
9+ property int headerAlignment: Text.AlignLeft;
910 property int fixedHeaderHeight: -1;
1011 property size fixedArtShapeSize: Qt.size(-1, -1);
1112 readonly property string title: cardData && cardData["title"] || "";
@@ -75,7 +76,7 @@ leftMargin: units.gu(1);
7576 wrapMode: Text.Wrap;
7677 maximumLineCount: 2;
7778 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
78− color: "grey";
79+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey";
7980 visible: showHeader ;
8081 text: root.title;
8182 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -88,11 +89,12 @@ Label {
8889 leftMargin: titleLabel.leftMargin;
8990 right: titleLabel.right;
9091 top: titleLabel.bottom;
91− topMargin: units.dp(2); }
92+ topMargin: units.dp(2);
93+ }
9294 elide: Text.ElideRight;
9395 fontSize: "small";
9496 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
95− color: "grey";
97+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey";
9698 visible: titleLabel.visible && titleLabel.text;
9799 text: cardData && cardData["subtitle"] || "";
98100 font.weight: Font.Light;
tests/plugins/Dash/cardcreator/4.res+4−3
@@ -5,7 +5,8 @@ AbstractButton {
55 property var cardData;
66 property var artShapeBorderSource: undefined;
77 property real fontScale: 1.0;
8− property int headerAlignment: Text.AlignLeft;
8+ property var scopeStyle: null;
9+ property int headerAlignment: Text.AlignLeft;
910 property int fixedHeaderHeight: -1;
1011 property size fixedArtShapeSize: Qt.size(-1, -1);
1112 readonly property string title: cardData && cardData["title"] || "";
@@ -68,7 +69,7 @@ data: [ Label {
6869 wrapMode: Text.Wrap;
6970 maximumLineCount: 2;
7071 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
71− color: "grey";
72+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey";
7273 visible: showHeader ;
7374 text: root.title;
7475 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -83,7 +84,7 @@ Label {
8384 elide: Text.ElideRight;
8485 fontSize: "small";
8586 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
86− color: "grey";
87+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey";
8788 visible: titleLabel.visible && titleLabel.text;
8889 text: cardData && cardData["subtitle"] || "";
8990 font.weight: Font.Light;
tests/plugins/Dash/cardcreator/5.res+3−2
@@ -4,8 +4,9 @@ AbstractButton {
44 property var components;
55 property var cardData;
66 property var artShapeBorderSource: undefined;
7− property real fontScale: 1.0;
8− property int headerAlignment: Text.AlignLeft;
7+ property real fontScale: 1.0;
8+ property var scopeStyle: null;
9+ property int headerAlignment: Text.AlignLeft;
910 property int fixedHeaderHeight: -1;
1011 property size fixedArtShapeSize: Qt.size(-1, -1);
1112 readonly property string title: cardData && cardData["title"] || "";
tests/plugins/Dash/tst_ScopeStyle.qmlnew file+151
@@ -0,0 +1,151 @@
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 Dash 0.1
20+import Ubuntu.Components 1.1
21+
22+Rectangle {
23+ width: units.gu(40)
24+ height: units.gu(71)
25+
26+ ScopeStyle {
27+ id: tool
28+ }
29+
30+ TestCase {
31+ id: testCase
32+ name: "ScopeStyle"
33+ when: windowShown
34+
35+ property color color
36+ property var styles: [
37+ {},
38+ { "foreground-color": "red", "background-color": "black", "page-header": { "logo": "/foo/bar" } },
39+ { "foreground-color": "green", "background-color": "white" },
40+ { "foreground-color": "blue", "background-color": "darkgrey" },
41+ ]
42+
43+ function cleanup() {
44+ testCase.color = "transparent";
45+ }
46+
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+ ];
58+ }
59+
60+ function test_luminance(data) {
61+ testCase.color = data.tag;
62+ compare(tool.luminance(testCase.color).toFixed(4), data.luminance.toFixed(4));
63+ }
64+
65+ function test_foreground_data() {
66+ return [
67+ { tag: "default", index: 0, foreground: "grey" },
68+ { tag: "red on black", index: 1, foreground: "red" },
69+ { tag: "green on white", index: 2, foreground: "green" },
70+ { tag: "blue on darkgrey", index: 3, foreground: "blue" },
71+ ];
72+ }
73+
74+ function test_foreground(data) {
75+ tool.style = testCase.styles[data.index];
76+ verify(Qt.colorEqual(tool.foreground, data.foreground),
77+ "Foreground color not equal: %1 != %2".arg(tool.foreground).arg(data.foreground));
78+ }
79+
80+ function test_background_data() {
81+ return [
82+ { tag: "default", index: 0, background: "transparent" },
83+ { tag: "red on black", index: 1, background: "black" },
84+ { tag: "green on white", index: 2, background: "white" },
85+ { tag: "blue on darkgrey", index: 3, background: "darkgrey" },
86+ ];
87+ }
88+
89+ function test_background(data) {
90+ tool.style = testCase.styles[data.index];
91+ verify(Qt.colorEqual(tool.background, data.background),
92+ "Background color not equal: %1 != %2".arg(tool.background).arg(data.background));
93+ }
94+
95+ function test_threshold_data() {
96+ return [
97+ { tag: "default", index: 0, threshold: 0.5020 },
98+ { tag: "red on black", index: 1, threshold: 0.1063 },
99+ { tag: "green on white", index: 2, threshold: 0.6795 },
100+ { tag: "blue on darkgrey", index: 3, threshold: 0.3675 },
101+ ];
102+ }
103+
104+ function test_threshold(data) {
105+ tool.style = testCase.styles[data.index];
106+ compare(tool.threshold.toFixed(4), data.threshold.toFixed(4), "Luminance threshold was incorrect.");
107+ }
108+
109+ function test_light_data() {
110+ return [
111+ { tag: "default", index: 0, light: "white" },
112+ { tag: "red on black", index: 1, light: "red" },
113+ { tag: "green on white", index: 2, light: "white" },
114+ { tag: "blue on darkgrey", index: 3, light: "darkgrey" },
115+ ];
116+ }
117+
118+ function test_light(data) {
119+ tool.style = testCase.styles[data.index];
120+ verify(Qt.colorEqual(tool.light, data.light),
121+ "Light color not equal: %1 != %2".arg(tool.light).arg(data.light));
122+ }
123+
124+ function test_dark_data() {
125+ return [
126+ { tag: "default", index: 0, dark: "grey" },
127+ { tag: "red on black", index: 1, dark: "black" },
128+ { tag: "green on white", index: 2, dark: "green" },
129+ { tag: "blue on darkgrey", index: 3, dark: "blue" },
130+ ];
131+ }
132+
133+ function test_dark(data) {
134+ tool.style = testCase.styles[data.index];
135+ verify(Qt.colorEqual(tool.dark, data.dark),
136+ "Dark color not equal: %1 != %2".arg(tool.dark).arg(data.dark));
137+ }
138+
139+ function test_headerLogo_data() {
140+ return [
141+ { tag: "default", index: 0, headerLogo: "" },
142+ { tag: "with logo", index: 1, headerLogo: "file:///foo/bar" },
143+ ];
144+ }
145+
146+ function test_headerLogo(data) {
147+ tool.style = testCase.styles[data.index];
148+ compare(tool.headerLogo, data.headerLogo, "Header logo was incorrect.");
149+ }
150+ }
151+}
tests/qmltests/Components/tst_PageHeader.qml+4−1
@@ -213,7 +213,10 @@ Item {
213213
214214 searchEntryEnabled: true
215215 title: "%^$%^%^&%^&%^$%GHR%"
216− imageSource: showImageCheckBox.checked ? titleImageSource : ""
216+ scopeStyle: QtObject {
217+ readonly property color foreground: "grey"
218+ readonly property url headerLogo: showImageCheckBox.checked ? pageHeader.titleImageSource : ""
219+ }
217220 showBackButton: showBackButtonCheckBox.checked
218221
219222 property string titleImageSource: Qt.resolvedUrl("tst_PageHeader/logo-ubuntu-orange.svg")
tests/qmltests/Dash/tst_GenericScopeView.qml+9−4
@@ -37,10 +37,6 @@ Item {
3737
3838 Scopes {
3939 id: scopes
40−
41− onLoadedChanged: {
42− genericScopeView.scope = scopes.getScope(2)
43− }
4440 }
4541
4642 property Item applicationManager: Item {
@@ -260,6 +256,15 @@ Item {
260256 }
261257 closePreview();
262258 }
259+
260+ function test_haeder_logo() {
261+ genericScopeView.scope = scopes.getScope(3);
262+
263+ var image = findChild(genericScopeView, "titleImage");
264+ verify(image, "Could not find the title image");
265+ compare(image.source, Qt.resolvedUrl("../Components/tst_PageHeader/logo-ubuntu-orange.svg"), "Title image has the wrong source");
266+
267+ }
263268 }
264269 }
265270}