on xenial help sign in
Merge from trunk
commit42d1f8e808b2704ac1c4c735b1a1d8f6c415cdf6 authorMichael Terry <michael.terry@canonical.com> dateFri 1 Aug 2014, 10:25 (12 years ago) parent59c1764ee994e5
45 files changed+880−1006
cmake/modules/QmlTest.cmake+1−1 debian/changelog+36 debian/control+1−1 plugins/Dash/CardAttributes.qml+65 plugins/Dash/CardCreator.js+76−7 plugins/Dash/qmldir+1 po/is.po+6−6 po/km.po+7−7 po/pl.po+7−7 po/ru.po+8−8 po/sv.po+6−6 qml/Components/PageHeader.qml+24−7 qml/Dash/Apps/CloseIcon.qml−62 qml/Dash/Apps/RunningApplicationTile.qml−136 qml/Dash/Apps/RunningApplicationsGrid.qml−105 qml/Dash/Apps/graphics/close_btn@9.png qml/Dash/CardTool.qml+27−2 qml/Dash/DashApps.qml−40 qml/Dash/DashContent.qml+1−3 qml/Dash/GenericScopeView.qml+1−10 qml/Launcher/LauncherDelegate.qml+9−6 qml/Launcher/graphics/notification@30.png qml/Launcher/graphics/notification@30.sci−5 qml/Panel/Indicators/DefaultIndicatorPage.qml+1−1 tests/autopilot/unity8/shell/emulators/dash.py−4 tests/autopilot/unity8/shell/tests/test_emulators.py+1−1 tests/mocks/Unity/CMakeLists.txt+1−1 tests/mocks/Unity/fake_scopes.cpp+5 tests/mocks/Unity/fake_scopes.h+1 tests/plugins/Dash/CMakeLists.txt+1 tests/plugins/Dash/cardcreator/1.res+63−61 tests/plugins/Dash/cardcreator/2.res+94−91 tests/plugins/Dash/cardcreator/3.res+77−74 tests/plugins/Dash/cardcreator/5.res+119−117 tests/plugins/Dash/cardcreator/7.res+128 tests/plugins/Dash/cardcreator/7.tst+3 tests/plugins/Dash/cardcreatortest.cpp+9−1 tests/plugins/Dash/listviewwithpageheadersectiontest.cpp−1 tests/plugins/Dash/tst_CardAttributes.qml+54 tests/qmltests/CMakeLists.txt−1 tests/qmltests/Components/tst_PageHeader.qml+36−6 tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml−217 tests/qmltests/Dash/CardHelpers.js+5−3 tests/qmltests/Dash/tst_Card.qml+4−2 tests/qmltests/tst_Shell.qml+2−6
cmake/modules/QmlTest.cmake+1−1
@@ -135,7 +135,7 @@ macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS)
135135 set(testCommand
136136 LD_LIBRARY_PATH=${LD_PATH}
137137 ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
138− -o ${CMAKE_BINARY_DIR}/${CLASSNAME}Test.xml,xunitxml
138+ -o ${CMAKE_BINARY_DIR}/${CLASS_NAME}Test.xml,xunitxml
139139 -o -,txt)
140140
141141 add_qmltest_target(test${CLASS_NAME} "${testCommand}" FALSE TRUE)
debian/changelog+36
@@ -1,3 +1,39 @@
1+unity8 (8.00+14.10.20140731.1-0ubuntu1) utopic; urgency=low
2+
3+ [ Gerry Boland ]
4+ * Fix the run.sh script - pretend to be running with qtmir and emit
5+ SIGSTOP at the right time
6+
7+ [ Ying-Chun Liu ]
8+ * Implement Attribute UI. (LP: #1282460)
9+
10+ [ Albert Astals ]
11+ * Hide search history popup as soon as you start typing As discussed
12+ with Mike and Saviq
13+ * Compile with for scopes-v3 unity-api
14+ * PageHeader: Unfocus search field when search entry is selected
15+ * Show search field if the search query changes
16+ * Test: Add a condition for art.height being > 0 means stuff has
17+ already been layouted a bit without it it can happen that we get 0
18+ for everything at startup and tests still pass
19+ * Remove leftover in test of an old headerless implementation
20+
21+ [ Michael Zanetti ]
22+ * Drop Recent apps category from Dash (LP: #1281092)
23+ * update launcher count emblems to match new spec (LP: #1338984)
24+
25+ [ Bill Filler ]
26+ * disable predictive text for dash search field (LP: #1340409)
27+
28+ [ CI bot ]
29+ * Resync trunk
30+
31+ [ Antti Kaijanmäki ]
32+ * DefaultIndicatorPage: use Loader status to determine the visible
33+ property. (LP: #1350555)
34+
35+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 31 Jul 2014 16:51:01 +0000
36+
137unity8 (8.00+14.10.20140729.1-0ubuntu1) utopic; urgency=low
238
339 [ Michael Terry ]
debian/control+1−1
@@ -24,7 +24,7 @@ Build-Depends: cmake,
2424 libpulse-dev,
2525 libqmenumodel-dev (>= 0.2.8),
2626 libqt5xmlpatterns5-dev,
27− libunity-api-dev (>= 7.85),
27+ libunity-api-dev (>= 7.87),
2828 libusermetricsoutput1-dev,
2929 libxcb1-dev,
3030 pkg-config,
plugins/Dash/CardAttributes.qmlnew file+65
@@ -0,0 +1,65 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.2
18+import QtQuick.Layouts 1.1
19+import Ubuntu.Components 1.1
20+import Ubuntu.Settings.Components 0.1
21+
22+GridLayout {
23+ id: grid
24+ anchors {
25+ left: parent.left;
26+ right: parent.right;
27+ }
28+ columns: 2 + repeater.count % 2
29+ property alias model: repeater.model
30+ property var color
31+
32+ Repeater {
33+ id: repeater
34+ delegate: Row {
35+ spacing: units.gu(0.5)
36+ readonly property int column: index % grid.columns;
37+ Layout.alignment: {
38+ if (column == 0) return Qt.AlignLeft;
39+ if (column == grid.columns - 1 || index == repeater.count - 1) return Qt.AlignRight;
40+ if (column == 1) return Qt.AlignHCenter;
41+ }
42+ Layout.columnSpan: index == repeater.count - 1 && grid.columns == 3 && column == 1 ? 2 : 1
43+ Layout.maximumWidth: Math.max(icon.width, label.x + label.implicitWidth)
44+ Layout.fillWidth: true
45+ StatusIcon {
46+ id: icon
47+ height: units.gu(2)
48+ sets: ["actions", "status", "apps"]
49+ source: "icon" in modelData ? modelData["icon"] : ""
50+ }
51+ Label {
52+ id: label
53+ width: parent.width - x
54+ anchors.verticalCenter: parent.verticalCenter
55+ text: "value" in modelData ? modelData["value"] : "";
56+ elide: Text.ElideRight
57+ maximumLineCount: 1
58+ font.weight: "style" in modelData && modelData["style"] == "highlighted" ? Font.DemiBold : Font.Light
59+ fontSize: "small"
60+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale)
61+ color: grid.color
62+ }
63+ }
64+ }
65+}
plugins/Dash/CardCreator.js+76−7
@@ -193,6 +193,21 @@ var kHeaderColumnCode = 'Column { \n\
193193 ] \n\
194194 }\n';
195195
196+// multiple column version of kHeaderColumnCode.
197+function kHeaderColumnCodeGenerator() {
198+ var headerColumnCodeTemplate = 'Column { \n\
199+ anchors.verticalCenter: parent.verticalCenter; \n\
200+ spacing: units.dp(2); \n\
201+ width: parent.width - x;\n\
202+ data: [ \n\
203+ %1 \n\
204+ ]\n\
205+ }\n';
206+ var args = Array.prototype.slice.call(arguments);
207+ var code = headerColumnCodeTemplate.arg(args.join(',\n'));
208+ return code;
209+}
210+
196211// %1 is used as anchors of mascotShapeLoader
197212var kMascotShapeLoaderCode = 'Loader { \n\
198213 id: mascotShapeLoader; \n\
@@ -224,7 +239,7 @@ var kMascotImageCode = 'Image { \n\
224239 }\n';
225240
226241// %1 is used as anchors of titleLabel
227−// %1 is used as color of titleLabel
242+// %2 is used as color of titleLabel
228243// %3 is used as extra condition for visible of titleLabel
229244var kTitleLabelCode = 'Label { \n\
230245 id: titleLabel; \n\
@@ -258,6 +273,15 @@ var kSubtitleLabelCode = 'Label { \n\
258273 horizontalAlignment: root.headerAlignment; \n\
259274 }\n';
260275
276+// %1 is used as anchors of attributesRow
277+var kAttributesRowCode = 'CardAttributes { \n\
278+ id: attributesRow; \n\
279+ objectName: "attributesRow"; \n\
280+ anchors { %1 } \n\
281+ color: %2; \n\
282+ model: cardData && cardData["attributes"] || undefined; \n\
283+ }\n';
284+
261285// %1 is used as top anchor of summary
262286// %2 is used as topMargin anchor of summary
263287// %3 is used as color of summary
@@ -308,6 +332,7 @@ function cardString(template, components) {
308332 var headerAsOverlay = hasArt && template && template["overlay"] === true && (hasTitle || hasMascot);
309333 var hasSubtitle = hasTitle && components["subtitle"] || false;
310334 var hasHeaderRow = hasMascot && hasTitle;
335+ var hasAttributes = hasTitle && components["attributes"] || false;
311336
312337 if (hasBackground) {
313338 code += kBackgroundLoaderCode;
@@ -377,6 +402,14 @@ function cardString(template, components) {
377402 code += 'readonly property int headerHeight: row.height;\n'
378403 } else if (hasMascot) {
379404 code += 'readonly property int headerHeight: mascotImage.height;\n'
405+ } else if (hasAttributes) {
406+ if (hasTitle && hasSubtitle) {
407+ code += 'readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin + attributesRow.height + attributesRow.anchors.topMargin;\n'
408+ } else if (hasTitle) {
409+ code += 'readonly property int headerHeight: titleLabel.height + attributesRow.height + attributesRow.anchors.topMargin;\n'
410+ } else {
411+ code += 'readonly property int headerHeight: attributesRow.height;\n'
412+ }
380413 } else if (hasSubtitle) {
381414 code += 'readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;\n'
382415 } else if (hasTitle) {
@@ -425,10 +458,12 @@ function cardString(template, components) {
425458
426459 var titleAnchors;
427460 var subtitleAnchors;
428− if (hasMascot && hasSubtitle) {
461+ var attributesAnchors;
462+ if (hasMascot && (hasSubtitle || hasAttributes)) {
429463 // Using row + column
430464 titleAnchors = 'left: parent.left; right: parent.right';
431465 subtitleAnchors = titleAnchors;
466+ attributesAnchors = subtitleAnchors;
432467 } else if (hasMascot) {
433468 // Using row + label
434469 titleAnchors = 'verticalCenter: parent.verticalCenter;\n'
@@ -438,11 +473,13 @@ function cardString(template, components) {
438473 titleAnchors = 'left: parent.left; \n\
439474 leftMargin: units.gu(1); \n\
440475 right: parent.right; \n\
476+ rightMargin: units.gu(1); \n\
441477 top: overlayLoader.top; \n\
442478 topMargin: units.gu(1);\n';
443479 } else {
444480 // Using anchors to the mascot/parent
445− titleAnchors = "right: parent.right;";
481+ titleAnchors = "right: parent.right;\n";
482+ titleAnchors += "rightMargin: units.gu(1);\n";
446483 titleAnchors += headerLeftAnchor;
447484 titleAnchors += headerVerticalAnchors;
448485 if (!headerLeftAnchorHasMargin) {
@@ -452,8 +489,19 @@ function cardString(template, components) {
452489 subtitleAnchors = 'left: titleLabel.left; \n\
453490 leftMargin: titleLabel.leftMargin; \n\
454491 right: titleLabel.right; \n\
492+ rightMargin: titleLabel.rightMargin; \n\
455493 top: titleLabel.bottom; \n\
456494 topMargin: units.dp(2);\n';
495+ if (hasSubtitle) {
496+ attributesAnchors = 'left: subtitleLabel.left; \n\
497+ leftMargin: subtitleLabel.leftMargin; \n\
498+ right: subtitleLabel.right; \n\
499+ rightMargin: subtitleLabel.rightMargin; \n\
500+ top: subtitleLabel.bottom; \n\
501+ topMargin: units.dp(2);\n';
502+ } else {
503+ attributesAnchors = subtitleAnchors;
504+ }
457505 }
458506
459507 var titleLabelVisibleExtra = (headerAsOverlay ? '&& overlayLoader.active': '');
@@ -463,11 +511,27 @@ function cardString(template, components) {
463511 subtitleCode += kSubtitleLabelCode.arg(subtitleAnchors).arg(color);
464512 }
465513
466− if (hasMascot && hasSubtitle) {
514+ if (hasMascot && (hasSubtitle || hasAttributes)) {
467515 // If using row + column wrap the code in the column
468516 titleSubtitleCode = kHeaderColumnCode.arg(titleCode).arg(subtitleCode);
517+ if (hasSubtitle && hasAttributes) {
518+ var attributesCode = kAttributesRowCode.arg(attributesAnchors).arg(color);
519+ titleSubtitleCode = kHeaderColumnCodeGenerator(titleCode, subtitleCode, attributesCode);
520+ } else if (hasSubtitle) {
521+ titleSubtitleCode = kHeaderColumnCode.arg(titleCode).arg(subtitleCode);
522+ } else if (hasAttributes) {
523+ var attributesCode = kAttributesRowCode.arg(attributesAnchors).arg(color);
524+ titleSubtitleCode = kHeaderColumnCode.arg(titleCode).arg(attributesCode);
525+ }
469526 } else {
470− titleSubtitleCode = titleCode + subtitleCode;
527+ titleSubtitleCode = titleCode;
528+ if (hasSubtitle) {
529+ titleSubtitleCode = titleSubtitleCode + subtitleCode;
530+ }
531+ if (hasAttributes) {
532+ var attributesCode = kAttributesRowCode.arg(attributesAnchors).arg(color);
533+ titleSubtitleCode = titleSubtitleCode + attributesCode;
534+ }
471535 }
472536 }
473537
@@ -487,6 +551,7 @@ function cardString(template, components) {
487551 else if (headerAsOverlay && hasArt) summaryTopAnchor = "artShapeHolder.bottom";
488552 else if (hasHeaderRow) summaryTopAnchor = "row.bottom";
489553 else if (hasMascot) summaryTopAnchor = "mascotImage.bottom";
554+ else if (hasAttributes) summaryTopAnchor = "attributesRow.bottom";
490555 else if (hasSubtitle) summaryTopAnchor = "subtitleLabel.bottom";
491556 else if (hasTitle) summaryTopAnchor = "titleLabel.bottom";
492557 else if (hasArt) summaryTopAnchor = "artShapeHolder.bottom";
@@ -499,7 +564,7 @@ function cardString(template, components) {
499564 color = 'root.scopeStyle ? root.scopeStyle.foreground : "grey"';
500565 }
501566
502− var summaryTopMargin = (hasMascot || hasSubtitle ? 'anchors.margins' : '0');
567+ var summaryTopMargin = (hasMascot || hasSubtitle || hasAttributes ? 'anchors.margins' : '0');
503568
504569 code += kSummaryLabelCode.arg(summaryTopAnchor).arg(summaryTopMargin).arg(color);
505570 }
@@ -510,6 +575,8 @@ function cardString(template, components) {
510575 code += 'implicitHeight: row.y + row.height + units.gu(1);\n';
511576 } else if (hasMascot) {
512577 code += 'implicitHeight: mascotImage.y + mascotImage.height;\n';
578+ } else if (hasAttributes) {
579+ code += 'implicitHeight: attributesRow.y + attributesRow.height + units.gu(1);\n';
513580 } else if (hasSubtitle) {
514581 code += 'implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);\n';
515582 } else if (hasTitle) {
@@ -526,8 +593,10 @@ function cardString(template, components) {
526593function createCardComponent(parent, template, components) {
527594 var imports = 'import QtQuick 2.2; \n\
528595 import Ubuntu.Components 0.1; \n\
529− import Ubuntu.Thumbnailer 0.1;\n';
596+ import Ubuntu.Thumbnailer 0.1;\n\
597+ import Dash 0.1;\n';
530598 var card = cardString(template, components);
531599 var code = imports + 'Component {\n' + card + '}\n';
600+
532601 return Qt.createQmlObject(code, parent, "createCardComponent");
533602}
plugins/Dash/qmldir+1
@@ -3,3 +3,4 @@ plugin Dash-qml
33typeinfo Dash.qmltypes
44singleton CardCreatorCache 0.1 CardCreatorCache.qml
55ScopeStyle 0.1 ScopeStyle.qml
6+CardAttributes 0.1 CardAttributes.qml
po/is.po+6−6
@@ -10,14 +10,14 @@ msgstr ""
1010"Project-Id-Version: po_unity8-is\n"
1111"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1212"POT-Creation-Date: 2014-07-11 14:59+0200\n"
13−"PO-Revision-Date: 2014-07-23 05:57+0000\n"
13+"PO-Revision-Date: 2014-07-31 16:10+0000\n"
1414"Last-Translator: Sveinn í Felli <sv1@fellsnet.is>\n"
1515"Language-Team: Icelandic <translation-team-is@lists.sourceforge.net>\n"
1616"MIME-Version: 1.0\n"
1717"Content-Type: text/plain; charset=UTF-8\n"
1818"Content-Transfer-Encoding: 8bit\n"
19−"X-Launchpad-Export-Date: 2014-07-26 07:16+0000\n"
20−"X-Generator: Launchpad (build 17131)\n"
19+"X-Launchpad-Export-Date: 2014-08-01 07:14+0000\n"
20+"X-Generator: Launchpad (build 17141)\n"
2121"Language: is\n"
2222
2323#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -117,7 +117,7 @@ msgstr "Nýlegt"
117117
118118#: qml/Dash/GenericScopeView.qml:330
119119msgid "See all"
120−msgstr ""
120+msgstr "Sjá allt"
121121
122122#: qml/Dash/Previews/PreviewActionCombo.qml:34
123123msgid "More..."
@@ -173,11 +173,11 @@ msgstr "Settu inn PIN fyrir SIM"
173173
174174#: qml/Panel/ActiveCallHint.qml:77
175175msgid "Tap to return to call..."
176−msgstr ""
176+msgstr "Slá til að svara símtali..."
177177
178178#: qml/Panel/ActiveCallHint.qml:90
179179msgid "Conference"
180−msgstr ""
180+msgstr "Ráðstefna"
181181
182182#: qml/Panel/Indicators/MenuItemFactory.qml:583
183183msgid "In queue…"
po/km.po+7−7
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-07-11 14:59+0200\n"
11−"PO-Revision-Date: 2014-07-16 01:37+0000\n"
12−"Last-Translator: Khoem Sokhem <khoemsokhem@khmeros.info>\n"
11+"PO-Revision-Date: 2014-08-01 04:02+0000\n"
12+"Last-Translator: Sok Sophea <sksophea@gmail.com>\n"
1313"Language-Team: Khmer <km@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-07-26 07:16+0000\n"
18−"X-Generator: Launchpad (build 17131)\n"
17+"X-Launchpad-Export-Date: 2014-08-01 07:14+0000\n"
18+"X-Generator: Launchpad (build 17141)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
2121#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -115,7 +115,7 @@ msgstr "បច្ចុប្បន្ន"
115115
116116#: qml/Dash/GenericScopeView.qml:330
117117msgid "See all"
118−msgstr ""
118+msgstr "មើល​ទាំងអស់"
119119
120120#: qml/Dash/Previews/PreviewActionCombo.qml:34
121121msgid "More..."
@@ -171,11 +171,11 @@ msgstr "បញ្ចូល​កូដ PIN របស់​ស៊ីមកាត"
171171
172172#: qml/Panel/ActiveCallHint.qml:77
173173msgid "Tap to return to call..."
174−msgstr ""
174+msgstr "ប៉ះ​ដើម្បី​ត្រឡប់​ទៅ​ការ​ហៅ..."
175175
176176#: qml/Panel/ActiveCallHint.qml:90
177177msgid "Conference"
178−msgstr ""
178+msgstr "សន្និសីទ"
179179
180180#: qml/Panel/Indicators/MenuItemFactory.qml:583
181181msgid "In queue…"
po/pl.po+7−7
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-07-11 14:59+0200\n"
11−"PO-Revision-Date: 2014-07-19 14:10+0000\n"
12−"Last-Translator: GTriderXC <gtriderxc@yahoo.com>\n"
11+"PO-Revision-Date: 2014-07-31 13:51+0000\n"
12+"Last-Translator: Michał Radwański <radwanski.michal@outlook.com>\n"
1313"Language-Team: Polish <pl@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-07-26 07:17+0000\n"
18−"X-Generator: Launchpad (build 17131)\n"
17+"X-Launchpad-Export-Date: 2014-08-01 07:14+0000\n"
18+"X-Generator: Launchpad (build 17141)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
2121#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -115,7 +115,7 @@ msgstr "Ostatnio używane"
115115
116116#: qml/Dash/GenericScopeView.qml:330
117117msgid "See all"
118−msgstr ""
118+msgstr "Zobacz wszystko"
119119
120120#: qml/Dash/Previews/PreviewActionCombo.qml:34
121121msgid "More..."
@@ -171,11 +171,11 @@ msgstr "Wprowadź kod PIN karty SIM"
171171
172172#: qml/Panel/ActiveCallHint.qml:77
173173msgid "Tap to return to call..."
174−msgstr ""
174+msgstr "Dotknij aby powrócić do połączenia..."
175175
176176#: qml/Panel/ActiveCallHint.qml:90
177177msgid "Conference"
178−msgstr ""
178+msgstr "Konferencja"
179179
180180#: qml/Panel/Indicators/MenuItemFactory.qml:583
181181msgid "In queue…"
po/ru.po+8−8
@@ -8,23 +8,23 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-07-11 14:59+0200\n"
11−"PO-Revision-Date: 2014-07-30 12:49+0000\n"
11+"PO-Revision-Date: 2014-07-31 09:39+0000\n"
1212"Last-Translator: Jay ZDLin <Unknown>\n"
1313"Language-Team: Russian <ru@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-07-31 07:15+0000\n"
18−"X-Generator: Launchpad (build 17131)\n"
17+"X-Launchpad-Export-Date: 2014-08-01 07:14+0000\n"
18+"X-Generator: Launchpad (build 17141)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
2121#: plugins/Unity/Launcher/launcheritem.cpp:73
2222msgid "Pin shortcut"
23−msgstr ""
23+msgstr "Закрепить ярлык"
2424
2525#: plugins/Unity/Launcher/launcheritem.cpp:73
2626msgid "Unpin shortcut"
27−msgstr ""
27+msgstr "Изъять ярлык"
2828
2929#: qml/Components/EdgeDemoOverlay.qml:151
3030msgid "Skip intro"
@@ -174,7 +174,7 @@ msgstr "Введите PIN код"
174174
175175#: qml/Panel/ActiveCallHint.qml:77
176176msgid "Tap to return to call..."
177−msgstr ""
177+msgstr "Коснитесь для возврата к вызову..."
178178
179179#: qml/Panel/ActiveCallHint.qml:90
180180msgid "Conference"
@@ -190,7 +190,7 @@ msgstr "Загрузка"
190190
191191#: qml/Panel/Indicators/MenuItemFactory.qml:589
192192msgid "Paused, tap to resume"
193−msgstr ""
193+msgstr "Приостановлено, коснитесь для возобновления"
194194
195195#: qml/Panel/Indicators/MenuItemFactory.qml:591
196196msgid "Canceled"
@@ -202,7 +202,7 @@ msgstr "Завершено"
202202
203203#: qml/Panel/Indicators/MenuItemFactory.qml:595
204204msgid "Failed, tap to retry"
205−msgstr ""
205+msgstr "Сбой, коснитесь для повтора"
206206
207207#: qml/Panel/SearchIndicator.qml:27
208208msgid "Search"
po/sv.po+6−6
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-07-11 14:59+0200\n"
11−"PO-Revision-Date: 2014-07-25 14:27+0000\n"
12−"Last-Translator: Josef Andersson <Unknown>\n"
11+"PO-Revision-Date: 2014-07-31 08:10+0000\n"
12+"Last-Translator: Peter Hultqvist <phq@silentorbit.com>\n"
1313"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-07-26 07:17+0000\n"
18−"X-Generator: Launchpad (build 17131)\n"
17+"X-Launchpad-Export-Date: 2014-08-01 07:14+0000\n"
18+"X-Generator: Launchpad (build 17141)\n"
1919"Language: sv\n"
2020
2121#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -102,11 +102,11 @@ msgstr "KLAR"
102102
103103#: qml/Components/SeeMore.qml:35
104104msgid "See more"
105−msgstr "Se mer"
105+msgstr "Visa mer"
106106
107107#: qml/Components/SeeMore.qml:58 qml/Dash/GenericScopeView.qml:330
108108msgid "See less"
109−msgstr "Se mindre"
109+msgstr "Visa mindre"
110110
111111#: qml/Dash/DashApps.qml:37
112112msgid "Recent"
qml/Components/PageHeader.qml+24−7
@@ -44,6 +44,12 @@ Item {
4444 signal backClicked()
4545
4646 onScopeStyleChanged: refreshLogo()
47+ onSearchQueryChanged: {
48+ // Make sure we are at the search page if the search query changes behind our feet
49+ if (searchQuery) {
50+ headerContainer.showSearch = true;
51+ }
52+ }
4753
4854 function triggerSearch() {
4955 if (searchEntryEnabled) {
@@ -52,6 +58,12 @@ Item {
5258 }
5359 }
5460
61+ function closePopup() {
62+ if (headerContainer.popover != null) {
63+ PopupUtils.close(headerContainer.popover);
64+ }
65+ }
66+
5567 function resetSearch(keepFocus) {
5668 if (searchHistory) {
5769 searchHistory.addQuery(searchTextField.text);
@@ -60,9 +72,7 @@ Item {
6072 unfocus();
6173 }
6274 searchTextField.text = "";
63− if (headerContainer.popover != null) {
64− PopupUtils.close(headerContainer.popover);
65− }
75+ closePopup();
6676 }
6777
6878 function unfocus() {
@@ -103,9 +113,7 @@ Item {
103113 anchors { fill: parent; margins: units.gu(1); bottomMargin: units.gu(3) + bottomContainer.height }
104114 visible: headerContainer.showSearch
105115 onPressed: {
106− if (headerContainer.popover) {
107− PopupUtils.close(headerContainer.popover);
108− }
116+ closePopup();
109117 if (!searchTextField.text) {
110118 headerContainer.showSearch = false;
111119 }
@@ -172,6 +180,7 @@ Item {
172180 property var contents: TextField {
173181 id: searchTextField
174182 objectName: "searchTextField"
183+ inputMethodHints: Qt.ImhNoPredictiveText
175184 hasClearButton: false
176185 anchors {
177186 fill: parent
@@ -221,6 +230,12 @@ Item {
221230 root.openSearchHistory();
222231 }
223232 }
233+
234+ onTextChanged: {
235+ if (text != "") {
236+ closePopup();
237+ }
238+ }
224239 }
225240 }
226241
@@ -298,6 +313,7 @@ Item {
298313
299314 Repeater {
300315 id: recentSearches
316+ objectName: "recentSearches"
301317 model: searchHistory
302318
303319 delegate: Standard {
@@ -306,7 +322,8 @@ Item {
306322 onClicked: {
307323 searchHistory.addQuery(text);
308324 searchTextField.text = text;
309− PopupUtils.close(popover);
325+ closePopup();
326+ unfocus();
310327 }
311328 }
312329 }
qml/Dash/Apps/CloseIcon.qmldeleted−62
@@ -1,62 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−
19−Item {
20− id: root
21−
22− Image {
23− id: closeIcon
24− anchors.centerIn: parent
25− source: "graphics/close_btn.png"
26−
27− state: (root.enabled) ? "shown" : "hidden"
28−
29− states: [
30− State {
31− name: "shown"
32− PropertyChanges {
33− target: closeIcon
34− height: root.height
35− width: root.width
36− }
37− },
38− State {
39− name: "hidden"
40− PropertyChanges {
41− target: closeIcon
42− height: 0
43− width: 0
44− }
45− }
46−
47− ]
48− transitions: [
49− Transition {
50− to: "shown"
51− NumberAnimation {
52− properties: "width, height"; duration: 300
53− easing { type: Easing.OutBack; overshoot: 5 }
54− }
55− },
56− Transition {
57− to: "hidden"
58− NumberAnimation { properties: "width, height"; duration: 250; }
59− }
60− ]
61− }
62−}
qml/Dash/Apps/RunningApplicationTile.qmldeleted−136
@@ -1,136 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−import Unity.Application 0.1
20−import "../../Components/"
21−import "../../Components/ListItems"
22−
23−AbstractButton {
24− id: root
25− property var application
26−
27− signal requestedApplicationActivation(var application)
28− signal requestedApplicationTermination(var application)
29− signal requestedActivationMode()
30− signal requestedTerminationMode()
31−
32− width: thumbnail.width
33− height: thumbnail.height + labelContainer.height
34−
35− property bool terminationModeEnabled: false
36−
37− onClicked: {
38− if (!terminationModeEnabled)
39− requestedApplicationActivation(application)
40− }
41−
42− onPressAndHold: {
43− if (terminationModeEnabled) {
44− requestedActivationMode()
45− } else {
46− requestedTerminationMode()
47− }
48− }
49−
50− Item {
51− id: thumbnail
52−
53− width: shapedApplicationImage.width
54− height: shapedApplicationImage.height
55−
56− UbuntuShape {
57− id: shapedApplicationImage
58− x: 0
59− y: 0
60− height: applicationImage.height
61− width: applicationImage.width
62− radius: "medium"
63−
64− image: Image {
65− id: applicationImage
66− source: application.screenshot
67− // height : width = ss.height : ss.width
68−
69− property bool isLandscape: sourceSize.width > sourceSize.height
70− property real maxDimension: units.gu(17)
71−
72− width: isLandscape ? Math.min(sourceSize.width, maxDimension) : height * (sourceSize.width / sourceSize.height)
73− height: isLandscape ? width * (sourceSize.height / sourceSize.width) : Math.min(sourceSize.height, maxDimension)
74− fillMode: Image.Stretch
75− }
76−
77− }
78−
79− UbuntuShape {
80− id: borderPressed
81−
82− anchors.fill: shapedApplicationImage
83− radius: "medium"
84− borderSource: "radius_pressed.sci"
85− opacity: root.pressed ? 1.0 : 0.0
86− Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutQuint } }
87− }
88− }
89−
90− // FIXME: label code duplicated with Tile
91− Item {
92− id: labelContainer
93− anchors {
94− left: thumbnail.left
95− right: thumbnail.right
96− top: thumbnail.bottom
97− }
98− height: units.gu(2)
99−
100− Label {
101− id: label
102− anchors {
103− baseline: parent.bottom
104− left: parent.left
105− right: parent.right
106− }
107− text: (application) ? application.name : ""
108−
109− // TODO karni: Update Ubuntu.Components.Themes.Palette and use theme color instead
110− color: "grey"
111− opacity: 0.9
112− fontSize: "small"
113− elide: Text.ElideMiddle
114− horizontalAlignment: Text.AlignHCenter
115− }
116− }
117−
118− CloseIcon {
119− objectName: "closeIcon " + model.name
120− anchors {
121− left: thumbnail.left
122− leftMargin: -units.gu(1)
123− top: thumbnail.top
124− topMargin: -units.gu(1)
125− }
126− height: units.gu(6)
127− width: units.gu(6)
128− id: closeIcon
129− enabled: root.terminationModeEnabled
130−
131− MouseArea {
132− anchors { fill: parent; margins: -units.gu(1) }
133− onClicked: requestedApplicationTermination(application)
134− }
135− }
136−}
qml/Dash/Apps/RunningApplicationsGrid.qmldeleted−105
@@ -1,105 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import "../../Components"
19−
20−import Ubuntu.Gestures 0.1
21−import Unity.Application 0.1
22−
23−ResponsiveFlowView {
24− id: root
25− clip: true
26−
27− signal updateScreenshots
28− property alias enableHeightBehavior: heightBehaviour.enabled
29− property bool enableHeightBehaviorOnNextCreation: model.count === 0
30−
31− Behavior on height {
32− id: heightBehaviour
33− enabled: false
34− NumberAnimation { duration: 200; easing.type: Easing.InOutQuad }
35− }
36−
37− Connections {
38− target: root.model
39− onCountChanged: {
40− heightBehaviour.enabled = true;
41− }
42− }
43−
44− property bool canEnableTerminationMode: true
45−
46− onCanEnableTerminationModeChanged: {
47− if (!canEnableTerminationMode)
48− terminationModeEnabled = false
49− }
50−
51− // when false, it means it's on activation mode
52− property bool terminationModeEnabled: false
53−
54− maximumNumberOfColumns: 10
55− minimumHorizontalSpacing: units.gu(2)
56− referenceDelegateWidth: units.gu(11)
57− verticalSpacing: units.gu(2)
58−
59− delegate: Item {
60− width: runningAppTile.width + root.horizontalSpacing
61− height: runningAppTile.height + root.verticalSpacing
62−
63− RunningApplicationTile {
64− id: runningAppTile
65− objectName: "runningAppTile " + model.name
66− anchors {
67− top: parent.top
68− horizontalCenter: parent.horizontalCenter
69− }
70− application: model
71− onRequestedActivationMode: { root.terminationModeEnabled = false }
72− onRequestedTerminationMode: {
73− if (canEnableTerminationMode)
74− root.terminationModeEnabled = true
75− }
76− onRequestedApplicationTermination: {
77− ApplicationManager.stopApplication(model.appId)
78− }
79− onRequestedApplicationActivation: {
80− ApplicationManager.requestFocusApplication(model.appId)
81− }
82−
83− terminationModeEnabled: root.terminationModeEnabled
84− }
85− }
86−
87− move: Transition {
88− NumberAnimation { properties: "x,y"; duration: 400; easing.type: Easing.OutCubic }
89− }
90−
91− MouseArea {
92− anchors.fill: parent
93− z: -1 // behind all RunningApplicationTiles
94− enabled: root.terminationModeEnabled
95− onPressed: { root.terminationModeEnabled = false; }
96− }
97−
98− PressedOutsideNotifier {
99− anchors.fill: parent
100− enabled: root.terminationModeEnabled
101− onPressedOutside: {
102− root.terminationModeEnabled = false;
103− }
104− }
105−}
qml/Dash/Apps/graphics/close_btn@9.pngdeleted

Binary file not shown.

qml/Dash/CardTool.qml+27−2
@@ -165,15 +165,40 @@ Item {
165165 }
166166 }
167167
168+ Item {
169+ id: attributesModel
170+ property int numOfAttributes: 0
171+ property var model: []
172+ property bool hasAttributes: {
173+ var attributes = components["attributes"];
174+ var hasAttributesFlag = (attributes != undefined) && attributes["field"];
175+
176+ if (hasAttributesFlag) {
177+ if (attributes["max-count"]) {
178+ numOfAttributes = attributes["max-count"];
179+ }
180+ }
181+ return hasAttributesFlag
182+ }
183+
184+ onNumOfAttributesChanged: {
185+ model = []
186+ for (var i = 0; i < numOfAttributes; i++) {
187+ model.push( {"value":"text"+(i+1), "icon":"image://theme/ok" } );
188+ }
189+ }
190+ }
191+
168192 Loader {
169193 id: cardLoader
170− property var fields: ["art", "mascot", "title", "subtitle", "summary"]
194+ property var fields: ["art", "mascot", "title", "subtitle", "summary", "attributes"]
171195 property var maxData: {
172196 "art": Qt.resolvedUrl("graphics/checkers.png"),
173197 "mascot": Qt.resolvedUrl("graphics/checkers.png"),
174198 "title": "—\n—",
175199 "subtitle": "—",
176− "summary": "—\n—\n—\n—\n—"
200+ "summary": "—\n—\n—\n—\n—",
201+ "attributes": attributesModel.model
177202 }
178203 sourceComponent: cardTool.cardComponent
179204 onLoaded: {
qml/Dash/DashApps.qmldeleted−40
@@ -1,40 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−import Utils 0.1
20−import Unity.Application 0.1
21−import "../Components"
22−import "../Components/ListItems"
23−import "Apps"
24−
25−GenericScopeView {
26− id: scopeView
27− objectName: "DashApps"
28−
29− property var runningApps: ApplicationManager
30−
31− QtObject {
32− id: countObject
33− property int count: scopeView.scope.searchQuery.length == 0 ? scopeView.runningApps.count : 0
34− }
35−
36− onScopeChanged: {
37− scopeView.scope.categories.addSpecialCategory("running.apps.category", i18n.tr("Recent"), "", "{ \"template\": { \"category-layout\": \"running-apps\" } }", countObject);
38− enableHeightBehaviorOnNextCreation = scopeView.runningApps.count === 0
39− }
40−}
qml/Dash/DashContent.qml+1−3
@@ -126,9 +126,7 @@ Item {
126126 return (xPositionRelativetoView > -width && xPositionRelativetoView < width) ? 1 : 0
127127 }
128128 asynchronous: true
129− // TODO This if will eventually go away since we're killing DashApps.qml
130− // once we move app closing to the spread
131− source: (scope.id == "clickscope") ? "DashApps.qml" : "GenericScopeView.qml"
129+ source: "GenericScopeView.qml"
132130 objectName: scope.id + " loader"
133131
134132 readonly property bool moving: item ? item.moving : false
qml/Dash/GenericScopeView.qml+1−10
@@ -18,7 +18,6 @@ import QtQuick 2.0
1818import Ubuntu.Components 0.1
1919import Utils 0.1
2020import Unity 0.2
21−import Unity.Application 0.1
2221import Dash 0.1
2322import "../Components"
2423import "../Components/ListItems" as ListItems
@@ -194,7 +193,6 @@ FocusScope {
194193 switch (cardTool.categoryLayout) {
195194 case "carousel": return "CardCarousel.qml";
196195 case "vertical-journal": return "CardVerticalJournal.qml";
197− case "running-apps": return "Apps/RunningApplicationsGrid.qml";
198196 case "grid":
199197 default: return "CardGrid.qml";
200198 }
@@ -205,14 +203,7 @@ FocusScope {
205203 item.enableHeightBehavior = scopeView.enableHeightBehaviorOnNextCreation;
206204 scopeView.enableHeightBehaviorOnNextCreation = false;
207205 }
208− if (source.toString().indexOf("Apps/RunningApplicationsGrid.qml") != -1) {
209− // TODO: this is still a kludge :D Ideally add some kind of hook so that we
210− // can do this from DashApps.qml or think a better way that needs no special casing
211− item.model = Qt.binding(function() { return runningApps; })
212− item.canEnableTerminationMode = Qt.binding(function() { return scopeView.isCurrent })
213− } else {
214− item.model = Qt.binding(function() { return results })
215− }
206+ item.model = Qt.binding(function() { return results })
216207 item.objectName = Qt.binding(function() { return categoryId })
217208 item.scopeStyle = scopeView.scopeStyle;
218209 if (baseItem.expandable) {
qml/Launcher/LauncherDelegate.qml+9−6
@@ -70,28 +70,31 @@ Item {
7070 height: root.itemHeight - units.gu(1)
7171 }
7272
73− BorderImage {
73+ UbuntuShape {
7474 objectName: "countEmblem"
7575 anchors {
7676 right: parent.right
7777 top: parent.top
7878 margins: units.dp(3)
7979 }
80− width: Math.min(root.itemWidth, Math.max(units.gu(3), countLabel.implicitWidth + units.gu(2.5)))
81− height: units.gu(3)
82− source: "graphics/notification.sci"
80+ width: Math.min(root.itemWidth, Math.max(units.gu(2), countLabel.implicitWidth + units.gu(1)))
81+ height: units.gu(2)
82+ color: UbuntuColors.orange
8383 visible: root.count > 0
84+ borderSource: "none"
8485
8586 Label {
8687 id: countLabel
8788 text: root.count
8889 anchors.centerIn: parent
90+ // FIXME: verticalCenter seems to be off wee bit and QML doesn't have a centerLine
91+ // property for Text: https://bugreports.qt-project.org/browse/QTBUG-40479
92+ anchors.verticalCenterOffset: -units.dp(.5)
8993 width: root.itemWidth - units.gu(1)
9094 horizontalAlignment: Text.AlignHCenter
9195 elide: Text.ElideRight
9296 color: "white"
93− fontSize: "small"
94− font.bold: true
97+ fontSize: "x-small"
9598 }
9699 }
97100
qml/Launcher/graphics/notification@30.pngdeleted

Binary file not shown.

qml/Launcher/graphics/notification@30.scideleted−5
@@ -1,5 +0,0 @@
1−border.left: 36
2−border.top: 36
3−border.bottom: 36
4−border.right: 36
5−source: notification@30.png
qml/Panel/Indicators/DefaultIndicatorPage.qml+1−1
@@ -126,7 +126,7 @@ IndicatorBase {
126126 id: loader
127127 objectName: "menuItem" + index
128128 asynchronous: false
129− visible: height > 0
129+ visible: status == Loader.Ready
130130
131131 property int modelIndex: index
132132
tests/autopilot/unity8/shell/emulators/dash.py−4
@@ -188,10 +188,6 @@ class GenericScopeView(emulators.UnityEmulatorBase):
188188 raise emulators.UnityEmulatorException(
189189 'No category found with name {}'.format(category))
190190
191−
192−class DashApps(GenericScopeView):
193− """Autopilot emulator for the applications scope."""
194−
195191 def get_applications(self, category):
196192 """Return the list of applications on a category.
197193
tests/autopilot/unity8/shell/tests/test_emulators.py+1−1
@@ -142,7 +142,7 @@ class DashEmulatorTestCase(DashBaseTestCase):
142142 scope_id = 'clickscope'
143143 scope = self.dash.open_scope(scope_id)
144144 self._assert_scope_is_opened(scope, scope_id)
145− self.assertIsInstance(scope, dash_emulators.DashApps)
145+ self.assertIsInstance(scope, dash_emulators.GenericScopeView)
146146
147147
148148class GenericScopeViewEmulatorTestCase(DashBaseTestCase):
tests/mocks/Unity/CMakeLists.txt+1−1
@@ -6,7 +6,7 @@ add_subdirectory(Notifications)
66pkg_search_module(DEE dee-1.0 REQUIRED)
77pkg_search_module(GOBJECT gobject-2.0 REQUIRED)
88pkg_search_module(DEEQT libdee-qt5 REQUIRED)
9−pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=2)
9+pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=3)
1010
1111include_directories(
1212 ${CMAKE_CURRENT_BINARY_DIR}
tests/mocks/Unity/fake_scopes.cpp+5
@@ -125,6 +125,11 @@ bool Scopes::loaded() const
125125 return m_loaded;
126126}
127127
128+unity::shell::scopes::ScopeInterface* Scopes::overviewScope() const
129+{
130+ return nullptr;
131+}
132+
128133void Scopes::addScope(Scope* scope)
129134{
130135 int index = rowCount();
tests/mocks/Unity/fake_scopes.h+1
@@ -50,6 +50,7 @@ public:
5050 QModelIndex parent ( const QModelIndex & index ) const;
5151
5252 bool loaded() const override;
53+ unity::shell::scopes::ScopeInterface* overviewScope() const override;
5354
5455private Q_SLOTS:
5556 void updateScopes();
tests/plugins/Dash/CMakeLists.txt+1
@@ -79,3 +79,4 @@ set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
7979set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
8080add_qml_test(. ScopeStyle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
8181add_qml_test(. ListViewWithPageHeaderQML IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
82+add_qml_test(. CardAttributes IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
tests/plugins/Dash/cardcreator/1.res+63−61
@@ -5,8 +5,8 @@ AbstractButton {
55 property var cardData;
66 property var artShapeBorderSource: undefined;
77 property real fontScale: 1.0;
8− property var scopeStyle: null;
9− property int headerAlignment: Text.AlignLeft;
8+ property var scopeStyle: null;
9+ property int headerAlignment: Text.AlignLeft;
1010 property int fixedHeaderHeight: -1;
1111 property size fixedArtShapeSize: Qt.size(-1, -1);
1212 readonly property string title: cardData && cardData["title"] || "";
@@ -16,71 +16,73 @@ AbstractButton {
1616onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
1717readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
1818Item {
19− id: artShapeHolder;
20− height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height;
21− width: root.fixedArtShapeSize.width > 0 ? root.fixedArtShapeSize.width : artShapeLoader.width;
22− anchors { horizontalCenter: parent.horizontalCenter; }
23− Loader {
24− id: artShapeLoader;
25− objectName: "artShapeLoader";
26− active: cardData && cardData["art"] || false;
27− asynchronous: root.asynchronous;
28− visible: status == Loader.Ready;
29− sourceComponent: UbuntuShape {
30− id: artShape;
31− objectName: "artShape";
32− radius: "medium";
33− visible: image.status == Image.Ready;
34− readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
35− readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
36− readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
37− Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
38− onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
39− Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
40− function updateWidthHeightBindings() {
41− if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
42− width = root.fixedArtShapeSize.width;
43− height = root.fixedArtShapeSize.height;
44− } else if (aspectSmallerThanImageAspect) {
45− width = Qt.binding(function() { return !visible ? 0 : image.width });
46− height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
47− } else {
48− width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
49− height = Qt.binding(function() { return !visible ? 0 : image.height });
50− }
51− }
52− image: Image {
53− objectName: "artImage";
54− source: cardData && cardData["art"] || "";
55− cache: true;
19+ id: artShapeHolder;
20+ height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height;
21+ width: root.fixedArtShapeSize.width > 0 ? root.fixedArtShapeSize.width : artShapeLoader.width;
22+ anchors { horizontalCenter: parent.horizontalCenter; }
23+ Loader {
24+ id: artShapeLoader;
25+ objectName: "artShapeLoader";
26+ active: cardData && cardData["art"] || false;
5627 asynchronous: root.asynchronous;
57− fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
58− readonly property real aspect: implicitWidth / implicitHeight;
59− width: root.width;
60− height: width / artShape.aspect;
28+ visible: status == Loader.Ready;
29+ sourceComponent: UbuntuShape {
30+ id: artShape;
31+ objectName: "artShape";
32+ radius: "medium";
33+ visible: image.status == Image.Ready;
34+ readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
35+ readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
36+ readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
37+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
38+ onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
39+ Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
40+ function updateWidthHeightBindings() {
41+ if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
42+ width = root.fixedArtShapeSize.width;
43+ height = root.fixedArtShapeSize.height;
44+ } else if (aspectSmallerThanImageAspect) {
45+ width = Qt.binding(function() { return !visible ? 0 : image.width });
46+ height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
47+ } else {
48+ width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
49+ height = Qt.binding(function() { return !visible ? 0 : image.height });
50+ }
51+ }
52+ image: Image {
53+ objectName: "artImage";
54+ source: cardData && cardData["art"] || "";
55+ cache: true;
56+ asynchronous: root.asynchronous;
57+ fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
58+ readonly property real aspect: implicitWidth / implicitHeight;
59+ width: root.width;
60+ height: width / artShape.aspect;
61+ }
62+ }
6163 }
62− }
63− }
64− }
64+ }
6565readonly property int headerHeight: titleLabel.height;
6666Label {
67− id: titleLabel;
68− objectName: "titleLabel";
69− anchors { right: parent.right;left: parent.left;
67+ id: titleLabel;
68+ objectName: "titleLabel";
69+ anchors { right: parent.right;
70+rightMargin: units.gu(1);
71+left: parent.left;
7072top: artShapeHolder.bottom;
7173 topMargin: units.gu(1);
7274leftMargin: units.gu(1);
73− }
74− elide: Text.ElideRight;
75− fontSize: "small";
76− wrapMode: Text.Wrap;
77− maximumLineCount: 2;
78− font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
79− color: root.scopeStyle ? root.scopeStyle.foreground : "grey";
80− visible: showHeader ;
81− text: root.title;
82− font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
83− horizontalAlignment: root.headerAlignment;
84− }
75+ }
76+ elide: Text.ElideRight;
77+ fontSize: "small";
78+ wrapMode: Text.Wrap;
79+ maximumLineCount: 2;
80+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
81+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey";
82+ visible: showHeader ;
83+ text: root.title;
84+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
85+ horizontalAlignment: root.headerAlignment;
86+ }
8587implicitHeight: titleLabel.y + titleLabel.height + units.gu(1);
8688}
tests/plugins/Dash/cardcreator/2.res+94−91
@@ -5,8 +5,8 @@ AbstractButton {
55 property var cardData;
66 property var artShapeBorderSource: undefined;
77 property real fontScale: 1.0;
8− property var scopeStyle: null;
9− property int headerAlignment: Text.AlignLeft;
8+ property var scopeStyle: null;
9+ property int headerAlignment: Text.AlignLeft;
1010 property int fixedHeaderHeight: -1;
1111 property size fixedArtShapeSize: Qt.size(-1, -1);
1212 readonly property string title: cardData && cardData["title"] || "";
@@ -14,104 +14,107 @@ AbstractButton {
1414 property bool showHeader: true;
1515 implicitWidth: childrenRect.width;
1616Loader {
17− id: backgroundLoader;
18− objectName: "backgroundLoader";
19− anchors.fill: parent;
20− asynchronous: root.asynchronous;
21− visible: status == Loader.Ready;
22− sourceComponent: UbuntuShape {
23− objectName: "background";
24− radius: "medium";
25− color: getColor(0) || "white";
26− gradientColor: getColor(1) || color;
27− anchors.fill: parent;
28− image: backgroundImage.source ? backgroundImage : null;
29− property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
30− property Image backgroundImage: Image {
31− objectName: "backgroundImage";
32− source: {
33− if (cardData && typeof cardData["background"] === "string") return cardData["background"];
34− else if (template && typeof template["card-background"] === "string") return template["card-background"];
35− else return "";
36− }
37− }
38− function getColor(index) {
39− if (cardData && typeof cardData["background"] === "object"
40− && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
41− return cardData["background"]["elements"][index];
42− } else if (template && typeof template["card-background"] === "object"
43− && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
44− return template["card-background"]["elements"][index];
45− } else return undefined;
46− }
47− }
48− }
17+ id: backgroundLoader;
18+ objectName: "backgroundLoader";
19+ anchors.fill: parent;
20+ asynchronous: root.asynchronous;
21+ visible: status == Loader.Ready;
22+ sourceComponent: UbuntuShape {
23+ objectName: "background";
24+ radius: "medium";
25+ color: getColor(0) || "white";
26+ gradientColor: getColor(1) || color;
27+ anchors.fill: parent;
28+ image: backgroundImage.source ? backgroundImage : null;
29+ property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
30+ property Image backgroundImage: Image {
31+ objectName: "backgroundImage";
32+ source: {
33+ if (cardData && typeof cardData["background"] === "string") return cardData["background"];
34+ else if (template && typeof template["card-background"] === "string") return template["card-background"];
35+ else return "";
36+ }
37+ }
38+ function getColor(index) {
39+ if (cardData && typeof cardData["background"] === "object"
40+ && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
41+ return cardData["background"]["elements"][index];
42+ } else if (template && typeof template["card-background"] === "object"
43+ && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
44+ return template["card-background"]["elements"][index];
45+ } else return undefined;
46+ }
47+ }
48+ }
4949readonly property size artShapeSize: Qt.size(-1, -1);
5050readonly property int headerHeight: row.height;
5151Row {
52− id: row;
53− objectName: "outerRow";
54− property real margins: units.gu(1);
55− spacing: margins;
56− height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight;
57− anchors { top: parent.top;
52+ id: row;
53+ objectName: "outerRow";
54+ property real margins: units.gu(1);
55+ spacing: margins;
56+ height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight;
57+ anchors { top: parent.top;
5858 topMargin: units.gu(1);
59− left: parent.left;
60−}
61− anchors.right: parent.right;
62− anchors.margins: margins;
63−data: [ Image {
64− id: mascotImage;
65− objectName: "mascotImage";
66− anchors { verticalCenter: parent.verticalCenter; }
67− readonly property int maxSize: Math.max(width, height) * 4;
68− source: cardData && cardData["mascot"];
69− width: units.gu(6);
70− height: units.gu(5.625);
71− sourceSize { width: maxSize; height: maxSize }
72− fillMode: Image.PreserveAspectCrop;
73− horizontalAlignment: Image.AlignHCenter;
74− verticalAlignment: Image.AlignVCenter;
75− visible: showHeader;
76− }
77−,
78−Column {
79− anchors.verticalCenter: parent.verticalCenter;
80− spacing: units.dp(2);
81− width: parent.width - x;
82− data: [ Label {
83− id: titleLabel;
84− objectName: "titleLabel";
85− anchors { left: parent.left; right: parent.right }
86− elide: Text.ElideRight;
87− fontSize: "small";
88− wrapMode: Text.Wrap;
89− maximumLineCount: 2;
90− font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
91− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
92− visible: showHeader ;
93− text: root.title;
94− font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
95− horizontalAlignment: root.headerAlignment;
96− }
97−,
98−Label {
99− id: subtitleLabel;
100− objectName: "subtitleLabel";
101− anchors { left: parent.left; right: parent.right }
59+left: parent.left;
60+ }
61+ anchors.right: parent.right;
62+ anchors.margins: margins;
63+ data: [ Image {
64+ id: mascotImage;
65+ objectName: "mascotImage";
66+ anchors { verticalCenter: parent.verticalCenter; }
67+ readonly property int maxSize: Math.max(width, height) * 4;
68+ source: cardData && cardData["mascot"];
69+ width: units.gu(6);
70+ height: units.gu(5.625);
71+ sourceSize { width: maxSize; height: maxSize }
72+ fillMode: Image.PreserveAspectCrop;
73+ horizontalAlignment: Image.AlignHCenter;
74+ verticalAlignment: Image.AlignVCenter;
75+ visible: showHeader;
76+ }
10277
78+ ,
79+ Column {
80+ anchors.verticalCenter: parent.verticalCenter;
81+ spacing: units.dp(2);
82+ width: parent.width - x;
83+ data: [ Label {
84+ id: titleLabel;
85+ objectName: "titleLabel";
86+ anchors { left: parent.left; right: parent.right }
10387 elide: Text.ElideRight;
10488 fontSize: "small";
89+ wrapMode: Text.Wrap;
90+ maximumLineCount: 2;
10591 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
106− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
107− visible: titleLabel.visible && titleLabel.text;
108− text: cardData && cardData["subtitle"] || "";
109− font.weight: Font.Light;
92+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
93+ visible: showHeader ;
94+ text: root.title;
95+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
11096 horizontalAlignment: root.headerAlignment;
11197 }
112−]
113−}
114−]
115−}
98+
99+ ,
100+ Label {
101+ id: subtitleLabel;
102+ objectName: "subtitleLabel";
103+ anchors { left: parent.left; right: parent.right }
104+ elide: Text.ElideRight;
105+ fontSize: "small";
106+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
107+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
108+ visible: titleLabel.visible && titleLabel.text;
109+ text: cardData && cardData["subtitle"] || "";
110+ font.weight: Font.Light;
111+ horizontalAlignment: root.headerAlignment;
112+ }
113+
114+ ]
115+ }
116+
117+ ]
118+ }
116119implicitHeight: row.y + row.height + units.gu(1);
117120}
tests/plugins/Dash/cardcreator/3.res+77−74
@@ -5,8 +5,8 @@ AbstractButton {
55 property var cardData;
66 property var artShapeBorderSource: undefined;
77 property real fontScale: 1.0;
8− property var scopeStyle: null;
9− property int headerAlignment: Text.AlignLeft;
8+ property var scopeStyle: null;
9+ property int headerAlignment: Text.AlignLeft;
1010 property int fixedHeaderHeight: -1;
1111 property size fixedArtShapeSize: Qt.size(-1, -1);
1212 readonly property string title: cardData && cardData["title"] || "";
@@ -16,89 +16,92 @@ AbstractButton {
1616onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
1717readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
1818Item {
19− id: artShapeHolder;
20− height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height;
21− width: root.fixedArtShapeSize.width > 0 ? root.fixedArtShapeSize.width : artShapeLoader.width;
22− anchors { horizontalCenter: parent.horizontalCenter; }
23− Loader {
24− id: artShapeLoader;
25− objectName: "artShapeLoader";
26− active: cardData && cardData["art"] || false;
27− asynchronous: root.asynchronous;
28− visible: status == Loader.Ready;
29− sourceComponent: UbuntuShape {
30− id: artShape;
31− objectName: "artShape";
32− radius: "medium";
33− visible: image.status == Image.Ready;
34− readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
35− readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
36− readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
37− Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
38− onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
39− Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
40− function updateWidthHeightBindings() {
41− if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
42− width = root.fixedArtShapeSize.width;
43− height = root.fixedArtShapeSize.height;
44− } else if (aspectSmallerThanImageAspect) {
45− width = Qt.binding(function() { return !visible ? 0 : image.width });
46− height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
47− } else {
48− width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
49− height = Qt.binding(function() { return !visible ? 0 : image.height });
50− }
51− }
52− image: Image {
53− objectName: "artImage";
54− source: cardData && cardData["art"] || "";
55− cache: true;
19+ id: artShapeHolder;
20+ height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height;
21+ width: root.fixedArtShapeSize.width > 0 ? root.fixedArtShapeSize.width : artShapeLoader.width;
22+ anchors { horizontalCenter: parent.horizontalCenter; }
23+ Loader {
24+ id: artShapeLoader;
25+ objectName: "artShapeLoader";
26+ active: cardData && cardData["art"] || false;
5627 asynchronous: root.asynchronous;
57− fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
58− readonly property real aspect: implicitWidth / implicitHeight;
59− width: root.width;
60− height: width / artShape.aspect;
28+ visible: status == Loader.Ready;
29+ sourceComponent: UbuntuShape {
30+ id: artShape;
31+ objectName: "artShape";
32+ radius: "medium";
33+ visible: image.status == Image.Ready;
34+ readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
35+ readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
36+ readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
37+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
38+ onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
39+ Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
40+ function updateWidthHeightBindings() {
41+ if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
42+ width = root.fixedArtShapeSize.width;
43+ height = root.fixedArtShapeSize.height;
44+ } else if (aspectSmallerThanImageAspect) {
45+ width = Qt.binding(function() { return !visible ? 0 : image.width });
46+ height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
47+ } else {
48+ width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
49+ height = Qt.binding(function() { return !visible ? 0 : image.height });
50+ }
51+ }
52+ image: Image {
53+ objectName: "artImage";
54+ source: cardData && cardData["art"] || "";
55+ cache: true;
56+ asynchronous: root.asynchronous;
57+ fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
58+ readonly property real aspect: implicitWidth / implicitHeight;
59+ width: root.width;
60+ height: width / artShape.aspect;
61+ }
62+ }
6163 }
62− }
63− }
64− }
64+ }
6565readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
6666Label {
67− id: titleLabel;
68− objectName: "titleLabel";
69− anchors { right: parent.right;left: parent.left;
67+ id: titleLabel;
68+ objectName: "titleLabel";
69+ anchors { right: parent.right;
70+rightMargin: units.gu(1);
71+left: parent.left;
7072top: artShapeHolder.bottom;
7173 topMargin: units.gu(1);
7274leftMargin: units.gu(1);
73−}
74− elide: Text.ElideRight;
75− fontSize: "small";
76− wrapMode: Text.Wrap;
77− maximumLineCount: 2;
78− font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
79− color: root.scopeStyle ? root.scopeStyle.foreground : "grey";
80− visible: showHeader ;
81− text: root.title;
82− font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
83− horizontalAlignment: root.headerAlignment;
84− }
85−Label {
86− id: subtitleLabel;
87− objectName: "subtitleLabel";
88− anchors { left: titleLabel.left;
89− leftMargin: titleLabel.leftMargin;
90− right: titleLabel.right;
91− top: titleLabel.bottom;
92− topMargin: units.dp(2);
93− }
75+ }
9476 elide: Text.ElideRight;
9577 fontSize: "small";
78+ wrapMode: Text.Wrap;
79+ maximumLineCount: 2;
9680 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
97− color: root.scopeStyle ? root.scopeStyle.foreground : "grey";
98− visible: titleLabel.visible && titleLabel.text;
99− text: cardData && cardData["subtitle"] || "";
100− font.weight: Font.Light;
81+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey";
82+ visible: showHeader ;
83+ text: root.title;
84+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
10185 horizontalAlignment: root.headerAlignment;
10286 }
87+Label {
88+ id: subtitleLabel;
89+ objectName: "subtitleLabel";
90+ anchors { left: titleLabel.left;
91+ leftMargin: titleLabel.leftMargin;
92+ right: titleLabel.right;
93+ rightMargin: titleLabel.rightMargin;
94+ top: titleLabel.bottom;
95+ topMargin: units.dp(2);
96+ }
97+ elide: Text.ElideRight;
98+ fontSize: "small";
99+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
100+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey";
101+ visible: titleLabel.visible && titleLabel.text;
102+ text: cardData && cardData["subtitle"] || "";
103+ font.weight: Font.Light;
104+ horizontalAlignment: root.headerAlignment;
105+ }
103106implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);
104107}
tests/plugins/Dash/cardcreator/5.res+119−117
@@ -4,9 +4,9 @@ AbstractButton {
44 property var components;
55 property var cardData;
66 property var artShapeBorderSource: undefined;
7− property real fontScale: 1.0;
8− property var scopeStyle: null;
9− property int headerAlignment: Text.AlignLeft;
7+ property real fontScale: 1.0;
8+ property var scopeStyle: null;
9+ property int headerAlignment: Text.AlignLeft;
1010 property int fixedHeaderHeight: -1;
1111 property size fixedArtShapeSize: Qt.size(-1, -1);
1212 readonly property string title: cardData && cardData["title"] || "";
@@ -16,130 +16,132 @@ AbstractButton {
1616onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
1717readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
1818Item {
19− id: artShapeHolder;
20− height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height;
21− width: root.fixedArtShapeSize.width > 0 ? root.fixedArtShapeSize.width : artShapeLoader.width;
22− anchors { horizontalCenter: parent.horizontalCenter; }
23− Loader {
24− id: artShapeLoader;
25− objectName: "artShapeLoader";
26− active: cardData && cardData["art"] || false;
27− asynchronous: root.asynchronous;
28− visible: status == Loader.Ready;
29− sourceComponent: UbuntuShape {
30− id: artShape;
31− objectName: "artShape";
32− radius: "medium";
33− visible: image.status == Image.Ready;
34− readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
35− readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
36− readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
37− Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
38− onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
39− Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
40− function updateWidthHeightBindings() {
41− if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
42− width = root.fixedArtShapeSize.width;
43− height = root.fixedArtShapeSize.height;
44− } else if (aspectSmallerThanImageAspect) {
45− width = Qt.binding(function() { return !visible ? 0 : image.width });
46− height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
47− } else {
48− width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
49− height = Qt.binding(function() { return !visible ? 0 : image.height });
50− }
51− }
52− image: Image {
53− objectName: "artImage";
54− source: cardData && cardData["art"] || "";
55− cache: true;
19+ id: artShapeHolder;
20+ height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height;
21+ width: root.fixedArtShapeSize.width > 0 ? root.fixedArtShapeSize.width : artShapeLoader.width;
22+ anchors { horizontalCenter: parent.horizontalCenter; }
23+ Loader {
24+ id: artShapeLoader;
25+ objectName: "artShapeLoader";
26+ active: cardData && cardData["art"] || false;
5627 asynchronous: root.asynchronous;
57− fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
58− readonly property real aspect: implicitWidth / implicitHeight;
59− width: root.width;
60− height: width / artShape.aspect;
28+ visible: status == Loader.Ready;
29+ sourceComponent: UbuntuShape {
30+ id: artShape;
31+ objectName: "artShape";
32+ radius: "medium";
33+ visible: image.status == Image.Ready;
34+ readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
35+ readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
36+ readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
37+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
38+ onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
39+ Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
40+ function updateWidthHeightBindings() {
41+ if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
42+ width = root.fixedArtShapeSize.width;
43+ height = root.fixedArtShapeSize.height;
44+ } else if (aspectSmallerThanImageAspect) {
45+ width = Qt.binding(function() { return !visible ? 0 : image.width });
46+ height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
47+ } else {
48+ width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
49+ height = Qt.binding(function() { return !visible ? 0 : image.height });
50+ }
51+ }
52+ image: Image {
53+ objectName: "artImage";
54+ source: cardData && cardData["art"] || "";
55+ cache: true;
56+ asynchronous: root.asynchronous;
57+ fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
58+ readonly property real aspect: implicitWidth / implicitHeight;
59+ width: root.width;
60+ height: width / artShape.aspect;
61+ }
62+ }
6163 }
62− }
63− }
64− }
64+ }
6565Loader {
66− id: overlayLoader;
67− anchors {
68− left: artShapeHolder.left;
69− right: artShapeHolder.right;
70− bottom: artShapeHolder.bottom;
71− }
72− active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false;
73− asynchronous: root.asynchronous;
74− visible: showHeader && status == Loader.Ready;
75− sourceComponent: ShaderEffect {
76− id: overlay;
77− height: (fixedHeaderHeight > 0 ? fixedHeaderHeight : headerHeight) + units.gu(2);
78− opacity: 0.6;
79− property var source: ShaderEffectSource {
80− id: shaderSource;
81− sourceItem: artShapeLoader.item;
82− onVisibleChanged: if (visible) scheduleUpdate();
83− live: false;
84− sourceRect: Qt.rect(0, artShapeLoader.height - overlay.height, artShapeLoader.width, overlay.height);
85− }
86− vertexShader: "
87− uniform highp mat4 qt_Matrix;
88− attribute highp vec4 qt_Vertex;
89− attribute highp vec2 qt_MultiTexCoord0;
90− varying highp vec2 coord;
91− void main() {
92− coord = qt_MultiTexCoord0;
93− gl_Position = qt_Matrix * qt_Vertex;
94− }";
95− fragmentShader: "
96− varying highp vec2 coord;
97− uniform sampler2D source;
98− uniform lowp float qt_Opacity;
99− void main() {
100− lowp vec4 tex = texture2D(source, coord);
101− gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity;
102− }";
103− }
104− }
66+ id: overlayLoader;
67+ anchors {
68+ left: artShapeHolder.left;
69+ right: artShapeHolder.right;
70+ bottom: artShapeHolder.bottom;
71+ }
72+ active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false;
73+ asynchronous: root.asynchronous;
74+ visible: showHeader && status == Loader.Ready;
75+ sourceComponent: ShaderEffect {
76+ id: overlay;
77+ height: (fixedHeaderHeight > 0 ? fixedHeaderHeight : headerHeight) + units.gu(2);
78+ opacity: 0.6;
79+ property var source: ShaderEffectSource {
80+ id: shaderSource;
81+ sourceItem: artShapeLoader.item;
82+ onVisibleChanged: if (visible) scheduleUpdate();
83+ live: false;
84+ sourceRect: Qt.rect(0, artShapeLoader.height - overlay.height, artShapeLoader.width, overlay.height);
85+ }
86+ vertexShader: "
87+ uniform highp mat4 qt_Matrix;
88+ attribute highp vec4 qt_Vertex;
89+ attribute highp vec2 qt_MultiTexCoord0;
90+ varying highp vec2 coord;
91+ void main() {
92+ coord = qt_MultiTexCoord0;
93+ gl_Position = qt_Matrix * qt_Vertex;
94+ }";
95+ fragmentShader: "
96+ varying highp vec2 coord;
97+ uniform sampler2D source;
98+ uniform lowp float qt_Opacity;
99+ void main() {
100+ lowp vec4 tex = texture2D(source, coord);
101+ gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity;
102+ }";
103+ }
104+ }
105105readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
106106Label {
107− id: titleLabel;
108− objectName: "titleLabel";
109− anchors { left: parent.left;
110− leftMargin: units.gu(1);
111− right: parent.right;
112− top: overlayLoader.top;
113− topMargin: units.gu(1);
114−}
115− elide: Text.ElideRight;
116− fontSize: "small";
117− wrapMode: Text.Wrap;
118− maximumLineCount: 2;
119− font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
120− color: "white";
121− visible: showHeader && overlayLoader.active;
122− text: root.title;
123− font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
124− horizontalAlignment: root.headerAlignment;
125− }
126−Label {
127− id: subtitleLabel;
128− objectName: "subtitleLabel";
129− anchors { left: titleLabel.left;
130− leftMargin: titleLabel.leftMargin;
131− right: titleLabel.right;
132− top: titleLabel.bottom;
133− topMargin: units.dp(2);
134− }
107+ id: titleLabel;
108+ objectName: "titleLabel";
109+ anchors { left: parent.left;
110+ leftMargin: units.gu(1);
111+ right: parent.right;
112+ rightMargin: units.gu(1);
113+ top: overlayLoader.top;
114+ topMargin: units.gu(1);
115+ }
135116 elide: Text.ElideRight;
136117 fontSize: "small";
118+ wrapMode: Text.Wrap;
119+ maximumLineCount: 2;
137120 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
138121 color: "white";
139− visible: titleLabel.visible && titleLabel.text;
140− text: cardData && cardData["subtitle"] || "";
141− font.weight: Font.Light;
122+ visible: showHeader && overlayLoader.active;
123+ text: root.title;
124+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
142125 horizontalAlignment: root.headerAlignment;
143126 }
127+Label {
128+ id: subtitleLabel;
129+ objectName: "subtitleLabel";
130+ anchors { left: titleLabel.left;
131+ leftMargin: titleLabel.leftMargin;
132+ right: titleLabel.right;
133+ rightMargin: titleLabel.rightMargin;
134+ top: titleLabel.bottom;
135+ topMargin: units.dp(2);
136+ }
137+ elide: Text.ElideRight;
138+ fontSize: "small";
139+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
140+ color: "white";
141+ visible: titleLabel.visible && titleLabel.text;
142+ text: cardData && cardData["subtitle"] || "";
143+ font.weight: Font.Light;
144+ horizontalAlignment: root.headerAlignment;
145+ }
144146implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);
145147}
tests/plugins/Dash/cardcreator/7.resnew file+128
@@ -0,0 +1,128 @@
1+AbstractButton {
2+ id: root;
3+ property var template;
4+ property var components;
5+ property var cardData;
6+ property var artShapeBorderSource: undefined;
7+ property real fontScale: 1.0;
8+ property var scopeStyle: null;
9+ property int headerAlignment: Text.AlignLeft;
10+ property int fixedHeaderHeight: -1;
11+ property size fixedArtShapeSize: Qt.size(-1, -1);
12+ readonly property string title: cardData && cardData["title"] || "";
13+ property bool asynchronous: true;
14+ property bool showHeader: true;
15+ implicitWidth: childrenRect.width;
16+Loader {
17+ id: backgroundLoader;
18+ objectName: "backgroundLoader";
19+ anchors.fill: parent;
20+ asynchronous: root.asynchronous;
21+ visible: status == Loader.Ready;
22+ sourceComponent: UbuntuShape {
23+ objectName: "background";
24+ radius: "medium";
25+ color: getColor(0) || "white";
26+ gradientColor: getColor(1) || color;
27+ anchors.fill: parent;
28+ image: backgroundImage.source ? backgroundImage : null;
29+ property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
30+ property Image backgroundImage: Image {
31+ objectName: "backgroundImage";
32+ source: {
33+ if (cardData && typeof cardData["background"] === "string") return cardData["background"];
34+ else if (template && typeof template["card-background"] === "string") return template["card-background"];
35+ else return "";
36+ }
37+ }
38+ function getColor(index) {
39+ if (cardData && typeof cardData["background"] === "object"
40+ && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
41+ return cardData["background"]["elements"][index];
42+ } else if (template && typeof template["card-background"] === "object"
43+ && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
44+ return template["card-background"]["elements"][index];
45+ } else return undefined;
46+ }
47+ }
48+ }
49+readonly property size artShapeSize: Qt.size(-1, -1);
50+readonly property int headerHeight: row.height;
51+Row {
52+ id: row;
53+ objectName: "outerRow";
54+ property real margins: units.gu(1);
55+ spacing: margins;
56+ height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight;
57+ anchors { top: parent.top;
58+ topMargin: units.gu(1);
59+left: parent.left;
60+ }
61+ anchors.right: parent.right;
62+ anchors.margins: margins;
63+ data: [ Image {
64+ id: mascotImage;
65+ objectName: "mascotImage";
66+ anchors { verticalCenter: parent.verticalCenter; }
67+ readonly property int maxSize: Math.max(width, height) * 4;
68+ source: cardData && cardData["mascot"];
69+ width: units.gu(6);
70+ height: units.gu(5.625);
71+ sourceSize { width: maxSize; height: maxSize }
72+ fillMode: Image.PreserveAspectCrop;
73+ horizontalAlignment: Image.AlignHCenter;
74+ verticalAlignment: Image.AlignVCenter;
75+ visible: showHeader;
76+ }
77+
78+ ,
79+ Column {
80+ anchors.verticalCenter: parent.verticalCenter;
81+ spacing: units.dp(2);
82+ width: parent.width - x;
83+ data: [
84+ Label {
85+ id: titleLabel;
86+ objectName: "titleLabel";
87+ anchors { left: parent.left; right: parent.right }
88+ elide: Text.ElideRight;
89+ fontSize: "small";
90+ wrapMode: Text.Wrap;
91+ maximumLineCount: 2;
92+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
93+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
94+ visible: showHeader ;
95+ text: root.title;
96+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
97+ horizontalAlignment: root.headerAlignment;
98+ }
99+,
100+Label {
101+ id: subtitleLabel;
102+ objectName: "subtitleLabel";
103+ anchors { left: parent.left; right: parent.right }
104+ elide: Text.ElideRight;
105+ fontSize: "small";
106+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
107+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
108+ visible: titleLabel.visible && titleLabel.text;
109+ text: cardData && cardData["subtitle"] || "";
110+ font.weight: Font.Light;
111+ horizontalAlignment: root.headerAlignment;
112+ }
113+,
114+CardAttributes {
115+ id: attributesRow;
116+ objectName: "attributesRow";
117+ anchors { left: parent.left; right: parent.right }
118+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
119+ model: cardData["attributes"]
120+ }
121+
122+ ]
123+ }
124+
125+ ]
126+ }
127+implicitHeight: row.y + row.height + units.gu(1);
128+}
tests/plugins/Dash/cardcreator/7.tstnew file+3
@@ -0,0 +1,3 @@
1+template: {"card-background":{"elements":["#E9E9E9"],"type":"color"},"card-layout":"vertical","card-size":"medium","category-layout":"grid","collapsed-rows":2}
2+components: {"art":{"aspect-ratio":1,"fill-mode":"crop"},"background":{"field":"background"},"mascot":{"field":"icon"},"subtitle":{"field":"author"},"title":{"field":"title"},"attributes":{"field":"attributes","max-count":2}}
3+result: 7.res
tests/plugins/Dash/cardcreatortest.cpp+9−1
@@ -19,6 +19,7 @@
1919#include <QQuickItem>
2020#include <QQuickView>
2121#include <QtTestGui>
22+#include <QTemporaryFile>
2223
2324class CardCreatorTest : public QObject
2425{
@@ -74,7 +75,14 @@ private Q_SLOTS:
7475 QVERIFY(testResultFile.open(QIODevice::ReadOnly));
7576 QTextStream ts2(&testResultFile);
7677 const QString expectedResult = ts2.readAll();
77− QCOMPARE(cardStringResult.toString().simplified(), expectedResult.simplified());
78+ const QString executedResult = cardStringResult.toString();
79+ // Record failed results to /tmp
80+ QTemporaryFile tmpFile(QDir::tempPath() + QDir::separator() + "testCardCreatorFailedResultXXXXXX");
81+ tmpFile.open();
82+ tmpFile.setAutoRemove(false);
83+ tmpFile.write(executedResult.toUtf8().constData());
84+ QCOMPARE(executedResult.simplified(), expectedResult.simplified());
85+ tmpFile.setAutoRemove(true); // Remove the result if it passed
7886
7987 QVariant createCardComponentResult;
8088 QMetaObject::invokeMethod(view->rootObject(), "createCardComponent", Q_RETURN_ARG(QVariant, createCardComponentResult), Q_ARG(QVariant, templateJSON), Q_ARG(QVariant, componentsJSON));
tests/plugins/Dash/listviewwithpageheadersectiontest.cpp−1
@@ -40,7 +40,6 @@ private:
4040 QTRY_COMPARE(lvwph->m_visibleItems[visibleIndex]->height(), height);
4141 QCOMPARE(QQuickItemPrivate::get(lvwph->m_visibleItems[visibleIndex]->m_item)->culled, culled);
4242 QCOMPARE(section(lvwph->m_visibleItems[visibleIndex]->sectionItem()), sectionHeader);
43− QTRY_COMPARE(hasSectionHeaderProperty(lvwph->m_visibleItems[visibleIndex]->m_item), !sectionHeader.isNull());
4443 if (!sectionHeader.isNull()) {
4544 QCOMPARE(QQuickItemPrivate::get(lvwph->m_visibleItems[visibleIndex]->sectionItem())->culled, sectionHeaderCulled);
4645 QCOMPARE(sectionDelegateIndex(lvwph->m_visibleItems[visibleIndex]->sectionItem()), lvwph->m_firstVisibleIndex + visibleIndex);
tests/plugins/Dash/tst_CardAttributes.qmlnew file+54
@@ -0,0 +1,54 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtTest 1.0
19+import Dash 0.1
20+
21+Item {
22+ width: units.gu(40)
23+ height: units.gu(4.5)
24+
25+ property var testData: [
26+ [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}],
27+ [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"},{"value":"text3"}],
28+ [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"},{"value":"text3"},{"value":"text4"}],
29+ [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"},{"value":"text3","style":"highlighted"},{"value":"text4","icon":"image://theme/close","style":"highlighted"},{"value":"text5"}]
30+ ]
31+
32+ CardAttributes {
33+ id: cardAttributes
34+ model: testData[3]
35+ clip: true
36+ }
37+
38+ TestCase {
39+ name: "CardAttributesTest"
40+ when: windowShown
41+
42+ function init() {
43+ }
44+
45+ function test_columns_data() {
46+ return testData;
47+ }
48+
49+ function test_columns(data) {
50+ cardAttributes.model = data;
51+ compare(cardAttributes.columns, 2 + data.length % 2);
52+ }
53+ }
54+}
tests/qmltests/CMakeLists.txt−1
@@ -40,7 +40,6 @@ add_qml_test(Dash Card)
4040add_qml_benchmark(Dash CardBenchmark 1000)
4141add_qml_test(Dash CardTool)
4242add_qml_test(Dash GenericScopeView)
43−add_qml_test(Dash/Apps RunningApplicationsGrid)
4443add_qml_test(Dash/Previews Preview)
4544add_qml_test(Dash/Previews PreviewActions)
4645add_qml_test(Dash/Previews PreviewAudioPlayback)
tests/qmltests/Components/tst_PageHeader.qml+36−6
@@ -36,6 +36,16 @@ Item {
3636
3737 function init() {
3838 searchEnabled = true;
39+
40+ // Reset to initial state
41+ pageHeader.searchQuery = "";
42+ pageHeader.closePopup();
43+ pageHeader.searchHistory.clear();
44+
45+ // Check initial state
46+ var headerContainer = findChild(pageHeader, "headerContainer");
47+ tryCompareFunction(function() { return headerContainer.popover === null; }, true);
48+ compare(pageHeader.searchHistory.count, 0);
3949 }
4050
4151 function test_search_disabled() {
@@ -80,9 +90,6 @@ Item {
8090 }
8191
8292 function test_history() {
83− pageHeader.searchHistory.clear()
84− compare(pageHeader.searchHistory.count, 0)
85−
8693 pageHeader.triggerSearch()
8794 typeString("humppa1")
8895 pageHeader.resetSearch()
@@ -166,16 +173,26 @@ Item {
166173 tryCompareFunction(function() { return headerContainer.popover !== null; }, true);
167174
168175 tryCompare(headerContainer.popover, "visible", true);
176+
177+ var searchTextField = findChild(pageHeader, "searchTextField");
178+ compare(searchTextField.focus, true);
179+
180+ var recentSearches = findChild(headerContainer.popover, "recentSearches");
181+ mouseClick(recentSearches.itemAt(0), 0, 0);
182+
183+ compare(pageHeader.searchQuery, "Search2");
184+ tryCompareFunction(function() { return headerContainer.popover === null; }, true);
185+ compare(searchTextField.focus, false);
169186 }
170187
171− function test_tap_outside_closes_popup_data() {
188+ function test_popup_closing_data() {
172189 return [
173190 { tag: "with search text", searchText: "foobar", hideSearch: false },
174191 { tag: "without search text", searchText: "", hideSearch: true }
175192 ];
176193 }
177194
178− function test_tap_outside_closes_popup(data) {
195+ function test_popup_closing(data) {
179196 searchEnabled = true;
180197 pageHeader.searchHistory.clear();
181198
@@ -191,13 +208,26 @@ Item {
191208
192209 pageHeader.searchQuery = data.searchText;
193210
194− mouseClick(root, root.width / 2, root.height - 1);
211+ if (data.searchText == "") {
212+ // When the text is empty the user can also close the
213+ // popup by clicking outside the header instead of by starting a search
214+ mouseClick(root, root.width / 2, root.height - 1);
215+ }
195216
196217 tryCompare(headerContainer, "showSearch", !data.hideSearch);
197218 tryCompareFunction(function() { return headerContainer.popover === null; }, true);
198219
199220 pageHeader.resetSearch();
200221 }
222+
223+ function test_search_change_shows_search() {
224+ var headerContainer = findChild(pageHeader, "headerContainer");
225+ compare(headerContainer.showSearch, false);
226+ compare(searchQuery, "");
227+
228+ searchQuery = "H";
229+ compare(headerContainer.showSearch, true);
230+ }
201231 }
202232
203233 Column {
tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qmldeleted−217
@@ -1,217 +0,0 @@
1−/*
2− * Copyright 2013 Canonical Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import QtTest 1.0
19−import "../../../../qml/Dash/Apps"
20−import Unity.Test 0.1 as UT
21−import Unity.Application 0.1
22−import Ubuntu.Components 1.1
23−
24−// Using Rectangle to have an opaque surface because AppManager paints app surfaces behind it.
25−Rectangle {
26− width: units.gu(70)
27− height: units.gu(40)
28−
29− function resetRunningApplications() {
30− while (ApplicationManager.count > 0) {
31− ApplicationManager.stopApplication(ApplicationManager.get(0).appId)
32− }
33−
34− ApplicationManager.startApplication("dialer-app");
35− ApplicationManager.startApplication("webbrowser-app");
36− }
37−
38− Component.onCompleted: {
39− resetRunningApplications()
40− }
41−
42− // The component under test
43− RunningApplicationsGrid {
44− id: runningApplicationsGrid
45− orientationAngle: orientationAngleSelector.selectedIndex * 90
46− anchors.left: parent.left
47− anchors.right: controlPanel.left
48− anchors.top: parent.top
49− anchors.bottom: parent.bottom
50− model: ApplicationManager
51− }
52−
53− Rectangle {
54− id: controlPanel
55− anchors.right: parent.right
56− anchors.top: parent.top
57− anchors.bottom: parent.bottom
58− width: units.gu(20)
59− color: "black"
60−
61− OptionSelector {
62− id: orientationAngleSelector
63− model: ["0","90","180","270"]
64− text: "Orientation Angle"
65− width: parent.width
66− }
67− }
68−
69− UT.UnityTestCase {
70− name: "RunningApplicationsGrid"
71− when: windowShown
72−
73− function init() {
74− runningApplicationsGrid.terminationModeEnabled = false
75− resetRunningApplications()
76− }
77−
78− property var browserTile
79− property var phoneTile
80−
81− property var isBrowserLongPressed: false
82− function onBrowserLongPressed() {isBrowserLongPressed = true}
83−
84− property var isPhoneLongPressed: false
85− function onPhoneLongPressed() {isPhoneLongPressed = true}
86−
87− // Tiles should go to termination mode when any one of them is long-pressed.
88− // Long-pressing when they're in termination mode brings them back to activation mode
89− function test_enterTerminationMode() {
90− browserTile = findChild(runningApplicationsGrid, "runningAppTile Browser")
91− verify(browserTile != undefined)
92− browserTile.onPressAndHold.connect(onBrowserLongPressed)
93−
94− phoneTile = findChild(runningApplicationsGrid, "runningAppTile Dialer")
95− verify(phoneTile != undefined)
96− phoneTile.onPressAndHold.connect(onPhoneLongPressed)
97−
98− compare(browserTile.terminationModeEnabled, false)
99− compare(phoneTile.terminationModeEnabled, false)
100− compare(runningApplicationsGrid.terminationModeEnabled, false)
101−
102− isBrowserLongPressed = false
103− mousePress(browserTile, browserTile.width/2, browserTile.height/2)
104− tryCompareFunction(checkSwitchToTerminationModeAfterLongPress, true)
105−
106− mouseRelease(browserTile, browserTile.width/2, browserTile.height/2)
107−
108− compare(browserTile.terminationModeEnabled, true)
109− compare(phoneTile.terminationModeEnabled, true)
110− compare(runningApplicationsGrid.terminationModeEnabled, true)
111−
112− isPhoneLongPressed = false
113− mousePress(phoneTile, phoneTile.width/2, phoneTile.height/2)
114− tryCompareFunction(checkSwitchToActivationModeAfterLongPress, true)
115−
116− mouseRelease(phoneTile, phoneTile.width/2, phoneTile.height/2)
117−
118− compare(browserTile.terminationModeEnabled, false)
119− compare(phoneTile.terminationModeEnabled, false)
120− compare(runningApplicationsGrid.terminationModeEnabled, false)
121−
122− browserTile.onPressAndHold.disconnect(onBrowserLongPressed)
123− phoneTile.onPressAndHold.disconnect(onPhoneLongPressed)
124− }
125−
126− // Checks that components swicth to termination mode after (and only after) a long
127− // press happens on Browser tile.
128− function checkSwitchToTerminationModeAfterLongPress() {
129− compare(browserTile.terminationModeEnabled, isBrowserLongPressed)
130− compare(phoneTile.terminationModeEnabled, isBrowserLongPressed)
131− compare(runningApplicationsGrid.terminationModeEnabled, isBrowserLongPressed)
132−
133− return isBrowserLongPressed &&
134− browserTile.terminationModeEnabled &&
135− phoneTile.terminationModeEnabled &&
136− runningApplicationsGrid.terminationModeEnabled
137− }
138−
139− // Checks that components swicth to activation mode after (and only after) a long
140− // press happens on Phone tile.
141− function checkSwitchToActivationModeAfterLongPress() {
142− compare(browserTile.terminationModeEnabled, !isPhoneLongPressed)
143− compare(phoneTile.terminationModeEnabled, !isPhoneLongPressed)
144− compare(runningApplicationsGrid.terminationModeEnabled, !isPhoneLongPressed)
145−
146− return isPhoneLongPressed &&
147− !browserTile.terminationModeEnabled &&
148− !phoneTile.terminationModeEnabled &&
149− !runningApplicationsGrid.terminationModeEnabled
150− }
151−
152− // While on termination mode, clicking a running application tile, outside of
153− // the close icon should do nothing
154− function test_clickTileNotClose() {
155− runningApplicationsGrid.terminationModeEnabled = true
156−
157− var browserTile = findChild(runningApplicationsGrid, "runningAppTile Browser")
158− verify(browserTile != undefined)
159−
160− verify(ApplicationManager.findApplication("webbrowser-app") !== null)
161−
162− mouseClick(browserTile, browserTile.width/2, browserTile.height/2)
163−
164− verify(ApplicationManager.findApplication("webbrowser-app") !== null)
165−
166− // The tile for the Browser app should stay there
167− tryCompareFunction(checkBrowserTileExists, true)
168− }
169−
170− // While in termination mode, clicking on a running application tile's close icon
171− // causes the corresponding application to be terminated
172− function test_clickCloseIconToTerminateApp() {
173− runningApplicationsGrid.terminationModeEnabled = true
174−
175− var browserTile = findChild(runningApplicationsGrid, "runningAppTile Browser")
176− var browserTileCloseButton = findChild(runningApplicationsGrid, "closeIcon Browser")
177−
178− verify(browserTile != undefined)
179− verify(browserTileCloseButton != undefined)
180− verify(ApplicationManager.findApplication("webbrowser-app") !== 0)
181−
182− mouseClick(browserTileCloseButton, browserTileCloseButton.width/2, browserTileCloseButton.height/2)
183− wait(0) // spin event loop to start any pending animation
184−
185− verify(ApplicationManager.findApplication("webbrowser-app") === null)
186−
187− // The tile for the Browser app should eventually vanish since the
188− // application has been terminated.
189− tryCompareFunction(checkBrowserTileExists, false)
190− }
191−
192− function checkBrowserTileExists() {
193− return findChild(runningApplicationsGrid, "runningAppTile Browser")
194− != undefined
195− }
196−
197− // While in termination mode, if you click outside any of the tiles, the
198− // termination mode is disabled (i.e. we switch back to activation mode).
199− function test_clickOutsideTilesDisablesTerminationMode() {
200− runningApplicationsGrid.terminationModeEnabled = true
201−
202− var browserTile = findChild(runningApplicationsGrid, "runningAppTile Browser")
203− verify(browserTile != undefined)
204−
205− verify(runningApplicationsGrid.terminationModeEnabled);
206−
207− // Click on the bottom right corner of the grid, where there's no
208− // RunningApplicationTile lying around
209− mouseClick(runningApplicationsGrid,
210− runningApplicationsGrid.width - 1, runningApplicationsGrid.height - 1);
211−
212− wait(0) // spin event loop to ensure that any pending signal emission went through
213−
214− verify(!runningApplicationsGrid.terminationModeEnabled);
215− }
216− }
217−}
tests/qmltests/Dash/CardHelpers.js+5−3
@@ -16,7 +16,7 @@
1616
1717.pragma library
1818
19−var components = ["title", "art", "subtitle", "mascot", "emblem", "old-price", "price", "alt-price", "rating", "alt-rating", "summary"]
19+var components = ["title", "art", "subtitle", "mascot", "emblem", "old-price", "price", "alt-price", "rating", "alt-rating", "summary", "attributes"]
2020
2121var defaultLayout = ' \
2222{ \
@@ -48,7 +48,8 @@ var defaultLayout = ' \
4848 "empty": "image://theme/rating-star-empty" \
4949 }, \
5050 "alt-rating": null, \
51− "summary": null \
51+ "summary": null, \
52+ "attributes": { "max-count": 2 } \
5253 }, \
5354 "resources": {} \
5455}'
@@ -59,7 +60,8 @@ var fullMapping = ' \
5960 "art": "art", \
6061 "subtitle": "subtitle", \
6162 "mascot": "mascot", \
62− "summary": "summary" \
63+ "summary": "summary", \
64+ "attributes": "attributes" \
6365}'
6466
6567
tests/qmltests/Dash/tst_Card.qml+4−2
@@ -33,7 +33,8 @@ Rectangle {
3333 "mascot": "../../../tests/qmltests/Dash/artwork/avatar.png",
3434 "title": "foo",
3535 "subtitle": "bar",
36− "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."
36+ "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.",
37+ "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}]
3738 }'
3839
3940 property var cardsModel: [
@@ -110,6 +111,7 @@ Rectangle {
110111 anchors { top: parent.top; left: parent.left; margins: units.gu(1) }
111112
112113 sourceComponent: cardTool.cardComponent
114+ clip: true
113115 onLoaded: {
114116 item.template = Qt.binding(function() { return cardTool.template; });
115117 item.components = Qt.binding(function() { return cardTool.components; });
@@ -359,7 +361,7 @@ Rectangle {
359361
360362 waitForRendering(card);
361363
362− tryCompareFunction(function() { return testCase.summary.y === data.top() }, true);
364+ tryCompareFunction(function() { return art.height > 0 && testCase.summary.y === data.top() }, true);
363365 }
364366
365367 function test_art_visibility() {
tests/qmltests/tst_Shell.qml+2−6
@@ -313,9 +313,9 @@ Item {
313313 - perform long left edge swipe to go minimize the app and go back to the dash.
314314
315315 Expected Results
316− - apps lens shown and Running Apps visible on screen
316+ - apps lens shown
317317 */
318− function test_minimizingAppTakesToRunningApps() {
318+ function test_minimizingAppTakesToDashApps() {
319319 var dashApps = findChild(shell, "clickscope");
320320 swipeUntilScopeViewIsReached(dashApps);
321321
@@ -353,10 +353,6 @@ Item {
353353 tryCompare(appsCategoryListView, "moving", false);
354354
355355 verify(itemIsOnScreen(dashApps));
356−
357− var runningApplicationsGrid = findChild(appsCategoryListView, "running.apps.category");
358− verify(runningApplicationsGrid);
359− verify(itemIsOnScreen(runningApplicationsGrid));
360356 }
361357
362358 function test_showInputMethod() {