on xenial help sign in
Merge trunk

[ Gerry Boland ] * Fix the run.sh script - pretend to be running with qtmir and emit SIGSTOP at the right time [ Ying-Chun Liu ] * Implement Attribute UI. (LP: #1282460) [ Albert Astals ] * Hide search history popup as soon as you start typing As discussed with Mike and Saviq * Compile with for scopes-v3 unity-api * PageHeader: Unfocus search field when search entry is selected * Show search field if the search query changes * Test: Add a condition for art.height being > 0 means stuff has already been layouted a bit without it it can happen that we get 0 for everything at startup and tests still pass * Remove leftover in test of an old headerless implementation [ Michael Zanetti ] * Drop Recent apps category from Dash (LP: #1281092) * update launcher count emblems to match new spec (LP: #1338984) [ Bill Filler ] * disable predictive text for dash search field (LP: #1340409) [ CI bot ] * Resync trunk [ Antti Kaijanmäki ] * DefaultIndicatorPage: use Loader status to determine the visible property. (LP: #1350555) [ Michael Terry ] * Check user's pin/password using PAM, instead of a plaintext keyfile. New build dependency: libpam0g-dev for phone unlock with PAM (LP: #1234983) [ Ubuntu daily release ] * New rebuild forced

commitce4ccaaf2b15a90148eda0ab359eefa1248c4a1a authorDaniel d'Andrada <daniel.dandrada@canonical.com> dateTue 5 Aug 2014, 07:15 (12 years ago) parent873e6a4a1c2c28
66 files changed+1384−630
cmake/modules/QmlTest.cmake+1−1 debian/changelog+50−2 debian/control+2−1 plugins/AccountsService/AccountsService.cpp+21−1 plugins/AccountsService/AccountsService.h+13 plugins/AccountsService/plugin.cpp+1 plugins/Dash/CardAttributes.qml+65 plugins/Dash/CardCreator.js+76−7 plugins/Dash/qmldir+1 plugins/LightDM/plugin.cpp+2 po/am.po+10−10 po/br.po+1−1 po/da.po+7−7 po/el.po+14−14 po/fa.po+51−49 po/hu.po+8−8 po/is.po+6−6 po/km.po+7−7 po/pl.po+7−7 po/pt_BR.po+11−11 po/ru.po+18−18 po/sl.po+4−4 po/sv.po+6−6 po/ug.po+5−5 po/uk.po+5−5 po/zh_CN.po+8−8 qml/Components/Dialogs.qml+1 qml/Components/PageHeader.qml+24−7 qml/Components/PassphraseLockscreen.qml+3 qml/Dash/CardTool.qml+27−2 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 qml/Shell.qml+27−21 run.sh+15−6 src/main.cpp+7 tests/autopilot/unity8/shell/tests/test_upstart.py+18−3 tests/mocks/AccountsService/AccountsService.cpp+12−2 tests/mocks/AccountsService/AccountsService.h+12 tests/mocks/AccountsService/CMakeLists.txt+4−1 tests/mocks/AccountsService/plugin.cpp+1 tests/mocks/LightDM/GreeterPrivate.h+2 tests/mocks/LightDM/demo/CMakeLists.txt+4−2 tests/mocks/LightDM/demo/GreeterPrivate.cpp+202−30 tests/mocks/LightDM/demo/UsersModelPrivate.cpp+1−1 tests/mocks/LightDM/full/GreeterPrivate.cpp+1−1 tests/mocks/LightDM/single-pin/GreeterPrivate.cpp+1−1 tests/mocks/LightDM/single-pin/UsersModelPrivate.cpp+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/Components/tst_PageHeader.qml+36−6 tests/qmltests/Dash/CardHelpers.js+5−3 tests/qmltests/Dash/tst_Card.qml+4−2 tests/qmltests/Greeter/tst_Lockscreen.qml+11−5
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+50−2
@@ -1,4 +1,49 @@
1−unity8 (8.00) UNRELEASED; urgency=medium
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+
37+unity8 (8.00+14.10.20140729.1-0ubuntu1) utopic; urgency=low
38+
39+ [ Michael Terry ]
40+ * Check user's pin/password using PAM, instead of a plaintext keyfile.
41+ New build dependency: libpam0g-dev for phone unlock with PAM (LP:
42+ #1234983)
43+
44+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 29 Jul 2014 23:36:30 +0000
45+
46+unity8 (8.00+14.10.20140729-0ubuntu1) utopic; urgency=medium
247
348 [ Gerry Boland ]
449 [ Daniel d'Andrada ]
@@ -10,7 +55,10 @@ unity8 (8.00) UNRELEASED; urgency=medium
1055 * Port phone right-edge spread code to use QtCompositor
1156 * Add right-edge spread animation for tablet
1257
13− -- Gerry Boland <gerry.boland@canonical.com> Thu, 10 Jul 2014 10:34:18 +0100
58+ [ Ubuntu daily release ]
59+ * New rebuild forced
60+
61+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 29 Jul 2014 15:07:32 +0000
1462
1563unity8 (7.90+14.10.20140725-0ubuntu1) utopic; urgency=low
1664
debian/control+2−1
@@ -19,11 +19,12 @@ Build-Depends: cmake,
1919 libgsettings-qt-dev,
2020 libhardware-dev,
2121 libhud-client2-dev,
22+ libpam0g-dev,
2223 libpay2-dev,
2324 libpulse-dev,
2425 libqmenumodel-dev (>= 0.2.8),
2526 libqt5xmlpatterns5-dev,
26− libunity-api-dev (>= 7.85),
27+ libunity-api-dev (>= 7.87),
2728 libusermetricsoutput1-dev,
2829 libxcb1-dev,
2930 pkg-config,
plugins/AccountsService/AccountsService.cpp+21−1
@@ -26,7 +26,8 @@ AccountsService::AccountsService(QObject* parent)
2626 m_service(new AccountsServiceDBusAdaptor(this)),
2727 m_user(qgetenv("USER")),
2828 m_demoEdges(false),
29− m_statsWelcomeScreen(false)
29+ m_statsWelcomeScreen(false),
30+ m_passwordDisplayHint(Keyboard)
3031{
3132 connect(m_service, SIGNAL(propertiesChanged(const QString &, const QString &, const QStringList &)),
3233 this, SLOT(propertiesChanged(const QString &, const QString &, const QStringList &)));
@@ -47,6 +48,7 @@ void AccountsService::setUser(const QString &user)
4748 updateDemoEdges();
4849 updateBackgroundFile();
4950 updateStatsWelcomeScreen();
51+ updatePasswordDisplayHint();
5052}
5153
5254bool AccountsService::demoEdges() const
@@ -70,6 +72,11 @@ bool AccountsService::statsWelcomeScreen() const
7072 return m_statsWelcomeScreen;
7173}
7274
75+AccountsService::PasswordDisplayHint AccountsService::passwordDisplayHint() const
76+{
77+ return m_passwordDisplayHint;
78+}
79+
7380void AccountsService::updateDemoEdges()
7481{
7582 auto demoEdges = m_service->getUserProperty(m_user, "com.canonical.unity.AccountsService", "demo-edges").toBool();
@@ -97,6 +104,15 @@ void AccountsService::updateStatsWelcomeScreen()
97104 }
98105}
99106
107+void AccountsService::updatePasswordDisplayHint()
108+{
109+ PasswordDisplayHint passwordDisplayHint = (PasswordDisplayHint)m_service->getUserProperty(m_user, "com.ubuntu.AccountsService.SecurityPrivacy", "PasswordDisplayHint").toInt();
110+ if (m_passwordDisplayHint != passwordDisplayHint) {
111+ m_passwordDisplayHint = passwordDisplayHint;
112+ Q_EMIT passwordDisplayHintChanged();
113+ }
114+}
115+
100116void AccountsService::propertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
101117{
102118 if (m_user != user) {
@@ -111,6 +127,10 @@ void AccountsService::propertiesChanged(const QString &user, const QString &inte
111127 if (changed.contains("StatsWelcomeScreen")) {
112128 updateStatsWelcomeScreen();
113129 }
130+ } else if (interface == "com.ubuntu.AccountsService.SecurityPrivacy") {
131+ if (changed.contains("PasswordDisplayHint")) {
132+ updatePasswordDisplayHint();
133+ }
114134 }
115135}
116136
plugins/AccountsService/AccountsService.h+13
@@ -27,6 +27,7 @@ class AccountsServiceDBusAdaptor;
2727class AccountsService: public QObject
2828{
2929 Q_OBJECT
30+ Q_ENUMS(PasswordDisplayHint)
3031 Q_PROPERTY (QString user
3132 READ user
3233 WRITE setUser
@@ -41,8 +42,16 @@ class AccountsService: public QObject
4142 Q_PROPERTY (bool statsWelcomeScreen
4243 READ statsWelcomeScreen
4344 NOTIFY statsWelcomeScreenChanged)
45+ Q_PROPERTY (PasswordDisplayHint passwordDisplayHint
46+ READ passwordDisplayHint
47+ NOTIFY passwordDisplayHintChanged)
4448
4549public:
50+ enum PasswordDisplayHint {
51+ Keyboard,
52+ Numeric,
53+ };
54+
4655 explicit AccountsService(QObject *parent = 0);
4756
4857 QString user() const;
@@ -51,12 +60,14 @@ public:
5160 void setDemoEdges(bool demoEdges);
5261 QString backgroundFile() const;
5362 bool statsWelcomeScreen() const;
63+ PasswordDisplayHint passwordDisplayHint() const;
5464
5565Q_SIGNALS:
5666 void userChanged();
5767 void demoEdgesChanged();
5868 void backgroundFileChanged();
5969 void statsWelcomeScreenChanged();
70+ void passwordDisplayHintChanged();
6071
6172private Q_SLOTS:
6273 void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
@@ -66,12 +77,14 @@ private:
6677 void updateDemoEdges();
6778 void updateBackgroundFile();
6879 void updateStatsWelcomeScreen();
80+ void updatePasswordDisplayHint();
6981
7082 AccountsServiceDBusAdaptor *m_service;
7183 QString m_user;
7284 bool m_demoEdges;
7385 QString m_backgroundFile;
7486 bool m_statsWelcomeScreen;
87+ PasswordDisplayHint m_passwordDisplayHint;
7588};
7689
7790#endif
plugins/AccountsService/plugin.cpp+1
@@ -33,5 +33,6 @@ void AccountsServicePlugin::registerTypes(const char *uri)
3333{
3434 Q_ASSERT(uri == QLatin1String("AccountsService"));
3535 qDBusRegisterMetaType<QList<QVariantMap>>();
36+ qRegisterMetaType<AccountsService::PasswordDisplayHint>("AccountsService::PasswordDisplayHint");
3637 qmlRegisterSingletonType<AccountsService>(uri, 0, 1, "AccountsService", service_provider);
3738}
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
plugins/LightDM/plugin.cpp+2
@@ -69,6 +69,8 @@ void LightDMPlugin::registerTypes(const char *uri)
6969 qmlRegisterType<UserMetricsOutput::ColorTheme>();
7070
7171 Q_ASSERT(uri == QLatin1String("LightDM"));
72+ qRegisterMetaType<QLightDM::Greeter::MessageType>("QLightDM::Greeter::MessageType");
73+ qRegisterMetaType<QLightDM::Greeter::PromptType>("QLightDM::Greeter::PromptType");
7274 qmlRegisterSingletonType<Greeter>(uri, 0, 1, "Greeter", greeter_provider);
7375 qmlRegisterSingletonType<UsersModel>(uri, 0, 1, "Users", users_provider);
7476 qmlRegisterUncreatableType<QLightDM::UsersModel>(uri, 0, 1, "UserRoles", "Type is not instantiable");
po/am.po+10−10
@@ -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-27 01:25+0000\n"
11+"PO-Revision-Date: 2014-08-04 16:30+0000\n"
1212"Last-Translator: samson <Unknown>\n"
1313"Language-Team: Amharic <am@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-28 06:44+0000\n"
18−"X-Generator: Launchpad (build 17131)\n"
17+"X-Launchpad-Export-Date: 2014-08-05 06:45+0000\n"
18+"X-Generator: Launchpad (build 17147)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
2121#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -36,7 +36,7 @@ msgstr "የቀኝ ጠርዝ"
3636
3737#: qml/Components/EdgeDemo.qml:114
3838msgid "Try swiping from the right edge to unlock the phone"
39−msgstr ""
39+msgstr "ስልኩን ለመክፈት ከ ቀኝ ጠርዝ በኩል ወደ ግራ ይጥረጉ"
4040
4141#: qml/Components/EdgeDemo.qml:145
4242msgid "Top edge"
@@ -44,7 +44,7 @@ msgstr "የላይ ጠርዝ"
4444
4545#: qml/Components/EdgeDemo.qml:146
4646msgid "Try swiping from the top edge to access the indicators"
47−msgstr ""
47+msgstr "ጠቋሚዎች ጋር ለመድረስ ከ ላይ ጠርዝ በኩል ወደ ታች ይጥረጉ"
4848
4949#: qml/Components/EdgeDemo.qml:171
5050msgid "Close"
@@ -52,7 +52,7 @@ msgstr "መዝጊያ"
5252
5353#: qml/Components/EdgeDemo.qml:172
5454msgid "Swipe up again to close the settings screen"
55−msgstr ""
55+msgstr "የማሰናጃውን መመልከቻ ለመዝጋት እንደገና ወደ ላይ ይጥረጉ"
5656
5757#: qml/Components/EdgeDemo.qml:199
5858msgid "Left edge"
@@ -60,11 +60,11 @@ msgstr "የግራ ጠርዝ"
6060
6161#: qml/Components/EdgeDemo.qml:200
6262msgid "Swipe from the left to reveal the launcher for quick access to apps"
63−msgstr ""
63+msgstr "ከ ግራ በኩል ወደ ቀኝ ይጥረጉ በፍጥነት መተግበሪያዎች ጋር ለመድረስ እና ለማስጀመር"
6464
6565#: qml/Components/EdgeDemo.qml:227
6666msgid "Well done"
67−msgstr ""
67+msgstr "ጥሩ አድረገዋል"
6868
6969#: qml/Components/EdgeDemo.qml:228
7070msgid ""
@@ -123,7 +123,7 @@ msgstr "አነስተኛ..."
123123
124124#: qml/Dash/Previews/PreviewRatingInput.qml:81
125125msgid "Rate this"
126−msgstr ""
126+msgstr "ለዚህ ደረጃ ይስጡ"
127127
128128#: qml/Dash/Previews/PreviewRatingInput.qml:126
129129msgid "Add a review"
@@ -135,7 +135,7 @@ msgstr "መላኪያ"
135135
136136#: qml/Greeter/Greeter.qml:157
137137msgid "Swipe to unlock"
138−msgstr ""
138+msgstr "ለመክፈት ይጥረጉ"
139139
140140#: qml/Hud/HudParametrizedActionsPage.qml:132
141141msgid "Confirm"
po/br.po+1−1
@@ -14,7 +14,7 @@ msgstr ""
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-28 06:44+0000\n"
17+"X-Launchpad-Export-Date: 2014-07-29 06:54+0000\n"
1818"X-Generator: Launchpad (build 17131)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
po/da.po+7−7
@@ -9,13 +9,13 @@ msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1111"POT-Creation-Date: 2014-07-11 14:59+0200\n"
12−"PO-Revision-Date: 2014-07-22 15:46+0000\n"
13−"Last-Translator: Kenneth Ramsussen <kentora@kentora.dk>\n"
12+"PO-Revision-Date: 2014-07-30 12:37+0000\n"
13+"Last-Translator: Ask Hjorth Larsen <asklarsen@gmail.com>\n"
1414"Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
1515"MIME-Version: 1.0\n"
1616"Content-Type: text/plain; charset=UTF-8\n"
1717"Content-Transfer-Encoding: 8bit\n"
18−"X-Launchpad-Export-Date: 2014-07-26 07:16+0000\n"
18+"X-Launchpad-Export-Date: 2014-07-31 07:15+0000\n"
1919"X-Generator: Launchpad (build 17131)\n"
2020
2121#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -72,7 +72,7 @@ msgid ""
7272"You have now mastered the edge gestures and can start using the "
7373"phone<br><br>Tap on the screen to start"
7474msgstr ""
75−"Du mestrer nu kantgestusserne og kan begynde at bruge telefonen<br><br>Tryk "
75+"Du mestrer nu kantbevægelserne og kan begynde at bruge telefonen<br><br>Tryk "
7676"på skærmen for at starte"
7777
7878#: qml/Components/Lockscreen.qml:214
@@ -114,7 +114,7 @@ msgstr "Seneste"
114114
115115#: qml/Dash/GenericScopeView.qml:330
116116msgid "See all"
117−msgstr ""
117+msgstr "Se alle"
118118
119119#: qml/Dash/Previews/PreviewActionCombo.qml:34
120120msgid "More..."
@@ -170,11 +170,11 @@ msgstr "Indtast venligst PIN for SIM"
170170
171171#: qml/Panel/ActiveCallHint.qml:77
172172msgid "Tap to return to call..."
173−msgstr ""
173+msgstr "Tryk for at besvare opkaldet..."
174174
175175#: qml/Panel/ActiveCallHint.qml:90
176176msgid "Conference"
177−msgstr ""
177+msgstr "Konference"
178178
179179#: qml/Panel/Indicators/MenuItemFactory.qml:583
180180msgid "In queue…"
po/el.po+14−14
@@ -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 19:27+0000\n"
12−"Last-Translator: George Christofis <geochr22@gmail.com>\n"
11+"PO-Revision-Date: 2014-08-04 18:44+0000\n"
12+"Last-Translator: madvinegar <madvinegar@hotmail.com>\n"
1313"Language-Team: Greek <el@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-05 06:45+0000\n"
18+"X-Generator: Launchpad (build 17147)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
2121#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -111,11 +111,11 @@ msgstr "Λιγότερα"
111111
112112#: qml/Dash/DashApps.qml:37
113113msgid "Recent"
114−msgstr ""
114+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..."
@@ -139,7 +139,7 @@ msgstr "Αποστολή"
139139
140140#: qml/Greeter/Greeter.qml:157
141141msgid "Swipe to unlock"
142−msgstr ""
142+msgstr "Σύρετε για να ξεκλειδώσετε"
143143
144144#: qml/Hud/HudParametrizedActionsPage.qml:132
145145msgid "Confirm"
@@ -171,11 +171,11 @@ msgstr "Παρακαλώ εισάγετε το PIN της κάρτας SIM"
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…"
@@ -183,23 +183,23 @@ msgstr ""
183183
184184#: qml/Panel/Indicators/MenuItemFactory.qml:587
185185msgid "Downloading"
186−msgstr ""
186+msgstr "Λήψη σε εξέλιξη"
187187
188188#: qml/Panel/Indicators/MenuItemFactory.qml:589
189189msgid "Paused, tap to resume"
190−msgstr ""
190+msgstr "Σταματημένο, αγγίξτε για συνέχιση"
191191
192192#: qml/Panel/Indicators/MenuItemFactory.qml:591
193193msgid "Canceled"
194−msgstr ""
194+msgstr "Ακυρώθηκε"
195195
196196#: qml/Panel/Indicators/MenuItemFactory.qml:593
197197msgid "Finished"
198−msgstr ""
198+msgstr "Ολοκληρώθηκε"
199199
200200#: qml/Panel/Indicators/MenuItemFactory.qml:595
201201msgid "Failed, tap to retry"
202−msgstr ""
202+msgstr "Αποτυχία, πατήστε για να ξαναπροσπαθήσετε"
203203
204204#: qml/Panel/SearchIndicator.qml:27
205205msgid "Search"
po/fa.po+51−49
@@ -8,200 +8,202 @@ 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: 2013-10-31 19:02+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2014-08-03 13:00+0000\n"
12+"Last-Translator: Arash Badie Modiri <arashbm@gmail.com>\n"
1313"Language-Team: Persian <fa@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-04 06:32+0000\n"
18+"X-Generator: Launchpad (build 17147)\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"
31−msgstr ""
31+msgstr "پرش از مقدّمه"
3232
3333#: qml/Components/EdgeDemo.qml:113
3434msgid "Right edge"
35−msgstr ""
35+msgstr "لبه‌ی راست"
3636
3737#: qml/Components/EdgeDemo.qml:114
3838msgid "Try swiping from the right edge to unlock the phone"
39−msgstr ""
39+msgstr "برای باز کردن قفل تلفن، از لبه‌ی راست بکشید"
4040
4141#: qml/Components/EdgeDemo.qml:145
4242msgid "Top edge"
43−msgstr ""
43+msgstr "لبه‌ی بالا"
4444
4545#: qml/Components/EdgeDemo.qml:146
4646msgid "Try swiping from the top edge to access the indicators"
47−msgstr ""
47+msgstr "برای دست‌رسی به نشانگرها، از لبه‌ی بالا بکشید"
4848
4949#: qml/Components/EdgeDemo.qml:171
5050msgid "Close"
51−msgstr ""
51+msgstr "بستن"
5252
5353#: qml/Components/EdgeDemo.qml:172
5454msgid "Swipe up again to close the settings screen"
55−msgstr ""
55+msgstr "برای بستن صفحه‌ی تنظیمات، دوباره به بالا بکشپد"
5656
5757#: qml/Components/EdgeDemo.qml:199
5858msgid "Left edge"
59−msgstr ""
59+msgstr "لبه‌ی چپ"
6060
6161#: qml/Components/EdgeDemo.qml:200
6262msgid "Swipe from the left to reveal the launcher for quick access to apps"
63−msgstr ""
63+msgstr "برای آشکار شدن اجراگر به منظور دست‌رسی سریع به اپ‌ها از سمت چپ بکشید"
6464
6565#: qml/Components/EdgeDemo.qml:227
6666msgid "Well done"
67−msgstr ""
67+msgstr "عالی بود"
6868
6969#: qml/Components/EdgeDemo.qml:228
7070msgid ""
7171"You have now mastered the edge gestures and can start using the "
7272"phone<br><br>Tap on the screen to start"
7373msgstr ""
74+"هم اکنون در استفاده از ژست های حرکتی مهارت لازم را دارید و می‌توانید استفاده "
75+"از تلفن را آغاز کنید<br><br>برای شروع صفحه را لمس کنید"
7476
7577#: qml/Components/Lockscreen.qml:214
7678msgid "Emergency Call"
77−msgstr ""
79+msgstr "تماس اضطراری"
7880
7981#: qml/Components/Lockscreen.qml:231
8082msgid "OK"
81−msgstr ""
83+msgstr "قبول"
8284
8385#: qml/Components/PassphraseLockscreen.qml:60
8486#, qt-format
8587msgid "Hello %1"
86−msgstr ""
88+msgstr "سلام %1"
8789
8890#: qml/Components/PassphraseLockscreen.qml:60
8991msgid "Hello"
90−msgstr ""
92+msgstr "سلام"
9193
9294#: qml/Components/PinLockscreen.qml:198
9395msgid "CANCEL"
94−msgstr ""
96+msgstr "لغو"
9597
9698#: qml/Components/PinLockscreen.qml:216
9799msgid "DONE"
98−msgstr ""
100+msgstr "انجام شد"
99101
100102#: qml/Components/SeeMore.qml:35
101103msgid "See more"
102−msgstr ""
104+msgstr "بیش‌تر ببینید"
103105
104106#: qml/Components/SeeMore.qml:58 qml/Dash/GenericScopeView.qml:330
105107msgid "See less"
106−msgstr ""
108+msgstr "کم‌تر ببینید"
107109
108110#: qml/Dash/DashApps.qml:37
109111msgid "Recent"
110−msgstr ""
112+msgstr "موارد اخیر"
111113
112114#: qml/Dash/GenericScopeView.qml:330
113115msgid "See all"
114−msgstr ""
116+msgstr "دیدن همه"
115117
116118#: qml/Dash/Previews/PreviewActionCombo.qml:34
117119msgid "More..."
118−msgstr ""
120+msgstr "بیش‌تر…"
119121
120122#: qml/Dash/Previews/PreviewActionCombo.qml:34
121123msgid "Less..."
122−msgstr ""
124+msgstr "کم‌تر…"
123125
124126#: qml/Dash/Previews/PreviewRatingInput.qml:81
125127msgid "Rate this"
126−msgstr ""
128+msgstr "امتیاز دادن"
127129
128130#: qml/Dash/Previews/PreviewRatingInput.qml:126
129131msgid "Add a review"
130−msgstr ""
132+msgstr "افزودن یک بازبینی"
131133
132134#: qml/Dash/Previews/PreviewRatingInput.qml:166
133135msgid "Send"
134−msgstr ""
136+msgstr "ارسال"
135137
136138#: qml/Greeter/Greeter.qml:157
137139msgid "Swipe to unlock"
138−msgstr ""
140+msgstr "برای باز کردن قفل بکشد"
139141
140142#: qml/Hud/HudParametrizedActionsPage.qml:132
141143msgid "Confirm"
142−msgstr ""
144+msgstr "تأیید"
143145
144146#: qml/Hud/Hud.qml:61 qml/Hud/Hud.qml:99 qml/Hud/Hud.qml:372
145147msgid "Type or say a command"
146−msgstr ""
148+msgstr "دستوری را بنویسید یا بگویید"
147149
148150#: qml/Hud/Hud.qml:84
149151msgid "Loading. Please Wait..."
150−msgstr ""
152+msgstr "در حال بار کردن. لطفاً صبر کنید…"
151153
152154#: qml/Hud/Hud.qml:89
153155msgid "Speak Now..."
154−msgstr ""
156+msgstr "اکنون صحبت کنید…"
155157
156158#: qml/Hud/Hud.qml:94
157159msgid "Speaking..."
158−msgstr ""
160+msgstr "در حال صحبت کردن…"
159161
160162#: qml/Notifications/NotificationMenuItemFactory.qml:91
161163msgid "Show password"
162−msgstr ""
164+msgstr "نمایش گذرواژه"
163165
164166#: qml/Notifications/NotificationMenuItemFactory.qml:103
165167msgid "Please enter SIM PIN"
166−msgstr ""
168+msgstr "لطفاً PIN سیم‌کارت را وارد کنید"
167169
168170#: qml/Panel/ActiveCallHint.qml:77
169171msgid "Tap to return to call..."
170−msgstr ""
172+msgstr "برای بازگشت به تماس ضربه بزنید…"
171173
172174#: qml/Panel/ActiveCallHint.qml:90
173175msgid "Conference"
174−msgstr ""
176+msgstr "کنفرانس"
175177
176178#: qml/Panel/Indicators/MenuItemFactory.qml:583
177179msgid "In queue…"
178−msgstr ""
180+msgstr "در صف…"
179181
180182#: qml/Panel/Indicators/MenuItemFactory.qml:587
181183msgid "Downloading"
182−msgstr ""
184+msgstr "در حال بارگیری"
183185
184186#: qml/Panel/Indicators/MenuItemFactory.qml:589
185187msgid "Paused, tap to resume"
186−msgstr ""
188+msgstr "مکث شد، برای از سر گیری ضربه بزنید"
187189
188190#: qml/Panel/Indicators/MenuItemFactory.qml:591
189191msgid "Canceled"
190−msgstr ""
192+msgstr "لغو شد"
191193
192194#: qml/Panel/Indicators/MenuItemFactory.qml:593
193195msgid "Finished"
194−msgstr ""
196+msgstr "پایان یافت"
195197
196198#: qml/Panel/Indicators/MenuItemFactory.qml:595
197199msgid "Failed, tap to retry"
198−msgstr ""
200+msgstr "شکست خورد، برای تلاش دوباره ضربه بزنید"
199201
200202#: qml/Panel/SearchIndicator.qml:27
201203msgid "Search"
202−msgstr ""
204+msgstr "جست‌وجو"
203205
204206#: qml/Shell.qml:359
205207#, qt-format
206208msgid "Please enter %1"
207−msgstr ""
209+msgstr "لطفا %1 را وارد کنید"
po/hu.po+8−8
@@ -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-27 15:04+0000\n"
11+"PO-Revision-Date: 2014-08-04 09:10+0000\n"
1212"Last-Translator: Richard Somlói <ricsipontaz@gmail.com>\n"
1313"Language-Team: Hungarian <hu@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-28 06:44+0000\n"
18−"X-Generator: Launchpad (build 17131)\n"
17+"X-Launchpad-Export-Date: 2014-08-05 06:45+0000\n"
18+"X-Generator: Launchpad (build 17147)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
2121#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -44,7 +44,7 @@ msgstr "Felső szél"
4444
4545#: qml/Components/EdgeDemo.qml:146
4646msgid "Try swiping from the top edge to access the indicators"
47−msgstr "A jelzők eléréséhez csúsztassa ujját a felső szélről"
47+msgstr "Az állapotmenük eléréséhez csúsztassa ujját a felső szélről"
4848
4949#: qml/Components/EdgeDemo.qml:171
5050msgid "Close"
@@ -82,16 +82,16 @@ msgstr "Vészhívás"
8282
8383#: qml/Components/Lockscreen.qml:231
8484msgid "OK"
85−msgstr "OK"
85+msgstr "Ok"
8686
8787#: qml/Components/PassphraseLockscreen.qml:60
8888#, qt-format
8989msgid "Hello %1"
90−msgstr "Szia %1"
90+msgstr "Üdv %1"
9191
9292#: qml/Components/PassphraseLockscreen.qml:60
9393msgid "Hello"
94−msgstr "Szia"
94+msgstr "Üdv"
9595
9696#: qml/Components/PinLockscreen.qml:198
9797msgid "CANCEL"
@@ -127,7 +127,7 @@ msgstr "Kevesebb…"
127127
128128#: qml/Dash/Previews/PreviewRatingInput.qml:81
129129msgid "Rate this"
130−msgstr "Osztályzás"
130+msgstr "Értékelje"
131131
132132#: qml/Dash/Previews/PreviewRatingInput.qml:126
133133msgid "Add a review"
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-02 06:39+0000\n"
18+"X-Generator: Launchpad (build 17147)\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/pt_BR.po+11−11
@@ -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-17 05:46+0000\n"
12−"Last-Translator: Tiago Hillebrandt <tiagohillebrandt@gmail.com>\n"
11+"PO-Revision-Date: 2014-08-04 18:33+0000\n"
12+"Last-Translator: Rafael Neri <Unknown>\n"
1313"Language-Team: Brazilian Portuguese <pt_BR@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-05 06:45+0000\n"
18+"X-Generator: Launchpad (build 17147)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
2121#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -99,7 +99,7 @@ msgstr "CANCELAR"
9999
100100#: qml/Components/PinLockscreen.qml:216
101101msgid "DONE"
102−msgstr ""
102+msgstr "FEITO"
103103
104104#: qml/Components/SeeMore.qml:35
105105msgid "See more"
@@ -115,7 +115,7 @@ msgstr "Recentes"
115115
116116#: qml/Dash/GenericScopeView.qml:330
117117msgid "See all"
118−msgstr ""
118+msgstr "Ver todos"
119119
120120#: qml/Dash/Previews/PreviewActionCombo.qml:34
121121msgid "More..."
@@ -171,11 +171,11 @@ msgstr "Por favor, digite o PIN do cartão SIM"
171171
172172#: qml/Panel/ActiveCallHint.qml:77
173173msgid "Tap to return to call..."
174−msgstr ""
174+msgstr "Toque para retornar para a chamada..."
175175
176176#: qml/Panel/ActiveCallHint.qml:90
177177msgid "Conference"
178−msgstr ""
178+msgstr "Conferência"
179179
180180#: qml/Panel/Indicators/MenuItemFactory.qml:583
181181msgid "In queue…"
@@ -187,7 +187,7 @@ msgstr "Baixando"
187187
188188#: qml/Panel/Indicators/MenuItemFactory.qml:589
189189msgid "Paused, tap to resume"
190−msgstr ""
190+msgstr "Em pausa, toque para continuar"
191191
192192#: qml/Panel/Indicators/MenuItemFactory.qml:591
193193msgid "Canceled"
@@ -195,11 +195,11 @@ msgstr "Cancelado"
195195
196196#: qml/Panel/Indicators/MenuItemFactory.qml:593
197197msgid "Finished"
198−msgstr ""
198+msgstr "Finalizado"
199199
200200#: qml/Panel/Indicators/MenuItemFactory.qml:595
201201msgid "Failed, tap to retry"
202−msgstr ""
202+msgstr "Falhou, toque para tentar novamente"
203203
204204#: qml/Panel/SearchIndicator.qml:27
205205msgid "Search"
po/ru.po+18−18
@@ -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-05-15 12:57+0000\n"
12−"Last-Translator: Zonov Roman <Unknown>\n"
11+"PO-Revision-Date: 2014-07-31 09:39+0000\n"
12+"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-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
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"
@@ -85,7 +85,7 @@ msgstr "Экстренный вызов"
8585
8686#: qml/Components/Lockscreen.qml:231
8787msgid "OK"
88−msgstr ""
88+msgstr "ОК"
8989
9090#: qml/Components/PassphraseLockscreen.qml:60
9191#, qt-format
@@ -98,11 +98,11 @@ msgstr "Привет"
9898
9999#: qml/Components/PinLockscreen.qml:198
100100msgid "CANCEL"
101−msgstr ""
101+msgstr "ОТМЕНИТЬ"
102102
103103#: qml/Components/PinLockscreen.qml:216
104104msgid "DONE"
105−msgstr ""
105+msgstr "ГОТОВО"
106106
107107#: qml/Components/SeeMore.qml:35
108108msgid "See more"
@@ -118,7 +118,7 @@ msgstr "Недавние"
118118
119119#: qml/Dash/GenericScopeView.qml:330
120120msgid "See all"
121−msgstr ""
121+msgstr "Показать всё"
122122
123123#: qml/Dash/Previews/PreviewActionCombo.qml:34
124124msgid "More..."
@@ -174,35 +174,35 @@ 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"
181−msgstr ""
181+msgstr "Конференция"
182182
183183#: qml/Panel/Indicators/MenuItemFactory.qml:583
184184msgid "In queue…"
185−msgstr ""
185+msgstr "В очереди..."
186186
187187#: qml/Panel/Indicators/MenuItemFactory.qml:587
188188msgid "Downloading"
189−msgstr ""
189+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"
197−msgstr ""
197+msgstr "Отменено"
198198
199199#: qml/Panel/Indicators/MenuItemFactory.qml:593
200200msgid "Finished"
201−msgstr ""
201+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/sl.po+4−4
@@ -8,13 +8,13 @@ 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-24 09:01+0000\n"
11+"PO-Revision-Date: 2014-07-28 12:32+0000\n"
1212"Last-Translator: Damir Jerovšek <Unknown>\n"
1313"Language-Team: Slovenian <sl@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"
17+"X-Launchpad-Export-Date: 2014-07-29 06:54+0000\n"
1818"X-Generator: Launchpad (build 17131)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -171,11 +171,11 @@ msgstr "Vnesite PIN kartice SIM"
171171
172172#: qml/Panel/ActiveCallHint.qml:77
173173msgid "Tap to return to call..."
174−msgstr ""
174+msgstr "Tapnite za vrnitev v klic ..."
175175
176176#: qml/Panel/ActiveCallHint.qml:90
177177msgid "Conference"
178−msgstr ""
178+msgstr "Konferenca"
179179
180180#: qml/Panel/Indicators/MenuItemFactory.qml:583
181181msgid "In queue…"
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"
po/ug.po+5−5
@@ -8,13 +8,13 @@ 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-21 02:30+0000\n"
11+"PO-Revision-Date: 2014-07-29 04:06+0000\n"
1212"Last-Translator: Gheyret T.Kenji <Unknown>\n"
1313"Language-Team: Uyghur Computer Science Association <UKIJ@yahoogroups.com>\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"
17+"X-Launchpad-Export-Date: 2014-07-31 07:15+0000\n"
1818"X-Generator: Launchpad (build 17131)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -113,7 +113,7 @@ msgstr "يېقىنقى"
113113
114114#: qml/Dash/GenericScopeView.qml:330
115115msgid "See all"
116−msgstr ""
116+msgstr "ھەممىنى كۆرۈش…"
117117
118118#: qml/Dash/Previews/PreviewActionCombo.qml:34
119119msgid "More..."
@@ -169,11 +169,11 @@ msgstr "سىم(SIM) كارتىڭىزنىڭ PIN نومۇرىنى كىرگۈزۈڭ
169169
170170#: qml/Panel/ActiveCallHint.qml:77
171171msgid "Tap to return to call..."
172−msgstr ""
172+msgstr "سۆزلىشىشكە قايتىش ئۈچۈن چېكىڭ…"
173173
174174#: qml/Panel/ActiveCallHint.qml:90
175175msgid "Conference"
176−msgstr ""
176+msgstr "يىغىن"
177177
178178#: qml/Panel/Indicators/MenuItemFactory.qml:583
179179msgid "In queue…"
po/uk.po+5−5
@@ -8,13 +8,13 @@ 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-13 17:03+0000\n"
11+"PO-Revision-Date: 2014-07-29 07:53+0000\n"
1212"Last-Translator: Yuri Chornoivan <yurchor@gmail.com>\n"
1313"Language-Team: Ukrainian <uk@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"
17+"X-Launchpad-Export-Date: 2014-07-31 07:15+0000\n"
1818"X-Generator: Launchpad (build 17131)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -116,7 +116,7 @@ msgstr "Нещодавні"
116116
117117#: qml/Dash/GenericScopeView.qml:330
118118msgid "See all"
119−msgstr ""
119+msgstr "Детальніше"
120120
121121#: qml/Dash/Previews/PreviewActionCombo.qml:34
122122msgid "More..."
@@ -172,11 +172,11 @@ msgstr "Введіть PIN-код SIM-картки"
172172
173173#: qml/Panel/ActiveCallHint.qml:77
174174msgid "Tap to return to call..."
175−msgstr ""
175+msgstr "Торкніться, щоб повернутися до дзвінка…"
176176
177177#: qml/Panel/ActiveCallHint.qml:90
178178msgid "Conference"
179−msgstr ""
179+msgstr "Конференція"
180180
181181#: qml/Panel/Indicators/MenuItemFactory.qml:583
182182msgid "In queue…"
po/zh_CN.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-27 15:48+0000\n"
12−"Last-Translator: Aron Xu <Unknown>\n"
11+"PO-Revision-Date: 2014-07-29 02:48+0000\n"
12+"Last-Translator: Luo Lei <luolei@ubuntukylin.com>\n"
1313"Language-Team: Chinese (Simplified) <zh_CN@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-28 06:44+0000\n"
17+"X-Launchpad-Export-Date: 2014-07-31 07:15+0000\n"
1818"X-Generator: Launchpad (build 17131)\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"
@@ -167,7 +167,7 @@ msgstr "请输入 SIM PIN"
167167
168168#: qml/Panel/ActiveCallHint.qml:77
169169msgid "Tap to return to call..."
170−msgstr ""
170+msgstr "点击回到通话..."
171171
172172#: qml/Panel/ActiveCallHint.qml:90
173173msgid "Conference"
@@ -175,7 +175,7 @@ msgstr "会议"
175175
176176#: qml/Panel/Indicators/MenuItemFactory.qml:583
177177msgid "In queue…"
178−msgstr ""
178+msgstr "排队中..."
179179
180180#: qml/Panel/Indicators/MenuItemFactory.qml:587
181181msgid "Downloading"
@@ -195,7 +195,7 @@ msgstr "已完成"
195195
196196#: qml/Panel/Indicators/MenuItemFactory.qml:595
197197msgid "Failed, tap to retry"
198−msgstr ""
198+msgstr "下载失败,请点击重试"
199199
200200#: qml/Panel/SearchIndicator.qml:27
201201msgid "Search"
qml/Components/Dialogs.qml+1
@@ -17,6 +17,7 @@
1717import QtQuick 2.0
1818
1919import Unity.Application 0.1
20+import Unity.Session 0.1
2021import Ubuntu.Components 0.1
2122import Ubuntu.Components.Popups 0.1
2223
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/Components/PassphraseLockscreen.qml+3
@@ -31,8 +31,10 @@ Item {
3131
3232 function clear(playAnimation) {
3333 pinentryField.text = "";
34+ pinentryField.enabled = true
3435 if (playAnimation) {
3536 wrongPasswordAnimation.start();
37+ pinentryField.forceActiveFocus();
3638 } else {
3739 pinentryField.focus = false
3840 }
@@ -78,6 +80,7 @@ Item {
7880
7981 onAccepted: {
8082 if (pinentryField.text) {
83+ pinentryField.enabled = false;
8184 root.entered(pinentryField.text);
8285 }
8386 }
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/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
qml/Shell.qml+27−21
@@ -244,6 +244,7 @@ Item {
244244 width: parent.width
245245 height: parent.height - panel.panelHeight
246246 background: shell.background
247+ alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
247248 minPinLength: 4
248249 maxPinLength: 4
249250
@@ -258,7 +259,7 @@ Item {
258259 onShownChanged: if (shown) greeter.fakeActiveForApp = ""
259260
260261 Component.onCompleted: {
261− if (LightDM.Users.count == 1) {
262+ if (greeter.narrowMode) {
262263 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
263264 }
264265 }
@@ -270,14 +271,17 @@ Item {
270271 onShowGreeter: greeter.show()
271272
272273 onShowPrompt: {
273− if (LightDM.Users.count == 1) {
274− // TODO: There's no better way for now to determine if its a PIN or a passphrase.
275− if (text == "PIN") {
276− lockscreen.alphaNumeric = false
277− } else {
278− lockscreen.alphaNumeric = true
279− }
280− lockscreen.placeholderText = i18n.tr("Please enter %1").arg(text);
274+ if (greeter.narrowMode) {
275+ lockscreen.placeholderText = i18n.tr("Please enter %1").arg(text.toLowerCase());
276+ lockscreen.show();
277+ }
278+ }
279+
280+ onPromptlessChanged: {
281+ if (LightDM.Greeter.promptless) {
282+ lockscreen.hide()
283+ } else {
284+ lockscreen.reset();
281285 lockscreen.show();
282286 }
283287 }
@@ -291,6 +295,9 @@ Item {
291295 greeter.login();
292296 } else {
293297 lockscreen.clear(true);
298+ if (greeter.narrowMode) {
299+ LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
300+ }
294301 }
295302 }
296303 }
@@ -345,25 +352,24 @@ Item {
345352
346353 function login() {
347354 enabled = false;
348− LightDM.Greeter.startSessionSync();
349− sessionStarted();
350− greeter.hide();
351− lockscreen.hide();
352− launcher.hide();
355+ if (LightDM.Greeter.startSessionSync()) {
356+ sessionStarted();
357+ greeter.hide();
358+ lockscreen.hide();
359+ launcher.hide();
360+ }
353361 enabled = true;
354362 }
355363
356364 onShownChanged: {
357365 if (shown) {
358− lockscreen.reset();
359− if (!LightDM.Greeter.promptless) {
360− lockscreen.show();
361− }
362− // If there is only one user, we start authenticating with that one here.
363− // If there are more users, the Greeter will handle that
364− if (LightDM.Users.count == 1) {
366+ if (greeter.narrowMode) {
365367 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
366368 }
369+ if (!LightDM.Greeter.promptless) {
370+ lockscreen.reset();
371+ lockscreen.show();
372+ }
367373 greeter.fakeActiveForApp = "";
368374 greeter.forceActiveFocus();
369375 }
run.sh+15−6
@@ -8,6 +8,7 @@ GDB=false
88FAKE=false
99PINLOCK=false
1010KEYLOCK=false
11+USE_MOCKS=false
1112MOUSE_TOUCH=true
1213
1314usage() {
@@ -31,9 +32,9 @@ eval set -- "$ARGS"
3132while [ $# -gt 0 ]
3233do
3334 case "$1" in
34− -f|--fake) FAKE=true;;
35− -p|--pinlock) PINLOCK=true;;
36− -k|--keylock) KEYLOCK=true;;
35+ -f|--fake) FAKE=true; USE_MOCKS=true;;
36+ -p|--pinlock) PINLOCK=true; USE_MOCKS=true;;
37+ -k|--keylock) KEYLOCK=true; USE_MOCKS=true;;
3738 -g|--gdb) GDB=true;;
3839 -h|--help) usage;;
3940 -m|--nomousetouch) MOUSE_TOUCH=false;;
@@ -43,20 +44,28 @@ do
4344done
4445
4546if $FAKE; then
46− export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$PWD/builddir/tests/mocks:$PWD/builddir/plugins:$PWD/builddir/modules
4747 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/LightDM/single
4848fi
4949
5050if $PINLOCK; then
51− export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$PWD/builddir/tests/mocks:$PWD/builddir/plugins:$PWD/builddir/modules
5251 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/LightDM/single-pin
5352fi
5453
5554if $KEYLOCK; then
56− export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$PWD/builddir/tests/mocks:$PWD/builddir/plugins:$PWD/builddir/modules
5755 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/LightDM/single-passphrase
5856fi
5957
58+if $USE_MOCKS; then
59+ rm -f $PWD/builddir/nonmirplugins/LightDM # undo symlink (from below) for cleanliness
60+ export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$PWD/builddir/tests/mocks:$PWD/builddir/plugins:$PWD/builddir/modules
61+else
62+ # Still fake no-login user for convenience (it's annoying to be prompted for your password when testing)
63+ # And in particular, just link our LightDM mock into the nonmirplugins folder. We don't want the rest of
64+ # our plugins to be used.
65+ ln -s $PWD/builddir/tests/mocks/LightDM $PWD/builddir/nonmirplugins/
66+ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/LightDM/single
67+fi
68+
6069QML_PHONE_SHELL_ARGS=""
6170if $MOUSE_TOUCH; then
6271 QML_PHONE_SHELL_ARGS="$QML_PHONE_SHELL_ARGS -mousetouch"
src/main.cpp+7
@@ -25,6 +25,7 @@
2525#include <QtQml/QQmlContext>
2626#include <QLibrary>
2727#include <QDebug>
28+#include <csignal>
2829#include <libintl.h>
2930
3031// local
@@ -140,6 +141,12 @@ Load the testability driver");
140141 view->setSource(source);
141142 QObject::connect(view->engine(), SIGNAL(quit()), application, SLOT(quit()));
142143
144+ if (!isMirServer && qEnvironmentVariableIsSet("UNITY_MIR_EMITS_SIGSTOP")) {
145+ // Emit SIGSTOP as expected by upstart, under Mir it's qtmir that will raise it.
146+ // see http://upstart.ubuntu.com/cookbook/#expect-stop
147+ raise(SIGSTOP);
148+ }
149+
143150 if (isMirServer || parser.isSet(fullscreenOption)) {
144151 view->showFullScreen();
145152 } else {
tests/autopilot/unity8/shell/tests/test_upstart.py+18−3
@@ -25,7 +25,8 @@ import signal
2525import subprocess
2626import time
2727
28−from testtools.matchers._basic import Equals
28+import fixtures
29+from testtools.matchers import Equals, MismatchError
2930from autopilot.matchers import Eventually
3031from autopilot.introspection import get_proxy_object_for_existing_process
3132
@@ -100,8 +101,21 @@ class UpstartIntegrationTests(UnityTestCase):
100101 emulator_base=UnityEmulatorBase,))
101102
102103 def test_no_sigstop(self):
103− self.patch_environment("UNITY_MIR_EMITS_SIGSTOP", "")
104+ self.useFixture(
105+ fixtures.EnvironmentVariable(
106+ 'UNITY_MIR_EMITS_SIGSTOP', newvalue=None))
104107 self._launch_unity()
108+
109+ try:
110+ self.assertThat(
111+ lambda: os.WIFSTOPPED(self._get_status()),
112+ Eventually(Equals(True)))
113+ except MismatchError:
114+ pass
115+ else:
116+ self.process.send_signal(signal.SIGCONT)
117+ self.fail('Unity8 raised SIGSTOP')
118+
105119 self._set_proxy()
106120
107121 logger.debug("Unity started, waiting for it to be ready.")
@@ -109,7 +123,8 @@ class UpstartIntegrationTests(UnityTestCase):
109123 logger.debug("Unity loaded and ready.")
110124
111125 def test_expect_sigstop(self):
112− self.patch_environment("UNITY_MIR_EMITS_SIGSTOP", "1")
126+ self.useFixture(
127+ fixtures.EnvironmentVariable('UNITY_MIR_EMITS_SIGSTOP', '1'))
113128 self._launch_unity()
114129 self.assertThat(
115130 lambda: os.WIFSTOPPED(self._get_status()),
tests/mocks/AccountsService/AccountsService.cpp+12−2
@@ -29,12 +29,14 @@ AccountsService::AccountsService(QObject* parent)
2929
3030QString AccountsService::user() const
3131{
32− return "testuser";
32+ return m_user;
3333}
3434
3535void AccountsService::setUser(const QString &user)
3636{
37− Q_UNUSED(user)
37+ m_user = user;
38+ Q_EMIT userChanged();
39+ Q_EMIT passwordDisplayHintChanged();
3840}
3941
4042bool AccountsService::demoEdges() const
@@ -68,3 +70,11 @@ void AccountsService::setStatsWelcomeScreen(bool statsWelcomeScreen)
6870 m_statsWelcomeScreen = statsWelcomeScreen;
6971 statsWelcomeScreenChanged();
7072}
73+
74+AccountsService::PasswordDisplayHint AccountsService::passwordDisplayHint() const
75+{
76+ if (m_user == "has-pin")
77+ return PasswordDisplayHint::Numeric;
78+ else
79+ return PasswordDisplayHint::Keyboard;
80+}
tests/mocks/AccountsService/AccountsService.h+12
@@ -27,6 +27,7 @@
2727class AccountsService: public QObject
2828{
2929 Q_OBJECT
30+ Q_ENUMS(PasswordDisplayHint)
3031 Q_PROPERTY (QString user
3132 READ user
3233 WRITE setUser
@@ -43,8 +44,16 @@ class AccountsService: public QObject
4344 READ statsWelcomeScreen
4445 WRITE setStatsWelcomeScreen // only available in mock
4546 NOTIFY statsWelcomeScreenChanged)
47+ Q_PROPERTY (PasswordDisplayHint passwordDisplayHint
48+ READ passwordDisplayHint
49+ NOTIFY passwordDisplayHintChanged)
4650
4751public:
52+ enum PasswordDisplayHint {
53+ Keyboard,
54+ Numeric,
55+ };
56+
4857 explicit AccountsService(QObject *parent = 0);
4958
5059 QString user() const;
@@ -55,15 +64,18 @@ public:
5564 void setBackgroundFile(const QString &backgroundFile);
5665 bool statsWelcomeScreen() const;
5766 void setStatsWelcomeScreen(bool statsWelcomeScreen);
67+ PasswordDisplayHint passwordDisplayHint() const;
5868
5969Q_SIGNALS:
6070 void userChanged();
6171 void demoEdgesChanged();
6272 void backgroundFileChanged();
6373 void statsWelcomeScreenChanged();
74+ void passwordDisplayHintChanged();
6475
6576private:
6677 QString m_backgroundFile;
78+ QString m_user;
6779 bool m_statsWelcomeScreen;
6880};
6981
tests/mocks/AccountsService/CMakeLists.txt+4−1
@@ -5,4 +5,7 @@ add_library(MockAccountsService-qml MODULE
55
66qt5_use_modules(MockAccountsService-qml DBus Qml)
77
8−add_unity8_mock(AccountsService 0.1 AccountsService TARGETS MockAccountsService-qml)
8+add_unity8_mock(AccountsService 0.1 AccountsService
9+ PREFIX mocks
10+ TARGETS MockAccountsService-qml
11+ )
tests/mocks/AccountsService/plugin.cpp+1
@@ -32,5 +32,6 @@ static QObject *service_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
3232void AccountsServicePlugin::registerTypes(const char *uri)
3333{
3434 Q_ASSERT(uri == QLatin1String("AccountsService"));
35+ qRegisterMetaType<AccountsService::PasswordDisplayHint>("AccountsService::PasswordDisplayHint");
3536 qmlRegisterSingletonType<AccountsService>(uri, 0, 1, "AccountsService", service_provider);
3637}
tests/mocks/LightDM/GreeterPrivate.h+2
@@ -24,6 +24,7 @@
2424namespace QLightDM
2525{
2626class Greeter;
27+class GreeterImpl;
2728
2829class GreeterPrivate
2930{
@@ -40,6 +41,7 @@ public:
4041 void handleRespond(const QString &response);
4142
4243protected:
44+ GreeterImpl *m_impl; // if the backend needs more private data
4345 Greeter * const q_ptr;
4446
4547private:
tests/mocks/LightDM/demo/CMakeLists.txt+4−2
@@ -11,11 +11,13 @@ set(LibLightDM_SOURCES
1111add_library(MockLightDM-demo STATIC ${LibLightDM_SOURCES})
1212
1313include_directories(
14+ ${CMAKE_CURRENT_BINARY_DIR}
1415 ${LIBUSERMETRICSOUTPUT_INCLUDE_DIRS}
1516)
1617
1718target_link_libraries(MockLightDM-demo
1819 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
19− )
20+ -lpam
21+)
2022
21−qt5_use_modules(MockLightDM-demo Gui)
23+qt5_use_modules(MockLightDM-demo Concurrent Gui)
tests/mocks/LightDM/demo/GreeterPrivate.cpp+202−30
@@ -18,53 +18,225 @@
1818
1919#include "../Greeter.h"
2020#include "../GreeterPrivate.h"
21−#include <QtCore/QDir>
22−#include <QtCore/QSettings>
21+#include <QFuture>
22+#include <QFutureInterface>
23+#include <QFutureWatcher>
24+#include <QQueue>
25+#include <QtConcurrent>
26+#include <QVector>
27+#include <security/pam_appl.h>
2328
2429namespace QLightDM
2530{
2631
32+class GreeterImpl : public QObject
33+{
34+ Q_OBJECT
35+
36+ typedef QFutureInterface<QString> ResponseFuture;
37+
38+public:
39+ explicit GreeterImpl(Greeter *parent, GreeterPrivate *greeterPrivate)
40+ : QObject(parent),
41+ greeter(parent),
42+ greeterPrivate(greeterPrivate),
43+ pamHandle(NULL)
44+ {
45+ qRegisterMetaType<QLightDM::GreeterImpl::ResponseFuture>("QLightDM::GreeterImpl::ResponseFuture");
46+
47+ connect(&futureWatcher, SIGNAL(finished()),
48+ this, SLOT(finishPam()));
49+ connect(this, SIGNAL(showMessage(QString, QLightDM::Greeter::MessageType)),
50+ greeter, SIGNAL(showMessage(QString, QLightDM::Greeter::MessageType)));
51+ // This next connect is how we pass ResponseFutures between threads
52+ connect(this, SIGNAL(showPrompt(QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)),
53+ this, SLOT(handlePrompt(QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)));
54+ }
55+
56+ void start(QString username)
57+ {
58+ // Clear out any existing PAM interactions first (we can't simply
59+ // cancel our QFuture because QtConcurrent::run doesn't support cancel)
60+ if (pamHandle != NULL) {
61+ pam_handle *handle = pamHandle;
62+ pamHandle = NULL; // to disable normal finishPam() handling
63+ while (respond(QString())); // clear our local queue of QFutures
64+ pam_end(handle, PAM_CONV_ERR);
65+ }
66+
67+ // Now actually start a new conversation with PAM
68+ pam_conv conversation;
69+ conversation.conv = converseWithPam;
70+ conversation.appdata_ptr = static_cast<void*>(this);
71+
72+ if (pam_start("lightdm", username.toUtf8(), &conversation, &pamHandle) == PAM_SUCCESS) {
73+ futureWatcher.setFuture(QtConcurrent::run(authenticateWithPam, pamHandle));
74+ } else {
75+ greeterPrivate->authenticated = false;
76+ Q_EMIT greeter->showMessage("Internal error: could not start PAM authentication", QLightDM::Greeter::MessageTypeError);
77+ Q_EMIT greeter->authenticationComplete();
78+ }
79+ }
80+
81+ static int authenticateWithPam(pam_handle* pamHandle)
82+ {
83+ int pamStatus = pam_authenticate(pamHandle, 0);
84+ if (pamStatus == PAM_SUCCESS) {
85+ pamStatus = pam_acct_mgmt(pamHandle, 0);
86+ }
87+ if (pamStatus == PAM_NEW_AUTHTOK_REQD) {
88+ pamStatus = pam_chauthtok(pamHandle, PAM_CHANGE_EXPIRED_AUTHTOK);
89+ }
90+ if (pamStatus == PAM_SUCCESS) {
91+ pam_setcred(pamHandle, PAM_REINITIALIZE_CRED);
92+ }
93+ return pamStatus;
94+ }
95+
96+ static int converseWithPam(int num_msg, const pam_message** msg,
97+ pam_response** resp, void* appdata_ptr)
98+ {
99+ if (num_msg <= 0)
100+ return PAM_CONV_ERR;
101+
102+ auto* tmp_response = static_cast<pam_response*>(calloc(num_msg, sizeof(pam_response)));
103+ if (!tmp_response)
104+ return PAM_CONV_ERR;
105+
106+ GreeterImpl* impl = static_cast<GreeterImpl*>(appdata_ptr);
107+
108+ int count;
109+ QVector<ResponseFuture> responses;
110+
111+ for (count = 0; count < num_msg; ++count)
112+ {
113+ switch (msg[count]->msg_style)
114+ {
115+ case PAM_PROMPT_ECHO_ON:
116+ {
117+ QString message(msg[count]->msg);
118+ responses.append(ResponseFuture());
119+ responses.last().reportStarted();
120+ Q_EMIT impl->showPrompt(message, Greeter::PromptTypeQuestion, responses.last());
121+ break;
122+ }
123+ case PAM_PROMPT_ECHO_OFF:
124+ {
125+ QString message(msg[count]->msg);
126+ responses.append(ResponseFuture());
127+ responses.last().reportStarted();
128+ Q_EMIT impl->showPrompt(message, Greeter::PromptTypeSecret, responses.last());
129+ break;
130+ }
131+ case PAM_TEXT_INFO:
132+ {
133+ QString message(msg[count]->msg);
134+ Q_EMIT impl->showMessage(message, Greeter::MessageTypeInfo);
135+ break;
136+ }
137+ default:
138+ {
139+ QString message(msg[count]->msg);
140+ Q_EMIT impl->showMessage(message, Greeter::MessageTypeError);
141+ break;
142+ }
143+ }
144+ }
145+
146+ int i = 0;
147+ bool raise_error = false;
148+
149+ for (auto &response : responses)
150+ {
151+ pam_response* resp_item = &tmp_response[i++];
152+ resp_item->resp_retcode = 0;
153+ resp_item->resp = strdup(response.future().result().toUtf8());
154+
155+ if (!resp_item->resp)
156+ {
157+ raise_error = true;
158+ break;
159+ }
160+ }
161+
162+ if (raise_error)
163+ {
164+ for (int i = 0; i < count; ++i)
165+ free(tmp_response[i].resp);
166+
167+ free(tmp_response);
168+ return PAM_CONV_ERR;
169+ }
170+ else
171+ {
172+ *resp = tmp_response;
173+ return PAM_SUCCESS;
174+ }
175+ }
176+
177+public Q_SLOTS:
178+ bool respond(QString response)
179+ {
180+ if (!futures.isEmpty()) {
181+ futures.dequeue().reportFinished(&response);
182+ return true;
183+ } else {
184+ return false;
185+ }
186+ }
187+
188+Q_SIGNALS:
189+ void showMessage(QString text, QLightDM::Greeter::MessageType type);
190+ void showPrompt(QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture response);
191+
192+private Q_SLOTS:
193+ void finishPam()
194+ {
195+ if (pamHandle == NULL) {
196+ return;
197+ }
198+
199+ int pamStatus = futureWatcher.result();
200+
201+ pam_end(pamHandle, pamStatus);
202+ pamHandle = NULL;
203+
204+ greeterPrivate->authenticated = (pamStatus == PAM_SUCCESS);
205+ Q_EMIT greeter->authenticationComplete();
206+ }
207+
208+ void handlePrompt(QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture future)
209+ {
210+ futures.enqueue(future);
211+ Q_EMIT greeter->showPrompt(text, type);
212+ }
213+
214+private:
215+ Greeter *greeter;
216+ GreeterPrivate *greeterPrivate;
217+ pam_handle* pamHandle;
218+ QFutureWatcher<int> futureWatcher;
219+ QQueue<ResponseFuture> futures;
220+};
221+
27222GreeterPrivate::GreeterPrivate(Greeter* parent)
28223 : authenticated(false),
29224 authenticationUser(),
225+ m_impl(new GreeterImpl(parent, this)),
30226 q_ptr(parent)
31227{
32228}
33229
34230void GreeterPrivate::handleAuthenticate()
35231{
36− Q_Q(Greeter);
37−
38− QSettings settings(QDir::homePath() + "/.unity8-greeter-demo", QSettings::NativeFormat);
39− settings.beginGroup(authenticationUser);
40− QVariant password = settings.value("password", "none");
41−
42− if (password == "pin") {
43− Q_EMIT q->showPrompt("PIN", Greeter::PromptTypeSecret);
44− } else if (password == "keyboard") {
45− Q_EMIT q->showPrompt("Password", Greeter::PromptTypeSecret);
46− } else {
47− authenticated = true;
48− Q_EMIT q->authenticationComplete();
49− }
232+ m_impl->start(authenticationUser);
50233}
51234
52235void GreeterPrivate::handleRespond(const QString &response)
53236{
54− Q_Q(Greeter);
55−
56− QSettings settings(QDir::homePath() + "/.unity8-greeter-demo", QSettings::NativeFormat);
57− settings.beginGroup(authenticationUser);
58− QVariant password = settings.value("password", "none");
59−
60− QString passwordValue;
61− if (password == "pin") {
62− passwordValue = settings.value("passwordValue", "1234").toString();
63− } else {
64− passwordValue = settings.value("passwordValue", "password").toString();
65− }
66− authenticated = (response == passwordValue);
67− Q_EMIT q->authenticationComplete();
237+ m_impl->respond(response);
68238}
69239
70240}
241+
242+#include "GreeterPrivate.moc"
tests/mocks/LightDM/demo/UsersModelPrivate.cpp+1−1
@@ -29,7 +29,7 @@ UsersModelPrivate::UsersModelPrivate(UsersModel* parent)
2929 : q_ptr(parent)
3030{
3131 QSettings settings(QDir::homePath() + "/.unity8-greeter-demo", QSettings::NativeFormat);
32− QStringList users = settings.value("users", QStringList() << "phablet").toStringList();
32+ QStringList users = settings.value("users", QStringList() << qgetenv("USER")).toStringList();
3333
3434 Q_FOREACH(const QString &user, users)
3535 {
tests/mocks/LightDM/full/GreeterPrivate.cpp+1−1
@@ -54,7 +54,7 @@ void GreeterPrivate::handleAuthenticate()
5454 authenticated = true;
5555 Q_EMIT q->authenticationComplete();
5656 } else if (authenticationUser == "has-pin"){
57− Q_EMIT q->showPrompt("PIN", Greeter::PromptTypeSecret);
57+ Q_EMIT q->showPrompt("Password:", Greeter::PromptTypeSecret);
5858 } else if (authenticationUser == "auth-error") {
5959 authenticated = false;
6060 Q_EMIT q->authenticationComplete();
tests/mocks/LightDM/single-pin/GreeterPrivate.cpp+1−1
@@ -32,7 +32,7 @@ GreeterPrivate::GreeterPrivate(Greeter* parent)
3232void GreeterPrivate::handleAuthenticate()
3333{
3434 Q_Q(Greeter);
35− Q_EMIT q->showPrompt("PIN", Greeter::PromptTypeSecret);
35+ Q_EMIT q->showPrompt("Password:", Greeter::PromptTypeSecret);
3636}
3737
3838void GreeterPrivate::handleRespond(const QString &response)
tests/mocks/LightDM/single-pin/UsersModelPrivate.cpp+1−1
@@ -26,7 +26,7 @@ UsersModelPrivate::UsersModelPrivate(UsersModel* parent)
2626{
2727 entries =
2828 {
29− { "single", "Has PIN", 0, 0, false, false, 0, 0 },
29+ { "has-pin", "Has PIN", 0, 0, false, false, 0, 0 },
3030 };
3131}
3232
tests/mocks/Unity/CMakeLists.txt+1−1
@@ -7,7 +7,7 @@ add_subdirectory(DashCommunicator)
77pkg_search_module(DEE dee-1.0 REQUIRED)
88pkg_search_module(GOBJECT gobject-2.0 REQUIRED)
99pkg_search_module(DEEQT libdee-qt5 REQUIRED)
10−pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=2)
10+pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=3)
1111
1212include_directories(
1313 ${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/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/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/Greeter/tst_Lockscreen.qml+11−5
@@ -18,8 +18,9 @@ import QtQuick 2.0
1818import QtTest 1.0
1919import ".."
2020import "../../../qml/Components"
21−import Ubuntu.Components 0.1
21+import AccountsService 0.1
2222import LightDM 0.1 as LightDM
23+import Ubuntu.Components 0.1
2324import Unity.Test 0.1 as UT
2425
2526Rectangle {
@@ -53,11 +54,16 @@ Rectangle {
5354 }
5455 }
5556
57+ function setUser(username) {
58+ AccountsService.user = username
59+ LightDM.Greeter.authenticate(username)
60+ }
61+
5662 Connections {
5763 target: LightDM.Greeter
5864
5965 onShowPrompt: {
60− if (text === "PIN") {
66+ if (AccountsService.passwordDisplayHint === AccountsService.Numeric) {
6167 pinPadCheckBox.checked = false
6268 } else {
6369 pinPadCheckBox.checked = true
@@ -139,12 +145,12 @@ Rectangle {
139145 Button {
140146 text: "start auth (1234)"
141147 width: parent.width
142− onClicked: LightDM.Greeter.authenticate("has-pin")
148+ onClicked: setUser("has-pin")
143149 }
144150 Button {
145151 text: "start auth (password)"
146152 width: parent.width
147− onClicked: LightDM.Greeter.authenticate("has-password")
153+ onClicked: setUser("has-password")
148154 }
149155
150156 TextField {
@@ -255,7 +261,7 @@ Rectangle {
255261 enteredLabel.text = ""
256262 minPinLengthTextField.text = data.minPinLength
257263 maxPinLengthTextField.text = data.maxPinLength
258− LightDM.Greeter.authenticate(data.username)
264+ setUser(data.username)
259265 waitForLockscreenReady();
260266
261267 var inputField = findChild(lockscreen, "pinentryField")