Merge lp:~aacid/unity8/dynamic_specialized_cards into lp:unity8

Proposed by Albert Astals Cid
Status: Merged
Approved by: Michael Zanetti
Approved revision: 909
Merged at revision: 890
Proposed branch: lp:~aacid/unity8/dynamic_specialized_cards
Merge into: lp:unity8
Diff against target: 3024 lines (+1590/-714)
48 files modified
debian/unity8-private.install (+1/-1)
plugins/CMakeLists.txt (+1/-1)
plugins/Dash/CMakeLists.txt (+5/-5)
plugins/Dash/CardCreator.js (+518/-0)
plugins/Dash/CardCreatorCache.qml (+40/-0)
plugins/Dash/plugin.cpp (+2/-2)
plugins/Dash/plugin.h (+3/-3)
plugins/Dash/qmldir (+3/-2)
qml/Dash/Card.qml (+0/-236)
qml/Dash/CardCarousel.qml (+13/-10)
qml/Dash/CardFilterGrid.qml (+19/-16)
qml/Dash/CardHeader.qml (+0/-126)
qml/Dash/CardTool.qml (+30/-22)
qml/Dash/Previews/PreviewHeader.qml (+86/-9)
qml/Dash/ScopeListView.qml (+1/-1)
tests/autopilot/unity8/shell/emulators/dash.py (+3/-4)
tests/mocks/Unity/fake_categories.cpp (+15/-2)
tests/mocks/Unity/fake_resultsmodel.cpp (+1/-0)
tests/plugins/CMakeLists.txt (+1/-1)
tests/plugins/Dash/CMakeLists.txt (+16/-9)
tests/plugins/Dash/cardcreator/1.res (+77/-0)
tests/plugins/Dash/cardcreator/1.tst (+3/-0)
tests/plugins/Dash/cardcreator/2.res (+115/-0)
tests/plugins/Dash/cardcreator/2.tst (+3/-0)
tests/plugins/Dash/cardcreator/3.res (+94/-0)
tests/plugins/Dash/cardcreator/3.tst (+3/-0)
tests/plugins/Dash/cardcreator/4.res (+95/-0)
tests/plugins/Dash/cardcreator/4.tst (+3/-0)
tests/plugins/Dash/cardcreator/5.res (+137/-0)
tests/plugins/Dash/cardcreator/5.tst (+3/-0)
tests/plugins/Dash/cardcreatortest.cpp (+91/-0)
tests/plugins/Dash/cardcreatortest.qml (+29/-0)
tests/plugins/Dash/horizontaljournaltest.qml (+1/-1)
tests/plugins/Dash/horizontaljournaltry.qml (+1/-1)
tests/plugins/Dash/listviewwithpageheadertest.qml (+1/-1)
tests/plugins/Dash/listviewwithpageheadertestsection.qml (+1/-1)
tests/plugins/Dash/listviewwithpageheadertestsectionexternalmodel.qml (+1/-1)
tests/plugins/Dash/organicgridtest.qml (+1/-1)
tests/plugins/Dash/organicgridtry.qml (+1/-1)
tests/plugins/Dash/tst_ListViewWithPageHeaderQML.qml (+1/-1)
tests/plugins/Dash/verticaljournaltest.qml (+1/-1)
tests/plugins/Dash/verticaljournaltry.qml (+1/-1)
tests/qmltests/CMakeLists.txt (+3/-3)
tests/qmltests/Dash/Previews/tst_PreviewHeader.qml (+66/-4)
tests/qmltests/Dash/tst_Card.qml (+79/-93)
tests/qmltests/Dash/tst_CardBenchmark.qml (+10/-7)
tests/qmltests/Dash/tst_CardHeader.qml (+0/-116)
tests/qmltests/Dash/tst_CardTool.qml (+11/-31)
To merge this branch: bzr merge lp:~aacid/unity8/dynamic_specialized_cards
Reviewer Review Type Date Requested Status
Michael Zanetti (community) Approve
PS Jenkins bot (community) continuous-integration Needs Fixing
Review via email: mp+218089@code.launchpad.net

Commit message

Create specialized Card code in Javascript instead of having various copied&pasted files

Description of the change

* Are there any related MPs required for this MP to build/function as expected?
None

 * Did you perform an exploratory manual test run of your code change and any related functionality?
Yes

 * Did you make sure that your branch does not contain spurious tags?
Yes

 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A

 * If you changed the UI, has there been a design review?
N/A

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
885. By Albert Astals Cid

just one pragma and missing (C)

886. By Albert Astals Cid

whitespace pedanticness

887. By Albert Astals Cid

split the card generating code to a different function

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

46 +.pragma library
64 +.pragma library

duplicate

===

141 + height: units.gu(8.625)';

not sure if its still valid, but someone somewhen told me not to use units.gu with anything that can't be devided by 0.5.

===

Now that we generate the code on the fly, do we still need things like this?

194 + code += 'Loader { \
288 + code += 'Loader { \

I suppose the loaders have been added before in order to improve performance when everything was still loaded. Seems like unused overhead now, given that the Loader itself won't be in there in the cases where it wouldn't have to load anything.

===

I know this is not final yet, just listing this so you'll have it easier to find them again:

471 +// console.log(code)

===
478 +++ plugins/Dash/CardCreatorCache.qml

Nice one! But I would be curious to know if having the cache as a QHash in C++ might be faster. How often are we calling that cache and how many items will be in there in average? Is it one entry per scope category?

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

uuhh... the performance is MUCH better. I would go as far as saying even better than with old scopes. Awesome job!

888. By Albert Astals Cid

Merge

Revision history for this message
Albert Astals Cid (aacid) wrote :

> 46 +.pragma library
> 64 +.pragma library
>
> duplicate

Gone in r885

> ===
>
> 141 + height: units.gu(8.625)';
>
> not sure if its still valid, but someone somewhen told me not to use units.gu
> with anything that can't be devided by 0.5.

Well, i just moved it around, hmmm, wait actually i didn't, where did that number come from? Let me investigate

> Now that we generate the code on the fly, do we still need things like this?
>
> 194 + code += 'Loader { \
> 288 + code += 'Loader { \
>
> I suppose the loaders have been added before in order to improve performance
> when everything was still loaded. Seems like unused overhead now, given that
> the Loader itself won't be in there in the cases where it wouldn't have to
> load anything.

Yes, we still need them since they are async loaders

> ===
>
> I know this is not final yet, just listing this so you'll have it easier to
> find them again:
>
> 471 +// console.log(code)

This is gone in r887

> ===
> 478 +++ plugins/Dash/CardCreatorCache.qml
>
> Nice one! But I would be curious to know if having the cache as a QHash in C++
> might be faster. How often are we calling that cache and how many items will
> be in there in average? Is it one entry per scope category?

We're calling it every time a category is created, that is not very often, some categories share the "view mode" so they will be having the same "key". About speed, i hope the js backend is using a qhash or similar so it should not really matter.

Revision history for this message
Albert Astals Cid (aacid) wrote :

> > 141 + height: units.gu(8.625)';
> >
> > not sure if its still valid, but someone somewhen told me not to use
> units.gu
> > with anything that can't be devided by 0.5.
>
> Well, i just moved it around, hmmm, wait actually i didn't, where did that
> number come from? Let me investigate

Ok, so instaead of 8.625 it has to be 7.625 which is the 5.625 + 2 units.gu(1), that if side is "useless" anyway since having an horizontal card and then putting nothing on the horizontal side is kind of silly :D

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
889. By Albert Astals Cid

8.625 -> 7.625

890. By Albert Astals Cid

Comment on why the hardcoded size and its sillyness

891. By Albert Astals Cid

move the code that sets headerHeight a bit

Makes it easier to understand and also allows me to add the final else

892. By Albert Astals Cid

don't set left anchor margin twice

893. By Albert Astals Cid

renamed

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
894. By Albert Astals Cid

Check more carefully

895. By Albert Astals Cid

Add some "well known" card creator input->output

896. By Albert Astals Cid

eol

897. By Albert Astals Cid

One with overlay

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
898. By Albert Astals Cid

Add newlines, makes debugging the code when it fails easier

899. By Albert Astals Cid

Better variable name

900. By Albert Astals Cid

no need for a variable here

901. By Albert Astals Cid

Move big chunks of boilerplate out of the main function

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
902. By Albert Astals Cid

Rework header row/column creation

This way we don't open it in one place and close it 100 lines later

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

192 +var headerRowCode = 'Row { \n\

Can you please add a comment on top of those describing what %1, %2... are supposed to do.

===

I also find it a bit confusing that sometimes its like this:

propName: %1 \n

and sometimes like this:

%1 \n

Would it be better like this?

anchors: %1 \n

so we could fill it with .arg("{left: foo; right: bar}"); It would restrict it a bit more what can be injected to a narrower defined set.

review: Needs Information
Revision history for this message
Michael Zanetti (mzanetti) wrote :

3: /home/mzanetti/Development/reviews/dynamic_specialized_cards/tests/plugins/Dash/cardcreatortest.cpp: bad whitespace in line 47

review: Needs Fixing
Revision history for this message
Michael Zanetti (mzanetti) wrote :
Download full text (3.7 KiB)

running this on the phone, being in the music scope and copying music to the device using adb push I get nasty flickering and those messages:

file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "headerHeight"
file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:1:130: QML AbstractButton: Binding loop detected for property "heade...

Read more...

Revision history for this message
Michael Zanetti (mzanetti) wrote :

flicking through the carousel I get this every time a new delegate is created:

file:///home/phablet/shell/qml/Dash/CardCarousel.qml:60: TypeError: Cannot read property of null

Revision history for this message
Albert Astals Cid (aacid) wrote :

> 3: /home/mzanetti/Development/reviews/dynamic_specialized_cards/tests/plugins/
> Dash/cardcreatortest.cpp: bad whitespace in line 47

You're on a really old revision?

Revision history for this message
Michael Zanetti (mzanetti) wrote :

file:///home/phablet/shell/builddir/plugins/Dash/createCardComponent:5:1: QML AbstractButton: Binding loop detected for property "headerHeight"

after updating to the latest revision (stupid me) the binding loop moved to line 5:1 instead of 1:130. But the rest is all still the same.

903. By Albert Astals Cid

Document and simplify the %1 replacements of artShapeHolderCode

904. By Albert Astals Cid

Document and narrow what you can do with the arg replacement in headerColumnCode

905. By Albert Astals Cid

Document and limit what you can do with subtitleLabelCode substitutions

906. By Albert Astals Cid

Document summaryLabelCode substitutions

907. By Albert Astals Cid

document and narrow the rest of substitutions

Revision history for this message
Albert Astals Cid (aacid) wrote :

> 192 +var headerRowCode = 'Row { \n\
>
> Can you please add a comment on top of those describing what %1, %2... are
> supposed to do.
>
> ===
>
> I also find it a bit confusing that sometimes its like this:
>
> propName: %1 \n
>
> and sometimes like this:
>
> %1 \n
>
> Would it be better like this?
>
> anchors: %1 \n
>
> so we could fill it with .arg("{left: foo; right: bar}"); It would restrict it
> a bit more what can be injected to a narrower defined set.

Done both

Revision history for this message
Albert Astals Cid (aacid) wrote :

> flicking through the carousel I get this every time a new delegate is created:
>
> file:///home/phablet/shell/qml/Dash/CardCarousel.qml:60: TypeError: Cannot
> read property of null

Fixed

908. By Albert Astals Cid

Remove line that creates warnings

I think this came from a wrong copy&paste since it wasn't there in the original code

909. By Albert Astals Cid

Don't use the .y to calculate height, otherwise we end up in loops here

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

 * Did you perform an exploratory manual test run of the code change and any related functionality?

Yes. Awesome improvement!

 * Did CI run pass? If not, please explain why.

nope... qmluitests job seems broken and I don't have permissions any more to change it. Will contact CI team to get it sorted.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/unity8-private.install'
2--- debian/unity8-private.install 2014-04-09 09:20:53 +0000
3+++ debian/unity8-private.install 2014-05-07 15:30:47 +0000
4@@ -1,5 +1,5 @@
5 usr/lib/*/unity8/qml/AccountsService
6-usr/lib/*/unity8/qml/DashViews
7+usr/lib/*/unity8/qml/Dash
8 usr/lib/*/unity8/qml/HudClient
9 usr/lib/*/unity8/qml/LightDM
10 usr/lib/*/unity8/qml/Powerd
11
12=== modified file 'plugins/CMakeLists.txt'
13--- plugins/CMakeLists.txt 2013-12-12 16:45:35 +0000
14+++ plugins/CMakeLists.txt 2014-05-07 15:30:47 +0000
15@@ -1,7 +1,7 @@
16 add_subdirectory(AccountsService)
17 add_subdirectory(HudClient)
18 add_subdirectory(LightDM)
19-add_subdirectory(DashViews)
20+add_subdirectory(Dash)
21 add_subdirectory(Powerd)
22 add_subdirectory(SessionBroadcast)
23 add_subdirectory(Ubuntu)
24
25=== renamed directory 'plugins/DashViews' => 'plugins/Dash'
26=== modified file 'plugins/Dash/CMakeLists.txt'
27--- plugins/DashViews/CMakeLists.txt 2014-01-14 12:51:08 +0000
28+++ plugins/Dash/CMakeLists.txt 2014-05-07 15:30:47 +0000
29@@ -30,17 +30,17 @@
30 organicgrid.cpp
31 )
32
33-add_library(DashViews-qml MODULE
34+add_library(Dash-qml MODULE
35 ${QMLPLUGIN_SRC}
36 )
37
38-target_link_libraries(DashViews-qml
39+target_link_libraries(Dash-qml
40 ${Qt5Gui_LIBRARIES}
41 ${Qt5Quick_LIBRARIES}
42 )
43
44-qt5_use_modules(DashViews-qml Qml Quick)
45+qt5_use_modules(Dash-qml Qml Quick)
46
47 # export the qmldir qmltypes and plugin files
48-export_qmlfiles(DashViews DashViews)
49-export_qmlplugin(DashViews 0.1 DashViews TARGETS DashViews-qml)
50+export_qmlfiles(Dash Dash)
51+export_qmlplugin(Dash 0.1 Dash TARGETS Dash-qml)
52
53=== added file 'plugins/Dash/CardCreator.js'
54--- plugins/Dash/CardCreator.js 1970-01-01 00:00:00 +0000
55+++ plugins/Dash/CardCreator.js 2014-05-07 15:30:47 +0000
56@@ -0,0 +1,518 @@
57+/*
58+ * Copyright (C) 2014 Canonical, Ltd.
59+ *
60+ * This program is free software; you can redistribute it and/or modify
61+ * it under the terms of the GNU General Public License as published by
62+ * the Free Software Foundation; version 3.
63+ *
64+ * This program is distributed in the hope that it will be useful,
65+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
66+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
67+ * GNU General Public License for more details.
68+ *
69+ * You should have received a copy of the GNU General Public License
70+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
71+ */
72+
73+.pragma library
74+
75+var kBackgroundLoaderCode = 'Loader {\n\
76+ id: backgroundLoader; \n\
77+ objectName: "backgroundLoader"; \n\
78+ anchors.fill: parent; \n\
79+ asynchronous: root.asynchronous; \n\
80+ visible: status == Loader.Ready; \n\
81+ sourceComponent: UbuntuShape { \n\
82+ objectName: "background"; \n\
83+ radius: "medium"; \n\
84+ color: getColor(0) || "white"; \n\
85+ gradientColor: getColor(1) || color; \n\
86+ anchors.fill: parent; \n\
87+ image: backgroundImage.source ? backgroundImage : null; \n\
88+ property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b; \n\
89+ property Image backgroundImage: Image { \n\
90+ objectName: "backgroundImage"; \n\
91+ source: { \n\
92+ if (cardData && typeof cardData["background"] === "string") return cardData["background"]; \n\
93+ else if (template && typeof template["card-background"] === "string") return template["card-background"]; \n\
94+ else return ""; \n\
95+ } \n\
96+ } \n\
97+ function getColor(index) { \n\
98+ if (cardData && typeof cardData["background"] === "object" \n\
99+ && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) { \n\
100+ return cardData["background"]["elements"][index]; \n\
101+ } else if (template && typeof template["card-background"] === "object" \n\
102+ && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) { \n\
103+ return template["card-background"]["elements"][index]; \n\
104+ } else return undefined; \n\
105+ } \n\
106+ } \n\
107+ }\n';
108+
109+// %1 is used as anchors of artShapeHolder
110+// %2 is used as image width
111+// %3 is used as image height
112+var kArtShapeHolderCode = 'Item { \n\
113+ id: artShapeHolder; \n\
114+ height: root.fixedArtShapeSize.height != -1 ? root.fixedArtShapeSize.height : artShapeLoader.height; \n\
115+ width: root.fixedArtShapeSize.width != -1 ? root.fixedArtShapeSize.width : artShapeLoader.width; \n\
116+ anchors { %1 } \n\
117+ Loader { \n\
118+ id: artShapeLoader; \n\
119+ objectName: "artShapeLoader"; \n\
120+ active: cardData && cardData["art"] || false; \n\
121+ asynchronous: root.asynchronous; \n\
122+ visible: status == Loader.Ready; \n\
123+ sourceComponent: UbuntuShape { \n\
124+ id: artShape; \n\
125+ objectName: "artShape"; \n\
126+ radius: "medium"; \n\
127+ readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1; \n\
128+ readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect; \n\
129+ Component.onCompleted: updateWidthHeightBindings(); \n\
130+ onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings(); \n\
131+ visible: image.status == Image.Ready; \n\
132+ function updateWidthHeightBindings() { \n\
133+ if (aspectSmallerThanImageAspect) { \n\
134+ width = Qt.binding(function() { return !visible ? 0 : image.width }); \n\
135+ height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect }); \n\
136+ } else { \n\
137+ width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect }); \n\
138+ height = Qt.binding(function() { return !visible ? 0 : image.height }); \n\
139+ } \n\
140+ } \n\
141+ image: Image { \n\
142+ objectName: "artImage"; \n\
143+ source: cardData && cardData["art"] || ""; \n\
144+ cache: true; \n\
145+ asynchronous: root.asynchronous; \n\
146+ fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop; \n\
147+ readonly property real aspect: implicitWidth / implicitHeight; \n\
148+ width: %2; \n\
149+ height: %3; \n\
150+ } \n\
151+ } \n\
152+ } \n\
153+ }\n';
154+
155+var kOverlayLoaderCode = 'Loader { \n\
156+ id: overlayLoader; \n\
157+ anchors { \n\
158+ left: artShapeHolder.left; \n\
159+ right: artShapeHolder.right; \n\
160+ bottom: artShapeHolder.bottom; \n\
161+ } \n\
162+ active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false; \n\
163+ asynchronous: root.asynchronous; \n\
164+ visible: showHeader && status == Loader.Ready; \n\
165+ sourceComponent: ShaderEffect { \n\
166+ id: overlay; \n\
167+ height: fixedHeaderHeight != -1 ? fixedHeaderHeight : headerHeight; \n\
168+ opacity: 0.6; \n\
169+ property var source: ShaderEffectSource { \n\
170+ id: shaderSource; \n\
171+ sourceItem: artShapeLoader.item; \n\
172+ onVisibleChanged: if (visible) scheduleUpdate(); \n\
173+ live: false; \n\
174+ sourceRect: Qt.rect(0, artShapeLoader.height - overlay.height, artShapeLoader.width, overlay.height); \n\
175+ } \n\
176+ vertexShader: " \n\
177+ uniform highp mat4 qt_Matrix; \n\
178+ attribute highp vec4 qt_Vertex; \n\
179+ attribute highp vec2 qt_MultiTexCoord0; \n\
180+ varying highp vec2 coord; \n\
181+ void main() { \n\
182+ coord = qt_MultiTexCoord0; \n\
183+ gl_Position = qt_Matrix * qt_Vertex; \n\
184+ }"; \n\
185+ fragmentShader: " \n\
186+ varying highp vec2 coord; \n\
187+ uniform sampler2D source; \n\
188+ uniform lowp float qt_Opacity; \n\
189+ void main() { \n\
190+ lowp vec4 tex = texture2D(source, coord); \n\
191+ gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity; \n\
192+ }"; \n\
193+ } \n\
194+ }\n';
195+
196+// %1 is used as anchors of row
197+// %2 is used as first child of the row
198+// %3 is used as second child of the row
199+var kHeaderRow2Code = 'Row { \n\
200+ id: row; \n\
201+ objectName: "outerRow"; \n\
202+ property real margins: units.gu(1); \n\
203+ spacing: margins; \n\
204+ anchors { %1 } \n\
205+ anchors.right: parent.right; \n\
206+ anchors.margins: margins;\n\
207+ data: [ %2\n\
208+ ,\n\
209+ %3 \n\
210+ ] \n\
211+ }\n';
212+
213+// %1 is used as anchors of row
214+// %2 is used as first child of the row
215+// %3 is used as second child of the row
216+// %4 is used as third child of the row
217+var kHeaderRow3Code = 'Row { \n\
218+ id: row; \n\
219+ objectName: "outerRow"; \n\
220+ property real margins: units.gu(1); \n\
221+ spacing: margins; \n\
222+ anchors { %1 } \n\
223+ anchors.right: parent.right; \n\
224+ anchors.margins: margins;\n\
225+ data: [ %2\n\
226+ ,\n\
227+ %3 \n\
228+ ,\n\
229+ %4 \n\
230+ ] \n\
231+ }\n';
232+
233+// %1 is used as first child of the column
234+// %2 is used as second child of the column
235+var kHeaderColumnCode = 'Column { \n\
236+ anchors.verticalCenter: parent.verticalCenter; \n\
237+ spacing: units.dp(2); \n\
238+ width: parent.width - x;\n\
239+ data: [ %1\n\
240+ ,\n\
241+ %2 \n\
242+ ] \n\
243+ }\n';
244+
245+// %1 is used as anchors of mascotShapeLoader
246+var kMascotShapeLoaderCode = 'Loader { \n\
247+ id: mascotShapeLoader; \n\
248+ objectName: "mascotShapeLoader"; \n\
249+ asynchronous: root.asynchronous; \n\
250+ active: mascotImage.status === Image.Ready; \n\
251+ visible: showHeader && active && status == Loader.Ready; \n\
252+ width: units.gu(6); \n\
253+ height: units.gu(5.625); \n\
254+ sourceComponent: UbuntuShape { image: mascotImage } \n\
255+ anchors { %1 } \n\
256+ }\n';
257+
258+// %1 is used as anchors of mascotImage
259+// %2 is used as visible of mascotImage
260+var kMascotImageCode = 'Image { \n\
261+ id: mascotImage; \n\
262+ objectName: "mascotImage"; \n\
263+ anchors { %1 } \n\
264+ readonly property int maxSize: Math.max(width, height) * 4; \n\
265+ source: cardData && cardData["mascot"]; \n\
266+ width: units.gu(6); \n\
267+ height: units.gu(5.625); \n\
268+ sourceSize { width: maxSize; height: maxSize } \n\
269+ fillMode: Image.PreserveAspectCrop; \n\
270+ horizontalAlignment: Image.AlignHCenter; \n\
271+ verticalAlignment: Image.AlignVCenter; \n\
272+ visible: %2; \n\
273+ }\n';
274+
275+// %1 is used as anchors of titleLabel
276+// %1 is used as color of titleLabel
277+// %3 is used as extra condition for visible of titleLabel
278+var kTitleLabelCode = 'Label { \n\
279+ id: titleLabel; \n\
280+ objectName: "titleLabel"; \n\
281+ anchors { %1 } \n\
282+ elide: Text.ElideRight; \n\
283+ fontSize: "small"; \n\
284+ wrapMode: Text.Wrap; \n\
285+ maximumLineCount: 2; \n\
286+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); \n\
287+ color: %2; \n\
288+ visible: showHeader %3; \n\
289+ text: root.title; \n\
290+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal; \n\
291+ horizontalAlignment: root.headerAlignment; \n\
292+ }\n';
293+
294+// %1 is used as anchors of subtitleLabel
295+// %2 is used as color of subtitleLabel
296+var kSubtitleLabelCode = 'Label { \n\
297+ id: subtitleLabel; \n\
298+ objectName: "subtitleLabel"; \n\
299+ anchors { %1 } \n\
300+ elide: Text.ElideRight; \n\
301+ fontSize: "small"; \n\
302+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); \n\
303+ color: %2; \n\
304+ visible: titleLabel.visible && titleLabel.text; \n\
305+ text: cardData && cardData["subtitle"] || ""; \n\
306+ font.weight: Font.Light; \n\
307+ horizontalAlignment: root.headerAlignment; \n\
308+ }\n';
309+
310+// %1 is used as top anchor of summary
311+// %2 is used as topMargin anchor of summary
312+// %3 is used as color of summary
313+var kSummaryLabelCode = 'Label { \n\
314+ id: summary; \n\
315+ objectName: "summaryLabel"; \n\
316+ anchors { \n\
317+ top: %1; \n\
318+ left: parent.left; \n\
319+ right: parent.right; \n\
320+ margins: units.gu(1); \n\
321+ topMargin: %2; \n\
322+ } \n\
323+ wrapMode: Text.Wrap; \n\
324+ maximumLineCount: 5; \n\
325+ elide: Text.ElideRight; \n\
326+ text: cardData && cardData["summary"] || ""; \n\
327+ height: text ? implicitHeight : 0; \n\
328+ fontSize: "small"; \n\
329+ color: %3; \n\
330+ }\n';
331+
332+function cardString(template, components) {
333+ var code;
334+ code = 'AbstractButton { \n\
335+ id: root; \n\
336+ property var template; \n\
337+ property var components; \n\
338+ property var cardData; \n\
339+ property real fontScale: 1.0; \n\
340+ property int headerAlignment: Text.AlignLeft; \n\
341+ property int fixedHeaderHeight: -1; \n\
342+ property size fixedArtShapeSize: Qt.size(-1, -1); \n\
343+ readonly property string title: cardData && cardData["title"] || ""; \n\
344+ property bool asynchronous: true; \n\
345+ property bool showHeader: true; \n\
346+ implicitWidth: childrenRect.width; \n';
347+
348+ var hasArt = components["art"] && components["art"]["field"] || false;
349+ var hasSummary = components["summary"] || false;
350+ var artAndSummary = hasArt && hasSummary;
351+ var isHorizontal = template["card-layout"] === "horizontal";
352+ var hasBackground = !isHorizontal && (template["card-background"] || components["background"] || artAndSummary);
353+ var hasTitle = components["title"] || false;
354+ var hasMascot = components["mascot"] || false;
355+ var headerAsOverlay = hasArt && template && template["overlay"] === true && (hasTitle || hasMascot);
356+ var hasSubtitle = components["subtitle"] || false;
357+ var hasHeaderRow = hasMascot && hasTitle;
358+
359+ if (hasBackground) {
360+ code += kBackgroundLoaderCode;
361+ }
362+
363+ if (hasArt) {
364+ code += 'readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);\n';
365+
366+ var widthCode, heightCode;
367+ var anchors;
368+ if (isHorizontal) {
369+ anchors = 'left: parent.left';
370+ if (hasMascot || hasTitle) {
371+ widthCode = 'height * artShape.aspect'
372+ heightCode = 'headerHeight';
373+ } else {
374+ // This side of the else is a bit silly, who wants an horizontal layout without mascot and title?
375+ // So we define a "random" height of the image height + 2 gu for the margins
376+ widthCode = 'height * artShape.aspect'
377+ heightCode = 'units.gu(7.625)';
378+ }
379+ } else {
380+ anchors = 'horizontalCenter: parent.horizontalCenter;';
381+ widthCode = 'root.width'
382+ heightCode = 'width / artShape.aspect';
383+ }
384+
385+ code += kArtShapeHolderCode.arg(anchors).arg(widthCode).arg(heightCode);
386+ } else {
387+ code += 'readonly property size artShapeSize: Qt.size(-1, -1);\n'
388+ }
389+
390+ if (headerAsOverlay) {
391+ code += kOverlayLoaderCode;
392+ }
393+
394+ var headerVerticalAnchors;
395+ if (headerAsOverlay) {
396+ headerVerticalAnchors = 'bottom: artShapeHolder.bottom; \n\
397+ bottomMargin: units.gu(1);\n';
398+ } else {
399+ if (hasArt) {
400+ if (isHorizontal) {
401+ headerVerticalAnchors = 'top: artShapeHolder.top; \n\
402+ topMargin: units.gu(1);\n';
403+ } else {
404+ headerVerticalAnchors = 'top: artShapeHolder.bottom; \n\
405+ topMargin: units.gu(1);\n';
406+ }
407+ } else {
408+ headerVerticalAnchors = 'top: parent.top; \n\
409+ topMargin: units.gu(1);\n';
410+ }
411+ }
412+ var headerLeftAnchor;
413+ var headerLeftAnchorHasMagin = false;
414+ if (isHorizontal && hasArt) {
415+ headerLeftAnchor = 'left: artShapeHolder.right; \n\
416+ leftMargin: units.gu(1);\n';
417+ headerLeftAnchorHasMagin = true;
418+ } else {
419+ headerLeftAnchor = 'left: parent.left;\n';
420+ }
421+
422+ if (hasHeaderRow) {
423+ code += 'readonly property int headerHeight: row.height + row.margins * 2;\n'
424+ } else if (hasMascot) {
425+ code += 'readonly property int headerHeight: mascotImage.height + units.gu(1) * 2;\n'
426+ } else if (hasSubtitle) {
427+ code += 'readonly property int headerHeight: titleLabel.height + titleLabel.anchors.topMargin * 2 + subtitleLabel.height + subtitleLabel.anchors.topMargin;\n'
428+ } else if (hasTitle) {
429+ code += 'readonly property int headerHeight: titleLabel.height + titleLabel.anchors.topMargin * 2;\n'
430+ } else {
431+ code += 'readonly property int headerHeight: 0;\n'
432+ }
433+
434+ var mascotShapeCode = "";
435+ var mascotCode = "";
436+ if (hasMascot) {
437+ var useMascotShape = !hasBackground && !headerAsOverlay;
438+ var anchors = "";
439+ if (!hasHeaderRow) {
440+ anchors += headerLeftAnchor;
441+ anchors += headerVerticalAnchors;
442+ if (!headerLeftAnchorHasMagin) {
443+ anchors += 'leftMargin: units.gu(1);\n'
444+ }
445+ } else {
446+ anchors = "verticalCenter: parent.verticalCenter;"
447+ }
448+
449+ if (useMascotShape) {
450+ mascotShapeCode = kMascotShapeLoaderCode.arg(anchors);
451+ }
452+
453+ var mascotImageVisible = useMascotShape ? 'false' : 'showHeader';
454+ mascotCode = kMascotImageCode.arg(anchors).arg(mascotImageVisible);
455+ }
456+
457+ var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : "grey"';
458+
459+ var titleSubtitleCode = "";
460+ if (hasTitle) {
461+ var color;
462+ if (headerAsOverlay) {
463+ color = '"white"';
464+ } else if (hasSummary) {
465+ color = 'summary.color';
466+ } else if (hasBackground) {
467+ color = summaryColorWithBackground;
468+ } else {
469+ color = '"grey"';
470+ }
471+
472+ var titleAnchors;
473+ var subtitleAnchors;
474+ if (hasMascot && hasSubtitle) {
475+ // Using row + column
476+ titleAnchors = 'left: parent.left; right: parent.right';
477+ subtitleAnchors = titleAnchors;
478+ } else if (hasMascot) {
479+ // Using row + label
480+ titleAnchors = 'verticalCenter: parent.verticalCenter;\n'
481+ } else {
482+ if (headerAsOverlay) {
483+ // Using anchors to the overlay
484+ titleAnchors = 'left: parent.left; \n\
485+ leftMargin: units.gu(1); \n\
486+ right: parent.right; \n\
487+ top: overlayLoader.top; \n\
488+ topMargin: units.gu(1);\n';
489+ } else {
490+ // Using anchors to the mascot/parent
491+ titleAnchors = "right: parent.right;";
492+ titleAnchors += headerLeftAnchor;
493+ titleAnchors += headerVerticalAnchors;
494+ }
495+ subtitleAnchors = 'left: titleLabel.left; \n\
496+ leftMargin: titleLabel.leftMargin; \n\
497+ right: titleLabel.right; \n\
498+ top: titleLabel.bottom; \n\
499+ topMargin: units.dp(2);\n';
500+ }
501+
502+ var titleLabelVisibleExtra = (headerAsOverlay ? '&& overlayLoader.active': '');
503+ var titleCode = kTitleLabelCode.arg(titleAnchors).arg(color).arg(titleLabelVisibleExtra);
504+ var subtitleCode = "";
505+ if (hasSubtitle) {
506+ subtitleCode += kSubtitleLabelCode.arg(subtitleAnchors).arg(color);
507+ }
508+
509+ if (hasMascot && hasSubtitle) {
510+ // If using row + column wrap the code in the column
511+ titleSubtitleCode = kHeaderColumnCode.arg(titleCode).arg(subtitleCode);
512+ } else {
513+ titleSubtitleCode = titleCode + subtitleCode;
514+ }
515+ }
516+
517+ if (hasHeaderRow) {
518+ if (mascotShapeCode != "") {
519+ code += kHeaderRow3Code.arg(headerVerticalAnchors + headerLeftAnchor).arg(mascotShapeCode).arg(mascotCode).arg(titleSubtitleCode);
520+ } else {
521+ code += kHeaderRow2Code.arg(headerVerticalAnchors + headerLeftAnchor).arg(mascotCode).arg(titleSubtitleCode);
522+ }
523+ } else {
524+ code += mascotShapeCode + mascotCode + titleSubtitleCode;
525+ }
526+
527+ if (hasSummary) {
528+ var summaryTopAnchor;
529+ if (isHorizontal && hasArt) summaryTopAnchor = "artShapeHolder.bottom";
530+ else if (headerAsOverlay && hasArt) summaryTopAnchor = "artShapeHolder.bottom";
531+ else if (hasHeaderRow) summaryTopAnchor = "row.bottom";
532+ else if (hasMascot) summaryTopAnchor = "mascotImage.bottom";
533+ else if (hasSubtitle) summaryTopAnchor = "subtitleLabel.bottom";
534+ else if (hasTitle) summaryTopAnchor = "titleLabel.bottom";
535+ else if (hasArt) summaryTopAnchor = "artShapeHolder.bottom";
536+ else summaryTopAnchor = "parent.top";
537+
538+ var color;
539+ if (hasBackground) {
540+ color = summaryColorWithBackground;
541+ } else {
542+ color = '"grey"';
543+ }
544+
545+ var summaryTopMargin = (hasMascot || hasSubtitle ? 'anchors.margins' : '0');
546+
547+ code += kSummaryLabelCode.arg(summaryTopAnchor).arg(summaryTopMargin).arg(color);
548+ }
549+
550+ if (hasSummary) {
551+ code += 'implicitHeight: summary.y + summary.height + (summary.text ? units.gu(1) : 0);\n';
552+ } else if (hasHeaderRow) {
553+ code += 'implicitHeight: row.y + row.height + units.gu(1);\n';
554+ } else if (hasMascot) {
555+ code += 'implicitHeight: mascotImage.y + mascotImage.height;\n';
556+ } else if (hasSubtitle) {
557+ code += 'implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);\n';
558+ } else if (hasTitle) {
559+ code += 'implicitHeight: titleLabel.y + titleLabel.height + units.gu(1);\n';
560+ }
561+ // Close the AbstractButton
562+ code += '}\n';
563+
564+ return code;
565+}
566+
567+function createCardComponent(parent, template, components) {
568+ var imports = 'import QtQuick 2.2; \n\
569+ import Ubuntu.Components 0.1; \n\
570+ import Ubuntu.Thumbnailer 0.1;\n';
571+ var card = cardString(template, components);
572+ var code = imports + 'Component {\n' + card + '}\n';
573+ return Qt.createQmlObject(code, parent, "createCardComponent");
574+}
575
576=== added file 'plugins/Dash/CardCreatorCache.qml'
577--- plugins/Dash/CardCreatorCache.qml 1970-01-01 00:00:00 +0000
578+++ plugins/Dash/CardCreatorCache.qml 2014-05-07 15:30:47 +0000
579@@ -0,0 +1,40 @@
580+/*
581+ * Copyright (C) 2014 Canonical, Ltd.
582+ *
583+ * This program is free software; you can redistribute it and/or modify
584+ * it under the terms of the GNU General Public License as published by
585+ * the Free Software Foundation; version 3.
586+ *
587+ * This program is distributed in the hope that it will be useful,
588+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
589+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
590+ * GNU General Public License for more details.
591+ *
592+ * You should have received a copy of the GNU General Public License
593+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
594+ */
595+
596+pragma Singleton
597+import QtQuick 2.2
598+import "CardCreator.js" as CardCreator
599+
600+QtObject {
601+ id: root
602+
603+ property var cache: new Object();
604+
605+ function getCardComponent(template, components) {
606+ if (template === undefined || components === undefined)
607+ return undefined;
608+
609+ var tString = JSON.stringify(template);
610+ var cString = JSON.stringify(components);
611+ var allString = tString + cString;
612+ var component = cache[allString];
613+ if (component === undefined) {
614+ component = CardCreator.createCardComponent(root, template, components);
615+ cache[allString] = component;
616+ }
617+ return component;
618+ }
619+}
620
621=== modified file 'plugins/Dash/plugin.cpp'
622--- plugins/DashViews/plugin.cpp 2014-01-10 16:29:19 +0000
623+++ plugins/Dash/plugin.cpp 2014-05-07 15:30:47 +0000
624@@ -24,9 +24,9 @@
625
626 #include <QAbstractItemModel>
627
628-void DashViewsPlugin::registerTypes(const char *uri)
629+void DashPlugin::registerTypes(const char *uri)
630 {
631- Q_ASSERT(uri == QLatin1String("DashViews"));
632+ Q_ASSERT(uri == QLatin1String("Dash"));
633 qmlRegisterType<QAbstractItemModel>();
634 qmlRegisterType<HorizontalJournal>(uri, 0, 1, "HorizontalJournal");
635 qmlRegisterType<ListViewWithPageHeader>(uri, 0, 1, "ListViewWithPageHeader");
636
637=== modified file 'plugins/Dash/plugin.h'
638--- plugins/DashViews/plugin.h 2013-12-12 16:45:35 +0000
639+++ plugins/Dash/plugin.h 2014-05-07 15:30:47 +0000
640@@ -15,13 +15,13 @@
641 *
642 */
643
644-#ifndef DASHVIEWS_PLUGIN_H
645-#define DASHVIEWS_PLUGIN_H
646+#ifndef DASH_PLUGIN_H
647+#define DASH_PLUGIN_H
648
649 #include <QtQml/QQmlEngine>
650 #include <QtQml/QQmlExtensionPlugin>
651
652-class DashViewsPlugin : public QQmlExtensionPlugin
653+class DashPlugin : public QQmlExtensionPlugin
654 {
655 Q_OBJECT
656 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
657
658=== modified file 'plugins/Dash/qmldir'
659--- plugins/DashViews/qmldir 2013-12-12 16:45:35 +0000
660+++ plugins/Dash/qmldir 2014-05-07 15:30:47 +0000
661@@ -1,3 +1,4 @@
662-module DashViews
663-plugin DashViews-qml
664+module Dash
665+plugin Dash-qml
666 typeinfo plugin.qmltypes
667+singleton CardCreatorCache 0.1 CardCreatorCache.qml
668
669=== removed file 'qml/Dash/Card.qml'
670--- qml/Dash/Card.qml 2014-04-16 11:04:49 +0000
671+++ qml/Dash/Card.qml 1970-01-01 00:00:00 +0000
672@@ -1,236 +0,0 @@
673-/*
674- * Copyright (C) 2013 Canonical, Ltd.
675- *
676- * This program is free software; you can redistribute it and/or modify
677- * it under the terms of the GNU General Public License as published by
678- * the Free Software Foundation; version 3.
679- *
680- * This program is distributed in the hope that it will be useful,
681- * but WITHOUT ANY WARRANTY; without even the implied warranty of
682- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
683- * GNU General Public License for more details.
684- *
685- * You should have received a copy of the GNU General Public License
686- * along with this program. If not, see <http://www.gnu.org/licenses/>.
687- */
688-
689-import QtQuick 2.0
690-import Ubuntu.Components 0.1
691-// For image://albumart and image://thumbnailer image providers
692-import Ubuntu.Thumbnailer 0.1
693-
694-AbstractButton {
695- id: root
696- property var template
697- property var components
698- property var cardData
699-
700- property real fontScale: 1.0
701- property int headerAlignment: Text.AlignLeft
702- readonly property int headerHeight: headerLoader.item ? headerLoader.item.height : 0
703- property int fixedHeaderHeight: -1
704- readonly property string title: cardData && cardData["title"] || ""
705-
706- property bool showHeader: true
707-
708- implicitWidth: childrenRect.width
709- implicitHeight: summary.y + summary.height + (summary.text && backgroundLoader.active ? units.gu(1) : 0)
710-
711- Loader {
712- id: backgroundLoader
713- objectName: "backgroundLoader"
714-
715- readonly property bool artAndSummary: components["art"]["field"] && components["summary"] || false
716- active: template["card-layout"] !== "horizontal" && (template["card-background"] || components["background"] || artAndSummary)
717- anchors.fill: parent
718-
719- sourceComponent: UbuntuShape {
720- objectName: "background"
721- radius: "medium"
722- color: getColor(0) || "white"
723- gradientColor: getColor(1) || color
724- anchors.fill: parent
725- image: backgroundImage.source ? backgroundImage : null
726-
727- property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b
728-
729- property Image backgroundImage: Image {
730- objectName: "backgroundImage"
731- source: {
732- if (cardData && typeof cardData["background"] === "string") return cardData["background"]
733- else if (template && typeof template["card-background"] === "string") return template["card-background"]
734- else return ""
735- }
736- }
737-
738- function getColor(index) {
739- if (cardData && typeof cardData["background"] === "object"
740- && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
741- return cardData["background"]["elements"][index];
742- } else if (template && typeof template["card-background"] === "object"
743- && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
744- return template["card-background"]["elements"][index];
745- } else return undefined;
746- }
747- }
748- }
749-
750- UbuntuShape {
751- id: artShape
752- radius: "medium"
753- objectName: "artShape"
754- anchors.horizontalCenter: template && template["card-layout"] === "horizontal" ? undefined : parent.horizontalCenter
755- anchors.left: template && template["card-layout"] === "horizontal" ? parent.left : undefined
756- visible: cardData && cardData["art"] || false
757-
758- readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1
759- readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect
760-
761- Component.onCompleted: updateWidthHeightBindings();
762- onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
763-
764- function updateWidthHeightBindings() {
765- if (aspectSmallerThanImageAspect) {
766- width = Qt.binding(function() { return !visible ? 0 : image.width });
767- height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
768- } else {
769- width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
770- height = Qt.binding(function() { return !visible ? 0 : image.height });
771- }
772- }
773-
774- image: Image {
775- objectName: "artImage"
776- source: cardData && cardData["art"] || ""
777- cache: true
778- // FIXME uncomment when having investigated / fixed the crash
779- //sourceSize.width: width > height ? width : 0
780- //sourceSize.height: height > width ? height : 0
781- fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop
782-
783- readonly property real aspect: implicitWidth / implicitHeight
784- readonly property bool isHorizontal: template && template["card-layout"] === "horizontal"
785-
786- Component.onCompleted: updateWidthHeightBindings();
787- onIsHorizontalChanged: updateWidthHeightBindings();
788-
789- function updateWidthHeightBindings() {
790- if (isHorizontal) {
791- width = Qt.binding(function() { return height * artShape.aspect });
792- height = Qt.binding(function() { return root.headerHeight });
793- } else {
794- width = Qt.binding(function() { return root.width });
795- height = Qt.binding(function() { return width / artShape.aspect });
796- }
797- }
798- }
799- }
800-
801- Loader {
802- id: overlayLoader
803- anchors {
804- left: artShape.left
805- right: artShape.right
806- bottom: artShape.bottom
807- }
808- active: template && template["overlay"] && artShape.visible && artShape.image.status === Image.Ready || false
809-
810- sourceComponent: ShaderEffect {
811- id: overlay
812-
813- height: headerLoader.item ? headerLoader.item.height : 0
814- opacity: headerLoader.item ? headerLoader.item.opacity * 0.6 : 0
815-
816- property var source: ShaderEffectSource {
817- id: shaderSource
818- sourceItem: artShape
819- onVisibleChanged: if (visible) scheduleUpdate()
820- live: false
821- sourceRect: Qt.rect(0, artShape.height - overlay.height, artShape.width, overlay.height)
822- }
823-
824- vertexShader: "
825- uniform highp mat4 qt_Matrix;
826- attribute highp vec4 qt_Vertex;
827- attribute highp vec2 qt_MultiTexCoord0;
828- varying highp vec2 coord;
829- void main() {
830- coord = qt_MultiTexCoord0;
831- gl_Position = qt_Matrix * qt_Vertex;
832- }"
833-
834- fragmentShader: "
835- varying highp vec2 coord;
836- uniform sampler2D source;
837- uniform lowp float qt_Opacity;
838- void main() {
839- lowp vec4 tex = texture2D(source, coord);
840- gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity;
841- }"
842- }
843- }
844-
845- Loader {
846- id: headerLoader
847- objectName: "cardHeaderLoader"
848-
849- anchors {
850- top: {
851- if (template) {
852- if (template["overlay"]) return overlayLoader.top;
853- if (template["card-layout"] === "horizontal") return artShape.top;
854- }
855- return artShape.bottom;
856- }
857- left: {
858- if (template) {
859- if (!template["overlay"] && template["card-layout"] === "horizontal") return artShape.right;
860- }
861- return parent.left;
862- }
863- right: parent.right
864- }
865- active: cardData && cardData["title"] || cardData && cardData["mascot"] || false
866-
867- sourceComponent: CardHeader {
868- id: header
869- objectName: "cardHeader"
870-
871- mascot: cardData && cardData["mascot"] || ""
872- title: root.title
873- subtitle: cardData && cardData["subtitle"] || ""
874-
875- titleWeight: components && components["subtitle"] ? Font.DemiBold : Font.Normal
876-
877- opacity: showHeader ? 1 : 0
878- inOverlay: root.template && root.template["overlay"] === true
879- fontColor: inOverlay ? "white" : summary.color
880- useMascotShape: !backgroundLoader.active && !inOverlay
881- headerAlignment: root.headerAlignment
882- height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight
883- fontScale: root.fontScale
884-
885- Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } }
886- }
887- }
888-
889- Label {
890- id: summary
891- objectName: "summaryLabel"
892- anchors {
893- top: headerLoader.active ? headerLoader.bottom : artShape.bottom
894- left: parent.left
895- right: parent.right
896- margins: backgroundLoader.active ? units.gu(1) : 0
897- topMargin: 0
898- }
899- wrapMode: Text.Wrap
900- maximumLineCount: 5
901- elide: Text.ElideRight
902- text: cardData && cardData["summary"] || ""
903- height: text ? implicitHeight : 0
904- fontSize: "small"
905- // TODO karni: Change "grey" to Ubuntu.Components.Palette color once updated.
906- color: backgroundLoader.active && backgroundLoader.item.luminance < 0.7 ? "white" : "grey"
907- }
908-}
909
910=== modified file 'qml/Dash/CardCarousel.qml'
911--- qml/Dash/CardCarousel.qml 2014-04-01 14:40:07 +0000
912+++ qml/Dash/CardCarousel.qml 2014-05-07 15:30:47 +0000
913@@ -49,21 +49,24 @@
914 property real fontScale: 1 / selectedItemScaleFactor
915 property real headerHeight: cardTool.headerHeight / selectedItemScaleFactor
916
917- itemComponent: Card {
918- id: card
919- objectName: "carouselDelegate" + index
920- fixedHeaderHeight: carousel.headerHeight
921- cardData: model
922- template: cardTool.template
923- components: cardTool.components
924+ itemComponent: Loader {
925+ id: loader
926
927 property bool explicitlyScaled
928 property var model
929-
930 enabled: false
931- showHeader: explicitlyScaled
932
933- fontScale: carousel.fontScale
934+ sourceComponent: cardTool.cardComponent
935+ onLoaded: {
936+ item.objectName = "carouselDelegate" + index;
937+ item.fixedHeaderHeight = Qt.binding(function() { return carousel.headerHeight; });
938+ item.height = Qt.binding(function() { return cardTool.cardHeight; });
939+ item.cardData = Qt.binding(function() { return model; });
940+ item.template = Qt.binding(function() { return cardTool.template; });
941+ item.components = Qt.binding(function() { return cardTool.components; });
942+ item.fontScale = Qt.binding(function() { return carousel.fontScale; });
943+ item.showHeader = Qt.binding(function() { return loader.explicitlyScaled; });
944+ }
945 }
946 }
947 }
948
949=== modified file 'qml/Dash/CardFilterGrid.qml'
950--- qml/Dash/CardFilterGrid.qml 2014-04-16 15:55:59 +0000
951+++ qml/Dash/CardFilterGrid.qml 2014-05-07 15:30:47 +0000
952@@ -45,25 +45,28 @@
953 collapsedRowCount: Math.min(2, cardTool && cardTool.template && cardTool.template["collapsed-rows"] || 2)
954 delegateCreationBegin: genericFilterGrid.delegateCreationBegin
955 delegateCreationEnd: genericFilterGrid.delegateCreationEnd
956- delegate: Loader {
957- asynchronous: true
958+ delegate: Item {
959 width: filterGrid.cellWidth
960 height: filterGrid.cellHeight
961- Card {
962- id: card
963- width: cardTool.cardWidth
964- height: cardTool.cardHeight
965- fixedHeaderHeight: cardTool.headerHeight
966+ Loader {
967+ id: loader
968+ sourceComponent: cardTool.cardComponent
969 anchors.horizontalCenter: parent.horizontalCenter
970- objectName: "delegate" + index
971- cardData: model
972- template: cardTool.template
973- components: cardTool.components
974-
975- headerAlignment: cardTool.headerAlignment
976-
977- onClicked: genericFilterGrid.clicked(index, card.y)
978- onPressAndHold: genericFilterGrid.pressAndHold(index, card.y)
979+ onLoaded: {
980+ item.objectName = "delegate" + index;
981+ item.width = Qt.binding(function() { return cardTool.cardWidth; });
982+ item.height = Qt.binding(function() { return cardTool.cardHeight; });
983+ item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
984+ item.cardData = Qt.binding(function() { return model; });
985+ item.template = Qt.binding(function() { return cardTool.template; });
986+ item.components = Qt.binding(function() { return cardTool.components; });
987+ item.headerAlignment = Qt.binding(function() { return cardTool.headerAlignment; });
988+ }
989+ Connections {
990+ target: loader.item
991+ onClicked: genericFilterGrid.clicked(index, item.y)
992+ onPressAndHold: genericFilterGrid.pressAndHold(index, item.y)
993+ }
994 }
995 }
996 }
997
998=== removed file 'qml/Dash/CardHeader.qml'
999--- qml/Dash/CardHeader.qml 2014-04-02 14:29:21 +0000
1000+++ qml/Dash/CardHeader.qml 1970-01-01 00:00:00 +0000
1001@@ -1,126 +0,0 @@
1002-/*
1003- * Copyright (C) 2013 Canonical, Ltd.
1004- *
1005- * This program is free software; you can redistribute it and/or modify
1006- * it under the terms of the GNU General Public License as published by
1007- * the Free Software Foundation; version 3.
1008- *
1009- * This program is distributed in the hope that it will be useful,
1010- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1011- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1012- * GNU General Public License for more details.
1013- *
1014- * You should have received a copy of the GNU General Public License
1015- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1016- */
1017-
1018-import QtQuick 2.0
1019-import Ubuntu.Components 0.1
1020-
1021-Item {
1022- id: root
1023- property url mascot: ""
1024- property alias title: titleLabel.text
1025- property var subtitle
1026-
1027- property alias titleWeight: titleLabel.font.weight
1028- property alias titleSize: titleLabel.fontSize
1029-
1030- // FIXME: Saviq, used to scale fonts down in Carousel
1031- property real fontScale: 1.0
1032-
1033- property alias headerAlignment: titleLabel.horizontalAlignment
1034-
1035- property bool inOverlay: false
1036- property bool useMascotShape: true
1037- property color fontColor: Theme.palette.selected.backgroundText
1038-
1039- visible: mascot != "" || title
1040- implicitHeight: row.height > 0 ? row.height + row.margins * 2 : 0
1041-
1042- Row {
1043- id: row
1044- objectName: "outerRow"
1045-
1046- property real margins: units.gu(1)
1047-
1048- spacing: mascotShapeLoader.active || mascotImageLoader.active || inOverlay ? margins : 0
1049- anchors {
1050- top: parent.top; left: parent.left; right: parent.right
1051- margins: margins
1052- leftMargin: spacing
1053- rightMargin: spacing
1054- }
1055-
1056- Loader {
1057- id: mascotShapeLoader
1058- objectName: "mascotShapeLoader"
1059-
1060- active: useMascotShape && mascotImageLoader.item && mascotImageLoader.item.status === Image.Ready
1061- visible: active
1062- anchors.verticalCenter: parent.verticalCenter
1063- // TODO karni: Icon aspect-ratio is 8:7.5. Revisit these values to avoid fraction of pixels.
1064- width: units.gu(6)
1065- height: units.gu(5.625)
1066- readonly property int maxSize: Math.max(width, height) * 4
1067-
1068- sourceComponent: UbuntuShape {
1069- image: mascotImageLoader.item
1070- }
1071- }
1072-
1073- Loader {
1074- id: mascotImageLoader
1075- active: root.mascot != ""
1076- visible: active && !useMascotShape && item.status === Image.Ready
1077- anchors.verticalCenter: parent.verticalCenter
1078- sourceComponent: Image {
1079- objectName: "mascotImage"
1080-
1081- source: root.mascot
1082- width: source ? mascotShapeLoader.width : 0
1083- height: mascotShapeLoader.height
1084-
1085- sourceSize { width: mascotShapeLoader.maxSize; height: mascotShapeLoader.maxSize }
1086- fillMode: Image.PreserveAspectCrop
1087- horizontalAlignment: Image.AlignHCenter
1088- verticalAlignment: Image.AlignVCenter
1089- }
1090- }
1091-
1092- Column {
1093- objectName: "column"
1094- width: parent.width - x
1095- spacing: units.dp(2)
1096- anchors.verticalCenter: parent.verticalCenter
1097-
1098- Label {
1099- id: titleLabel
1100- objectName: "titleLabel"
1101- anchors { left: parent.left; right: parent.right }
1102- elide: Text.ElideRight
1103- font.weight: Font.Normal
1104- fontSize: "small"
1105- wrapMode: Text.Wrap
1106- maximumLineCount: 2
1107- font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale)
1108- color: fontColor
1109- }
1110-
1111- Loader {
1112- active: titleLabel.text && root.subtitle
1113- anchors { left: parent.left; right: parent.right }
1114- sourceComponent: Label {
1115- id: subtitleLabel
1116- objectName: "subtitleLabel"
1117- elide: Text.ElideRight
1118- fontSize: "small"
1119- font.weight: Font.Light
1120- font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale)
1121- color: fontColor
1122- text: root.subtitle
1123- }
1124- }
1125- }
1126- }
1127-}
1128
1129=== modified file 'qml/Dash/CardTool.qml'
1130--- qml/Dash/CardTool.qml 2014-03-17 11:44:05 +0000
1131+++ qml/Dash/CardTool.qml 2014-05-07 15:30:47 +0000
1132@@ -15,6 +15,7 @@
1133 */
1134
1135 import QtQuick 2.0
1136+import Dash 0.1
1137
1138 /*!
1139 \brief Tool for introspecting Card properties.
1140@@ -65,6 +66,8 @@
1141 return layout;
1142 }
1143
1144+ property var cardComponent: CardCreatorCache.getCardComponent(cardTool.template, cardTool.components);
1145+
1146 // FIXME: Saviq
1147 // Only way for the card below to actually be laid out completely.
1148 // If invisible or in "data" array, some components are not taken into account.
1149@@ -108,7 +111,7 @@
1150 if (template["card-size"] >= 12 && template["card-size"] <= 38) return units.gu(template["card-size"]);
1151 return units.gu(18.5);
1152 case "grid":
1153- return card.implicitHeight
1154+ return cardLoader.item ? cardLoader.item.implicitHeight : 0
1155 case "carousel":
1156 return cardWidth / (components ? components["art"]["aspect-ratio"] : 1)
1157 case undefined:
1158@@ -122,7 +125,8 @@
1159 /*!
1160 type:real \brief Height of the card's header.
1161 */
1162- readonly property alias headerHeight: card.headerHeight
1163+ readonly property int headerHeight: cardLoader.item ? cardLoader.item.headerHeight : 0
1164+ readonly property size artShapeSize: cardLoader.item ? cardLoader.item.artShapeSize : 0
1165
1166 /*!
1167 \brief Desired alignment of header components.
1168@@ -161,15 +165,8 @@
1169 }
1170 }
1171
1172- Card {
1173- id: card
1174- objectName: "cardToolCard"
1175- template: cardTool.template
1176- components: cardTool.components
1177-
1178- width: cardTool.cardWidth || implicitWidth
1179- height: cardTool.cardHeight || implicitHeight
1180-
1181+ Loader {
1182+ id: cardLoader
1183 property var fields: ["art", "mascot", "title", "subtitle", "summary"]
1184 property var maxData: {
1185 "art": Qt.resolvedUrl("graphics/checkers.png"),
1186@@ -178,19 +175,30 @@
1187 "subtitle": "—",
1188 "summary": "—\n—\n—\n—\n—"
1189 }
1190-
1191- onComponentsChanged: {
1192- var data = {};
1193- for (var k in fields) {
1194- var component = components[fields[k]];
1195- var key = fields[k];
1196- if ((typeof component === "string" && component.length > 0) ||
1197- (typeof component === "object" && component !== null
1198- && typeof component["field"] === "string" && component["field"].length > 0)) {
1199- data[key] = maxData[key];
1200+ sourceComponent: cardTool.cardComponent
1201+ onLoaded: {
1202+ item.objectName = "cardToolCard";
1203+ item.asynchronous = false;
1204+ item.template = Qt.binding(function() { return cardTool.template; });
1205+ item.components = Qt.binding(function() { return cardTool.components; });
1206+ item.width = Qt.binding(function() { return cardTool.cardWidth || item.implicitWidth; });
1207+ item.height = Qt.binding(function() { return cardTool.cardHeight || item.implicitHeight; });
1208+ }
1209+ Connections {
1210+ target: cardLoader.item
1211+ onComponentsChanged: {
1212+ var data = {};
1213+ for (var k in cardLoader.fields) {
1214+ var component = cardLoader.item.components[cardLoader.fields[k]];
1215+ var key = cardLoader.fields[k];
1216+ if ((typeof component === "string" && component.length > 0) ||
1217+ (typeof component === "object" && component !== null
1218+ && typeof component["field"] === "string" && component["field"].length > 0)) {
1219+ data[key] = cardLoader.maxData[key];
1220+ }
1221 }
1222+ cardLoader.item.cardData = data;
1223 }
1224- cardData = data;
1225 }
1226 }
1227 }
1228
1229=== modified file 'qml/Dash/Previews/PreviewHeader.qml'
1230--- qml/Dash/Previews/PreviewHeader.qml 2014-03-18 09:37:29 +0000
1231+++ qml/Dash/Previews/PreviewHeader.qml 2014-05-07 15:30:47 +0000
1232@@ -15,9 +15,10 @@
1233 */
1234
1235 import QtQuick 2.0
1236+import Ubuntu.Components 0.1
1237 import "../"
1238
1239-/*! This preview widget shows a header that is the same as the card header
1240+/*! This preview widget shows a header
1241 * The title comes in widgetData["title"]
1242 * The mascot comes in widgetData["mascot"]
1243 * The subtitle comes in widgetData["subtitle"]
1244@@ -28,15 +29,91 @@
1245
1246 height: childrenRect.height
1247
1248- CardHeader {
1249- objectName: "cardHeader"
1250- mascot: root.widgetData["mascot"] || ""
1251- title: root.widgetData["title"] || ""
1252- subtitle: root.widgetData["subtitle"] || ""
1253+ Item {
1254+ id: headerRoot
1255+ objectName: "innerPreviewHeader"
1256+ readonly property url mascot: root.widgetData["mascot"] || ""
1257+ readonly property string title: root.widgetData["title"] || ""
1258+ readonly property string subtitle: root.widgetData["subtitle"] || ""
1259+ readonly property color fontColor: "grey"
1260+
1261+ implicitHeight: row.height + row.margins * 2
1262 width: parent.width
1263
1264- titleSize: "large"
1265- // TODO Change "grey" to Ubuntu.Components.Palette color once updated.
1266- fontColor: "grey"
1267+ Row {
1268+ id: row
1269+ objectName: "outerRow"
1270+
1271+ property real margins: units.gu(1)
1272+
1273+ spacing: mascotShapeLoader.active ? margins : 0
1274+ anchors {
1275+ top: parent.top; left: parent.left; right: parent.right
1276+ margins: margins
1277+ leftMargin: spacing
1278+ rightMargin: spacing
1279+ }
1280+
1281+ Loader {
1282+ id: mascotShapeLoader
1283+
1284+ anchors.verticalCenter: parent.verticalCenter
1285+ // TODO karni: Icon aspect-ratio is 8:7.5. Revisit these values to avoid fraction of pixels.
1286+ width: units.gu(6)
1287+ height: units.gu(5.625)
1288+ readonly property int maxSize: Math.max(width, height) * 4
1289+ asynchronous: true
1290+
1291+ sourceComponent: UbuntuShape {
1292+ objectName: "mascotShape"
1293+ visible: image.status === Image.Ready
1294+ image: Image {
1295+ source: headerRoot.mascot
1296+ width: source ? mascotShapeLoader.width : 0
1297+ height: mascotShapeLoader.height
1298+
1299+ sourceSize { width: mascotShapeLoader.maxSize; height: mascotShapeLoader.maxSize }
1300+ fillMode: Image.PreserveAspectCrop
1301+ horizontalAlignment: Image.AlignHCenter
1302+ verticalAlignment: Image.AlignVCenter
1303+ }
1304+ }
1305+ }
1306+
1307+ Column {
1308+ objectName: "column"
1309+ width: parent.width - x
1310+ spacing: units.dp(2)
1311+ anchors.verticalCenter: parent.verticalCenter
1312+
1313+ Label {
1314+ id: titleLabel
1315+ objectName: "titleLabel"
1316+ anchors { left: parent.left; right: parent.right }
1317+ elide: Text.ElideRight
1318+ font.weight: Font.Normal
1319+ fontSize: "large"
1320+ wrapMode: Text.Wrap
1321+ maximumLineCount: 2
1322+ color: headerRoot.fontColor
1323+ text: headerRoot.title
1324+ }
1325+
1326+ Loader {
1327+ active: titleLabel.text && headerRoot.subtitle
1328+ anchors { left: parent.left; right: parent.right }
1329+ sourceComponent: Label {
1330+ id: subtitleLabel
1331+ objectName: "subtitleLabel"
1332+ elide: Text.ElideRight
1333+ fontSize: "small"
1334+ font.weight: Font.Light
1335+ color: headerRoot.fontColor
1336+ text: headerRoot.subtitle
1337+ }
1338+ }
1339+ }
1340+ }
1341 }
1342+
1343 }
1344
1345=== modified file 'qml/Dash/ScopeListView.qml'
1346--- qml/Dash/ScopeListView.qml 2013-12-12 16:45:35 +0000
1347+++ qml/Dash/ScopeListView.qml 2014-05-07 15:30:47 +0000
1348@@ -15,7 +15,7 @@
1349 */
1350
1351 import QtQuick 2.0
1352-import DashViews 0.1
1353+import Dash 0.1
1354
1355 ListViewWithPageHeader {
1356 maximumFlickVelocity: height * 10
1357
1358=== modified file 'tests/autopilot/unity8/shell/emulators/dash.py'
1359--- tests/autopilot/unity8/shell/emulators/dash.py 2014-04-16 13:43:19 +0000
1360+++ tests/autopilot/unity8/shell/emulators/dash.py 2014-05-07 15:30:47 +0000
1361@@ -161,7 +161,7 @@
1362
1363 """
1364 category_element = self._get_category_element(category)
1365- icon = category_element.select_single('Card', title=app_name)
1366+ icon = category_element.select_single('AbstractButton', title=app_name)
1367 # FIXME some categories need a long press in order to see the preview.
1368 # Some categories do not show previews, like recent apps.
1369 # --elopio - 2014-1-14
1370@@ -191,7 +191,7 @@
1371
1372 """
1373 category_element = self._get_category_element(category)
1374- application_cards = category_element.select_many('Card')
1375+ application_cards = category_element.select_many('AbstractButton')
1376
1377 # sort by y, x
1378 application_cards = sorted(
1379@@ -201,8 +201,7 @@
1380 result = []
1381 for card in application_cards:
1382 if card.objectName != 'cardToolCard':
1383- card_header = card.select_single('CardHeader')
1384- result.append(card_header.title)
1385+ result.append(card.title)
1386 return result
1387
1388
1389
1390=== modified file 'tests/mocks/Unity/fake_categories.cpp'
1391--- tests/mocks/Unity/fake_categories.cpp 2014-02-25 15:43:42 +0000
1392+++ tests/mocks/Unity/fake_categories.cpp 2014-05-07 15:30:47 +0000
1393@@ -108,7 +108,12 @@
1394 case RoleRenderer:
1395 {
1396 QVariantMap map;
1397- map["category-layout"] = index.row() % 2 == 0 ? "grid" : "carousel";
1398+ if (index.row() % 2 == 0) {
1399+ map["category-layout"] = "grid";
1400+ } else {
1401+ map["category-layout"] = "carousel";
1402+ map["overlay"] = true;
1403+ }
1404 map["card-size"] = "small";
1405 return map;
1406 }
1407@@ -146,7 +151,14 @@
1408 case RoleRenderer:
1409 {
1410 QVariantMap map;
1411- map["category-layout"] = index.row() % 2 == 0 ? "grid" : "carousel";
1412+ if (index.row() % 2 == 0) {
1413+ map["category-layout"] = "grid";
1414+ } else {
1415+ map["category-layout"] = "carousel";
1416+ map["card-size"] = "medium";
1417+ map["collapsed-rows"] = 2;
1418+ map["overlay"] = true;
1419+ }
1420 map["card-size"] = "small";
1421 return map;
1422 }
1423@@ -157,6 +169,7 @@
1424 artMap["field"] = "art";
1425 map["art"] = artMap;
1426 map["title"] = "HOLA";
1427+ map["subtitle"] = "HOLA";
1428 return map;
1429 }
1430 case RoleProgressSource:
1431
1432=== modified file 'tests/mocks/Unity/fake_resultsmodel.cpp'
1433--- tests/mocks/Unity/fake_resultsmodel.cpp 2014-03-10 11:47:04 +0000
1434+++ tests/mocks/Unity/fake_resultsmodel.cpp 2014-05-07 15:30:47 +0000
1435@@ -103,6 +103,7 @@
1436 case RoleArt:
1437 return qmlDirectory() + "graphics/applicationIcons/dash.png";
1438 case RoleSubtitle:
1439+ return QString("Subtitle.%1.%2").arg(m_categoryId).arg(index.row());
1440 case RoleMascot:
1441 case RoleEmblem:
1442 case RoleOldPrice:
1443
1444=== modified file 'tests/plugins/CMakeLists.txt'
1445--- tests/plugins/CMakeLists.txt 2013-12-12 16:45:35 +0000
1446+++ tests/plugins/CMakeLists.txt 2014-05-07 15:30:47 +0000
1447@@ -1,6 +1,6 @@
1448 add_subdirectory(AccountsService)
1449 add_subdirectory(LightDM)
1450-add_subdirectory(DashViews)
1451+add_subdirectory(Dash)
1452 add_subdirectory(Ubuntu)
1453 add_subdirectory(Unity)
1454 add_subdirectory(Utils)
1455
1456=== renamed directory 'tests/plugins/DashViews' => 'tests/plugins/Dash'
1457=== modified file 'tests/plugins/Dash/CMakeLists.txt'
1458--- tests/plugins/DashViews/CMakeLists.txt 2014-04-10 13:49:37 +0000
1459+++ tests/plugins/Dash/CMakeLists.txt 2014-05-07 15:30:47 +0000
1460@@ -16,7 +16,7 @@
1461 ${Qt5Quick_INCLUDE_DIRS}
1462 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
1463 ${Qt5V8_PRIVATE_INCLUDE_DIR}
1464- ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews
1465+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/Dash
1466 ${CMAKE_CURRENT_BINARY_DIR}
1467 )
1468
1469@@ -28,11 +28,11 @@
1470 macro(add_lvwph_test FILENAME TESTNAME)
1471 add_executable(${TESTNAME}TestExec
1472 ${FILENAME}test.cpp
1473- ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/listviewwithpageheader.cpp)
1474+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/Dash/listviewwithpageheader.cpp)
1475 qt5_use_modules(${TESTNAME}TestExec Test Core Qml)
1476 target_link_libraries(${TESTNAME}TestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
1477
1478- add_binary_qml_test(${TESTNAME} "" "DashViews-qml")
1479+ add_binary_qml_test(${TESTNAME} "" "Dash-qml")
1480 endmacro()
1481
1482 add_lvwph_test(listviewwithpageheader ListViewWithPageHeader)
1483@@ -42,24 +42,24 @@
1484 macro(add_dashview_try_test FILENAME TESTNAME)
1485 add_executable(${TESTNAME}TestExec
1486 ${FILENAME}test.cpp
1487- ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/${FILENAME}.cpp
1488- ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/abstractdashview.cpp
1489+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/Dash/${FILENAME}.cpp
1490+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/Dash/abstractdashview.cpp
1491 )
1492 qt5_use_modules(${TESTNAME}TestExec Test Core Qml)
1493 target_link_libraries(${TESTNAME}TestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
1494
1495- add_binary_qml_test(${TESTNAME} "" "DashViews-qml")
1496+ add_binary_qml_test(${TESTNAME} "" "Dash-qml")
1497
1498 add_executable(${TESTNAME}tryExec
1499 ${FILENAME}try.cpp
1500- ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/${FILENAME}.cpp
1501- ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/abstractdashview.cpp
1502+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/Dash/${FILENAME}.cpp
1503+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/Dash/abstractdashview.cpp
1504 )
1505 qt5_use_modules(${TESTNAME}tryExec Test Core Qml)
1506 target_link_libraries(${TESTNAME}tryExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
1507 add_custom_target(try${TESTNAME}
1508 ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME}tryExec
1509- DEPENDS ${TESTNAME}tryExec DashViews-qml
1510+ DEPENDS ${TESTNAME}tryExec Dash-qml
1511 )
1512 endmacro()
1513
1514@@ -67,6 +67,13 @@
1515 add_dashview_try_test(horizontaljournal HorizontalJournal)
1516 add_dashview_try_test(organicgrid OrganicGrid)
1517
1518+# CardCreator test
1519+add_executable(CardCreatorTestExec cardcreatortest.cpp)
1520+qt5_use_modules(CardCreatorTestExec Test Core Qml)
1521+target_link_libraries(CardCreatorTestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
1522+add_binary_qml_test(CardCreator "" "Dash-qml")
1523+
1524+# plain qml test
1525 set(qmltest_DEFAULT_TARGETS qmluitests)
1526 set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
1527 add_qml_test(. ListViewWithPageHeaderQML IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
1528
1529=== added directory 'tests/plugins/Dash/cardcreator'
1530=== added file 'tests/plugins/Dash/cardcreator/1.res'
1531--- tests/plugins/Dash/cardcreator/1.res 1970-01-01 00:00:00 +0000
1532+++ tests/plugins/Dash/cardcreator/1.res 2014-05-07 15:30:47 +0000
1533@@ -0,0 +1,77 @@
1534+AbstractButton {
1535+ id: root;
1536+ property var template;
1537+ property var components;
1538+ property var cardData;
1539+ property real fontScale: 1.0;
1540+ property int headerAlignment: Text.AlignLeft;
1541+ property int fixedHeaderHeight: -1;
1542+ property size fixedArtShapeSize: Qt.size(-1, -1);
1543+ readonly property string title: cardData && cardData["title"] || "";
1544+ property bool asynchronous: true;
1545+ property bool showHeader: true;
1546+ implicitWidth: childrenRect.width;
1547+readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
1548+Item {
1549+ id: artShapeHolder;
1550+ height: root.fixedArtShapeSize.height != -1 ? root.fixedArtShapeSize.height : artShapeLoader.height;
1551+ width: root.fixedArtShapeSize.width != -1 ? root.fixedArtShapeSize.width : artShapeLoader.width;
1552+ anchors { horizontalCenter: parent.horizontalCenter; }
1553+ Loader {
1554+ id: artShapeLoader;
1555+ objectName: "artShapeLoader";
1556+ active: cardData && cardData["art"] || false;
1557+ asynchronous: root.asynchronous;
1558+ visible: status == Loader.Ready;
1559+ sourceComponent: UbuntuShape {
1560+ id: artShape;
1561+ objectName: "artShape";
1562+ radius: "medium";
1563+ readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1;
1564+ readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
1565+ Component.onCompleted: updateWidthHeightBindings();
1566+ onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
1567+ visible: image.status == Image.Ready;
1568+ function updateWidthHeightBindings() {
1569+ if (aspectSmallerThanImageAspect) {
1570+ width = Qt.binding(function() { return !visible ? 0 : image.width });
1571+ height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
1572+ } else {
1573+ width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
1574+ height = Qt.binding(function() { return !visible ? 0 : image.height });
1575+ }
1576+ }
1577+ image: Image {
1578+ objectName: "artImage";
1579+ source: cardData && cardData["art"] || "";
1580+ cache: true;
1581+ asynchronous: root.asynchronous;
1582+ fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
1583+ readonly property real aspect: implicitWidth / implicitHeight;
1584+ width: root.width;
1585+ height: width / artShape.aspect;
1586+ }
1587+ }
1588+ }
1589+ }
1590+readonly property int headerHeight: titleLabel.height + titleLabel.anchors.topMargin * 2;
1591+Label {
1592+ id: titleLabel;
1593+ objectName: "titleLabel";
1594+ anchors { right: parent.right;left: parent.left;
1595+top: artShapeHolder.bottom;
1596+ topMargin: units.gu(1);
1597+ }
1598+ elide: Text.ElideRight;
1599+ fontSize: "small";
1600+ wrapMode: Text.Wrap;
1601+ maximumLineCount: 2;
1602+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
1603+ color: "grey";
1604+ visible: showHeader ;
1605+ text: root.title;
1606+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
1607+ horizontalAlignment: root.headerAlignment;
1608+ }
1609+implicitHeight: titleLabel.y + titleLabel.height + units.gu(1);
1610+}
1611
1612=== added file 'tests/plugins/Dash/cardcreator/1.tst'
1613--- tests/plugins/Dash/cardcreator/1.tst 1970-01-01 00:00:00 +0000
1614+++ tests/plugins/Dash/cardcreator/1.tst 2014-05-07 15:30:47 +0000
1615@@ -0,0 +1,3 @@
1616+template: {"card-layout":"vertical","card-size":"small","category-layout":"grid","collapsed-rows":2}
1617+components: {"art":{"aspect-ratio":1.6,"field":"art","fill-mode":"fit"},"title":{"field":"title"}}
1618+result: 1.res
1619
1620=== added file 'tests/plugins/Dash/cardcreator/2.res'
1621--- tests/plugins/Dash/cardcreator/2.res 1970-01-01 00:00:00 +0000
1622+++ tests/plugins/Dash/cardcreator/2.res 2014-05-07 15:30:47 +0000
1623@@ -0,0 +1,115 @@
1624+AbstractButton {
1625+ id: root;
1626+ property var template;
1627+ property var components;
1628+ property var cardData;
1629+ property real fontScale: 1.0;
1630+ property int headerAlignment: Text.AlignLeft;
1631+ property int fixedHeaderHeight: -1;
1632+ property size fixedArtShapeSize: Qt.size(-1, -1);
1633+ readonly property string title: cardData && cardData["title"] || "";
1634+ property bool asynchronous: true;
1635+ property bool showHeader: true;
1636+ implicitWidth: childrenRect.width;
1637+Loader {
1638+ id: backgroundLoader;
1639+ objectName: "backgroundLoader";
1640+ anchors.fill: parent;
1641+ asynchronous: root.asynchronous;
1642+ visible: status == Loader.Ready;
1643+ sourceComponent: UbuntuShape {
1644+ objectName: "background";
1645+ radius: "medium";
1646+ color: getColor(0) || "white";
1647+ gradientColor: getColor(1) || color;
1648+ anchors.fill: parent;
1649+ image: backgroundImage.source ? backgroundImage : null;
1650+ property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
1651+ property Image backgroundImage: Image {
1652+ objectName: "backgroundImage";
1653+ source: {
1654+ if (cardData && typeof cardData["background"] === "string") return cardData["background"];
1655+ else if (template && typeof template["card-background"] === "string") return template["card-background"];
1656+ else return "";
1657+ }
1658+ }
1659+ function getColor(index) {
1660+ if (cardData && typeof cardData["background"] === "object"
1661+ && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
1662+ return cardData["background"]["elements"][index];
1663+ } else if (template && typeof template["card-background"] === "object"
1664+ && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
1665+ return template["card-background"]["elements"][index];
1666+ } else return undefined;
1667+ }
1668+ }
1669+ }
1670+readonly property size artShapeSize: Qt.size(-1, -1);
1671+readonly property int headerHeight: row.height + row.margins * 2;
1672+Row {
1673+ id: row;
1674+ objectName: "outerRow";
1675+ property real margins: units.gu(1);
1676+ spacing: margins;
1677+ anchors { top: parent.top;
1678+ topMargin: units.gu(1);
1679+ left: parent.left;
1680+}
1681+ anchors.right: parent.right;
1682+ anchors.margins: margins;
1683+data: [ Image {
1684+ id: mascotImage;
1685+ objectName: "mascotImage";
1686+ anchors { verticalCenter: parent.verticalCenter; }
1687+ readonly property int maxSize: Math.max(width, height) * 4;
1688+ source: cardData && cardData["mascot"];
1689+ width: units.gu(6);
1690+ height: units.gu(5.625);
1691+ sourceSize { width: maxSize; height: maxSize }
1692+ fillMode: Image.PreserveAspectCrop;
1693+ horizontalAlignment: Image.AlignHCenter;
1694+ verticalAlignment: Image.AlignVCenter;
1695+ visible: showHeader;
1696+ }
1697+,
1698+Column {
1699+ anchors.verticalCenter: parent.verticalCenter;
1700+ spacing: units.dp(2);
1701+ width: parent.width - x;
1702+ data: [
1703+Label {
1704+ id: titleLabel;
1705+ objectName: "titleLabel";
1706+ anchors { left: parent.left; right: parent.right }
1707+ elide: Text.ElideRight;
1708+ fontSize: "small";
1709+ wrapMode: Text.Wrap;
1710+ maximumLineCount: 2;
1711+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
1712+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : "grey";
1713+ visible: showHeader ;
1714+ text: root.title;
1715+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
1716+ horizontalAlignment: root.headerAlignment;
1717+ }
1718+,
1719+Label {
1720+ id: subtitleLabel;
1721+ objectName: "subtitleLabel";
1722+ anchors { left: parent.left; right: parent.right }
1723+
1724+ elide: Text.ElideRight;
1725+ fontSize: "small";
1726+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
1727+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : "grey";
1728+ visible: titleLabel.visible && titleLabel.text;
1729+ text: cardData && cardData["subtitle"] || "";
1730+ font.weight: Font.Light;
1731+ horizontalAlignment: root.headerAlignment;
1732+ }
1733+]
1734+}
1735+]
1736+}
1737+implicitHeight: row.y + row.height + units.gu(1);
1738+}
1739
1740=== added file 'tests/plugins/Dash/cardcreator/2.tst'
1741--- tests/plugins/Dash/cardcreator/2.tst 1970-01-01 00:00:00 +0000
1742+++ tests/plugins/Dash/cardcreator/2.tst 2014-05-07 15:30:47 +0000
1743@@ -0,0 +1,3 @@
1744+template: {"card-background":{"elements":["#E9E9E9"],"type":"color"},"card-layout":"vertical","card-size":"medium","category-layout":"grid","collapsed-rows":2}
1745+components: {"art":{"aspect-ratio":1,"fill-mode":"crop"},"background":{"field":"background"},"mascot":{"field":"icon"},"subtitle":{"field":"author"},"title":{"field":"title"}}
1746+result: 2.res
1747
1748=== added file 'tests/plugins/Dash/cardcreator/3.res'
1749--- tests/plugins/Dash/cardcreator/3.res 1970-01-01 00:00:00 +0000
1750+++ tests/plugins/Dash/cardcreator/3.res 2014-05-07 15:30:47 +0000
1751@@ -0,0 +1,94 @@
1752+AbstractButton {
1753+ id: root;
1754+ property var template;
1755+ property var components;
1756+ property var cardData;
1757+ property real fontScale: 1.0;
1758+ property int headerAlignment: Text.AlignLeft;
1759+ property int fixedHeaderHeight: -1;
1760+ property size fixedArtShapeSize: Qt.size(-1, -1);
1761+ readonly property string title: cardData && cardData["title"] || "";
1762+ property bool asynchronous: true;
1763+ property bool showHeader: true;
1764+ implicitWidth: childrenRect.width;
1765+readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
1766+Item {
1767+ id: artShapeHolder;
1768+ height: root.fixedArtShapeSize.height != -1 ? root.fixedArtShapeSize.height : artShapeLoader.height;
1769+ width: root.fixedArtShapeSize.width != -1 ? root.fixedArtShapeSize.width : artShapeLoader.width;
1770+ anchors { horizontalCenter: parent.horizontalCenter; }
1771+ Loader {
1772+ id: artShapeLoader;
1773+ objectName: "artShapeLoader";
1774+ active: cardData && cardData["art"] || false;
1775+ asynchronous: root.asynchronous;
1776+ visible: status == Loader.Ready;
1777+ sourceComponent: UbuntuShape {
1778+ id: artShape;
1779+ objectName: "artShape";
1780+ radius: "medium";
1781+ readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1;
1782+ readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
1783+ Component.onCompleted: updateWidthHeightBindings();
1784+ onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
1785+ visible: image.status == Image.Ready;
1786+ function updateWidthHeightBindings() {
1787+ if (aspectSmallerThanImageAspect) {
1788+ width = Qt.binding(function() { return !visible ? 0 : image.width });
1789+ height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
1790+ } else {
1791+ width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
1792+ height = Qt.binding(function() { return !visible ? 0 : image.height });
1793+ }
1794+ }
1795+ image: Image {
1796+ objectName: "artImage";
1797+ source: cardData && cardData["art"] || "";
1798+ cache: true;
1799+ asynchronous: root.asynchronous;
1800+ fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
1801+ readonly property real aspect: implicitWidth / implicitHeight;
1802+ width: root.width;
1803+ height: width / artShape.aspect;
1804+ }
1805+ }
1806+ }
1807+ }
1808+readonly property int headerHeight: titleLabel.height + titleLabel.anchors.topMargin * 2 + subtitleLabel.height + subtitleLabel.anchors.topMargin;
1809+Label {
1810+ id: titleLabel;
1811+ objectName: "titleLabel";
1812+ anchors { right: parent.right;left: parent.left;
1813+top: artShapeHolder.bottom;
1814+ topMargin: units.gu(1);
1815+}
1816+ elide: Text.ElideRight;
1817+ fontSize: "small";
1818+ wrapMode: Text.Wrap;
1819+ maximumLineCount: 2;
1820+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
1821+ color: "grey";
1822+ visible: showHeader ;
1823+ text: root.title;
1824+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
1825+ horizontalAlignment: root.headerAlignment;
1826+ }
1827+Label {
1828+ id: subtitleLabel;
1829+ objectName: "subtitleLabel";
1830+ anchors { left: titleLabel.left;
1831+ leftMargin: titleLabel.leftMargin;
1832+ right: titleLabel.right;
1833+ top: titleLabel.bottom;
1834+ topMargin: units.dp(2); }
1835+ elide: Text.ElideRight;
1836+ fontSize: "small";
1837+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
1838+ color: "grey";
1839+ visible: titleLabel.visible && titleLabel.text;
1840+ text: cardData && cardData["subtitle"] || "";
1841+ font.weight: Font.Light;
1842+ horizontalAlignment: root.headerAlignment;
1843+ }
1844+implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);
1845+}
1846
1847=== added file 'tests/plugins/Dash/cardcreator/3.tst'
1848--- tests/plugins/Dash/cardcreator/3.tst 1970-01-01 00:00:00 +0000
1849+++ tests/plugins/Dash/cardcreator/3.tst 2014-05-07 15:30:47 +0000
1850@@ -0,0 +1,3 @@
1851+template: {"card-layout":"vertical","card-size":"small","category-layout":"grid","collapsed-rows":2}
1852+components: {"art":{"aspect-ratio":0.75,"field":"art","fill-mode":"crop"},"subtitle":{"field":"price"},"title":{"field":"title"}}
1853+result: 3.res
1854
1855=== added file 'tests/plugins/Dash/cardcreator/4.res'
1856--- tests/plugins/Dash/cardcreator/4.res 1970-01-01 00:00:00 +0000
1857+++ tests/plugins/Dash/cardcreator/4.res 2014-05-07 15:30:47 +0000
1858@@ -0,0 +1,95 @@
1859+AbstractButton {
1860+ id: root;
1861+ property var template;
1862+ property var components;
1863+ property var cardData;
1864+ property real fontScale: 1.0;
1865+ property int headerAlignment: Text.AlignLeft;
1866+ property int fixedHeaderHeight: -1;
1867+ property size fixedArtShapeSize: Qt.size(-1, -1);
1868+ readonly property string title: cardData && cardData["title"] || "";
1869+ property bool asynchronous: true;
1870+ property bool showHeader: true;
1871+ implicitWidth: childrenRect.width;
1872+readonly property size artShapeSize: Qt.size(-1, -1);
1873+readonly property int headerHeight: row.height + row.margins * 2;
1874+Row {
1875+ id: row;
1876+ objectName: "outerRow";
1877+ property real margins: units.gu(1);
1878+ spacing: margins;
1879+ anchors { top: parent.top;
1880+ topMargin: units.gu(1);
1881+ left: parent.left;
1882+}
1883+ anchors.right: parent.right;
1884+ anchors.margins: margins;
1885+data: [ Loader {
1886+ id: mascotShapeLoader;
1887+ objectName: "mascotShapeLoader";
1888+ asynchronous: root.asynchronous;
1889+ active: mascotImage.status === Image.Ready;
1890+ visible: showHeader && active && status == Loader.Ready;
1891+ width: units.gu(6);
1892+ height: units.gu(5.625);
1893+ sourceComponent: UbuntuShape { image: mascotImage }
1894+ anchors { verticalCenter: parent.verticalCenter; }
1895+ }
1896+
1897+,
1898+Image {
1899+ id: mascotImage;
1900+ objectName: "mascotImage";
1901+ anchors { verticalCenter: parent.verticalCenter; }
1902+ readonly property int maxSize: Math.max(width, height) * 4;
1903+ source: cardData && cardData["mascot"];
1904+ width: units.gu(6);
1905+ height: units.gu(5.625);
1906+ sourceSize { width: maxSize; height: maxSize }
1907+ fillMode: Image.PreserveAspectCrop;
1908+ horizontalAlignment: Image.AlignHCenter;
1909+ verticalAlignment: Image.AlignVCenter;
1910+ visible: false;
1911+ }
1912+
1913+,
1914+Column {
1915+ anchors.verticalCenter: parent.verticalCenter;
1916+ spacing: units.dp(2);
1917+ width: parent.width - x;
1918+data: [ Label {
1919+ id: titleLabel;
1920+ objectName: "titleLabel";
1921+ anchors { left: parent.left; right: parent.right }
1922+ elide: Text.ElideRight;
1923+ fontSize: "small";
1924+ wrapMode: Text.Wrap;
1925+ maximumLineCount: 2;
1926+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
1927+ color: "grey";
1928+ visible: showHeader ;
1929+ text: root.title;
1930+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
1931+ horizontalAlignment: root.headerAlignment;
1932+ }
1933+,
1934+Label {
1935+ id: subtitleLabel;
1936+ objectName: "subtitleLabel";
1937+ anchors { left: parent.left; right: parent.right }
1938+
1939+ elide: Text.ElideRight;
1940+ fontSize: "small";
1941+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
1942+ color: "grey";
1943+ visible: titleLabel.visible && titleLabel.text;
1944+ text: cardData && cardData["subtitle"] || "";
1945+ font.weight: Font.Light;
1946+ horizontalAlignment: root.headerAlignment;
1947+ }
1948+]
1949+}
1950+]
1951+}
1952+implicitHeight: row.y + row.height + units.gu(1);
1953+}
1954
1955=== added file 'tests/plugins/Dash/cardcreator/4.tst'
1956--- tests/plugins/Dash/cardcreator/4.tst 1970-01-01 00:00:00 +0000
1957+++ tests/plugins/Dash/cardcreator/4.tst 2014-05-07 15:30:47 +0000
1958@@ -0,0 +1,3 @@
1959+template: {"card-layout":"horizontal","card-size":"large","category-layout":"grid","collapsed-rows":2}
1960+components: {"art":{"aspect-ratio":1,"fill-mode":"crop"},"mascot":{"field":"mascot"},"subtitle":{"field":"domain"},"title":{"field":"title"}}
1961+result: 4.res
1962
1963=== added file 'tests/plugins/Dash/cardcreator/5.res'
1964--- tests/plugins/Dash/cardcreator/5.res 1970-01-01 00:00:00 +0000
1965+++ tests/plugins/Dash/cardcreator/5.res 2014-05-07 15:30:47 +0000
1966@@ -0,0 +1,137 @@
1967+AbstractButton {
1968+ id: root;
1969+ property var template;
1970+ property var components;
1971+ property var cardData;
1972+ property real fontScale: 1.0;
1973+ property int headerAlignment: Text.AlignLeft;
1974+ property int fixedHeaderHeight: -1;
1975+ property size fixedArtShapeSize: Qt.size(-1, -1);
1976+ readonly property string title: cardData && cardData["title"] || "";
1977+ property bool asynchronous: true;
1978+ property bool showHeader: true;
1979+ implicitWidth: childrenRect.width;
1980+readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
1981+Item {
1982+ id: artShapeHolder;
1983+ height: root.fixedArtShapeSize.height != -1 ? root.fixedArtShapeSize.height : artShapeLoader.height;
1984+ width: root.fixedArtShapeSize.width != -1 ? root.fixedArtShapeSize.width : artShapeLoader.width;
1985+ anchors { horizontalCenter: parent.horizontalCenter; }
1986+ Loader {
1987+ id: artShapeLoader;
1988+ objectName: "artShapeLoader";
1989+ active: cardData && cardData["art"] || false;
1990+ asynchronous: root.asynchronous;
1991+ visible: status == Loader.Ready;
1992+ sourceComponent: UbuntuShape {
1993+ id: artShape;
1994+ objectName: "artShape";
1995+ radius: "medium";
1996+ readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1;
1997+ readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
1998+ Component.onCompleted: updateWidthHeightBindings();
1999+ onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
2000+ visible: image.status == Image.Ready;
2001+ function updateWidthHeightBindings() {
2002+ if (aspectSmallerThanImageAspect) {
2003+ width = Qt.binding(function() { return !visible ? 0 : image.width });
2004+ height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
2005+ } else {
2006+ width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
2007+ height = Qt.binding(function() { return !visible ? 0 : image.height });
2008+ }
2009+ }
2010+ image: Image {
2011+ objectName: "artImage";
2012+ source: cardData && cardData["art"] || "";
2013+ cache: true;
2014+ asynchronous: root.asynchronous;
2015+ fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
2016+ readonly property real aspect: implicitWidth / implicitHeight;
2017+ width: root.width;
2018+ height: width / artShape.aspect;
2019+ }
2020+ }
2021+ }
2022+ }
2023+Loader {
2024+ id: overlayLoader;
2025+ anchors {
2026+ left: artShapeHolder.left;
2027+ right: artShapeHolder.right;
2028+ bottom: artShapeHolder.bottom;
2029+ }
2030+ active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false;
2031+ asynchronous: root.asynchronous;
2032+ visible: showHeader && status == Loader.Ready;
2033+ sourceComponent: ShaderEffect {
2034+ id: overlay;
2035+ height: fixedHeaderHeight != -1 ? fixedHeaderHeight : headerHeight;
2036+ opacity: 0.6;
2037+ property var source: ShaderEffectSource {
2038+ id: shaderSource;
2039+ sourceItem: artShapeLoader.item;
2040+ onVisibleChanged: if (visible) scheduleUpdate();
2041+ live: false;
2042+ sourceRect: Qt.rect(0, artShapeLoader.height - overlay.height, artShapeLoader.width, overlay.height);
2043+ }
2044+ vertexShader: "
2045+ uniform highp mat4 qt_Matrix;
2046+ attribute highp vec4 qt_Vertex;
2047+ attribute highp vec2 qt_MultiTexCoord0;
2048+ varying highp vec2 coord;
2049+ void main() {
2050+ coord = qt_MultiTexCoord0;
2051+ gl_Position = qt_Matrix * qt_Vertex;
2052+ }";
2053+ fragmentShader: "
2054+ varying highp vec2 coord;
2055+ uniform sampler2D source;
2056+ uniform lowp float qt_Opacity;
2057+ void main() {
2058+ lowp vec4 tex = texture2D(source, coord);
2059+ gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity;
2060+ }";
2061+ }
2062+ }
2063+readonly property int headerHeight: titleLabel.height + titleLabel.anchors.topMargin * 2 + subtitleLabel.height + subtitleLabel.anchors.topMargin;
2064+Label {
2065+ id: titleLabel;
2066+ objectName: "titleLabel";
2067+ anchors { left: parent.left;
2068+ leftMargin: units.gu(1);
2069+ right: parent.right;
2070+ top: overlayLoader.top;
2071+ topMargin: units.gu(1);
2072+}
2073+ elide: Text.ElideRight;
2074+ fontSize: "small";
2075+ wrapMode: Text.Wrap;
2076+ maximumLineCount: 2;
2077+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
2078+ color: "white";
2079+ visible: showHeader && overlayLoader.active;
2080+ text: root.title;
2081+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
2082+ horizontalAlignment: root.headerAlignment;
2083+ }
2084+Label {
2085+ id: subtitleLabel;
2086+ objectName: "subtitleLabel";
2087+ anchors { left: titleLabel.left;
2088+ leftMargin: titleLabel.leftMargin;
2089+ right: titleLabel.right;
2090+ top: titleLabel.bottom;
2091+ topMargin: units.dp(2);
2092+ }
2093+ elide: Text.ElideRight;
2094+ fontSize: "small";
2095+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
2096+ color: "white";
2097+ visible: titleLabel.visible && titleLabel.text;
2098+ text: cardData && cardData["subtitle"] || "";
2099+ font.weight: Font.Light;
2100+ horizontalAlignment: root.headerAlignment;
2101+ }
2102+implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);
2103+}
2104
2105=== added file 'tests/plugins/Dash/cardcreator/5.tst'
2106--- tests/plugins/Dash/cardcreator/5.tst 1970-01-01 00:00:00 +0000
2107+++ tests/plugins/Dash/cardcreator/5.tst 2014-05-07 15:30:47 +0000
2108@@ -0,0 +1,3 @@
2109+template: {"card-layout":"vertical","card-size":"medium","category-layout":"carousel","collapsed-rows":2,"overlay":true}
2110+components: {"art":{"aspect-ratio":1,"field":"art","fill-mode":"crop"},"subtitle":{"field":"artist"},"title":{"field":"title"}}
2111+result: 5.res
2112
2113=== added file 'tests/plugins/Dash/cardcreatortest.cpp'
2114--- tests/plugins/Dash/cardcreatortest.cpp 1970-01-01 00:00:00 +0000
2115+++ tests/plugins/Dash/cardcreatortest.cpp 2014-05-07 15:30:47 +0000
2116@@ -0,0 +1,91 @@
2117+/*
2118+ * Copyright (C) 2014 Canonical, Ltd.
2119+ *
2120+ * This program is free software; you can redistribute it and/or modify
2121+ * it under the terms of the GNU General Public License as published by
2122+ * the Free Software Foundation; version 3.
2123+ *
2124+ * This program is distributed in the hope that it will be useful,
2125+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2126+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2127+ * GNU General Public License for more details.
2128+ *
2129+ * You should have received a copy of the GNU General Public License
2130+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2131+ */
2132+
2133+#include <QDir>
2134+#include <QQmlEngine>
2135+#include <QQuickItem>
2136+#include <QQuickView>
2137+#include <QtTestGui>
2138+
2139+class CardCreatorTest : public QObject
2140+{
2141+ Q_OBJECT
2142+
2143+private Q_SLOTS:
2144+
2145+ void initTestCase()
2146+ {
2147+ }
2148+
2149+ void init()
2150+ {
2151+ view = new QQuickView();
2152+ view->engine()->addImportPath(BUILT_PLUGINS_DIR);
2153+ view->setSource(QUrl::fromLocalFile(DASHVIEWSTEST_FOLDER "/cardcreatortest.qml"));
2154+ view->show();
2155+ QTest::qWaitForWindowExposed(view);
2156+ }
2157+
2158+ void cleanup()
2159+ {
2160+ delete view;
2161+ }
2162+
2163+ void testKnownCases()
2164+ {
2165+ const QString templateString("template: ");
2166+ const QString componentsString("components: ");
2167+ const QString resultString("result: ");
2168+
2169+ const QString testDirPath = DASHVIEWSTEST_FOLDER "/cardcreator/";
2170+ QDir d(testDirPath);
2171+ const QStringList testFiles = d.entryList(QStringList() << "*.tst");
2172+ foreach(const QString &testFileName, testFiles) {
2173+ qDebug() << testFileName;
2174+ QFile testFile(testDirPath + testFileName);
2175+ QVERIFY(testFile.open(QIODevice::ReadOnly));
2176+ QTextStream ts(&testFile);
2177+ const QStringList lines = ts.readAll().split("\n");
2178+
2179+ QVERIFY(lines[0].startsWith(templateString));
2180+ QVERIFY(lines[1].startsWith(componentsString));
2181+ QVERIFY(lines[2].startsWith(resultString));
2182+ const QString templateJSON = lines[0].mid(templateString.length());
2183+ const QString componentsJSON = lines[1].mid(componentsString.length());
2184+ const QString resultFileName = lines[2].mid(resultString.length());
2185+
2186+ QVariant cardStringResult;
2187+ QMetaObject::invokeMethod(view->rootObject(), "cardString", Q_RETURN_ARG(QVariant, cardStringResult), Q_ARG(QVariant, templateJSON), Q_ARG(QVariant, componentsJSON));
2188+
2189+ QFile testResultFile(testDirPath + resultFileName);
2190+ QVERIFY(testResultFile.open(QIODevice::ReadOnly));
2191+ QTextStream ts2(&testResultFile);
2192+ const QString expectedResult = ts2.readAll();
2193+ QCOMPARE(cardStringResult.toString().simplified(), expectedResult.simplified());
2194+
2195+ QVariant createCardComponentResult;
2196+ QMetaObject::invokeMethod(view->rootObject(), "createCardComponent", Q_RETURN_ARG(QVariant, createCardComponentResult), Q_ARG(QVariant, templateJSON), Q_ARG(QVariant, componentsJSON));
2197+ QVERIFY(createCardComponentResult.toBool());
2198+ }
2199+ }
2200+
2201+private:
2202+ QQuickView *view;
2203+};
2204+
2205+QTEST_MAIN(CardCreatorTest)
2206+
2207+#include "cardcreatortest.moc"
2208
2209=== added file 'tests/plugins/Dash/cardcreatortest.qml'
2210--- tests/plugins/Dash/cardcreatortest.qml 1970-01-01 00:00:00 +0000
2211+++ tests/plugins/Dash/cardcreatortest.qml 2014-05-07 15:30:47 +0000
2212@@ -0,0 +1,29 @@
2213+/*
2214+ * Copyright (C) 2014 Canonical, Ltd.
2215+ *
2216+ * This program is free software; you can redistribute it and/or modify
2217+ * it under the terms of the GNU General Public License as published by
2218+ * the Free Software Foundation; version 3.
2219+ *
2220+ * This program is distributed in the hope that it will be useful,
2221+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2222+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2223+ * GNU General Public License for more details.
2224+ *
2225+ * You should have received a copy of the GNU General Public License
2226+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2227+ */
2228+
2229+import QtQuick 2.2
2230+import "../../../plugins/Dash/CardCreator.js" as CardCreator
2231+
2232+Item {
2233+ id: root
2234+ function cardString(template, components) {
2235+ return CardCreator.cardString(JSON.parse(template), JSON.parse(components));
2236+ }
2237+
2238+ function createCardComponent(template, components) {
2239+ return CardCreator.createCardComponent(root, JSON.parse(template), JSON.parse(components)) !== null;
2240+ }
2241+}
2242
2243=== modified file 'tests/plugins/Dash/horizontaljournaltest.qml'
2244--- tests/plugins/DashViews/horizontaljournaltest.qml 2013-12-19 16:02:38 +0000
2245+++ tests/plugins/Dash/horizontaljournaltest.qml 2014-05-07 15:30:47 +0000
2246@@ -15,7 +15,7 @@
2247 */
2248
2249 import QtQuick 2.1
2250-import DashViews 0.1
2251+import Dash 0.1
2252
2253 Item {
2254
2255
2256=== modified file 'tests/plugins/Dash/horizontaljournaltry.qml'
2257--- tests/plugins/DashViews/horizontaljournaltry.qml 2013-12-19 16:02:38 +0000
2258+++ tests/plugins/Dash/horizontaljournaltry.qml 2014-05-07 15:30:47 +0000
2259@@ -15,7 +15,7 @@
2260 */
2261
2262 import QtQuick 2.1
2263-import DashViews 0.1
2264+import Dash 0.1
2265
2266 Item {
2267 id: root
2268
2269=== modified file 'tests/plugins/Dash/listviewwithpageheadertest.qml'
2270--- tests/plugins/DashViews/listviewwithpageheadertest.qml 2013-12-13 09:44:17 +0000
2271+++ tests/plugins/Dash/listviewwithpageheadertest.qml 2014-05-07 15:30:47 +0000
2272@@ -15,7 +15,7 @@
2273 */
2274
2275 import QtQuick 2.0
2276-import DashViews 0.1
2277+import Dash 0.1
2278
2279 Rectangle {
2280 width: 300
2281
2282=== modified file 'tests/plugins/Dash/listviewwithpageheadertestsection.qml'
2283--- tests/plugins/DashViews/listviewwithpageheadertestsection.qml 2013-12-13 09:44:17 +0000
2284+++ tests/plugins/Dash/listviewwithpageheadertestsection.qml 2014-05-07 15:30:47 +0000
2285@@ -15,7 +15,7 @@
2286 */
2287
2288 import QtQuick 2.0
2289-import DashViews 0.1
2290+import Dash 0.1
2291
2292 Rectangle {
2293 width: 300
2294
2295=== modified file 'tests/plugins/Dash/listviewwithpageheadertestsectionexternalmodel.qml'
2296--- tests/plugins/DashViews/listviewwithpageheadertestsectionexternalmodel.qml 2013-12-13 09:44:17 +0000
2297+++ tests/plugins/Dash/listviewwithpageheadertestsectionexternalmodel.qml 2014-05-07 15:30:47 +0000
2298@@ -15,7 +15,7 @@
2299 */
2300
2301 import QtQuick 2.0
2302-import DashViews 0.1
2303+import Dash 0.1
2304
2305 Rectangle {
2306 width: 300
2307
2308=== modified file 'tests/plugins/Dash/organicgridtest.qml'
2309--- tests/plugins/DashViews/organicgridtest.qml 2014-01-14 12:25:52 +0000
2310+++ tests/plugins/Dash/organicgridtest.qml 2014-05-07 15:30:47 +0000
2311@@ -15,7 +15,7 @@
2312 */
2313
2314 import QtQuick 2.1
2315-import DashViews 0.1
2316+import Dash 0.1
2317
2318 Item {
2319
2320
2321=== modified file 'tests/plugins/Dash/organicgridtry.qml'
2322--- tests/plugins/DashViews/organicgridtry.qml 2014-01-14 12:25:52 +0000
2323+++ tests/plugins/Dash/organicgridtry.qml 2014-05-07 15:30:47 +0000
2324@@ -15,7 +15,7 @@
2325 */
2326
2327 import QtQuick 2.1
2328-import DashViews 0.1
2329+import Dash 0.1
2330
2331 Item {
2332 id: root
2333
2334=== modified file 'tests/plugins/Dash/tst_ListViewWithPageHeaderQML.qml'
2335--- tests/plugins/DashViews/tst_ListViewWithPageHeaderQML.qml 2014-04-10 13:49:37 +0000
2336+++ tests/plugins/Dash/tst_ListViewWithPageHeaderQML.qml 2014-05-07 15:30:47 +0000
2337@@ -16,7 +16,7 @@
2338
2339 import QtQuick 2.0
2340 import QtTest 1.0
2341-import DashViews 0.1
2342+import Dash 0.1
2343
2344 Rectangle {
2345 width: 300
2346
2347=== modified file 'tests/plugins/Dash/verticaljournaltest.qml'
2348--- tests/plugins/DashViews/verticaljournaltest.qml 2013-12-17 15:02:45 +0000
2349+++ tests/plugins/Dash/verticaljournaltest.qml 2014-05-07 15:30:47 +0000
2350@@ -15,7 +15,7 @@
2351 */
2352
2353 import QtQuick 2.1
2354-import DashViews 0.1
2355+import Dash 0.1
2356
2357 Item {
2358
2359
2360=== modified file 'tests/plugins/Dash/verticaljournaltry.qml'
2361--- tests/plugins/DashViews/verticaljournaltry.qml 2013-12-17 16:35:44 +0000
2362+++ tests/plugins/Dash/verticaljournaltry.qml 2014-05-07 15:30:47 +0000
2363@@ -15,7 +15,7 @@
2364 */
2365
2366 import QtQuick 2.1
2367-import DashViews 0.1
2368+import Dash 0.1
2369
2370 Item {
2371 id: root
2372
2373=== modified file 'tests/qmltests/CMakeLists.txt'
2374--- tests/qmltests/CMakeLists.txt 2014-05-05 12:08:16 +0000
2375+++ tests/qmltests/CMakeLists.txt 2014-05-07 15:30:47 +0000
2376@@ -35,10 +35,10 @@
2377 add_qml_test(Components ZoomableImage)
2378 add_qml_test(Dash Dash IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
2379 add_qml_test(Dash DashContent IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
2380-add_qml_test(Dash Card)
2381-add_qml_benchmark(Dash CardBenchmark 1000)
2382+add_qml_test(Dash Card IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
2383+add_qml_benchmark(Dash CardBenchmark 1000 IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
2384 add_qml_test(Dash CardHeader)
2385-add_qml_test(Dash CardTool)
2386+add_qml_test(Dash CardTool IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
2387 add_qml_test(Dash GenericScopeView IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
2388 add_qml_test(Dash/Apps RunningApplicationsGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}
2389 ${CMAKE_BINARY_DIR}/tests/mocks
2390
2391=== modified file 'tests/qmltests/Dash/Previews/tst_PreviewHeader.qml'
2392--- tests/qmltests/Dash/Previews/tst_PreviewHeader.qml 2014-02-07 14:08:40 +0000
2393+++ tests/qmltests/Dash/Previews/tst_PreviewHeader.qml 2014-05-07 15:30:47 +0000
2394@@ -24,6 +24,12 @@
2395 width: units.gu(60)
2396 height: units.gu(80)
2397
2398+ property var origHeaderjson: {
2399+ "title": "THE TITLE",
2400+ "subtitle": "Something catchy",
2401+ "mascot": "../graphics/play_button.png"
2402+ }
2403+
2404 property var headerjson: {
2405 "title": "THE TITLE",
2406 "subtitle": "Something catchy",
2407@@ -43,14 +49,70 @@
2408 }
2409
2410 UT.UnityTestCase {
2411+ id: testCase
2412 name: "PreviewHeaderTest"
2413 when: windowShown
2414
2415+ property Item mascot: findChild(previewHeader, "mascotShape")
2416+ property Item outerRow: findChild(previewHeader, "outerRow")
2417+ property Item column: findChild(previewHeader, "column")
2418+
2419+ function initTestCase() {
2420+ verify(typeof testCase.mascot === "object", "Couldn't find mascot object.");
2421+ verify(typeof testCase.outerRow === "object", "Couldn't find outerRow object.");
2422+ verify(typeof testCase.column === "object", "Couldn't find column object.");
2423+
2424+ headerjson.mascot = "";
2425+ headerjson.title = "";
2426+ headerjson.subtitle = "";
2427+ previewHeader.widgetData = headerjson;
2428+ }
2429+
2430+ function test_mascot_data() {
2431+ return [
2432+ { tag: "Empty", source: "", visible: false },
2433+ { tag: "Invalid", source: "bad_path", visible: false },
2434+ { tag: "Valid", source: "../graphics/play_button.png", visible: true },
2435+ ]
2436+ }
2437+
2438+ function test_mascot(data) {
2439+ headerjson.mascot = data.source;
2440+ previewHeader.widgetData = headerjson;
2441+
2442+ tryCompare(testCase.mascot, "visible", data.visible);
2443+ }
2444+
2445+ function test_dimensions_data() {
2446+ return [
2447+ { tag: "Column width with mascot", object: column, width: previewHeader.width - mascot.width - outerRow.margins * 3, mascot: "artwork/avatar.png" },
2448+ { tag: "Header height", object: previewHeader, height: function() { return outerRow.height + outerRow.margins * 2 } },
2449+ ]
2450+ }
2451+
2452+ function test_dimensions(data) {
2453+ if (data.hasOwnProperty("mascot")) {
2454+ headerjson.mascot = data.mascot;
2455+ }
2456+ previewHeader.widgetData = headerjson;
2457+
2458+ if (data.hasOwnProperty("width")) {
2459+ tryCompare(data.object, "width", data.width);
2460+ }
2461+
2462+ if (data.hasOwnProperty("height")) {
2463+ tryCompareFunction(function() { return data.object.height === data.height() }, true);
2464+ }
2465+ }
2466+
2467 function test_json() {
2468- var cardHeader = findChild(previewHeader, "cardHeader");
2469- compare(cardHeader.title, "THE TITLE");
2470- compare(cardHeader.subtitle, "Something catchy");
2471- compare(cardHeader.mascot.toString().slice(-24), "graphics/play_button.png");
2472+ headerjson = origHeaderjson;
2473+ previewHeader.widgetData = headerjson;
2474+
2475+ var innerPreviewHeader = findChild(previewHeader, "innerPreviewHeader");
2476+ compare(innerPreviewHeader.title, "THE TITLE");
2477+ compare(innerPreviewHeader.subtitle, "Something catchy");
2478+ compare(innerPreviewHeader.mascot.toString().slice(-24), "graphics/play_button.png");
2479 }
2480 }
2481 }
2482
2483=== modified file 'tests/qmltests/Dash/tst_Card.qml'
2484--- tests/qmltests/Dash/tst_Card.qml 2014-04-02 14:29:21 +0000
2485+++ tests/qmltests/Dash/tst_Card.qml 2014-05-07 15:30:47 +0000
2486@@ -29,8 +29,8 @@
2487
2488 property string cardData: '
2489 {
2490- "art": "../../tests/qmltests/Dash/artwork/music-player-design.png",
2491- "mascot": "../../tests/qmltests/Dash/artwork/avatar.png",
2492+ "art": "../../../tests/qmltests/Dash/artwork/music-player-design.png",
2493+ "mascot": "../../../tests/qmltests/Dash/artwork/avatar.png",
2494 "title": "foo",
2495 "subtitle": "bar",
2496 "summary": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
2497@@ -98,21 +98,26 @@
2498
2499 CardTool {
2500 id: cardTool
2501- template: card.template
2502- components: card.components
2503+ template: Helpers.update(JSON.parse(Helpers.defaultLayout), Helpers.tryParse(layoutArea.text, layoutError))['template'];
2504+ components: Helpers.update(JSON.parse(Helpers.defaultLayout), Helpers.tryParse(layoutArea.text, layoutError))['components'];
2505 viewWidth: units.gu(48)
2506 }
2507
2508- Card {
2509- id: card
2510+ readonly property var card: loader.item
2511+
2512+ Loader {
2513+ id: loader
2514 anchors { top: parent.top; left: parent.left; margins: units.gu(1) }
2515
2516- width: cardTool.cardWidth || implicitWidth
2517- height: cardTool.cardHeight || implicitHeight
2518-
2519- template: Helpers.update(JSON.parse(Helpers.defaultLayout), Helpers.tryParse(layoutArea.text, layoutError))['template'];
2520- components: Helpers.update(JSON.parse(Helpers.defaultLayout), Helpers.tryParse(layoutArea.text, layoutError))['components'];
2521- cardData: Helpers.mapData(dataArea.text, components, dataError)
2522+ sourceComponent: cardTool.cardComponent
2523+ onLoaded: {
2524+ item.template = Qt.binding(function() { return cardTool.template; });
2525+ item.components = Qt.binding(function() { return cardTool.components; });
2526+ item.cardData = Qt.binding(function() { return Helpers.mapData(dataArea.text, cardTool.components, dataError); });
2527+ item.width = Qt.binding(function() { return cardTool.cardWidth || item.implicitWidth; });
2528+ item.height = Qt.binding(function() { return cardTool.cardHeight || item.implicitHeight; });
2529+ item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight; });
2530+ }
2531 }
2532
2533 Rectangle {
2534@@ -181,60 +186,45 @@
2535
2536 when: windowShown
2537
2538- property Item header: findChild(card, "cardHeader")
2539- property Item headerLoader: findChild(card, "cardHeaderLoader")
2540- property Item title: findChild(header, "titleLabel")
2541- property Item art: findChild(card, "artShape")
2542+ property Item title: findChild(card, "titleLabel")
2543+ property Item subtitle: findChild(card, "subtitleLabel")
2544+ property var headerRow: findChild(card, "outerRow")
2545+ property var art: findChild(card, "artShape")
2546 property Item artImage: findChild(card, "artImage")
2547 property Item summary: findChild(card, "summaryLabel")
2548 property Item background: findChild(card, "background")
2549 property Item backgroundLoader: findChild(card, "backgroundLoader")
2550 property Item backgroundImage: findChild(card, "backgroundImage")
2551-
2552- function initTestCase() {
2553- verify(typeof testCase.header === "object", "Couldn't find header object.");
2554- verify(typeof testCase.art === "object", "Couldn't find art object.");
2555- verify(typeof testCase.artImage === "object", "Couldn't find artImage object.");
2556- verify(typeof testCase.summary === "object", "Couldn't find summary object.");
2557- verify(typeof testCase.artImage === "object", "Couldn't find background object.");
2558- verify(typeof testCase.summary === "object", "Couldn't find backgroundImage object.");
2559- }
2560+ property Item mascotImage: findChild(card, "mascotImage");
2561
2562 function cleanup() {
2563 selector.selectedIndex = -1;
2564 }
2565
2566-
2567- function test_header_binding_data() {
2568- return [
2569- { tag: "Mascot", property: "mascot", value: Qt.resolvedUrl("artwork/avatar.png"), index: 0 },
2570- { tag: "Title", property: "title", value: "foo", index: 0 },
2571- { tag: "Subtitle", property: "subtitle", value: "bar", index: 0 },
2572- ];
2573- }
2574-
2575- function test_header_binding(data) {
2576- selector.selectedIndex = data.index;
2577- tryCompare(testCase.header, data.property, data.value);
2578- }
2579-
2580 function test_card_binding_data() {
2581 return [
2582- { tag: "Art", object: artImage, property: "source", value: Qt.resolvedUrl("artwork/music-player-design.png"), index: 0 },
2583- { tag: "Summary", object: summary, property: "text", field: "summary", index: 0 },
2584- { tag: "Fit", object: art, fill: Image.PreserveAspectFit, index: 4 },
2585+ { tag: "Art", object: "artImage", property: "source", value: Qt.resolvedUrl("artwork/music-player-design.png"), index: 0 },
2586+ { tag: "Summary", object: "summary", property: "text", field: "summary", index: 0 },
2587+ { tag: "Fit", object: "art", fill: Image.PreserveAspectFit, index: 4 },
2588+ { tag: "Mascot", object: "mascotImage", property: "source", value: Qt.resolvedUrl("artwork/avatar.png"), index: 0 },
2589+ { tag: "Title", object: "title", property: "text", value: "foo", index: 0 },
2590+ { tag: "Subtitle", object: "subtitle", property: "text", value: "bar", index: 0 },
2591 ];
2592 }
2593
2594 function test_card_binding(data) {
2595 selector.selectedIndex = data.index;
2596+ waitForRendering(card);
2597+
2598+ tryCompareFunction(function() { return testCase[data.object] !== null }, true);
2599+ var object = testCase[data.object];
2600
2601 if (data.hasOwnProperty('value')) {
2602- tryCompare(data.object, data.property, data.value);
2603+ tryCompare(object, data.property, data.value);
2604 }
2605
2606 if (data.hasOwnProperty('field')) {
2607- tryCompare(data.object, data.property, card.cardData[data.field]);
2608+ tryCompare(object, data.property, card.cardData[data.field]);
2609 }
2610 }
2611
2612@@ -243,31 +233,21 @@
2613 { tag: "Medium", width: units.gu(18.5), index: 0 },
2614 { tag: "Small", width: units.gu(12), index: 1 },
2615 { tag: "Large", width: units.gu(38), index: 2 },
2616- { tag: "Wide", width: units.gu(18.5), aspect: 0.5, index: 0 },
2617+ { tag: "Wide", width: units.gu(18.5), index: 0 },
2618 { tag: "Horizontal", width: units.gu(38), index: 5 },
2619 // Make sure card ends with header when there's no summary
2620- { tag: "NoSummary", height: function() { return headerLoader.y + headerLoader.height }, index: 6 },
2621- { tag: "HorizontalNoSummary", height: function() { return headerLoader.height }, card_layout: "horizontal", index: 6 },
2622+ { tag: "NoSummary", height: function() { var cardToolRow = findChild(cardTool, "outerRow");
2623+ return cardToolRow.y + cardToolRow.height + units.gu(1) }, index: 6 },
2624+ { tag: "HorizontalNoSummary", height: function() { return headerRow.height + units.gu(2) }, card_layout: "horizontal", index: 6 },
2625 ]
2626 }
2627
2628 function test_card_size(data) {
2629- waitForRendering(card);
2630 selector.selectedIndex = data.index;
2631
2632- if (data.hasOwnProperty("size")) {
2633- card.template['card-size'] = data.size;
2634- card.templateChanged();
2635- }
2636-
2637 if (data.hasOwnProperty("card_layout")) {
2638- card.template['card-layout'] = data.card_layout;
2639- card.templateChanged();
2640- }
2641-
2642- if (data.hasOwnProperty("aspect")) {
2643- card.components['art']['aspect-ratio'] = data.aspect;
2644- card.componentsChanged();
2645+ cardTool.template['card-layout'] = data.card_layout;
2646+ cardTool.templateChanged();
2647 }
2648
2649 if (data.hasOwnProperty("width")) {
2650@@ -288,25 +268,26 @@
2651 { tag: "Large", width: units.gu(38), index: 2 },
2652 { tag: "Wide", height: units.gu(19), size: "large", index: 3 },
2653 { tag: "Fit", height: units.gu(38), size: "large", width: units.gu(19), index: 4 },
2654- { tag: "VerticalWidth", width: function() { return header.width }, index: 0 },
2655- { tag: "HorizontalHeight", height: function() { return header.height }, index: 5 },
2656- { tag: "HorizontalWidth", width: function() { return headerLoader.x }, index: 5 },
2657+ { tag: "VerticalWidth", width: function() { return headerRow.width + units.gu(1) * 2 }, index: 0 },
2658+ { tag: "HorizontalHeight", height: function() { return headerRow.height + units.gu(1) * 2 }, index: 5 },
2659+ { tag: "HorizontalWidth", width: function() { return headerRow.x - units.gu(1) }, index: 5 },
2660 ]
2661 }
2662
2663 function test_art_size(data) {
2664 selector.selectedIndex = data.index;
2665-
2666 if (data.hasOwnProperty("size")) {
2667- card.template['card-size'] = data.size;
2668- card.templateChanged();
2669+ cardTool.template['card-size'] = data.size;
2670+ cardTool.templateChanged();
2671 }
2672
2673 if (data.hasOwnProperty("aspect")) {
2674- card.components['art']['aspect-ratio'] = data.aspect;
2675- card.componentsChanged();
2676+ cardTool.components['art']['aspect-ratio'] = data.aspect;
2677+ cardTool.componentsChanged();
2678 }
2679
2680+ waitForRendering(card);
2681+
2682 if (data.hasOwnProperty("width")) {
2683 if (typeof data.width === "function") {
2684 tryCompareFunction(function() { return art.width === data.width() }, true);
2685@@ -326,38 +307,40 @@
2686
2687 function test_art_layout_data() {
2688 return [
2689- { tag: "Vertical", left: function() { return 0 }, index: 0},
2690- { tag: "Horizontal", left: function() { return art.width }, index: 5 },
2691+ { tag: "Vertical", left: function() { return units.gu(1); }, index: 0},
2692+ { tag: "Horizontal", left: function() { return art.width + units.gu(1); }, index: 5 },
2693 ];
2694 }
2695
2696 function test_art_layout(data) {
2697 selector.selectedIndex = data.index;
2698+ waitForRendering(card);
2699
2700- tryCompare(testCase.headerLoader, "x", data.left());
2701+ tryCompare(headerRow, "x", data.left());
2702 }
2703
2704 function test_header_layout_data() {
2705 return [
2706- { tag: "Vertical", top: function() { return art.y + art.height },
2707- left: function() { return art.x }, index: 0 },
2708- { tag: "Horizontal", top: function() { return art.y },
2709- left: function() { return art.x + art.width }, index: 5 },
2710- { tag: "Overlay", top: function() { return art.y + art.height - header.height },
2711- left: function() { return art.x }, index: 9 },
2712+ { tag: "Vertical", top: function() { return art.y + art.height + units.gu(1) },
2713+ left: function() { return art.x + units.gu(1) }, index: 0 },
2714+ { tag: "Horizontal", top: function() { return art.y + units.gu(1) },
2715+ left: function() { return art.x + art.width + units.gu(1) }, index: 5 },
2716+ { tag: "Overlay", top: function() { return art.y + art.height - headerRow.height - units.gu(1) },
2717+ left: function() { return art.x + units.gu(1) }, index: 9 },
2718 ]
2719 }
2720
2721 function test_header_layout(data) {
2722 selector.selectedIndex = data.index;
2723+ waitForRendering(card);
2724
2725- tryCompareFunction(function() { return testCase.headerLoader.y === data.top() }, true);
2726- tryCompareFunction(function() { return testCase.headerLoader.x === data.left() }, true);
2727+ tryCompareFunction(function() { return testCase.headerRow.y === data.top() }, true);
2728+ tryCompareFunction(function() { return testCase.headerRow.x === data.left() }, true);
2729 }
2730
2731 function test_summary_layout_data() {
2732 return [
2733- { tag: "With header", top: function() { return headerLoader.y + headerLoader.height }, index: 0 },
2734+ { tag: "With header", top: function() { return headerRow.y + headerRow.height + units.gu(1); }, index: 0 },
2735 { tag: "Without header", top: function() { return art.y + art.height }, index: 7 },
2736 ]
2737 }
2738@@ -365,16 +348,16 @@
2739 function test_summary_layout(data) {
2740 selector.selectedIndex = data.index;
2741
2742+ waitForRendering(card);
2743+
2744 tryCompareFunction(function() { return testCase.summary.y === data.top() }, true);
2745 }
2746
2747 function test_art_visibility() {
2748 selector.selectedIndex = 8;
2749
2750- tryCompare(testCase.artImage, "source", "");
2751- compare(testCase.art.visible, false);
2752- compare(testCase.art.height, 0);
2753- compare(testCase.art.width, 0);
2754+ compare(testCase.artImage, null);
2755+ compare(testCase.art, null);
2756 }
2757
2758 function test_background_data() {
2759@@ -402,7 +385,11 @@
2760
2761 waitForRendering(card);
2762
2763- tryCompare(backgroundLoader, "active", data.visible);
2764+ if (data.visible) {
2765+ tryCompare(backgroundLoader, "active", true);
2766+ } else {
2767+ compare(backgroundLoader, null);
2768+ }
2769
2770 if (data.hasOwnProperty("color")) {
2771 tryCompare(background, "color", data.color);
2772@@ -449,13 +436,15 @@
2773
2774 function test_fontColor(data) {
2775 selector.selectedIndex = 10;
2776+ waitForRendering(card);
2777
2778 background.color = data.tag;
2779
2780 var fontColor = data.dark ? "grey" : "white";
2781
2782 tryCompareFunction(function() { return Qt.colorEqual(summary.color, fontColor); }, true);
2783- tryCompareFunction(function() { return Qt.colorEqual(header.fontColor, fontColor); }, true);
2784+ tryCompareFunction(function() { return Qt.colorEqual(title.color, fontColor); }, true);
2785+ tryCompareFunction(function() { return Qt.colorEqual(subtitle.color, fontColor); }, true);
2786 }
2787
2788 function test_mascotShape_data() {
2789@@ -468,15 +457,12 @@
2790
2791 function test_mascotShape(data) {
2792 selector.selectedIndex = data.index;
2793+ waitForRendering(card);
2794
2795 var shape = findChild(card, "mascotShapeLoader");
2796- var image = findChild(card, "mascotImage");
2797-
2798- verify(shape, "Could not find shape.");
2799- verify(image, "Could not find image.");
2800-
2801- tryCompare(shape, "active", data.shape);
2802- tryCompare(image, "visible", !data.shape);
2803+
2804+ compare(shape !== null, data.shape);
2805+ tryCompare(mascotImage, "visible", !data.shape);
2806 }
2807 }
2808 }
2809
2810=== modified file 'tests/qmltests/Dash/tst_CardBenchmark.qml'
2811--- tests/qmltests/Dash/tst_CardBenchmark.qml 2014-04-16 11:20:38 +0000
2812+++ tests/qmltests/Dash/tst_CardBenchmark.qml 2014-05-07 15:30:47 +0000
2813@@ -54,13 +54,16 @@
2814 Repeater {
2815 id: cardRepeater
2816 model: 0
2817- Card {
2818- width: cardTool.cardWidth || implicitWidth
2819- height: cardTool.cardHeight || implicitHeight
2820-
2821- template: cardTool.template
2822- components: cardTool.components
2823- cardData: Helpers.mapData(root.cardData, components)
2824+ Loader {
2825+ sourceComponent: cardTool.cardComponent
2826+ onLoaded: {
2827+ item.objectName = "delegate" + index;
2828+ item.width = Qt.binding(function() { return cardTool.cardWidth || implicitWidth; });
2829+ item.height = Qt.binding(function() { return cardTool.cardHeight || implicitHeight; });
2830+ item.cardData = Qt.binding(function() { return Helpers.mapData(root.cardData, cardTool.components); });
2831+ item.template = Qt.binding(function() { return cardTool.template; });
2832+ item.components = Qt.binding(function() { return cardTool.components; });
2833+ }
2834 }
2835 }
2836
2837
2838=== removed file 'tests/qmltests/Dash/tst_CardHeader.qml'
2839--- tests/qmltests/Dash/tst_CardHeader.qml 2014-04-02 14:29:21 +0000
2840+++ tests/qmltests/Dash/tst_CardHeader.qml 1970-01-01 00:00:00 +0000
2841@@ -1,116 +0,0 @@
2842-/*
2843- * Copyright (C) 2013 Canonical, Ltd.
2844- *
2845- * This program is free software; you can redistribute it and/or modify
2846- * it under the terms of the GNU General Public License as published by
2847- * the Free Software Foundation; version 3.
2848- *
2849- * This program is distributed in the hope that it will be useful,
2850- * but WITHOUT ANY WARRANTY; without even the implied warranty of
2851- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2852- * GNU General Public License for more details.
2853- *
2854- * You should have received a copy of the GNU General Public License
2855- * along with this program. If not, see <http://www.gnu.org/licenses/>.
2856- */
2857-
2858-import QtQuick 2.0
2859-import QtTest 1.0
2860-import Ubuntu.Components 0.1
2861-import Unity.Test 0.1 as UT
2862-import "../../../qml/Dash"
2863-
2864-
2865-Rectangle {
2866- width: units.gu(40)
2867- height: units.gu(72)
2868- color: "lightgrey"
2869-
2870- CardHeader {
2871- id: cardHeader
2872- anchors { left: parent.left; right: parent.right }
2873- }
2874-
2875- Rectangle {
2876- anchors.fill: cardHeader
2877- color: "lightblue"
2878- opacity: 0.5
2879- }
2880-
2881- UT.UnityTestCase {
2882- id: testCase
2883- name: "CardHeader"
2884-
2885- when: windowShown
2886-
2887- property Item mascot: findChild(cardHeader, "mascotShapeLoader")
2888- property Item titleLabel: findChild(cardHeader, "titleLabel")
2889- property Item subtitleLabel: findChild(cardHeader, "subtitleLabel")
2890- property Item oldPriceLabel: findChild(cardHeader, "oldPriceLabel")
2891- property Item outerRow: findChild(cardHeader, "outerRow")
2892- property Item column: findChild(cardHeader, "column")
2893-
2894- function initTestCase() {
2895- verify(typeof testCase.mascot === "object", "Couldn't find mascot object.");
2896- verify(typeof testCase.titleLabel === "object", "Couldn't find titleLabel object.");
2897- verify(typeof testCase.subtitleLabel === "object", "Couldn't find subtitleLabel object.");
2898- verify(typeof testCase.oldPriceLabel === "object", "Couldn't find oldPriceLabel object.");
2899- }
2900-
2901- function cleanup() {
2902- cardHeader.mascot = "";
2903- cardHeader.title = "";
2904- cardHeader.subtitle = "";
2905- }
2906-
2907- function test_mascot_data() {
2908- return [
2909- { tag: "Empty", source: "", visible: false },
2910- { tag: "Invalid", source: "bad_path", visible: false },
2911- { tag: "Valid", source: Qt.resolvedUrl("artwork/avatar.png"), visible: true },
2912- ]
2913- }
2914-
2915- function test_mascot(data) {
2916- cardHeader.mascot = data.source;
2917- tryCompare(testCase.mascot, "visible", data.visible);
2918- }
2919-
2920- function test_labels_data() {
2921- return [
2922- { tag: "Empty", visible: false },
2923- { tag: "Title only", title: "Foo", visible: true },
2924- { tag: "Subtitle only", subtitle: "Bar", visible: false },
2925- { tag: "Both", title: "Foo", subtitle: "Bar", visible: true },
2926- ]
2927- }
2928-
2929- function test_labels(data) {
2930- cardHeader.title = data.title !== undefined ? data.title : "";
2931- cardHeader.subtitle = data.subtitle !== undefined ? data.subtitle : "";
2932- tryCompare(cardHeader, "visible", data.visible);
2933- }
2934-
2935- function test_dimensions_data() {
2936- return [
2937- { tag: "Column width", object: column, width: cardHeader.width },
2938- { tag: "Column width with mascot", object: column, width: cardHeader.width - mascot.width - outerRow.margins * 3, mascot: "artwork/avatar.png" },
2939- { tag: "Header height", object: cardHeader, height: function() { return outerRow.height + outerRow.margins * 2 } },
2940- ]
2941- }
2942-
2943- function test_dimensions(data) {
2944- if (data.hasOwnProperty("mascot")) {
2945- cardHeader.mascot = data.mascot;
2946- }
2947-
2948- if (data.hasOwnProperty("width")) {
2949- tryCompare(data.object, "width", data.width);
2950- }
2951-
2952- if (data.hasOwnProperty("height")) {
2953- tryCompareFunction(function() { return data.object.height === data.height() }, true);
2954- }
2955- }
2956- }
2957-}
2958
2959=== modified file 'tests/qmltests/Dash/tst_CardTool.qml'
2960--- tests/qmltests/Dash/tst_CardTool.qml 2014-03-17 11:44:05 +0000
2961+++ tests/qmltests/Dash/tst_CardTool.qml 2014-05-07 15:30:47 +0000
2962@@ -127,26 +127,6 @@
2963 }
2964 }
2965 }
2966-
2967- Card {
2968- id: card
2969- template: cardTool.template;
2970- components: cardTool.components;
2971- cardData: Helpers.mapData(dataArea.text, components, dataError)
2972-
2973- width: cardTool.cardWidth || implicitWidth
2974- height: cardTool.cardHeight || implicitHeight
2975-
2976- clip: true
2977- headerAlignment: cardTool.headerAlignment
2978-
2979- Rectangle {
2980- anchors.fill: parent
2981- color: "transparent"
2982- border.width: 1
2983- border.color: "green"
2984- }
2985- }
2986 }
2987
2988 Rectangle {
2989@@ -257,7 +237,7 @@
2990 id: testCase
2991 name: "Card"
2992
2993- property Card internalCard: findChild(cardTool, "cardToolCard")
2994+ property var internalCard: findChild(cardTool, "cardToolCard")
2995
2996 when: windowShown
2997
2998@@ -272,16 +252,16 @@
2999
3000 function test_card_size_data() {
3001 return [
3002- { tag: "Medium", width: units.gu(18.5), height: function() { return card.height }, index: 0 },
3003- { tag: "No art", width: units.gu(18.5), height: function() { return card.height }, index: 1 },
3004- { tag: "No summary", width: units.gu(18.5), height: function() { return card.height }, index: 2 },
3005- { tag: "Just header", width: units.gu(18.5), height: function() { return card.height }, index: 3 },
3006- { tag: "Just title", width: units.gu(18.5), height: function() { return card.height }, index: 4 },
3007- { tag: "Title and subtitle", width: units.gu(18.5), height: function() { return card.height }, index: 5 },
3008- { tag: "Title and mascot", width: units.gu(18.5), height: function() { return card.height }, index: 6 },
3009- { tag: "Small", width: units.gu(12), height: function() { return card.height }, index: 7 },
3010- { tag: "Large", width: units.gu(38), height: function() { return card.height }, index: 8 },
3011- { tag: "Horizontal", width: units.gu(38), height: function() { return card.height }, index: 9 },
3012+ { tag: "Medium", width: units.gu(18.5), height: function() { return internalCard ? internalCard.height : 0 }, index: 0 },
3013+ { tag: "No art", width: units.gu(18.5), height: function() { return internalCard ? internalCard.height : 0 }, index: 1 },
3014+ { tag: "No summary", width: units.gu(18.5), height: function() { return internalCard ? internalCard.height : 0 }, index: 2 },
3015+ { tag: "Just header", width: units.gu(18.5), height: function() { return internalCard ? internalCard.height : 0 }, index: 3 },
3016+ { tag: "Just title", width: units.gu(18.5), height: function() { return internalCard ? internalCard.height : 0 }, index: 4 },
3017+ { tag: "Title and subtitle", width: units.gu(18.5), height: function() { return internalCard ? internalCard.height : 0 }, index: 5 },
3018+ { tag: "Title and mascot", width: units.gu(18.5), height: function() { return internalCard ? internalCard.height : 0 }, index: 6 },
3019+ { tag: "Small", width: units.gu(12), height: function() { return internalCard ? internalCard.height : 0 }, index: 7 },
3020+ { tag: "Large", width: units.gu(38), height: function() { return internalCard ? internalCard.height : 0 }, index: 8 },
3021+ { tag: "Horizontal", width: units.gu(38), height: function() { return internalCard ? internalCard.height : 0 }, index: 9 },
3022 { tag: "OrganicGrid", width: undefined, height: undefined, index: 0, layout_index: 1},
3023 { tag: "Journal", width: undefined, height: units.gu(20), size: 20, index: 0, layout_index: 2 },
3024 { tag: "OversizedJournal", width: undefined, height: units.gu(18.5), size: 40, index: 0, layout_index: 2 },

Subscribers

People subscribed via source and target branches