on xenial help sign in
merged with trunk
commit3ecd374ab8d631310f70bff1d2e5a7677ac5b1d0 authorNick Dedekind <nicholas.dedekind@gmail.com> dateWed 27 Aug 2014, 21:58 (12 years ago) parentd3821d42b59b8e
113 files changed+2760−753
data/CMakeLists.txt+3 data/unity8-dash.conf+2 data/unity8-dash.url-dispatcher+6 debian/changelog+43 debian/unity8.install+1 include/paths.h.in+1−1 plugins/AccountsService/AccountsServiceDBusAdaptor.cpp+1−1 plugins/Dash/CardAttributes.qml+7−3 plugins/Dash/CardCreator.js+9−5 plugins/Dash/ScopeStyle.qml+38−40 plugins/HudClient/hudclient.cpp+12−12 plugins/HudClient/volumepeakdetector.cpp+9−9 plugins/Powerd/Powerd.cpp+1−1 plugins/Unity/Indicators/menucontentactivator.cpp+2−2 plugins/Unity/Indicators/menucontentactivator.h+1−1 plugins/Unity/Indicators/modelprinter.cpp+3−3 plugins/Unity/Indicators/rootactionstate.cpp+4−4 plugins/Unity/Indicators/unitymenumodelcache.h+1−1 plugins/Unity/Indicators/unitymenumodelstack.cpp+4−4 plugins/Unity/Indicators/unitymenumodelstack.h+1−1 plugins/Utils/Style.js+37 plugins/Utils/qlimitproxymodelqml.cpp+6−6 plugins/Utils/qmldir+1 plugins/Utils/qsortfilterproxymodelqml.cpp+7−7 plugins/Utils/timeformatter.cpp+8−8 po/az.po+70−42 po/br.po+44−27 po/ca.po+4−4 po/de.po+9−9 po/fi.po+10−10 po/fr.po+6−6 po/nl.po+9−9 po/ro.po+6−6 po/ru.po+9−9 po/uk.po+9−9 po/unity8.pot+77−24 qml/Components/Background.qml+10 qml/Dash/Dash.qml+1−1 qml/Dash/DashContent.qml+29−4 qml/Dash/DashNavigation.qml+62−159 qml/Dash/DashNavigationButton.qml+226 qml/Dash/DashNavigationList.qml+21−4 qml/Dash/GenericScopeView.qml+136−38 qml/Dash/PageHeader.qml+81−27 qml/Dash/PreviewListView.qml+13−1 qml/Dash/Previews/PreviewActionButton.qml+1−4 qml/Dash/Previews/PreviewActionCombo.qml+4−2 qml/Dash/Previews/PreviewActions.qml+6 qml/Dash/Previews/PreviewExpandable.qml+3−2 qml/Dash/Previews/PreviewTable.qml+80 qml/Dash/Previews/PreviewWidgetFactory.qml+2−1 qml/Dash/ScopeSettings/ScopeSetting.qml+37 qml/Dash/ScopeSettings/ScopeSettingBoolean.qml+61 qml/Dash/ScopeSettings/ScopeSettingList.qml+50 qml/Dash/ScopeSettings/ScopeSettingNumber.qml+21 qml/Dash/ScopeSettings/ScopeSettingString.qml+83 qml/Dash/ScopeSettings/ScopeSettingsWidgetFactory.qml+57 qml/Dash/ScopeSettingsPage.qml+62 qml/Dash/ScopesOverview.qml+22−1 qml/Dash/graphics/PageHeaderBaseDivider@18.png qml/Dash/graphics/PageHeaderBaseDivider@18.sci−5 qml/Dash/graphics/navigation_shadow@18.png qml/Dash/graphics/navigation_shadow_light@18.png qml/Shell.qml+2−2 qml/Stages/SpreadDelegate.qml+3−7 qml/Stages/graphics/dropshadow.png qml/Stages/graphics/dropshadow2gu@30.png qml/Stages/graphics/dropshadow2gu@30.sci+5 tests/autopilot/unity8/shell/emulators/dash.py+5−5 tests/mocks/GSettings.1.0/fake_gsettings.h+2−2 tests/mocks/HudClient/fake_libhud_client.cpp+9−9 tests/mocks/LightDM/demo/GreeterPrivate.cpp+5−5 tests/mocks/QMenuModel/unitymenumodel.cpp+4−4 tests/mocks/QMenuModel/unitymenumodel.h+2−2 tests/mocks/QtMultimedia/audio.cpp+1−1 tests/mocks/Unity/Application/ApplicationInfo.h+3−3 tests/mocks/Unity/Application/ApplicationManager.h+1−1 tests/mocks/Unity/CMakeLists.txt+2 tests/mocks/Unity/fake_navigation.cpp+6−1 tests/mocks/Unity/fake_scope.cpp+18−3 tests/mocks/Unity/fake_scope.h+1 tests/mocks/Unity/fake_scopes.h+1−1 tests/mocks/Unity/fake_settingsmodel.cpp+95 tests/mocks/Unity/fake_settingsmodel.h+54 tests/mocks/Unity/fake_unity_plugin.cpp+2 tests/plugins/Dash/cardcreator/2.res+3−3 tests/plugins/Dash/cardcreator/5.res+5−3 tests/plugins/Dash/cardcreator/6.res+4−4 tests/plugins/Dash/cardcreator/7.res+5−4 tests/plugins/Dash/tst_CardAttributes.qml+4−1 tests/plugins/Dash/tst_ScopeStyle.qml+73−68 tests/plugins/Unity/Indicators/rootactionstatetest.cpp+4−4 tests/plugins/Unity/Indicators/unitymenumodelstacktest.cpp+1−1 tests/plugins/Utils/CMakeLists.txt+5 tests/plugins/Utils/tst_UtilsStyle.qml+47 tests/qmltests/CMakeLists.txt+6 tests/qmltests/Components/tst_Background.qml+5−4 tests/qmltests/Dash/Previews/tst_PreviewActions.qml+8−8 tests/qmltests/Dash/Previews/tst_PreviewExpandable.qml+5−1 tests/qmltests/Dash/Previews/tst_PreviewTable.qml+114 tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qml+2−1 tests/qmltests/Dash/ScopeSettings/tst_ScopeSettingBoolean.qml+62 tests/qmltests/Dash/ScopeSettings/tst_ScopeSettingList.qml+84 tests/qmltests/Dash/ScopeSettings/tst_ScopeSettingNumber.qml+98 tests/qmltests/Dash/ScopeSettings/tst_ScopeSettingString.qml+88 tests/qmltests/Dash/ScopeSettings/tst_ScopeSettingsWidgetFactory.qml+65 tests/qmltests/Dash/artwork/background.png tests/qmltests/Dash/tst_Card.qml+62−1 tests/qmltests/Dash/tst_DashContent.qml+152−48 tests/qmltests/Dash/tst_GenericScopeView.qml+102−39 tests/qmltests/Dash/tst_PageHeader.qml+3−1 tests/qmltests/tst_ShellWithPin.qml+2−2 tests/uqmlscene/main.cpp+1−1
data/CMakeLists.txt+3
@@ -8,3 +8,6 @@ install(FILES
88 ${CMAKE_CURRENT_BINARY_DIR}/${DASH_APP}.desktop
99 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
1010 )
11+
12+# install url dispatcher files
13+install(FILES unity8-dash.url-dispatcher DESTINATION ${CMAKE_INSTALL_DATADIR}/url-dispatcher/urls)
data/unity8-dash.conf+2
@@ -10,6 +10,8 @@ kill timeout 120
1010
1111respawn
1212
13+env APP_ID=unity8-dash
14+
1315pre-start script
1416 if [ -z "$UNITY_SCOPES_LIST" ]; then
1517 # FIXME: remove once we have this in dconf
data/unity8-dash.url-dispatchernew file+6
@@ -0,0 +1,6 @@
1+[
2+ {
3+ "protocol": "scope"
4+ }
5+]
6+
debian/changelog+43
@@ -1,3 +1,46 @@
1+unity8 (8.00+14.10.20140827.2-0ubuntu1) utopic; urgency=low
2+
3+ [ Michał Sawicz ]
4+ * Implement scope header customization options
5+ * Don't ignore empty attributes in CardAttributes.qml and improve its
6+ encapsulation (LP: #1355901)
7+ * Add support for preview button color customization. Deprecate
8+ support for action icons.
9+ * Add table preview widget
10+ * Make "See Less" get stuck at the bottom of the view
11+ * Support alternative navigation in the dash.
12+ * Add scope settings UI
13+ * Add scope favoriting support
14+ * Passcode, not PIN (LP: #1361114)
15+
16+ [ Jussi Pakkanen ]
17+ * Use nullptr instead of NULL.
18+
19+ [ Albert Astals ]
20+ * Add table preview widget
21+ * Make "See Less" get stuck at the bottom of the view
22+ * Support alternative navigation in the dash.
23+ * Make the PageHeaderLabelTest pass under valgrind
24+
25+ [ Benjamin Zeller ]
26+ * Add support for scope:// url in the dash (LP: #1361349)
27+
28+ [ Marcus Tomlinson ]
29+ * Handle the openScope signal in "tempScopeItem" (ScopesOverview.qml)
30+ as is done with "scopeItem" (Dash.qml) (LP: #1356410)
31+
32+ [ Daniel d'Andrada ]
33+ * Make "See Less" get stuck at the bottom of the view
34+
35+ [ Andrea Cimitan ]
36+ * Fix right padding on overlay card
37+ * Add scope settings UI
38+
39+ [ Michael Zanetti ]
40+ * use a smaller asset for the app's dropshadow (LP: #1359157)
41+
42+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 27 Aug 2014 15:37:05 +0000
43+
144unity8 (8.00+14.10.20140825.3-0ubuntu1) utopic; urgency=low
245
346 [ Daniel d'Andrada ]
debian/unity8.install+1
@@ -10,3 +10,4 @@ usr/share/unity8/Launcher
1010usr/share/unity8/Panel
1111usr/share/unity8/Shell.qml
1212usr/share/unity8/Stages
13+usr/share/url-dispatcher/urls/unity8-dash.url-dispatcher
include/paths.h.in+1−1
@@ -91,7 +91,7 @@ inline QString mockPluginsDir() {
9191inline QStringList shellDataDirs() {
9292 QStringList dirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
9393 if (!isRunningInstalled()) {
94− if (getenv("UNITY_TEST_ENV")==NULL) {
94+ if (getenv("UNITY_TEST_ENV")==nullptr) {
9595 dirs.prepend("@CMAKE_BINARY_DIR@/share");
9696 }
9797 }
plugins/AccountsService/AccountsServiceDBusAdaptor.cpp+1−1
@@ -24,7 +24,7 @@
2424
2525AccountsServiceDBusAdaptor::AccountsServiceDBusAdaptor(QObject* parent)
2626 : QObject(parent),
27− m_accountsManager(NULL),
27+ m_accountsManager(nullptr),
2828 m_users()
2929{
3030 QDBusConnection connection = QDBusConnection::SM_BUSNAME();
plugins/Dash/CardAttributes.qml+7−3
@@ -26,8 +26,10 @@ GridLayout {
2626 right: parent.right;
2727 }
2828 columns: 2 + repeater.count % 2
29+ rowSpacing: units.gu(.5)
2930 property alias model: repeater.model
30− property var color
31+ property color color: Theme.palette.normal.baseText
32+ property real fontScale: 1.0
3133
3234 Repeater {
3335 id: repeater
@@ -42,17 +44,19 @@ GridLayout {
4244 Layout.columnSpan: index == repeater.count - 1 && grid.columns == 3 && column == 1 ? 2 : 1
4345 Layout.maximumWidth: Math.max(icon.width, label.x + label.implicitWidth)
4446 Layout.fillWidth: true
47+ height: units.gu(2)
4548 StatusIcon {
4649 id: icon
4750 height: units.gu(2)
4851 sets: ["actions", "status", "apps"]
49− source: "icon" in modelData ? modelData["icon"] : ""
52+ source: "icon" in modelData && modelData["icon"] || ""
53+ color: grid.color
5054 }
5155 Label {
5256 id: label
5357 width: parent.width - x
5458 anchors.verticalCenter: parent.verticalCenter
55− text: "value" in modelData ? modelData["value"] : "";
59+ text: "value" in modelData && modelData["value"] || "";
5660 elide: Text.ElideRight
5761 maximumLineCount: 1
5862 font.weight: "style" in modelData && modelData["style"] == "highlighted" ? Font.DemiBold : Font.Light
plugins/Dash/CardCreator.js+9−5
@@ -29,7 +29,7 @@ var kBackgroundLoaderCode = 'Loader {\n\
2929 gradientColor: getColor(1) || color; \n\
3030 anchors.fill: parent; \n\
3131 image: backgroundImage.source ? backgroundImage : null; \n\
32− property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b; \n\
32+ property real luminance: Style.luminance(color); \n\
3333 property Image backgroundImage: Image { \n\
3434 objectName: "backgroundImage"; \n\
3535 source: { \n\
@@ -108,7 +108,7 @@ var kOverlayLoaderCode = 'Loader { \n\
108108 sourceComponent: ShaderEffect { \n\
109109 id: overlay; \n\
110110 height: (fixedHeaderHeight > 0 ? fixedHeaderHeight : headerHeight) + units.gu(2); \n\
111− property real luminance: 0.2126 * overlayColor.r + 0.7152 * overlayColor.g + 0.0722 * overlayColor.b; \n\
111+ property real luminance: Style.luminance(overlayColor); \n\
112112 property color overlayColor: cardData && cardData["overlayColor"] || "#99000000"; \n\
113113 property var source: ShaderEffectSource { \n\
114114 id: shaderSource; \n\
@@ -274,6 +274,7 @@ var kAttributesRowCode = 'CardAttributes { \n\
274274 objectName: "attributesRow"; \n\
275275 anchors { %1 } \n\
276276 color: %2; \n\
277+ fontScale: root.fontScale; \n\
277278 model: cardData && cardData["attributes"]; \n\
278279 }\n';
279280
@@ -439,14 +440,14 @@ function cardString(template, components) {
439440 mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible);
440441 }
441442
442− var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText)';
443+ var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white")';
443444
444445 var hasTitleContainer = hasTitle && (hasEmblem || (hasMascot && (hasSubtitle || hasAttributes)));
445446 var titleSubtitleCode = '';
446447 if (hasTitle) {
447448 var titleColor;
448449 if (headerAsOverlay) {
449− titleColor = 'overlayLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText)';
450+ titleColor = 'root.scopeStyle ? root.scopeStyle.getTextColor(overlayLoader.item.luminance) : (overlayLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white")';
450451 } else if (hasSummary) {
451452 titleColor = 'summary.color';
452453 } else if (hasBackground) {
@@ -466,6 +467,8 @@ function cardString(template, components) {
466467 if (!touchdownOnArtShape) {
467468 extraRightAnchor = 'rightMargin: units.gu(1); \n';
468469 extraLeftAnchor = 'leftMargin: units.gu(1); \n';
470+ } else if (headerAsOverlay && !hasEmblem) {
471+ extraRightAnchor = 'rightMargin: units.gu(1); \n';
469472 }
470473
471474 if (hasMascot) {
@@ -652,7 +655,8 @@ function cardString(template, components) {
652655function createCardComponent(parent, template, components) {
653656 var imports = 'import QtQuick 2.2; \n\
654657 import Ubuntu.Components 1.1; \n\
655− import Dash 0.1;\n';
658+ import Dash 0.1;\n\
659+ import Utils 0.1;\n';
656660 var card = cardString(template, components);
657661 var code = imports + 'Component {\n' + card + '}\n';
658662
plugins/Dash/ScopeStyle.qml+38−40
@@ -15,6 +15,8 @@
1515 */
1616
1717import QtQuick 2.2
18+import Utils 0.1
19+import Ubuntu.Components 1.1
1820
1921/*! \brief Helper for processing scope customization options.
2022
@@ -27,69 +29,65 @@ QtObject {
2729 /// Style object passed from the scope
2830 property var style: Object()
2931
30− /*! \brief Calculate luminance of the passed color
31−
32− \note If not fully opaque, luminance is dependant on blending.
33− */
34− function luminance(color) {
35− return 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
36− }
37−
3832 /// Color used for text and symbolic icons
3933 readonly property color foreground: "foreground-color" in style ? style["foreground-color"] : d.defaultDark
4034
4135 /// Luminance of the foreground color
42− readonly property real foregroundLuminance: foreground ? luminance(foreground) : d.defaultForegroundLuminance
36+ readonly property real foregroundLuminance: foreground ? Style.luminance(foreground) : Style.luminance(d.defaultDark)
4337
4438 /// Color used for the overall background
45− readonly property color background: "background-color" in style ? style["background-color"] : "transparent"
46−
47− /// Luminance of the foreground color
48− readonly property real backgroundLuminance: background ? luminance(background) : d.defaultBackgroundLuminance
49−
50− /*! \brief Luminance threshold for switching between fore and background color
51−
52− \note If background colour is not fully opaque, the defaultLightLuminance it's taken into account instead of it.
53− */
54− readonly property real threshold: background.a !== 1.0 ? (foregroundLuminance + d.defaultLightLuminance) / 2
55− : (foregroundLuminance + backgroundLuminance) / 2
56−
57− /*! \brief The lighter of foreground and background colors
58−
59− \note If background color is not fully opaque, it's not taken into account
60− and defaults to the theme-provided light color.
61− */
62− readonly property color light: {
63− if (background.a !== 1.0) return foregroundLuminance > d.defaultLightLuminance ? foreground : d.defaultLight;
64− return foregroundLuminance > backgroundLuminance ? foreground : background;
65− }
39+ readonly property color background: "background-color" in style ? style["background-color"] : "#00f5f5f5"
6640
67− /*! \brief The darker of foreground and background colors
41+ /// Luminance of the background color
42+ readonly property real backgroundLuminance: background ? Style.luminance(background) : Style.luminance(d.defaultLight)
6843
69− \note If background color is not fully opaque, it's not taken into account
70− and defaults to the theme-provided dark color.
44+ /*! \brief Get the most contrasting available color based on luminance
45+ *
46+ * If background color is transparent, theme provided colors are taken into account
7147 */
72− readonly property color dark: {
73− if (background.a !== 1.0) return foregroundLuminance < d.defaultDarkLuminance ? foreground : d.defaultDark;
74− return foregroundLuminance < backgroundLuminance ? foreground : background;
48+ function getTextColor(luminance) {
49+ if (Math.abs(foregroundLuminance - luminance) >
50+ Math.abs(d.opaqueBackgroundLuminance - luminance)) {
51+ return foreground;
52+ } else {
53+ return d.opaqueBackground;
54+ }
7555 }
7656
7757 /// Source of the logo image for the header
7858 readonly property url headerLogo: "logo" in d.headerStyle ? d.headerStyle["logo"] : ""
7959
8060 /// Background style for the header
81− readonly property url headerBackground: "background" in d.headerStyle ? d.headerStyle["background"] : ""
61+ readonly property url headerBackground: "background" in d.headerStyle ? d.headerStyle["background"] : "color:///#f5f5f5"
8262
8363 /// Foreground color for the header
8464 readonly property color headerForeground: "foreground-color" in d.headerStyle ? d.headerStyle["foreground-color"] : foreground
8565
66+ /// Color of the header divider
67+ readonly property color headerDividerColor: "divider-color" in d.headerStyle ? d.headerStyle["divider-color"] : "#e0e0e0"
68+
69+ /// Background style for the navigation
70+ readonly property url navigationBackground: "navigation-background" in d.headerStyle ? d.headerStyle["navigation-background"] : "color:///#f5f5f5"
71+
72+ /// Color of the primary preview button
73+ readonly property color previewButtonColor: "preview-button-color" in style ? style["preview-button-color"] : Theme.palette.selected.foreground
74+
8675 //! @cond
8776 property var d: QtObject {
8877 // FIXME: should be taken from the theme
8978 readonly property color defaultLight: "white"
90− readonly property color defaultDark: Theme.palette.normal.baseText
91− readonly property real defaultLightLuminance: luminance(defaultLight)
92− readonly property real defaultDarkLuminance: luminance(defaultDark)
79+ readonly property color defaultDark: UbuntuColors.darkGrey
80+ readonly property real defaultLightLuminance: Style.luminance(defaultLight)
81+ readonly property real defaultDarkLuminance: Style.luminance(defaultDark)
82+
83+ readonly property color opaqueBackground: {
84+ background.a > 0 ?
85+ background :
86+ (Math.abs(foregroundLuminance - defaultLightLuminance) >
87+ Math.abs(foregroundLuminance - defaultDarkLuminance)) ?
88+ defaultLight : defaultDark
89+ }
90+ readonly property real opaqueBackgroundLuminance: Style.luminance(opaqueBackground)
9391
9492 readonly property var headerStyle: "page-header" in style ? style["page-header"] : { }
9593 }
plugins/HudClient/hudclient.cpp+12−12
@@ -82,7 +82,7 @@ HudClient::HudClient()
8282 m_results = new DeeListModel();
8383 m_clientQuery = hud_client_query_new("");
8484 m_toolBarModel = new HudToolBarModel(m_clientQuery);
85− m_currentActionParam = NULL;
85+ m_currentActionParam = nullptr;
8686 m_results->setModel(hud_client_query_get_results_model(m_clientQuery));
8787
8888 g_signal_connect(G_OBJECT(m_clientQuery), "voice-query-loading", G_CALLBACK(loadingCB), this);
@@ -151,13 +151,13 @@ void HudClient::executeParametrizedAction(const QVariant &values)
151151 updateParametrizedAction(values);
152152 hud_client_param_send_commit(m_currentActionParam);
153153 g_object_unref(m_currentActionParam);
154− m_currentActionParam = NULL;
154+ m_currentActionParam = nullptr;
155155 Q_EMIT commandExecuted();
156156}
157157
158158void HudClient::updateParametrizedAction(const QVariant &values)
159159{
160− if (m_currentActionParam != NULL) {
160+ if (m_currentActionParam != nullptr) {
161161 const QVariantMap map = values.value<QVariantMap>();
162162 GActionGroup *ag = hud_client_param_get_actions(m_currentActionParam);
163163
@@ -179,10 +179,10 @@ void HudClient::updateParametrizedAction(const QVariant &values)
179179
180180void HudClient::cancelParametrizedAction()
181181{
182− if (m_currentActionParam != NULL) {
182+ if (m_currentActionParam != nullptr) {
183183 hud_client_param_send_cancel(m_currentActionParam);
184184 g_object_unref(m_currentActionParam);
185− m_currentActionParam = NULL;
185+ m_currentActionParam = nullptr;
186186 }
187187}
188188
@@ -212,9 +212,9 @@ void HudClient::executeCommand(int index)
212212 GVariant *is_parametrized = dee_model_get_value(model, iter, 7);
213213 if (g_variant_get_boolean(is_parametrized)) {
214214 m_currentActionParam = hud_client_query_execute_param_command(m_clientQuery, command_key, /* timestamp */ 0);
215− if (m_currentActionParam != NULL) {
215+ if (m_currentActionParam != nullptr) {
216216 GMenuModel *menuModel = hud_client_param_get_model (m_currentActionParam);
217− if (menuModel == NULL) {
217+ if (menuModel == nullptr) {
218218 g_signal_connect(m_currentActionParam, HUD_CLIENT_PARAM_SIGNAL_MODEL_READY, G_CALLBACK(modelReadyCB), this);
219219 } else {
220220 modelReady(false);
@@ -244,9 +244,9 @@ void HudClient::modelReady(bool needDisconnect)
244244}
245245
246246static void addAttribute(QVariantMap &properties, GMenuModel *menuModel, int item, const char *attribute) {
247− GVariant *v = g_menu_model_get_item_attribute_value(menuModel, item, attribute, NULL);
247+ GVariant *v = g_menu_model_get_item_attribute_value(menuModel, item, attribute, nullptr);
248248
249− if (v == NULL)
249+ if (v == nullptr)
250250 return;
251251
252252 properties.insert(attribute, DeeListModel::VariantForData(v));
@@ -264,10 +264,10 @@ void HudClient::modelReallyReady(bool needDisconnect)
264264 for (int i = 0; i < g_menu_model_get_n_items(menuModel); i++) {
265265 GVariant *v = g_menu_model_get_item_attribute_value(menuModel, i, "parameter-type", G_VARIANT_TYPE_STRING);
266266
267− if (v == NULL)
267+ if (v == nullptr)
268268 continue;
269269
270− const QString type = QString::fromUtf8(g_variant_get_string(v, NULL));
270+ const QString type = QString::fromUtf8(g_variant_get_string(v, nullptr));
271271 if (type == "slider") {
272272 const char *sliderAttributes[] = { "label", "min", "max", "step", "value", "live", "action" };
273273 QVariantMap properties;
@@ -283,7 +283,7 @@ void HudClient::modelReallyReady(bool needDisconnect)
283283 DeeModel *model = hud_client_query_get_results_model(m_clientQuery);
284284 DeeModelIter *iter = dee_model_get_iter_at_row(model, m_currentActionIndex);
285285 GVariant *actionTextVariant = dee_model_get_value(model, iter, 1);
286− const QString actionText = QString::fromUtf8(g_variant_get_string(actionTextVariant, NULL));
286+ const QString actionText = QString::fromUtf8(g_variant_get_string(actionTextVariant, nullptr));
287287 g_variant_unref(actionTextVariant);
288288 Q_EMIT showParametrizedAction(actionText, QVariant::fromValue(items));
289289}
plugins/HudClient/volumepeakdetector.cpp+9−9
@@ -45,15 +45,15 @@ PulseAudioVolumePeakDetector::PulseAudioVolumePeakDetector()
4545 : m_accumulatedValue(0)
4646 , m_nAccumulatedValues(0)
4747 , m_accumulatedValuesLimit(1)
48− , m_context(NULL)
49− , m_mainloop_api(NULL)
50− , m_stream(NULL)
48+ , m_context(nullptr)
49+ , m_mainloop_api(nullptr)
50+ , m_stream(nullptr)
5151{
5252}
5353
5454void PulseAudioVolumePeakDetector::start()
5555{
56− pa_mainloop *mainloop = NULL;
56+ pa_mainloop *mainloop = nullptr;
5757
5858 /* Set up a new main loop */
5959 mainloop = pa_mainloop_new();
@@ -63,7 +63,7 @@ void PulseAudioVolumePeakDetector::start()
6363 m_mainloop_api = pa_mainloop_get_api(mainloop);
6464
6565 /* Create a new connection context */
66− m_context = pa_context_new(m_mainloop_api, NULL);
66+ m_context = pa_context_new(m_mainloop_api, nullptr);
6767 if (!m_context) {
6868 goto quit;
6969 }
@@ -71,12 +71,12 @@ void PulseAudioVolumePeakDetector::start()
7171 pa_context_set_state_callback(m_context, context_state_callback, this);
7272
7373 /* Connect the context */
74− if (pa_context_connect(m_context, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0) {
74+ if (pa_context_connect(m_context, nullptr, PA_CONTEXT_NOFLAGS, nullptr) < 0) {
7575 goto quit;
7676 }
7777
7878 /* Run the main loop */
79− if (pa_mainloop_run(mainloop, NULL) < 0) {
79+ if (pa_mainloop_run(mainloop, nullptr) < 0) {
8080 goto quit;
8181 }
8282
@@ -130,7 +130,7 @@ void PulseAudioVolumePeakDetector::startStream()
130130 pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
131131 pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, "HUD Peak Detector");
132132
133− m_stream = pa_stream_new_with_proplist(m_context, NULL, &sample_spec, &channel_map, proplist);
133+ m_stream = pa_stream_new_with_proplist(m_context, nullptr, &sample_spec, &channel_map, proplist);
134134 if (!m_stream) {
135135 pa_proplist_free(proplist);
136136 quit();
@@ -144,7 +144,7 @@ void PulseAudioVolumePeakDetector::startStream()
144144 buffer_attr.maxlength = (uint32_t) -1;
145145 buffer_attr.fragsize = sizeof(float);
146146
147− if (pa_stream_connect_record(m_stream, NULL, &buffer_attr, (pa_stream_flags_t)(PA_STREAM_PEAK_DETECT | PA_STREAM_ADJUST_LATENCY)) < 0) {
147+ if (pa_stream_connect_record(m_stream, nullptr, &buffer_attr, (pa_stream_flags_t)(PA_STREAM_PEAK_DETECT | PA_STREAM_ADJUST_LATENCY)) < 0) {
148148 quit();
149149 }
150150}
plugins/Powerd/Powerd.cpp+1−1
@@ -39,7 +39,7 @@ void dimTimeoutChanged(GSettings *settings, const gchar *key, QDBusInterface *un
3939
4040Powerd::Powerd(QObject* parent)
4141 : QObject(parent),
42− unityScreen(NULL)
42+ unityScreen(nullptr)
4343{
4444 unityScreen = new QDBusInterface("com.canonical.Unity.Screen",
4545 "/com/canonical/Unity/Screen",
plugins/Unity/Indicators/menucontentactivator.cpp+2−2
@@ -45,7 +45,7 @@ public:
4545 m_baseIndex(0),
4646 m_delta(0),
4747 m_count(0),
48− m_timer(NULL),
48+ m_timer(nullptr),
4949 q(parent)
5050 {}
5151
@@ -55,7 +55,7 @@ public:
5555 m_content.clear();
5656 }
5757
58− int findNextInactiveDelta(bool* finished = NULL);
58+ int findNextInactiveDelta(bool* finished = nullptr);
5959
6060 static int content_count(QQmlListProperty<MenuContentState> *prop);
6161 static MenuContentState* content_at(QQmlListProperty<MenuContentState> *prop, int index);
plugins/Unity/Indicators/menucontentactivator.h+1−1
@@ -71,7 +71,7 @@ class UNITYINDICATORS_EXPORT MenuContentActivator : public QObject
7171 Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged)
7272 Q_PROPERTY(QQmlListProperty<MenuContentState> content READ content NOTIFY contentChanged DESIGNABLE false)
7373public:
74− MenuContentActivator(QObject* parent = NULL);
74+ MenuContentActivator(QObject* parent = nullptr);
7575 ~MenuContentActivator();
7676
7777 Q_INVOKABLE void restart();
plugins/Unity/Indicators/modelprinter.cpp+3−3
@@ -27,13 +27,13 @@
2727
2828ModelPrinter::ModelPrinter(QObject *parent)
2929 : QObject(parent)
30− , m_model(NULL)
30+ , m_model(nullptr)
3131{
3232}
3333
3434void ModelPrinter::setSourceModel(UnityMenuModel * sourceModel)
3535{
36− if (m_model != NULL) {
36+ if (m_model != nullptr) {
3737 disconnect(m_model);
3838 }
3939 if (m_model != sourceModel) {
@@ -41,7 +41,7 @@ void ModelPrinter::setSourceModel(UnityMenuModel * sourceModel)
4141 Q_EMIT modelChanged();
4242 Q_EMIT textChanged();
4343 }
44− if (m_model != NULL) {
44+ if (m_model != nullptr) {
4545 connect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(textChanged()));
4646 connect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(textChanged()));
4747 connect(m_model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)), SIGNAL(textChanged()));
plugins/Unity/Indicators/rootactionstate.cpp+4−4
@@ -31,7 +31,7 @@ extern "C" {
3131
3232RootActionState::RootActionState(QObject *parent)
3333 : ActionStateParser(parent),
34− m_menu(NULL)
34+ m_menu(nullptr)
3535{
3636}
3737
@@ -95,7 +95,7 @@ void RootActionState::onModelDataChanged(const QModelIndex& topLeft, const QMode
9595void RootActionState::reset()
9696{
9797 m_cachedState.clear();
98− m_menu = NULL;
98+ m_menu = nullptr;
9999
100100 Q_EMIT menuChanged();
101101 Q_EMIT updated();
@@ -171,7 +171,7 @@ static QString iconUri(GIcon *icon)
171171 const gchar* const* iconNames = g_themed_icon_get_names (G_THEMED_ICON (icon));
172172
173173 QStringList iconNameList;
174− for (uint index = 0; iconNames[index] != NULL; index++) {
174+ for (uint index = 0; iconNames[index] != nullptr; index++) {
175175 iconNameList << iconNames[index];
176176 }
177177
@@ -282,7 +282,7 @@ QVariant RootActionState::toQVariant(GVariant* state) const
282282 qmap["accessible-desc"] = accessible_name ? QString::fromUtf8(accessible_name) : "";
283283 qmap["visible"] = visible;
284284
285− gicon = g_icon_new_for_string (icon, NULL);
285+ gicon = g_icon_new_for_string (icon, nullptr);
286286 if (gicon) {
287287 qmap["icons"] = QStringList() << iconUri(gicon);
288288 g_object_unref (gicon);
plugins/Unity/Indicators/unitymenumodelcache.h+1−1
@@ -31,7 +31,7 @@ class UNITYINDICATORS_EXPORT UnityMenuModelCache : public QObject
3131{
3232 Q_OBJECT
3333public:
34− UnityMenuModelCache(QObject*parent=NULL);
34+ UnityMenuModelCache(QObject*parent=nullptr);
3535 ~UnityMenuModelCache();
3636
3737 Q_INVOKABLE UnityMenuModel* model(const QByteArray& bus,
plugins/Unity/Indicators/unitymenumodelstack.cpp+4−4
@@ -90,7 +90,7 @@ UnityMenuModelStack::~UnityMenuModelStack()
9090
9191UnityMenuModel* UnityMenuModelStack::head() const
9292{
93− return !m_menuModels.isEmpty() ? m_menuModels.first()->model() : NULL;
93+ return !m_menuModels.isEmpty() ? m_menuModels.first()->model() : nullptr;
9494}
9595
9696void UnityMenuModelStack::setHead(UnityMenuModel* model)
@@ -106,7 +106,7 @@ void UnityMenuModelStack::setHead(UnityMenuModel* model)
106106
107107UnityMenuModel* UnityMenuModelStack::tail() const
108108{
109− return !m_menuModels.isEmpty() ? m_menuModels.last()->model() : NULL;
109+ return !m_menuModels.isEmpty() ? m_menuModels.last()->model() : nullptr;
110110}
111111
112112int UnityMenuModelStack::count() const
@@ -127,7 +127,7 @@ void UnityMenuModelStack::push(UnityMenuModel* model, int index)
127127UnityMenuModel* UnityMenuModelStack::pop()
128128{
129129 if (m_menuModels.isEmpty()) {
130− return NULL;
130+ return nullptr;
131131 }
132132 UnityMenuModelEntry* entry = m_menuModels.takeLast();
133133 UnityMenuModel* model = entry->model();
@@ -135,7 +135,7 @@ UnityMenuModel* UnityMenuModelStack::pop()
135135
136136 Q_EMIT tailChanged(tail());
137137 if (m_menuModels.isEmpty()) {
138− Q_EMIT headChanged(NULL);
138+ Q_EMIT headChanged(nullptr);
139139 }
140140 Q_EMIT countChanged(m_menuModels.count());
141141
plugins/Unity/Indicators/unitymenumodelstack.h+1−1
@@ -40,7 +40,7 @@ class UNITYINDICATORS_EXPORT UnityMenuModelStack : public QObject
4040 Q_PROPERTY(UnityMenuModel* tail READ tail NOTIFY tailChanged)
4141 Q_PROPERTY(int count READ count NOTIFY countChanged)
4242public:
43− UnityMenuModelStack(QObject*parent=NULL);
43+ UnityMenuModelStack(QObject*parent=nullptr);
4444 ~UnityMenuModelStack();
4545
4646 UnityMenuModel* head() const;
plugins/Utils/Style.jsnew file+37
@@ -0,0 +1,37 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+.pragma library
18+
19+/*! \brief Calculate average luminance of the passed colors
20+
21+ \note If not fully opaque, luminance is dependant on blending.
22+ */
23+function luminance() {
24+ var sum = 0;
25+ // TODO this was originally
26+ // for (var k in arguments) {
27+ // but for some unkown reason was causing crashes in testDash/testDashContent
28+ // investigate when we have some time
29+ for (var k = 0; k < arguments.length; ++k) {
30+ // only way to convert string to color
31+ var c = Qt.lighter(arguments[k], 1.0);
32+
33+ sum += 0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b;
34+ }
35+
36+ return sum / arguments.length;
37+}
plugins/Utils/qlimitproxymodelqml.cpp+6−6
@@ -42,18 +42,18 @@ void
4242QLimitProxyModelQML::setModel(QAbstractItemModel *itemModel)
4343{
4444 if (itemModel != sourceModel()) {
45− if (sourceModel() != NULL) {
45+ if (sourceModel() != nullptr) {
4646 sourceModel()->disconnect(this);
4747 }
4848
4949 setSourceModel(itemModel);
5050
51− if (sourceModel() != NULL) {
51+ if (sourceModel() != nullptr) {
5252 // Disconnect the QIdentityProxyModel handling for rows removed/added...
53− disconnect(sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, NULL);
54− disconnect(sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, NULL);
55− disconnect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, NULL);
56− disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)), this, NULL);
53+ disconnect(sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, nullptr);
54+ disconnect(sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, nullptr);
55+ disconnect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, nullptr);
56+ disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)), this, nullptr);
5757
5858 // ... and use our own
5959 connect(sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
plugins/Utils/qmldir+1
@@ -1,3 +1,4 @@
11module Utils
22plugin Utils-qml
33typeinfo Utils.qmltypes
4+Style 0.1 Style.js
plugins/Utils/qsortfilterproxymodelqml.cpp+7−7
@@ -34,7 +34,7 @@ QSortFilterProxyModelQML::QSortFilterProxyModelQML(QObject *parent)
3434 */
3535int QSortFilterProxyModelQML::mapRowToSource(int row)
3636{
37− if (sourceModel() == NULL)
37+ if (sourceModel() == nullptr)
3838 return -1;
3939
4040 return QSortFilterProxyModel::mapToSource(index(row, 0)).row();
@@ -48,12 +48,12 @@ QHash<int, QByteArray> QSortFilterProxyModelQML::roleNames() const
4848void
4949QSortFilterProxyModelQML::setModel(QAbstractItemModel *itemModel)
5050{
51− if (itemModel == NULL) {
51+ if (itemModel == nullptr) {
5252 return;
5353 }
5454
5555 if (itemModel != sourceModel()) {
56− if (sourceModel() != NULL) {
56+ if (sourceModel() != nullptr) {
5757 sourceModel()->disconnect(this);
5858 }
5959
@@ -83,7 +83,7 @@ QSortFilterProxyModelQML::get(int row)
8383QVariant
8484QSortFilterProxyModelQML::data(int row, int role)
8585{
86− if (sourceModel() == NULL) {
86+ if (sourceModel() == nullptr) {
8787 return QVariant();
8888 }
8989
@@ -93,7 +93,7 @@ QSortFilterProxyModelQML::data(int row, int role)
9393int
9494QSortFilterProxyModelQML::totalCount() const
9595{
96− if (sourceModel() != NULL) {
96+ if (sourceModel() != nullptr) {
9797 return sourceModel()->rowCount();
9898 } else {
9999 return 0;
@@ -149,7 +149,7 @@ QSortFilterProxyModelQML::findFirst(int role, const QVariant& value) const
149149int
150150QSortFilterProxyModelQML::mapFromSource(int row)
151151{
152− if (sourceModel() != NULL) {
152+ if (sourceModel() != nullptr) {
153153 return QSortFilterProxyModel::mapFromSource(sourceModel()->index(row, 0)).row();
154154 } else {
155155 return -1;
@@ -159,7 +159,7 @@ QSortFilterProxyModelQML::mapFromSource(int row)
159159int
160160QSortFilterProxyModelQML::mapToSource(int row)
161161{
162− if (sourceModel() != NULL) {
162+ if (sourceModel() != nullptr) {
163163 return QSortFilterProxyModel::mapToSource(index(row, 0)).row();
164164 } else {
165165 return -1;
plugins/Utils/timeformatter.cpp+8−8
@@ -57,9 +57,9 @@ timedate1_properties_changed (GDBusConnection *connection,
5757 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)")))
5858 return;
5959
60− g_variant_get (parameters, "(s@a{sv}as)", NULL, &changed, &iter);
60+ g_variant_get (parameters, "(s@a{sv}as)", nullptr, &changed, &iter);
6161
62− if (g_variant_lookup (changed, "Timezone", "s", NULL)) {
62+ if (g_variant_lookup (changed, "Timezone", "s", nullptr)) {
6363 priv->formatter->update();
6464 }
6565 else {
@@ -81,10 +81,10 @@ got_bus(GObject *object, GAsyncResult *result, gpointer user_data)
8181 Q_UNUSED(object);
8282
8383 TimeFormatterPrivate *priv = (TimeFormatterPrivate *)user_data;
84− GError *error = NULL;
84+ GError *error = nullptr;
8585
8686 priv->system_bus = g_bus_get_finish (result, &error);
87− if (priv->system_bus == NULL) {
87+ if (priv->system_bus == nullptr) {
8888 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
8989 qWarning("TimeFormatter: cannot connect to the system bus: %s", error->message);
9090 g_error_free (error);
@@ -98,14 +98,14 @@ got_bus(GObject *object, GAsyncResult *result, gpointer user_data)
9898 * and keep the process alive.
9999 */
100100 priv->subscription_id = g_dbus_connection_signal_subscribe (priv->system_bus,
101− NULL, /* sender */
101+ nullptr, /* sender */
102102 "org.freedesktop.DBus.Properties",
103103 "PropertiesChanged",
104− NULL,
104+ nullptr,
105105 "org.freedesktop.timedate1",
106106 G_DBUS_SIGNAL_FLAGS_NONE,
107107 timedate1_properties_changed,
108− priv, NULL);
108+ priv, nullptr);
109109}
110110
111111TimeFormatter::TimeFormatter(QObject *parent): QObject(parent)
@@ -114,7 +114,7 @@ TimeFormatter::TimeFormatter(QObject *parent): QObject(parent)
114114 priv->formatter = this;
115115 priv->time = 0;
116116 priv->format = "yyyy-MM-dd hh:mm";
117− priv->system_bus = NULL;
117+ priv->system_bus = nullptr;
118118 priv->subscription_id = 0;
119119 priv->cancellable = g_cancellable_new ();
120120
po/az.po+70−42
@@ -8,43 +8,43 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-08-21 11:36+0200\n"
11−"PO-Revision-Date: 2014-01-15 09:50+0000\n"
11+"PO-Revision-Date: 2014-08-26 22:12+0000\n"
1212"Last-Translator: Turan Mahmudov <turan.mahmudov@gmail.com>\n"
1313"Language-Team: Azerbaijani <az@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-08-23 07:16+0000\n"
18−"X-Generator: Launchpad (build 17163)\n"
17+"X-Launchpad-Export-Date: 2014-08-27 07:46+0000\n"
18+"X-Generator: Launchpad (build 17174)\n"
1919
2020#: plugins/LightDM/Greeter.cpp:129
2121msgid "Password: "
22−msgstr ""
22+msgstr "Parol: "
2323
2424#: plugins/Unity/Launcher/launcheritem.cpp:43
2525#: plugins/Unity/Launcher/launcheritem.cpp:73
2626msgid "Pin shortcut"
27−msgstr ""
27+msgstr "Pin qısayolu"
2828
2929#: plugins/Unity/Launcher/launcheritem.cpp:73
3030msgid "Unpin shortcut"
31−msgstr ""
31+msgstr "Açma qısayolu"
3232
3333#: qml/Components/Dialogs.qml:142
3434msgid "Are you sure you would like to turn power off?"
35−msgstr ""
35+msgstr "Söndürmək istədiyinizə əminsiniz?"
3636
3737#: qml/Components/Dialogs.qml:144
3838msgid "Power off"
39−msgstr ""
39+msgstr "Söndür"
4040
4141#: qml/Components/Dialogs.qml:153
4242msgid "Restart"
43−msgstr ""
43+msgstr "Yenidən Başlat"
4444
4545#: qml/Components/Dialogs.qml:162
4646msgid "Cancel"
47−msgstr ""
47+msgstr "İmtina"
4848
4949#: qml/Components/EdgeDemoOverlay.qml:151
5050msgid "Skip intro"
@@ -92,14 +92,16 @@ msgid ""
9292"You have now mastered the edge gestures and can start using the "
9393"phone<br><br>Tap on the screen to start"
9494msgstr ""
95+"Siz künc jestlərini mənimsəmisiniz və telefonu istifadə etməyə başlaya "
96+"bilərsiniz.<br><br>Başlamaq üçün ekrana toxunun"
9597
9698#: qml/Components/Lockscreen.qml:167
9799msgid "Please wait"
98−msgstr ""
100+msgstr "Lütfən gözləyin"
99101
100102#: qml/Components/Lockscreen.qml:172
101103msgid "Too many incorrect attempts"
102−msgstr ""
104+msgstr "Çoxlu yanlış cəhd"
103105
104106#: qml/Components/Lockscreen.qml:206
105107msgid "Emergency Call"
@@ -107,7 +109,7 @@ msgstr "Zərurət zəngi"
107109
108110#: qml/Components/Lockscreen.qml:226
109111msgid "OK"
110−msgstr ""
112+msgstr "ОК"
111113
112114#: qml/Components/PassphraseLockscreen.qml:61
113115#, qt-format
@@ -120,11 +122,11 @@ msgstr "Xoş gördük"
120122
121123#: qml/Dash/GenericScopeView.qml:361
122124msgid "See less"
123−msgstr ""
125+msgstr "Daha az gör"
124126
125127#: qml/Dash/GenericScopeView.qml:361
126128msgid "See all"
127−msgstr ""
129+msgstr "Hamısını gör"
128130
129131#: qml/Dash/GenericScopeView.qml:424 qml/Panel/SearchIndicator.qml:27
130132msgid "Search"
@@ -132,11 +134,11 @@ msgstr "Axtar"
132134
133135#: qml/Dash/Previews/PreviewActionCombo.qml:34
134136msgid "More..."
135−msgstr ""
137+msgstr "Ətraflı..."
136138
137139#: qml/Dash/Previews/PreviewActionCombo.qml:34
138140msgid "Less..."
139−msgstr ""
141+msgstr "Az..."
140142
141143#: qml/Dash/Previews/PreviewRatingInput.qml:81
142144msgid "Rate this"
@@ -156,23 +158,23 @@ msgstr ""
156158
157159#: qml/Dash/ScopesOverview.qml:408
158160msgid "Done"
159−msgstr ""
161+msgstr "Başa çatdı"
160162
161163#: qml/Dash/ScopesOverview.qml:434
162164msgid "Store"
163−msgstr ""
165+msgstr "Saxla"
164166
165167#: qml/Dash/ScopesOverviewTab.qml:36
166168msgid "Favorites"
167−msgstr ""
169+msgstr "Seçilmişlər"
168170
169171#: qml/Dash/ScopesOverviewTab.qml:54
170172msgid "All"
171−msgstr ""
173+msgstr "Hamısı"
172174
173175#: qml/Greeter/Greeter.qml:157
174176msgid "Swipe to unlock"
175−msgstr ""
177+msgstr "Kilidi açmaq üçün sürüşdürün"
176178
177179#: qml/Hud/HudParametrizedActionsPage.qml:132
178180msgid "Confirm"
@@ -200,94 +202,120 @@ msgstr "Parolu göstər"
200202
201203#: qml/Notifications/NotificationMenuItemFactory.qml:120
202204msgid "Enter SIM PIN"
203−msgstr ""
205+msgstr "SIM PIN kodunuzu daxil edin"
204206
205207#: qml/Notifications/NotificationMenuItemFactory.qml:121 qml/Shell.qml:270
206208msgid "Sorry, incorrect PIN"
207−msgstr ""
209+msgstr "Təəssüf ki, PIN yanlışdır"
208210
209211#: qml/Panel/ActiveCallHint.qml:77
210212msgid "Tap to return to call..."
211−msgstr ""
213+msgstr "Zəngə qayıtmaq üçün toxunun..."
212214
213215#: qml/Panel/ActiveCallHint.qml:90
214216msgid "Conference"
215−msgstr ""
217+msgstr "Konfrans"
216218
217219#: qml/Panel/Indicators/MenuItemFactory.qml:651
218220msgid "In queue…"
219−msgstr ""
221+msgstr "Növbədədir..."
220222
221223#: qml/Panel/Indicators/MenuItemFactory.qml:655
222224msgid "Downloading"
223−msgstr ""
225+msgstr "Endirilir"
224226
225227#: qml/Panel/Indicators/MenuItemFactory.qml:657
226228msgid "Paused, tap to resume"
227−msgstr ""
229+msgstr "Saxlanıldı, davam etmək üçün toxunun"
228230
229231#: qml/Panel/Indicators/MenuItemFactory.qml:659
230232msgid "Canceled"
231−msgstr ""
233+msgstr "İmtina edildi"
232234
233235#: qml/Panel/Indicators/MenuItemFactory.qml:661
234236msgid "Finished"
235−msgstr ""
237+msgstr "Bitdi"
236238
237239#: qml/Panel/Indicators/MenuItemFactory.qml:663
238240msgid "Failed, tap to retry"
239−msgstr ""
241+msgstr "Alınmadı, davam etmək üçün toxunun"
240242
241243#: qml/Panel/Indicators/ModemInfoItem.qml:105
242244msgid "Unlock SIM"
243−msgstr ""
245+msgstr "SIM kilidini aç"
244246
245247#: qml/Panel/Indicators/RoamingIndication.qml:27
246248msgid "Roaming"
247−msgstr ""
249+msgstr "Rominq"
248250
249251#: qml/Shell.qml:266
250252msgid "Enter your passphrase"
251−msgstr ""
253+msgstr "Açar sözünüzü daxil edin"
252254
253255#: qml/Shell.qml:267
254256msgid "Sorry, incorrect passphrase"
255−msgstr ""
257+msgstr "Təəssüf ki, açar söz yanlışdır"
256258
257259#: qml/Shell.qml:269
258260msgid "Enter your PIN"
259−msgstr ""
261+msgstr "PIN kodunuzu daxil edin"
260262
261263#: qml/Shell.qml:273
262264#, qt-format
263265msgid "Enter your %1"
264−msgstr ""
266+msgstr "%1 daxil edin"
265267
266268#: qml/Shell.qml:274
267269#, qt-format
268270msgid "Sorry, incorrect %1"
269−msgstr ""
271+msgstr "Təəssüf ki, %1 yanlışdır"
270272
271273#: qml/Shell.qml:310
272274msgid "Sorry, incorrect passphrase."
273−msgstr ""
275+msgstr "Təəssüf ki, açar söz yanlışdır."
274276
275277#: qml/Shell.qml:311
276278msgid "Sorry, incorrect passcode."
277−msgstr ""
279+msgstr "Təəssüf ki, açar kod yanlışdır."
278280
279281#: qml/Shell.qml:312
280282msgid "This will be your last attempt."
281−msgstr ""
283+msgstr "Bu sizin son cəhdiniz olacaq."
282284
283285#: qml/Shell.qml:314
284286msgid ""
285287"If passphrase is entered incorrectly, your phone will conduct a factory "
286288"reset and all personal data will be deleted."
287289msgstr ""
290+"Əgər açar söz yanlış daxil edilibsə, onda telefonunuz fabrika parametrlərinə "
291+"geri qayıdacaq və bütün şəxsi məlumatlarınız silinəcək."
288292
289293#: qml/Shell.qml:315
290294msgid ""
291295"If passcode is entered incorrectly, your phone will conduct a factory reset "
292296"and all personal data will be deleted."
293297msgstr ""
298+"Əgər açar kod yanlış daxil edilibsə, onda telefonunuz fabrika parametrlərinə "
299+"geri qayıdacaq və bütün şəxsi məlumatlarınız silinəcək."
300+
301+#~ msgid "DONE"
302+#~ msgstr "OLDU"
303+
304+#~ msgid "CANCEL"
305+#~ msgstr "İMTİNA"
306+
307+#~ msgid "Please enter SIM PIN"
308+#~ msgstr "SIM PIN kodunu yazın"
309+
310+#, qt-format
311+#~ msgid "Incorrect %1"
312+#~ msgstr "Yanlış %1"
313+
314+#~ msgid "Please re-enter"
315+#~ msgstr "Lütfən yenidən daxil edin"
316+
317+#~ msgid "passphrase"
318+#~ msgstr "açar söz"
319+
320+#~ msgid "passcode"
321+#~ msgstr "açar kod"
po/br.po+44−27
@@ -8,18 +8,18 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-08-21 11:36+0200\n"
11−"PO-Revision-Date: 2014-07-28 06:44+0000\n"
11+"PO-Revision-Date: 2014-08-26 08:28+0000\n"
1212"Last-Translator: Fohanno Thierry <thierry.fohanno@ofis-bzh.org>\n"
1313"Language-Team: Breton <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-08-23 07:16+0000\n"
18−"X-Generator: Launchpad (build 17163)\n"
17+"X-Launchpad-Export-Date: 2014-08-27 07:46+0000\n"
18+"X-Generator: Launchpad (build 17174)\n"
1919
2020#: plugins/LightDM/Greeter.cpp:129
2121msgid "Password: "
22−msgstr ""
22+msgstr "Ger-tremen : "
2323
2424#: plugins/Unity/Launcher/launcheritem.cpp:43
2525#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -32,19 +32,19 @@ msgstr "Dispilhennañ ar verradenn"
3232
3333#: qml/Components/Dialogs.qml:142
3434msgid "Are you sure you would like to turn power off?"
35−msgstr ""
35+msgstr "Ha sur oc'h e fell deoc'h lazhañ ar benveg ?"
3636
3737#: qml/Components/Dialogs.qml:144
3838msgid "Power off"
39−msgstr ""
39+msgstr "Lazhañ"
4040
4141#: qml/Components/Dialogs.qml:153
4242msgid "Restart"
43−msgstr ""
43+msgstr "Adloc'hañ"
4444
4545#: qml/Components/Dialogs.qml:162
4646msgid "Cancel"
47−msgstr ""
47+msgstr "Nullañ"
4848
4949#: qml/Components/EdgeDemoOverlay.qml:151
5050msgid "Skip intro"
@@ -101,11 +101,11 @@ msgstr ""
101101
102102#: qml/Components/Lockscreen.qml:167
103103msgid "Please wait"
104−msgstr ""
104+msgstr "Gortozit, mar plij"
105105
106106#: qml/Components/Lockscreen.qml:172
107107msgid "Too many incorrect attempts"
108−msgstr ""
108+msgstr "Re a daolioù-esae direizh"
109109
110110#: qml/Components/Lockscreen.qml:206
111111msgid "Emergency Call"
@@ -158,23 +158,23 @@ msgstr "Kas"
158158
159159#: qml/Dash/ScopesOverview.qml:201
160160msgid "Manage Dash"
161−msgstr ""
161+msgstr "Merañ an daolenn-vourzh"
162162
163163#: qml/Dash/ScopesOverview.qml:408
164164msgid "Done"
165−msgstr ""
165+msgstr "Graet eo"
166166
167167#: qml/Dash/ScopesOverview.qml:434
168168msgid "Store"
169−msgstr ""
169+msgstr "Enrollañ"
170170
171171#: qml/Dash/ScopesOverviewTab.qml:36
172172msgid "Favorites"
173−msgstr ""
173+msgstr "Gwellañ deuet"
174174
175175#: qml/Dash/ScopesOverviewTab.qml:54
176176msgid "All"
177−msgstr ""
177+msgstr "An holl anezho"
178178
179179#: qml/Greeter/Greeter.qml:157
180180msgid "Swipe to unlock"
@@ -206,11 +206,11 @@ msgstr "Diskouez ar ger-tremen"
206206
207207#: qml/Notifications/NotificationMenuItemFactory.qml:120
208208msgid "Enter SIM PIN"
209−msgstr ""
209+msgstr "Ebarzhit kod PIN ar gartenn SIM"
210210
211211#: qml/Notifications/NotificationMenuItemFactory.qml:121 qml/Shell.qml:270
212212msgid "Sorry, incorrect PIN"
213−msgstr ""
213+msgstr "Siwazh, kod PIN direizh"
214214
215215#: qml/Panel/ActiveCallHint.qml:77
216216msgid "Tap to return to call..."
@@ -246,57 +246,61 @@ msgstr "C'hwitet, touchit evit adkregiñ"
246246
247247#: qml/Panel/Indicators/ModemInfoItem.qml:105
248248msgid "Unlock SIM"
249−msgstr ""
249+msgstr "Dibrennañ ar gartenn SIM"
250250
251251#: qml/Panel/Indicators/RoamingIndication.qml:27
252252msgid "Roaming"
253−msgstr ""
253+msgstr "O kantren"
254254
255255#: qml/Shell.qml:266
256256msgid "Enter your passphrase"
257−msgstr ""
257+msgstr "Ebarzhit ho frazenn-dremen"
258258
259259#: qml/Shell.qml:267
260260msgid "Sorry, incorrect passphrase"
261−msgstr ""
261+msgstr "Siwazh, frazenn-dremen direizh"
262262
263263#: qml/Shell.qml:269
264264msgid "Enter your PIN"
265−msgstr ""
265+msgstr "Ebarzhit ho kod PIN"
266266
267267#: qml/Shell.qml:273
268268#, qt-format
269269msgid "Enter your %1"
270−msgstr ""
270+msgstr "Ebarzhit ho %1"
271271
272272#: qml/Shell.qml:274
273273#, qt-format
274274msgid "Sorry, incorrect %1"
275−msgstr ""
275+msgstr "Siwazh, %1 direizh"
276276
277277#: qml/Shell.qml:310
278278msgid "Sorry, incorrect passphrase."
279−msgstr ""
279+msgstr "Siwazh, frazenn-dremen direizh"
280280
281281#: qml/Shell.qml:311
282282msgid "Sorry, incorrect passcode."
283−msgstr ""
283+msgstr "Siwazh, kod-tremen direizh"
284284
285285#: qml/Shell.qml:312
286286msgid "This will be your last attempt."
287−msgstr ""
287+msgstr "Ho taol-esae diwezhañ e vo."
288288
289289#: qml/Shell.qml:314
290290msgid ""
291291"If passphrase is entered incorrectly, your phone will conduct a factory "
292292"reset and all personal data will be deleted."
293293msgstr ""
294+"Ma vez ebarzhet ur frazenn-dremen direizh, e vo adderaouekaet ho pellgomzer "
295+"gant arventennoù al labouradeg hag e vo diverket an holl roadennoù personel"
294296
295297#: qml/Shell.qml:315
296298msgid ""
297299"If passcode is entered incorrectly, your phone will conduct a factory reset "
298300"and all personal data will be deleted."
299301msgstr ""
302+"Ma vez ebarzhet ur c'hod-tremen direizh, e vo adderaouekaet ho pellgomzer "
303+"gant arventennoù al labouradeg hag e vo diverket an holl roadennoù personel."
300304
301305#~ msgid "CANCEL"
302306#~ msgstr "NULLAÑ"
@@ -306,3 +310,16 @@ msgstr ""
306310
307311#~ msgid "Please enter SIM PIN"
308312#~ msgstr "Ebarzhit kod PIN ho kartenn SIM, mar plij"
313+
314+#~ msgid "passcode"
315+#~ msgstr "kod-tremen"
316+
317+#~ msgid "passphrase"
318+#~ msgstr "frazenn-dremen"
319+
320+#, qt-format
321+#~ msgid "Incorrect %1"
322+#~ msgstr "%1 direizh"
323+
324+#~ msgid "Please re-enter"
325+#~ msgstr "Ebarzhit en-dro, mar plij"
po/ca.po+4−4
@@ -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-08-21 11:36+0200\n"
11−"PO-Revision-Date: 2014-08-17 03:42+0000\n"
11+"PO-Revision-Date: 2014-08-26 17:37+0000\n"
1212"Last-Translator: David Planella <david.planella@ubuntu.com>\n"
1313"Language-Team: Catalan <ca@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-08-23 07:16+0000\n"
18−"X-Generator: Launchpad (build 17163)\n"
17+"X-Launchpad-Export-Date: 2014-08-27 07:46+0000\n"
18+"X-Generator: Launchpad (build 17174)\n"
1919
2020#: plugins/LightDM/Greeter.cpp:129
2121msgid "Password: "
@@ -207,7 +207,7 @@ msgstr "Mostra la contrasenya"
207207
208208#: qml/Notifications/NotificationMenuItemFactory.qml:120
209209msgid "Enter SIM PIN"
210−msgstr ""
210+msgstr "Introduïu el PIN de la tarjeta SIM"
211211
212212#: qml/Notifications/NotificationMenuItemFactory.qml:121 qml/Shell.qml:270
213213msgid "Sorry, incorrect PIN"
po/de.po+9−9
@@ -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-08-21 11:36+0200\n"
11−"PO-Revision-Date: 2014-08-21 16:18+0000\n"
11+"PO-Revision-Date: 2014-08-26 10:17+0000\n"
1212"Last-Translator: Martin Pitt <martin.pitt@ubuntu.com>\n"
1313"Language-Team: German <de@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-08-23 07:17+0000\n"
18−"X-Generator: Launchpad (build 17163)\n"
17+"X-Launchpad-Export-Date: 2014-08-27 07:46+0000\n"
18+"X-Generator: Launchpad (build 17174)\n"
1919
2020#: plugins/LightDM/Greeter.cpp:129
2121msgid "Password: "
@@ -204,11 +204,11 @@ msgstr "Passwort anzeigen"
204204
205205#: qml/Notifications/NotificationMenuItemFactory.qml:120
206206msgid "Enter SIM PIN"
207−msgstr ""
207+msgstr "Geben Sie die SIM-PIN ein"
208208
209209#: qml/Notifications/NotificationMenuItemFactory.qml:121 qml/Shell.qml:270
210210msgid "Sorry, incorrect PIN"
211−msgstr ""
211+msgstr "Die PIN ist nicht korrekt"
212212
213213#: qml/Panel/ActiveCallHint.qml:77
214214msgid "Tap to return to call..."
@@ -252,15 +252,15 @@ msgstr "Roaming"
252252
253253#: qml/Shell.qml:266
254254msgid "Enter your passphrase"
255−msgstr ""
255+msgstr "Geben Sie Ihr Passwort ein"
256256
257257#: qml/Shell.qml:267
258258msgid "Sorry, incorrect passphrase"
259−msgstr ""
259+msgstr "Das Passwort ist nicht korrekt"
260260
261261#: qml/Shell.qml:269
262262msgid "Enter your PIN"
263−msgstr ""
263+msgstr "Geben Sie Ihre PIN ein"
264264
265265#: qml/Shell.qml:273
266266#, qt-format
@@ -270,7 +270,7 @@ msgstr "%1 eingeben"
270270#: qml/Shell.qml:274
271271#, qt-format
272272msgid "Sorry, incorrect %1"
273−msgstr ""
273+msgstr "%1 falsch"
274274
275275#: qml/Shell.qml:310
276276msgid "Sorry, incorrect passphrase."
po/fi.po+10−10
@@ -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-08-21 11:36+0200\n"
11−"PO-Revision-Date: 2014-08-22 08:55+0000\n"
12−"Last-Translator: Timo Jyrinki <timo.jyrinki@canonical.com>\n"
11+"PO-Revision-Date: 2014-08-26 18:32+0000\n"
12+"Last-Translator: Jiri Grönroos <Unknown>\n"
1313"Language-Team: Finnish <fi@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-08-23 07:17+0000\n"
18−"X-Generator: Launchpad (build 17163)\n"
17+"X-Launchpad-Export-Date: 2014-08-27 07:46+0000\n"
18+"X-Generator: Launchpad (build 17174)\n"
1919
2020#: plugins/LightDM/Greeter.cpp:129
2121msgid "Password: "
@@ -201,11 +201,11 @@ msgstr "Näytä salasana"
201201
202202#: qml/Notifications/NotificationMenuItemFactory.qml:120
203203msgid "Enter SIM PIN"
204−msgstr ""
204+msgstr "Anna SIM-kortin PIN-koodi"
205205
206206#: qml/Notifications/NotificationMenuItemFactory.qml:121 qml/Shell.qml:270
207207msgid "Sorry, incorrect PIN"
208−msgstr ""
208+msgstr "Väärä PIN-koodi"
209209
210210#: qml/Panel/ActiveCallHint.qml:77
211211msgid "Tap to return to call..."
@@ -249,15 +249,15 @@ msgstr "Verkkovierailu"
249249
250250#: qml/Shell.qml:266
251251msgid "Enter your passphrase"
252−msgstr ""
252+msgstr "Anna tunnuslause"
253253
254254#: qml/Shell.qml:267
255255msgid "Sorry, incorrect passphrase"
256−msgstr ""
256+msgstr "Virheellinen tunnuslause"
257257
258258#: qml/Shell.qml:269
259259msgid "Enter your PIN"
260−msgstr ""
260+msgstr "Anna PIN-koodi"
261261
262262#: qml/Shell.qml:273
263263#, qt-format
@@ -267,7 +267,7 @@ msgstr "Anna %1"
267267#: qml/Shell.qml:274
268268#, qt-format
269269msgid "Sorry, incorrect %1"
270−msgstr ""
270+msgstr "Väärä %1"
271271
272272#: qml/Shell.qml:310
273273msgid "Sorry, incorrect passphrase."
po/fr.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-08-21 11:36+0200\n"
11−"PO-Revision-Date: 2014-08-16 12:13+0000\n"
12−"Last-Translator: Jean Marc <Unknown>\n"
11+"PO-Revision-Date: 2014-08-27 07:33+0000\n"
12+"Last-Translator: Sébastien Maes <nautilussebastien@gmail.com>\n"
1313"Language-Team: French <fr@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-08-23 07:17+0000\n"
18−"X-Generator: Launchpad (build 17163)\n"
17+"X-Launchpad-Export-Date: 2014-08-27 07:46+0000\n"
18+"X-Generator: Launchpad (build 17174)\n"
1919
2020#: plugins/LightDM/Greeter.cpp:129
2121msgid "Password: "
@@ -212,7 +212,7 @@ msgstr ""
212212
213213#: qml/Notifications/NotificationMenuItemFactory.qml:121 qml/Shell.qml:270
214214msgid "Sorry, incorrect PIN"
215−msgstr ""
215+msgstr "Désolé, code PIN incorrect"
216216
217217#: qml/Panel/ActiveCallHint.qml:77
218218msgid "Tap to return to call..."
@@ -264,7 +264,7 @@ msgstr ""
264264
265265#: qml/Shell.qml:269
266266msgid "Enter your PIN"
267−msgstr ""
267+msgstr "Entrez votre code PIN"
268268
269269#: qml/Shell.qml:273
270270#, qt-format
po/nl.po+9−9
@@ -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-08-21 11:36+0200\n"
11−"PO-Revision-Date: 2014-08-14 19:08+0000\n"
11+"PO-Revision-Date: 2014-08-26 19:39+0000\n"
1212"Last-Translator: rob <linuxned@gmail.com>\n"
1313"Language-Team: Dutch <nl@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-08-23 07:16+0000\n"
18−"X-Generator: Launchpad (build 17163)\n"
17+"X-Launchpad-Export-Date: 2014-08-27 07:46+0000\n"
18+"X-Generator: Launchpad (build 17174)\n"
1919
2020#: plugins/LightDM/Greeter.cpp:129
2121msgid "Password: "
@@ -201,11 +201,11 @@ msgstr "Wachtwoord tonen"
201201
202202#: qml/Notifications/NotificationMenuItemFactory.qml:120
203203msgid "Enter SIM PIN"
204−msgstr ""
204+msgstr "Voer SIM-pincode in"
205205
206206#: qml/Notifications/NotificationMenuItemFactory.qml:121 qml/Shell.qml:270
207207msgid "Sorry, incorrect PIN"
208−msgstr ""
208+msgstr "Sorry, onjuiste pincode"
209209
210210#: qml/Panel/ActiveCallHint.qml:77
211211msgid "Tap to return to call..."
@@ -249,15 +249,15 @@ msgstr "Roaming"
249249
250250#: qml/Shell.qml:266
251251msgid "Enter your passphrase"
252−msgstr ""
252+msgstr "Voer uw wachtwoordzin in"
253253
254254#: qml/Shell.qml:267
255255msgid "Sorry, incorrect passphrase"
256−msgstr ""
256+msgstr "Sorry, onjuiste wachtwoordzin"
257257
258258#: qml/Shell.qml:269
259259msgid "Enter your PIN"
260−msgstr ""
260+msgstr "Voer uw pincode in"
261261
262262#: qml/Shell.qml:273
263263#, qt-format
@@ -267,7 +267,7 @@ msgstr "Voer uw %1 in"
267267#: qml/Shell.qml:274
268268#, qt-format
269269msgid "Sorry, incorrect %1"
270−msgstr ""
270+msgstr "Sorry, onjuiste %1"
271271
272272#: qml/Shell.qml:310
273273msgid "Sorry, incorrect passphrase."
po/ro.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-08-21 11:36+0200\n"
11−"PO-Revision-Date: 2014-08-14 19:34+0000\n"
12−"Last-Translator: Marian Vasile <marianvasile1972@gmail.com>\n"
11+"PO-Revision-Date: 2014-08-26 18:53+0000\n"
12+"Last-Translator: Meriuță Cornel <meriutacornel@neacornel.eu>\n"
1313"Language-Team: Romanian <ro@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-08-23 07:17+0000\n"
18−"X-Generator: Launchpad (build 17163)\n"
17+"X-Launchpad-Export-Date: 2014-08-27 07:46+0000\n"
18+"X-Generator: Launchpad (build 17174)\n"
1919
2020#: plugins/LightDM/Greeter.cpp:129
2121msgid "Password: "
@@ -204,7 +204,7 @@ msgstr "Arată parola"
204204
205205#: qml/Notifications/NotificationMenuItemFactory.qml:120
206206msgid "Enter SIM PIN"
207−msgstr ""
207+msgstr "Introduceți codul PIN pentru SIM"
208208
209209#: qml/Notifications/NotificationMenuItemFactory.qml:121 qml/Shell.qml:270
210210msgid "Sorry, incorrect PIN"
@@ -252,7 +252,7 @@ msgstr "Roaming"
252252
253253#: qml/Shell.qml:266
254254msgid "Enter your passphrase"
255−msgstr ""
255+msgstr "Introduceți fraza dumneavoastră secretă"
256256
257257#: qml/Shell.qml:267
258258msgid "Sorry, incorrect passphrase"
po/ru.po+9−9
@@ -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-08-21 11:36+0200\n"
11−"PO-Revision-Date: 2014-08-20 12:41+0000\n"
11+"PO-Revision-Date: 2014-08-26 10:01+0000\n"
1212"Last-Translator: Jay ZDLin <Unknown>\n"
1313"Language-Team: Russian <ru@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-08-23 07:17+0000\n"
18−"X-Generator: Launchpad (build 17163)\n"
17+"X-Launchpad-Export-Date: 2014-08-27 07:46+0000\n"
18+"X-Generator: Launchpad (build 17174)\n"
1919
2020#: plugins/LightDM/Greeter.cpp:129
2121msgid "Password: "
@@ -206,11 +206,11 @@ msgstr "Показать пароль"
206206
207207#: qml/Notifications/NotificationMenuItemFactory.qml:120
208208msgid "Enter SIM PIN"
209−msgstr ""
209+msgstr "Введите ПИН-код SIM-карты"
210210
211211#: qml/Notifications/NotificationMenuItemFactory.qml:121 qml/Shell.qml:270
212212msgid "Sorry, incorrect PIN"
213−msgstr ""
213+msgstr "Введён неверный ПИН-код"
214214
215215#: qml/Panel/ActiveCallHint.qml:77
216216msgid "Tap to return to call..."
@@ -254,15 +254,15 @@ msgstr "Роуминг"
254254
255255#: qml/Shell.qml:266
256256msgid "Enter your passphrase"
257−msgstr ""
257+msgstr "Введите кодовую фразу"
258258
259259#: qml/Shell.qml:267
260260msgid "Sorry, incorrect passphrase"
261−msgstr ""
261+msgstr "Введена неверная кодовая фраза"
262262
263263#: qml/Shell.qml:269
264264msgid "Enter your PIN"
265−msgstr ""
265+msgstr "Введите ПИН-код"
266266
267267#: qml/Shell.qml:273
268268#, qt-format
@@ -272,7 +272,7 @@ msgstr "Введите %1"
272272#: qml/Shell.qml:274
273273#, qt-format
274274msgid "Sorry, incorrect %1"
275−msgstr ""
275+msgstr "Некорректный ввод %1"
276276
277277#: qml/Shell.qml:310
278278msgid "Sorry, incorrect passphrase."
po/uk.po+9−9
@@ -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-08-21 11:36+0200\n"
11−"PO-Revision-Date: 2014-08-17 14:49+0000\n"
11+"PO-Revision-Date: 2014-08-26 19:06+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-08-23 07:17+0000\n"
18−"X-Generator: Launchpad (build 17163)\n"
17+"X-Launchpad-Export-Date: 2014-08-27 07:46+0000\n"
18+"X-Generator: Launchpad (build 17174)\n"
1919
2020#: plugins/LightDM/Greeter.cpp:129
2121msgid "Password: "
@@ -204,11 +204,11 @@ msgstr "Показати пароль"
204204
205205#: qml/Notifications/NotificationMenuItemFactory.qml:120
206206msgid "Enter SIM PIN"
207−msgstr ""
207+msgstr "Введіть PIN-код SIM-картки"
208208
209209#: qml/Notifications/NotificationMenuItemFactory.qml:121 qml/Shell.qml:270
210210msgid "Sorry, incorrect PIN"
211−msgstr ""
211+msgstr "Вибачте, помилковий PIN-код"
212212
213213#: qml/Panel/ActiveCallHint.qml:77
214214msgid "Tap to return to call..."
@@ -252,15 +252,15 @@ msgstr "Роумінг"
252252
253253#: qml/Shell.qml:266
254254msgid "Enter your passphrase"
255−msgstr ""
255+msgstr "Введіть ваш пароль"
256256
257257#: qml/Shell.qml:267
258258msgid "Sorry, incorrect passphrase"
259−msgstr ""
259+msgstr "Вибачте, помилковий пароль"
260260
261261#: qml/Shell.qml:269
262262msgid "Enter your PIN"
263−msgstr ""
263+msgstr "Введіть ваш PIN-код"
264264
265265#: qml/Shell.qml:273
266266#, qt-format
@@ -270,7 +270,7 @@ msgstr "Вкажіть ваш %1"
270270#: qml/Shell.qml:274
271271#, qt-format
272272msgid "Sorry, incorrect %1"
273−msgstr ""
273+msgstr "Вибачте, помилковий %1"
274274
275275#: qml/Shell.qml:310
276276msgid "Sorry, incorrect passphrase."
po/unity8.pot+77−24
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2014-08-21 11:36+0200\n"
11+"POT-Creation-Date: 2014-08-26 18:24+0200\n"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,7 +17,7 @@ msgstr ""
1717"Content-Type: text/plain; charset=UTF-8\n"
1818"Content-Transfer-Encoding: 8bit\n"
1919
20−#: plugins/LightDM/Greeter.cpp:129
20+#: plugins/LightDM/Greeter.cpp:130
2121msgid "Password: "
2222msgstr ""
2323
@@ -30,19 +30,55 @@ msgstr ""
3030msgid "Unpin shortcut"
3131msgstr ""
3232
33−#: qml/Components/Dialogs.qml:142
34−msgid "Are you sure you would like to turn power off?"
33+#: qml/Components/Dialogs.qml:70
34+msgid "Log out"
35+msgstr ""
36+
37+#: qml/Components/Dialogs.qml:71
38+msgid "Are you sure you want to log out?"
39+msgstr ""
40+
41+#: qml/Components/Dialogs.qml:73 qml/Components/Dialogs.qml:97
42+#: qml/Components/Dialogs.qml:122
43+msgid "No"
44+msgstr ""
45+
46+#: qml/Components/Dialogs.qml:80 qml/Components/Dialogs.qml:104
47+#: qml/Components/Dialogs.qml:129
48+msgid "Yes"
49+msgstr ""
50+
51+#: qml/Components/Dialogs.qml:94
52+msgid "Shut down"
53+msgstr ""
54+
55+#: qml/Components/Dialogs.qml:95
56+msgid "Are you sure you want to shut down?"
57+msgstr ""
58+
59+#: qml/Components/Dialogs.qml:119
60+msgid "Reboot"
61+msgstr ""
62+
63+#: qml/Components/Dialogs.qml:120
64+msgid "Are you sure you want to reboot?"
3565msgstr ""
3666
3767#: qml/Components/Dialogs.qml:144
38−msgid "Power off"
68+msgid "Power"
3969msgstr ""
4070
41−#: qml/Components/Dialogs.qml:153
42−msgid "Restart"
71+#: qml/Components/Dialogs.qml:145
72+msgid ""
73+"Are you sure you would like\n"
74+"to power off?"
4375msgstr ""
4476
45−#: qml/Components/Dialogs.qml:162
77+#: qml/Components/Dialogs.qml:147
78+msgid "Power off"
79+msgstr ""
80+
81+#: qml/Components/Dialogs.qml:157
4682msgid "Cancel"
4783msgstr ""
4884
@@ -125,10 +161,23 @@ msgstr ""
125161msgid "See all"
126162msgstr ""
127163
128−#: qml/Dash/GenericScopeView.qml:424 qml/Panel/SearchIndicator.qml:27
164+#: qml/Dash/GenericScopeView.qml:424 qml/Dash/PageHeader.qml:260
165+#: qml/Panel/SearchIndicator.qml:27
129166msgid "Search"
130167msgstr ""
131168
169+#: qml/Dash/PageHeader.qml:270
170+msgid "Settings"
171+msgstr ""
172+
173+#: qml/Dash/PageHeader.qml:279
174+msgid "Remove from Favorites"
175+msgstr ""
176+
177+#: qml/Dash/PageHeader.qml:279
178+msgid "Add to Favorites"
179+msgstr ""
180+
132181#: qml/Dash/Previews/PreviewActionCombo.qml:34
133182msgid "More..."
134183msgstr ""
@@ -153,11 +202,11 @@ msgstr ""
153202msgid "Manage Dash"
154203msgstr ""
155204
156−#: qml/Dash/ScopesOverview.qml:408
205+#: qml/Dash/ScopesOverview.qml:417
157206msgid "Done"
158207msgstr ""
159208
160−#: qml/Dash/ScopesOverview.qml:434
209+#: qml/Dash/ScopesOverview.qml:443
161210msgid "Store"
162211msgstr ""
163212
@@ -173,7 +222,7 @@ msgstr ""
173222msgid "Swipe to unlock"
174223msgstr ""
175224
176−#: qml/Hud/HudParametrizedActionsPage.qml:132
225+#: qml/Hud/HudParametrizedActionsPage.qml:139
177226msgid "Confirm"
178227msgstr ""
179228
@@ -201,7 +250,7 @@ msgstr ""
201250msgid "Enter SIM PIN"
202251msgstr ""
203252
204−#: qml/Notifications/NotificationMenuItemFactory.qml:121 qml/Shell.qml:270
253+#: qml/Notifications/NotificationMenuItemFactory.qml:121
205254msgid "Sorry, incorrect PIN"
206255msgstr ""
207256
@@ -245,47 +294,51 @@ msgstr ""
245294msgid "Roaming"
246295msgstr ""
247296
248−#: qml/Shell.qml:266
297+#: qml/Shell.qml:279
249298msgid "Enter your passphrase"
250299msgstr ""
251300
252−#: qml/Shell.qml:267
301+#: qml/Shell.qml:280
253302msgid "Sorry, incorrect passphrase"
254303msgstr ""
255304
256−#: qml/Shell.qml:269
257−msgid "Enter your PIN"
305+#: qml/Shell.qml:282
306+msgid "Enter your passcode"
307+msgstr ""
308+
309+#: qml/Shell.qml:283
310+msgid "Sorry, incorrect passcode"
258311msgstr ""
259312
260−#: qml/Shell.qml:273
313+#: qml/Shell.qml:286
261314#, qt-format
262315msgid "Enter your %1"
263316msgstr ""
264317
265−#: qml/Shell.qml:274
318+#: qml/Shell.qml:287
266319#, qt-format
267320msgid "Sorry, incorrect %1"
268321msgstr ""
269322
270−#: qml/Shell.qml:310
323+#: qml/Shell.qml:323
271324msgid "Sorry, incorrect passphrase."
272325msgstr ""
273326
274−#: qml/Shell.qml:311
327+#: qml/Shell.qml:324
275328msgid "Sorry, incorrect passcode."
276329msgstr ""
277330
278−#: qml/Shell.qml:312
331+#: qml/Shell.qml:325
279332msgid "This will be your last attempt."
280333msgstr ""
281334
282−#: qml/Shell.qml:314
335+#: qml/Shell.qml:327
283336msgid ""
284337"If passphrase is entered incorrectly, your phone will conduct a factory "
285338"reset and all personal data will be deleted."
286339msgstr ""
287340
288−#: qml/Shell.qml:315
341+#: qml/Shell.qml:328
289342msgid ""
290343"If passcode is entered incorrectly, your phone will conduct a factory reset "
291344"and all personal data will be deleted."
qml/Components/Background.qml+10
@@ -15,6 +15,8 @@
1515 */
1616
1717import QtQuick 2.0
18+import Ubuntu.Components 1.1
19+import Utils 0.1 as Utils
1820
1921Loader {
2022 id: root
@@ -23,6 +25,14 @@ Loader {
2325 property url style
2426 readonly property var parsedStyle: String(style)
2527 .match(/^(color|gradient):\/\/\/(?:(#(?:[0-9a-f]{3,4}){1,2}|[a-z]{3,}))(?:\/(#(?:[0-9a-f]{3,4}){1,2}|[a-z]{3,}))?\/?$/i)
28+ readonly property var luminance: {
29+ if (!parsedStyle) return 0.5;
30+ if (parsedStyle[1] === "color") return Utils.Style.luminance(parsedStyle[2]);
31+ else if (parsedStyle[1] === "gradient") return Utils.Style.luminance(parsedStyle[2], parsedStyle[3]);
32+ }
33+
34+ // FIXME this is only here for highlight purposes (see DashNavigation.qml, too)
35+ readonly property color topColor: parsedStyle ? parsedStyle[2] : UbuntuColors.lightGrey
2636
2737 sourceComponent: {
2838 if (style == "") return null;
qml/Dash/Dash.qml+1−1
@@ -341,7 +341,7 @@ Showable {
341341 objectName: "overviewDragHandle"
342342 z: 1
343343 direction: Direction.Upwards
344− enabled: !dashContent.previewShown &&
344+ enabled: !dashContent.subPageShown &&
345345 dashContent.currentScope &&
346346 dashContent.currentScope.searchQuery == "" &&
347347 (overviewController.progress == 0 || dragging)
qml/Dash/DashContent.qml+29−4
@@ -27,8 +27,8 @@ Item {
2727 readonly property alias currentIndex: dashContentList.currentIndex
2828 readonly property string currentScopeId: dashContentList.currentItem ? dashContentList.currentItem.scopeId : ""
2929 readonly property var currentScope: dashContentList.currentItem ? dashContentList.currentItem.theScope : null
30− readonly property bool previewShown: dashContentList.currentItem && dashContentList.currentItem.item ?
31− dashContentList.currentItem.item.previewShown : false
30+ readonly property bool subPageShown: dashContentList.currentItem && dashContentList.currentItem.item ?
31+ dashContentList.currentItem.item.subPageShown : false
3232 readonly property bool processing: dashContentList.currentItem && dashContentList.currentItem.item
3333 && dashContentList.currentItem.item.processing || false
3434 readonly property bool pageHeaderTotallyVisible: dashContentList.currentItem && dashContentList.currentItem.item
@@ -52,6 +52,11 @@ Item {
5252 }
5353 }
5454
55+ Connections {
56+ target: UriHandler
57+ onOpened: dashContentList.currentItem.theScope.performQuery(uris[0])
58+ }
59+
5560 function setCurrentScopeAtIndex(index, animate, reset) {
5661 // if the scopes haven't loaded yet, then wait until they are.
5762 if (!scopes.loaded) {
@@ -96,8 +101,7 @@ Item {
96101 id: dashContentList
97102 objectName: "dashContentList"
98103
99− interactive: dashContent.scopes.loaded && currentItem && !currentItem.moving && !currentItem.navigationShown
100−
104+ interactive: dashContent.scopes.loaded && currentItem && !currentItem.moving && !currentItem.navigationShown && !currentItem.subPageShown
101105 anchors.fill: parent
102106 orientation: ListView.Horizontal
103107 boundsBehavior: Flickable.DragAndOvershootBounds
@@ -111,6 +115,26 @@ Item {
111115 onMovementStarted: currentItem.item.showHeader();
112116 clip: parent.x != 0
113117
118+ // TODO QTBUG-40846 and QTBUG-40848
119+ // The remove transition doesn't happen when removing the last item
120+ // And can't work around it because index is reset to -1 regardless of
121+ // ListView.delayRemove
122+
123+ remove: Transition {
124+ SequentialAnimation {
125+ PropertyAction { property: "layer.enabled"; value: true }
126+ PropertyAction { property: "ListView.delayRemove"; value: true }
127+ ParallelAnimation {
128+ PropertyAnimation { properties: "scale"; to: 0.25; duration: UbuntuAnimation.SnapDuration }
129+ PropertyAnimation { properties: "y"; to: dashContent.height; duration: UbuntuAnimation.SnapDuration }
130+ }
131+ PropertyAction { property: "ListView.delayRemove"; value: false }
132+ }
133+ }
134+ removeDisplaced: Transition {
135+ PropertyAnimation { property: "x"; duration: UbuntuAnimation.SnapDecision }
136+ }
137+
114138 // If the number of items is less than the current index, then need to reset to another item.
115139 onCountChanged: {
116140 if (count > 0) {
@@ -137,6 +161,7 @@ Item {
137161
138162 readonly property bool moving: item ? item.moving : false
139163 readonly property bool navigationShown: item ? item.navigationShown : false
164+ readonly property bool subPageShown: item ? item.subPageShown : false
140165 readonly property var categoryView: item ? item.categoryView : null
141166 readonly property var theScope: scope
142167
qml/Dash/DashNavigation.qml+62−159
@@ -16,195 +16,98 @@
1616
1717import QtQuick 2.2
1818import Ubuntu.Components 1.1
19+import Dash 0.1
20+import "../Components"
1921
20−AbstractButton {
22+Item {
2123 id: root
2224 objectName: "dashNavigation"
2325
2426 property var scope: null
25−
26− property bool showList: false
27−
28− readonly property var currentNavigation: scope && scope.hasNavigation ? scope.getNavigation(scope.currentNavigationId) : null
27+ property var scopeStyle: null
2928
3029 property alias windowWidth: blackRect.width
3130 property alias windowHeight: blackRect.height
32− property var scopeStyle: null
31+ readonly property var openList: {
32+ if (navigationButton.showList) return navigationButton.listView;
33+ if (altNavigationButton.showList) return altNavigationButton.listView;
34+ return null;
35+ }
3336
34− // Are we drilling down the tree or up?
35− property bool isGoingBack: false
37+ // FIXME this is only here for highlight purposes (see Background.qml, too)
38+ readonly property var background: backgroundItem
3639
37− visible: root.currentNavigation != null
40+ visible: navigationButton.currentNavigation || altNavigationButton.currentNavigation
3841
3942 height: visible ? units.gu(5) : 0
4043
41− onClicked: {
42− navigationListView.updateMaxHeight();
43− root.showList = !root.showList;
44+ QtObject {
45+ id: d
46+ readonly property color foregroundColor: root.scopeStyle
47+ ? root.scopeStyle.getTextColor(backgroundItem.luminance)
48+ : Theme.palette.normal.baseText
49+ readonly property bool bothVisible: altNavigationButton.visible && navigationButton.visible
50+ readonly property real navigationWidth: root.width >= units.gu(60) ? units.gu(40) : root.width
51+ readonly property real buttonWidth: navigationWidth / (bothVisible ? 2 : 1)
4452 }
4553
4654 Rectangle {
4755 id: blackRect
4856 color: "black"
49− opacity: navigationListView.currentItem && navigationListView.currentItem.visible ? 0.3 : 0
57+ opacity: openList && openList.currentItem && openList.currentItem.visible ? 0.5 : 0
5058 Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
51− anchors.top: navigationListView.top
52− anchors.right: parent.right
53− visible: opacity != 0
59+ anchors { left: parent.left; right: parent.right }
60+ visible: opacity > 0
5461 }
5562
56− Image {
57− anchors {
58− top: parent.top
59− left: parent.left
60− right: parent.right
61− }
62− fillMode: Image.Stretch
63− source: "graphics/dash_divider_top_lightgrad.png"
64− z: -1
63+ Background {
64+ id: backgroundItem
65+ anchors.fill: parent
66+ style: scopeStyle ? scopeStyle.navigationBackground : "color:///#f5f5f5"
6567 }
6668
6769 Image {
68− anchors {
69− bottom: parent.bottom
70− left: parent.left
71− right: parent.right
72− }
7370 fillMode: Image.Stretch
74− source: "graphics/dash_divider_top_darkgrad.png"
75− z: -1
71+ source: scopeStyle.backgroundLuminance > 0.2 ? "graphics/navigation_shadow.png" : "graphics/navigation_shadow_light.png"
72+ anchors { top: parent.bottom; left: parent.left; right: parent.right }
7673 }
7774
78− Label {
79− anchors.fill: parent
80− anchors.margins: units.gu(2)
81− verticalAlignment: Text.AlignVCenter
82− text: root.currentNavigation ? root.currentNavigation.label : ""
83− color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
75+ DashNavigationButton {
76+ id: altNavigationButton
77+ objectName: "altNavigationButton"
78+ height: root.height
79+ width: d.buttonWidth
80+ scope: root.scope
81+ scopeStyle: root.scopeStyle
82+ foregroundColor: d.foregroundColor
83+ listView.width: d.navigationWidth
84+ isAltNavigation: true
85+ showDivider: navigationButton.visible || root.width > d.navigationWidth
86+ // needed so that InverseMouseArea is above navigationButton
87+ z: listView.height > 0 ? 1 : 0
8488 }
8589
86− Icon {
87− anchors.verticalCenter: parent.verticalCenter
88− anchors.right: parent.right
89− anchors.rightMargin: units.gu(2)
90− name: showList ? "up" : "down"
91− height: units.gu(2)
92− width: height
93− color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
90+ DashNavigationButton {
91+ id: navigationButton
92+ objectName: "navigationButton"
93+ height: root.height
94+ width: altNavigationButton.visible ? d.buttonWidth : d.navigationWidth
95+ x: altNavigationButton.visible ? d.buttonWidth : 0
96+ scope: root.scope
97+ scopeStyle: root.scopeStyle
98+ foregroundColor: d.foregroundColor
99+ listView.width: d.navigationWidth
100+ listView.x: -x
101+ showDivider: root.width > d.navigationWidth
94102 }
95103
96− // navigationListView is outside root
97− ListView {
98− id: navigationListView
99− objectName: "navigationListView"
100− orientation: ListView.Horizontal
101− interactive: false
102− clip: root.width != windowWidth
103− model: ListModel {
104− id: navigationModel
105− // We have two roles
106− // navigationId: the navigation id of the navigation the list represents
107− // nullifyNavigation: overrides navigationId to be null
108− // This is used to "clear" the delegate when going "right" on the tree
109− }
110− anchors {
111− left: parent.left
112− right: parent.right
113− top: root.bottom
114− }
115− property int maxHeight: -1
116− Component.onCompleted: updateMaxHeight();
117− function updateMaxHeight()
118− {
119− maxHeight = (windowHeight - mapToItem(null, 0, 0).y) - units.gu(8);
120− }
121− property int prevHeight: maxHeight
122− height: currentItem ? currentItem.height : maxHeight
123− onHeightChanged: {
124− if (root.showList) {
125− prevHeight = currentItem.desiredHeight;
126− }
127− }
128− highlightMoveDuration: UbuntuAnimation.FastDuration
129− delegate: DashNavigationList {
130− objectName: "navigation" + index
131− visible: height != 0
132− width: navigationListView.width
133− scopeStyle: root.scopeStyle
134− property real desiredHeight: {
135− if (root.showList) {
136− if (navigation && navigation.loaded && x == navigationListView.contentX)
137− {
138− navigationListView.updateMaxHeight();
139− return Math.min(implicitHeight, navigationListView.maxHeight);
140− } else {
141− return navigationListView.prevHeight;
142− }
143− } else {
144− return 0;
145− }
146− }
147− height: desiredHeight
148− navigation: (nullifyNavigation || !scope) ? null : scope.getNavigation(navigationId)
149− currentNavigation: root.currentNavigation
150− onEnterNavigation: {
151− scope.setNavigationState(newNavigationId, false);
152− // We only need to add a new item to the model
153− // if we have children, otherwise just load it
154− if (hasChildren) {
155− isGoingBack = false;
156− navigationModel.append({"navigationId": newNavigationId, "nullifyNavigation": false});
157− navigationListView.currentIndex++;
158− } else {
159− showList = false;
160− }
161− }
162− onGoBackToParentClicked: {
163− scope.setNavigationState(navigation.parentNavigationId, false);
164− isGoingBack = true;
165− navigationModel.setProperty(navigationListView.currentIndex - 1, "nullifyNavigation", false);
166− navigationListView.currentIndex--;
167− }
168− onAllNavigationClicked: {
169− showList = false;
170− if (root.currentNavigation.parentNavigationId == navigation.navigationId) {
171− // For leaves we have to go to the parent too
172− scope.setNavigationState(root.currentNavigation.parentNavigationId, false);
173− }
174− }
175− }
176− onContentXChanged: {
177− if (contentX == width * navigationListView.currentIndex) {
178− if (isGoingBack) {
179− navigationModel.remove(navigationListView.currentIndex + 1);
180− } else {
181− navigationModel.setProperty(navigationListView.currentIndex - 1, "nullifyNavigation", true);
182− }
183− }
184− }
185− }
186−
187− InverseMouseArea {
188− anchors.fill: navigationListView
189− enabled: root.showList
190− onClicked: root.showList = false
191− }
192−
193− onScopeChanged: {
194− navigationModel.clear();
195− if (scope && scope.hasNavigation) {
196− navigationModel.append({"navigationId": scope.currentNavigationId, "nullifyNavigation": false});
197− }
198− }
199−
200− Connections {
201− target: scope
202− onHasNavigationChanged: {
203− if (scope.hasNavigation) {
204− navigationModel.append({"navigationId": scope.currentNavigationId, "nullifyNavigation": false});
205− } else {
206− navigationModel.clear();
207− }
208− }
104+ Image {
105+ fillMode: Image.Stretch
106+ source: backgroundItem.luminance > 0.7 ? "graphics/navigation_shadow.png" : "graphics/navigation_shadow_light.png"
107+ x: navigationButton.listView.height > 0 ? altNavigationButton.x : navigationButton.x
108+ width: d.buttonWidth
109+ rotation: 180
110+ anchors.bottom: parent.bottom
111+ visible: d.bothVisible && (navigationButton.listView.height > 0 || altNavigationButton.listView.height > 0)
209112 }
210113}
qml/Dash/DashNavigationButton.qmlnew file+226
@@ -0,0 +1,226 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+
20+AbstractButton {
21+ id: root
22+ objectName: "dashNavigation"
23+
24+ // Set by parent
25+ property var scope: null
26+ property var scopeStyle: null
27+ property color foregroundColor: Theme.palette.normal.baseText
28+ property bool isAltNavigation: false
29+ property bool showDivider: false
30+
31+ // Used by parent
32+ readonly property var currentNavigation: scope && scope[hasNavigation] ? getNavigation(scope[currentNavigationId]) : null
33+ readonly property alias listView: navigationListView
34+ property bool showList: false
35+
36+ // Internal
37+ // Are we drilling down the tree or up?
38+ property bool isGoingBack: false
39+ readonly property string hasNavigation: isAltNavigation ? "hasAltNavigation" : "hasNavigation"
40+ readonly property string currentNavigationId: isAltNavigation ? "currentAltNavigationId" : "currentNavigationId"
41+ function getNavigation(navId) {
42+ if (isAltNavigation) {
43+ return scope.getAltNavigation(navId);
44+ } else {
45+ return scope.getNavigation(navId);
46+ }
47+ }
48+
49+ visible: root.currentNavigation != null
50+
51+ onClicked: {
52+ navigationListView.updateMaxHeight();
53+ root.showList = !root.showList;
54+ }
55+
56+ Label {
57+ anchors.fill: parent
58+ anchors.margins: units.gu(2)
59+ anchors.rightMargin: units.gu(5)
60+ verticalAlignment: Text.AlignVCenter
61+ text: root.currentNavigation ? root.currentNavigation.label : ""
62+ color: root.foregroundColor
63+ elide: Text.ElideRight
64+ maximumLineCount: 1
65+ }
66+
67+ Icon {
68+ anchors.verticalCenter: parent.verticalCenter
69+ anchors.right: parent.right
70+ anchors.rightMargin: units.gu(2)
71+ name: showList ? "up" : "down"
72+ height: units.gu(2)
73+ width: height
74+ color: root.foregroundColor
75+ }
76+
77+ // navigationListView is outside root
78+ ListView {
79+ id: navigationListView
80+ objectName: "navigationListView"
81+ visible: height > 0
82+ orientation: ListView.Horizontal
83+ interactive: false
84+ clip: root.width != windowWidth
85+ model: ListModel {
86+ id: navigationModel
87+ // We have two roles
88+ // navigationId: the navigation id of the navigation the list represents
89+ // nullifyNavigation: overrides navigationId to be null
90+ // This is used to "clear" the delegate when going "right" on the tree
91+ }
92+ anchors.top: root.bottom
93+ property int maxHeight: -1
94+ Component.onCompleted: updateMaxHeight();
95+ function updateMaxHeight()
96+ {
97+ maxHeight = (windowHeight - mapToItem(null, 0, 0).y) - units.gu(8);
98+ }
99+ property int prevHeight: maxHeight
100+ height: currentItem ? currentItem.height : maxHeight
101+ onHeightChanged: {
102+ if (root.showList) {
103+ prevHeight = currentItem.desiredHeight;
104+ }
105+ }
106+ highlightMoveDuration: UbuntuAnimation.FastDuration
107+ delegate: DashNavigationList {
108+ objectName: "navigation" + index
109+ visible: height > 0
110+ width: navigationListView.width
111+ scopeStyle: root.scopeStyle
112+ foregroundColor: root.foregroundColor
113+ property real desiredHeight: {
114+ if (root.showList) {
115+ if (navigation && navigation.loaded && x == navigationListView.contentX)
116+ {
117+ navigationListView.updateMaxHeight();
118+ return Math.min(implicitHeight, navigationListView.maxHeight);
119+ } else {
120+ return navigationListView.prevHeight;
121+ }
122+ } else {
123+ return 0;
124+ }
125+ }
126+ height: desiredHeight
127+ navigation: (nullifyNavigation || !scope) ? null : getNavigation(navigationId)
128+ currentNavigation: root.currentNavigation
129+ onEnterNavigation: {
130+ scope.setNavigationState(newNavigationId, isAltNavigation);
131+ // We only need to add a new item to the model
132+ // if we have children, otherwise just load it
133+ if (hasChildren) {
134+ isGoingBack = false;
135+ navigationModel.append({"navigationId": newNavigationId, "nullifyNavigation": false});
136+ navigationListView.currentIndex++;
137+ } else {
138+ showList = false;
139+ }
140+ }
141+ onGoBackToParentClicked: {
142+ scope.setNavigationState(navigation.parentNavigationId, isAltNavigation);
143+ isGoingBack = true;
144+ navigationModel.setProperty(navigationListView.currentIndex - 1, "nullifyNavigation", false);
145+ navigationListView.currentIndex--;
146+ }
147+ onAllNavigationClicked: {
148+ showList = false;
149+ if (root.currentNavigation.parentNavigationId == navigation.navigationId) {
150+ // For leaves we have to go to the parent too
151+ scope.setNavigationState(root.currentNavigation.parentNavigationId, isAltNavigation);
152+ }
153+ }
154+ }
155+ onContentXChanged: {
156+ if (contentX == width * navigationListView.currentIndex) {
157+ if (isGoingBack) {
158+ navigationModel.remove(navigationListView.currentIndex + 1);
159+ } else {
160+ navigationModel.setProperty(navigationListView.currentIndex - 1, "nullifyNavigation", true);
161+ }
162+ }
163+ }
164+ }
165+
166+ Image {
167+ anchors {
168+ top: navigationListView.bottom
169+ left: navigationListView.left
170+ right: navigationListView.right
171+ }
172+ fillMode: Image.Stretch
173+ source: "graphics/navigation_shadow.png"
174+ visible: root.showList
175+ }
176+
177+ onScopeChanged: {
178+ navigationModel.clear();
179+ if (scope && scope[hasNavigation]) {
180+ navigationModel.append({"navigationId": scope[currentNavigationId], "nullifyNavigation": false});
181+ }
182+ }
183+
184+ Connections {
185+ target: scope
186+ // This is duplicated since we can't have something based on the dynamic hasNavigation string property
187+ onHasNavigationChanged: {
188+ if (!root.isAltNavigation) {
189+ if (scope.hasNavigation) {
190+ navigationModel.append({"navigationId": scope.currentNavigationId, "nullifyNavigation": false});
191+ } else {
192+ navigationModel.clear();
193+ }
194+ }
195+ }
196+ onHasAltNavigationChanged: {
197+ if (root.isAltNavigation) {
198+ if (scope.hasAltNavigation) {
199+ navigationModel.append({"navigationId": scope.currentAltNavigationId, "nullifyNavigation": false});
200+ } else {
201+ navigationModel.clear();
202+ }
203+ }
204+ }
205+ }
206+
207+ InverseMouseArea {
208+ anchors.fill: navigationListView
209+ enabled: root.showList
210+ onPressed: root.showList = false
211+ }
212+
213+ Rectangle {
214+ visible: root.showDivider
215+ anchors {
216+ top: parent.top
217+ topMargin: units.dp(1)
218+ bottom: parent.bottom
219+ left: parent.right
220+ leftMargin: -units.dp(0.5)
221+ }
222+ width: units.dp(1)
223+ color: root.foregroundColor
224+ opacity: 0.2
225+ }
226+}
qml/Dash/DashNavigationList.qml+21−4
@@ -16,22 +16,23 @@
1616
1717import QtQuick 2.2
1818import Ubuntu.Components 1.1
19+import "../Components"
1920
2021Item {
2122 id: root
2223 property var navigation: null
2324 property var currentNavigation: null
2425 property var scopeStyle: null
26+ property color foregroundColor: Theme.palette.normal.baseText
2527 signal enterNavigation(var newNavigationId, bool hasChildren)
2628 signal goBackToParentClicked()
2729 signal allNavigationClicked()
2830
2931 readonly property int itemHeight: units.gu(5)
30− readonly property color foregroundColor: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
3132 implicitHeight: flickable.contentHeight
3233
33− Rectangle {
34− color: !root.scopeStyle || Qt.colorEqual(root.scopeStyle.background, "transparent") ? "white" : root.scopeStyle.background
34+ Background {
35+ style: root.scopeStyle ? root.scopeStyle.navigationBackground : "color:///#f5f5f5"
3536 anchors.fill: parent
3637 }
3738
@@ -86,10 +87,15 @@ Item {
8687 anchors {
8788 verticalCenter: parent.verticalCenter
8889 left: backImage.right
90+ right: parent.right
8991 leftMargin: units.gu(0.5)
92+ rightMargin: units.gu(2)
9093 }
9194 text: navigation ? navigation.parentLabel : ""
9295 color: root.foregroundColor
96+ wrapMode: Text.Wrap
97+ maximumLineCount: 2
98+ elide: Text.ElideMiddle
9399 }
94100
95101 Rectangle {
@@ -110,18 +116,23 @@ Item {
110116 id: allButton
111117 objectName: "allButton"
112118 width: parent.width
113− visible: navigation && (!navigation.isRoot || (root.currentNavigation && !root.currentNavigation.isRoot && root.currentNavigation.parentNavigationId == navigation.navigationId)) || false
119+ visible: navigation && (!navigation.isRoot || (!navigation.hidden && root.currentNavigation && !root.currentNavigation.isRoot && root.currentNavigation.parentNavigationId == navigation.navigationId)) || false
114120 height: itemHeight
115121
116122 Label {
117123 anchors {
118124 verticalCenter: parent.verticalCenter
119125 left: parent.left
126+ right: parent.right
120127 leftMargin: units.gu(2)
128+ rightMargin: units.gu(2)
121129 }
122130 text: navigation ? (navigation.allLabel != "" ? navigation.allLabel : navigation.label) : ""
123131 font.bold: true
124132 color: root.foregroundColor
133+ wrapMode: Text.Wrap
134+ maximumLineCount: 2
135+ elide: Text.ElideMiddle
125136 }
126137
127138 Rectangle {
@@ -155,12 +166,18 @@ Item {
155166 verticalCenter: parent.verticalCenter
156167 left: parent.left
157168 leftMargin: units.gu(2)
169+ right: rightIcon.visible ? rightIcon.left : parent.right
170+ rightMargin: rightIcon.visible ? units.gu(0.5) : units.gu(2)
158171 }
159172 text: label
160173 color: root.foregroundColor
174+ wrapMode: Text.Wrap
175+ maximumLineCount: 2
176+ elide: Text.ElideMiddle
161177 }
162178
163179 Icon {
180+ id: rightIcon
164181 anchors {
165182 verticalCenter: parent.verticalCenter
166183 right: parent.right
qml/Dash/GenericScopeView.qml+136−38
@@ -34,7 +34,7 @@ FocusScope {
3434 property bool enableHeightBehaviorOnNextCreation: false
3535 property var categoryView: categoryView
3636 property bool showPageHeader: true
37− readonly property alias previewShown: previewListView.open
37+ readonly property alias subPageShown: subPageLoader.subPageShown
3838 property int paginationCount: 0
3939 property int paginationIndex: 0
4040 property alias pageHeaderTotallyVisible: categoryView.pageHeaderTotallyVisible
@@ -43,7 +43,7 @@ FocusScope {
4343 style: scope ? scope.customizations : {}
4444 }
4545
46− readonly property bool processing: scope ? scope.searchInProgress || previewListView.processing : false
46+ readonly property bool processing: scope ? scope.searchInProgress || subPageLoader.processing : false
4747
4848 signal backClicked()
4949
@@ -56,7 +56,7 @@ FocusScope {
5656 }
5757
5858 function closePreview() {
59− previewListView.open = false;
59+ subPageLoader.closeSubPage()
6060 }
6161
6262 function itemClicked(index, result, item, itemModel, resultsModel, limitedCategoryItemCount) {
@@ -80,19 +80,19 @@ FocusScope {
8080 if (limitedCategoryItemCount > 0) {
8181 previewLimitModel.model = resultsModel;
8282 previewLimitModel.limit = limitedCategoryItemCount;
83− previewListView.model = previewLimitModel;
83+ subPageLoader.model = previewLimitModel;
8484 } else {
85− previewListView.model = resultsModel;
85+ subPageLoader.model = resultsModel;
8686 }
87− previewListView.currentIndex = -1;
88− previewListView.currentIndex = index;
89− previewListView.open = true;
87+ subPageLoader.initialIndex = -1;
88+ subPageLoader.initialIndex = index;
89+ subPageLoader.openSubPage("preview");
9090 }
9191
9292 Binding {
9393 target: scope
9494 property: "isActive"
95− value: isCurrent && !previewListView.open
95+ value: isCurrent && !subPageLoader.open
9696 }
9797
9898 SortFilterProxyModel {
@@ -105,10 +105,10 @@ FocusScope {
105105 }
106106
107107 onIsCurrentChanged: {
108− if (showPageHeader) {
108+ if (pageHeaderLoader.item && showPageHeader) {
109109 pageHeaderLoader.item.resetSearch();
110110 }
111− previewListView.open = false;
111+ subPageLoader.closeSubPage();
112112 }
113113
114114 Binding {
@@ -127,8 +127,8 @@ FocusScope {
127127
128128 Connections {
129129 target: scopeView.scope
130− onShowDash: previewListView.open = false;
131− onHideDash: previewListView.open = false;
130+ onShowDash: subPageLoader.closeSubPage()
131+ onHideDash: subPageLoader.closeSubPage()
132132 }
133133
134134 Rectangle {
@@ -141,17 +141,19 @@ FocusScope {
141141 id: categoryView
142142 objectName: "categoryListView"
143143
144− x: previewListView.open ? -width : 0
144+ x: subPageLoader.open ? -width : 0
145145 Behavior on x { UbuntuNumberAnimation { } }
146146 width: parent.width
147− height: parent.height
147+ height: floatingSeeLess.visible ? parent.height - floatingSeeLess.height + floatingSeeLess.yOffset
148+ : parent.height
149+ clip: height != parent.height
148150
149151 model: scopeView.categories
150− forceNoClip: previewListView.open
152+ forceNoClip: subPageLoader.open
151153 pixelAligned: true
152154 interactive: !navigationShown
153155
154− property string expandedCategoryId: ""
156+ property Item expandedCategoryItem: null
155157
156158 readonly property bool pageHeaderTotallyVisible: scopeView.showPageHeader &&
157159 ((headerItemShownHeight == 0 && categoryView.contentY <= categoryView.originY) || (headerItemShownHeight == pageHeaderLoader.item.height))
@@ -162,6 +164,8 @@ FocusScope {
162164 highlightWhenPressed: false
163165 showDivider: false
164166
167+ property Item seeAllButton: seeAll
168+
165169 readonly property bool expandable: {
166170 if (categoryView.model.count === 1) return false;
167171 if (cardTool.template && cardTool.template["collapsed-rows"] === 0) return false;
@@ -191,7 +195,7 @@ FocusScope {
191195 // This can happen with the VJ that doesn't know how height it will be on creation
192196 // so doesn't set expandable until a bit too late for onLoaded
193197 if (expandable) {
194− var shouldExpand = baseItem.category === categoryView.expandedCategoryId;
198+ var shouldExpand = baseItem === categoryView.expandedCategoryItem;
195199 baseItem.expand(shouldExpand, false /*animate*/);
196200 }
197201 }
@@ -242,7 +246,7 @@ FocusScope {
242246 item.objectName = Qt.binding(function() { return categoryId })
243247 item.scopeStyle = scopeView.scopeStyle;
244248 if (baseItem.expandable) {
245− var shouldExpand = categoryId === categoryView.expandedCategoryId;
249+ var shouldExpand = baseItem === categoryView.expandedCategoryItem;
246250 baseItem.expand(shouldExpand, false /*animate*/);
247251 }
248252 updateDelegateCreationRange();
@@ -279,21 +283,21 @@ FocusScope {
279283 }
280284 Connections {
281285 target: categoryView
282− onExpandedCategoryIdChanged: {
286+ onExpandedCategoryItemChanged: {
283287 collapseAllButExpandedCategory();
284288 }
285289 function collapseAllButExpandedCategory() {
286290 var item = rendererLoader.item;
287291 if (baseItem.expandable) {
288− var shouldExpand = categoryId === categoryView.expandedCategoryId;
292+ var shouldExpand = baseItem === categoryView.expandedCategoryItem;
289293 if (shouldExpand != baseItem.expanded) {
290294 // If the filter animation will be seen start it, otherwise, just flip the switch
291295 var shrinkingVisible = !shouldExpand && y + item.collapsedHeight + seeAll.height < categoryView.height;
292296 var growingVisible = shouldExpand && y + height < categoryView.height;
293− if (!previewListView.open || shouldExpand) {
297+ if (!subPageLoader.open || shouldExpand) {
294298 var animate = shrinkingVisible || growingVisible;
295299 baseItem.expand(shouldExpand, animate)
296− if (shouldExpand && !previewListView.open) {
300+ if (shouldExpand && !subPageLoader.open) {
297301 categoryView.maximizeVisibleArea(index, item.expandedHeight + seeAll.height);
298302 }
299303 }
@@ -346,13 +350,13 @@ FocusScope {
346350 left: parent.left
347351 right: parent.right
348352 }
349− height: seeAllLabel.visible ? seeAllLabel.font.pixelSize + units.gu(6) : 0
353+ height: seeAllLabel.visible ? seeAllLabel.font.pixelSize + units.gu(4) : 0
350354
351355 onClicked: {
352− if (categoryView.expandedCategoryId != baseItem.category) {
353− categoryView.expandedCategoryId = baseItem.category;
356+ if (categoryView.expandedCategoryItem !== baseItem) {
357+ categoryView.expandedCategoryItem = baseItem;
354358 } else {
355− categoryView.expandedCategoryId = "";
359+ categoryView.expandedCategoryItem = null;
356360 }
357361 }
358362
@@ -424,44 +428,138 @@ FocusScope {
424428 searchHint: scopeView.scope && scopeView.scope.searchHint || i18n.tr("Search")
425429 showBackButton: scopeView.hasBackAction
426430 searchEntryEnabled: true
431+ settingsEnabled: scopeView.scope && scopeView.scope.settings && scopeView.scope.settings.count > 0 || false
432+ favoriteEnabled: scopeView.scope && scopeView.scope.id !== "clickscope"
433+ favorite: scopeView.scope && scopeView.scope.favorite
427434 scopeStyle: scopeView.scopeStyle
428435 paginationCount: scopeView.paginationCount
429436 paginationIndex: scopeView.paginationIndex
430437
431438 bottomItem: DashNavigation {
432439 scope: scopeView.scope
433− width: parent.width <= units.gu(60) ? parent.width : units.gu(40)
434− anchors.right: parent.right
440+ anchors { left: parent.left; right: parent.right }
435441 windowHeight: scopeView.height
436442 windowWidth: scopeView.width
437443 scopeStyle: scopeView.scopeStyle
438444 }
439445
440446 onBackClicked: scopeView.backClicked()
447+ onSettingsClicked: subPageLoader.openSubPage("settings")
448+ onFavoriteClicked: scopeView.scope.favorite = !scopeView.scope.favorite
441449 }
442450 }
443451 }
444452 }
445453
454+ AbstractButton {
455+ id: floatingSeeLess
456+ objectName: "floatingSeeLess"
457+
458+ property Item companionTo: companionBase ? companionBase.seeAllButton : null
459+ property Item companionBase: categoryView.expandedCategoryItem
460+ property bool showBecausePosition: false
461+ property real yOffset: 0
462+
463+ anchors {
464+ left: categoryView.left
465+ right: categoryView.right
466+ }
467+ y: parent.height - height + yOffset
468+ height: seeLessLabel.font.pixelSize + units.gu(4)
469+ visible: companionTo && showBecausePosition
470+
471+ onClicked: categoryView.expandedCategoryItem = null;
472+
473+ function updateVisibility() {
474+ var companionPos = companionTo.mapToItem(floatingSeeLess, 0, 0);
475+ showBecausePosition = companionPos.y > 0;
476+
477+ var posToBase = floatingSeeLess.mapToItem(companionBase, 0, -yOffset).y;
478+ yOffset = Math.max(0, companionBase.item.collapsedHeight - posToBase);
479+ yOffset = Math.min(yOffset, height);
480+ }
481+
482+ Label {
483+ id: seeLessLabel
484+ text: i18n.tr("See less")
485+ anchors {
486+ centerIn: parent
487+ verticalCenterOffset: units.gu(-0.5)
488+ }
489+ fontSize: "small"
490+ font.weight: Font.Bold
491+ color: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.baseText
492+ }
493+
494+ Connections {
495+ target: floatingSeeLess.companionTo ? categoryView : null
496+ onContentYChanged: floatingSeeLess.updateVisibility();
497+ }
498+
499+ Connections {
500+ target: floatingSeeLess.companionTo
501+ onYChanged: floatingSeeLess.updateVisibility();
502+ }
503+ }
504+
446505 LimitProxyModel {
447506 id: previewLimitModel
448507 }
449508
450− PreviewListView {
451− id: previewListView
452− objectName: "previewListView"
509+ Loader {
510+ id: subPageLoader
511+ objectName: "subPageLoader"
453512 visible: x != width
454− scope: scopeView.scope
455− scopeStyle: scopeView.scopeStyle
456513 width: parent.width
457514 height: parent.height
458515 anchors.left: categoryView.right
459516
460− onOpenChanged: {
461− if (showPageHeader) {
462− pageHeaderLoader.item.unfocus();
517+ property bool open: false
518+ property var scope: scopeView.scope
519+ property var scopeStyle: scopeView.scopeStyle
520+ property int initialIndex: -1
521+ property var model: null
522+
523+ readonly property bool processing: item && item.processing || false
524+ readonly property int count: item && item.count || 0
525+ readonly property int currentIndex: item && item.currentIndex || 0
526+ readonly property var currentItem: item && item.currentItem || null
527+
528+ property string subPage: ""
529+ readonly property bool subPageShown: visible && status === Loader.Ready
530+
531+ function openSubPage(page) {
532+ subPage = page;
533+ }
534+
535+ function closeSubPage() {
536+ open = false;
537+ }
538+
539+ source: switch(subPage) {
540+ case "preview": return "PreviewListView.qml";
541+ case "settings": return "ScopeSettingsPage.qml";
542+ default: return "";
543+ }
544+
545+ onLoaded: {
546+ item.scope = Qt.binding(function() { return subPageLoader.scope; } )
547+ item.scopeStyle = Qt.binding(function() { return subPageLoader.scopeStyle; } )
548+ if (subPage == "preview") {
549+ item.open = Qt.binding(function() { return subPageLoader.open; } )
550+ item.initialIndex = Qt.binding(function() { return subPageLoader.initialIndex; } )
551+ item.model = Qt.binding(function() { return subPageLoader.model; } )
463552 }
553+ open = true;
464554 }
465− }
466555
556+ onOpenChanged: pageHeaderLoader.item.unfocus()
557+
558+ onVisibleChanged: if (!visible) subPage = ""
559+
560+ Connections {
561+ target: subPageLoader.item
562+ onBackClicked: subPageLoader.closeSubPage()
563+ }
564+ }
467565}
qml/Dash/PageHeader.qml+81−27
@@ -31,6 +31,9 @@ Item {
3131 property string title
3232
3333 property bool searchEntryEnabled: false
34+ property bool settingsEnabled: false
35+ property bool favoriteEnabled: false
36+ property bool favorite: false
3437 property ListModel searchHistory: SearchHistoryModel
3538 property alias searchQuery: searchTextField.text
3639 property alias searchHint: searchTextField.placeholderText
@@ -40,12 +43,11 @@ Item {
4043 property int paginationCount: 0
4144 property int paginationIndex: -1
4245
43− // TODO We should use foreground for the icons
44− // of the toolbar but unfortunately Action does not have
45− // the keyColor property as Icon does :-/
4646 property var scopeStyle: null
4747
4848 signal backClicked()
49+ signal settingsClicked()
50+ signal favoriteClicked()
4951
5052 onScopeStyleChanged: refreshLogo()
5153 onSearchQueryChanged: {
@@ -240,6 +242,7 @@ Item {
240242 contentHeight: height
241243 opacity: headerContainer.clip || !headerContainer.showSearch ? 1 : 0 // setting visible false cause column to relayout
242244 separatorSource: ""
245+ separatorBottomSource: ""
243246 property var styledItem: header
244247 property string title: root.title
245248 property var config: PageHeadConfiguration {
@@ -247,20 +250,33 @@ Item {
247250 backAction: Action {
248251 iconName: "back"
249252 visible: root.showBackButton
250− onTriggered: {
251− root.backClicked();
252− }
253+ onTriggered: root.backClicked()
253254 }
254255
255256 actions: [
256257 Action {
257258 objectName: "search"
259+ text: i18n.tr("Search")
258260 iconName: "search"
259261 visible: root.searchEntryEnabled
260262 onTriggered: {
261263 headerContainer.showSearch = true;
262264 searchTextField.forceActiveFocus();
263265 }
266+ },
267+ Action {
268+ objectName: "settings"
269+ text: i18n.tr("Settings")
270+ iconName: "settings"
271+ visible: root.settingsEnabled
272+ onTriggered: root.settingsClicked()
273+ },
274+ Action {
275+ objectName: "favorite"
276+ text: root.favorite ? i18n.tr("Remove from Favorites") : i18n.tr("Add to Favorites")
277+ iconName: root.favorite ? "starred" : "non-starred"
278+ visible: root.favoriteEnabled
279+ onTriggered: root.favoriteClicked()
264280 }
265281 ]
266282 }
@@ -288,25 +304,6 @@ Item {
288304 }
289305 }
290306
291− Row {
292− spacing: units.gu(.5)
293− Repeater {
294− objectName: "paginationRepeater"
295− model: root.paginationCount
296− Image {
297− objectName: "paginationDots_" + index
298− height: units.gu(1)
299− width: height
300− source: (index == root.paginationIndex) ? "graphics/pagination_dot_on.png" : "graphics/pagination_dot_off.png"
301− }
302− }
303− anchors {
304− top: headerContainer.bottom
305− horizontalCenter: headerContainer.horizontalCenter
306− topMargin: units.gu(.5)
307− }
308− }
309−
310307 Component {
311308 id: popoverComponent
312309 Popover {
@@ -344,7 +341,7 @@ Item {
344341 }
345342 }
346343
347− BorderImage {
344+ Rectangle {
348345 id: bottomBorder
349346 anchors {
350347 top: headerContainer.bottom
@@ -353,7 +350,64 @@ Item {
353350 bottom: bottomContainer.top
354351 }
355352
356− source: "graphics/PageHeaderBaseDivider.sci"
353+ color: scopeStyle ? scopeStyle.headerDividerColor : "#e0e0e0"
354+
355+ Rectangle {
356+ anchors {
357+ top: parent.top
358+ left: parent.left
359+ right: parent.right
360+ }
361+ height: units.dp(1)
362+ color: Qt.darker(parent.color, 1.1)
363+ }
364+ }
365+
366+ Row {
367+ visible: bottomBorder.visible
368+ spacing: units.gu(.5)
369+ Repeater {
370+ objectName: "paginationRepeater"
371+ model: root.paginationCount
372+ Image {
373+ objectName: "paginationDots_" + index
374+ height: units.gu(1)
375+ width: height
376+ source: (index == root.paginationIndex) ? "graphics/pagination_dot_on.png" : "graphics/pagination_dot_off.png"
377+ }
378+ }
379+ anchors {
380+ top: headerContainer.bottom
381+ horizontalCenter: headerContainer.horizontalCenter
382+ topMargin: units.gu(.5)
383+ }
384+ }
385+
386+ // FIXME this doesn't work with solid scope backgrounds due to z-ordering
387+ Item {
388+ id: bottomHighlight
389+ visible: bottomBorder.visible
390+ anchors {
391+ top: bottomContainer.top
392+ left: parent.left
393+ right: parent.right
394+ }
395+ z: 1
396+ height: units.dp(1)
397+ opacity: 0.6
398+
399+ // FIXME this should be a shader when bottomItem exists
400+ // to support image backgrounds
401+ Rectangle {
402+ anchors.fill: parent
403+ color: if (bottomItem && bottomItem.background) {
404+ Qt.lighter(Qt.rgba(bottomItem.background.topColor.r,
405+ bottomItem.background.topColor.g,
406+ bottomItem.background.topColor.b, 1.0), 1.2);
407+ } else if (!bottomItem && scopeStyle) {
408+ Qt.lighter(Qt.rgba(scopeStyle.background.r, scopeStyle.background.g, scopeStyle.background.b, 1.0), 1.2);
409+ } else "#CCFFFFFF"
410+ }
357411 }
358412
359413 Item {
qml/Dash/PreviewListView.qml+13−1
@@ -23,9 +23,12 @@ import "Previews" as Previews
2323Item {
2424 id: root
2525
26+ property int initialIndex: -1
2627 property var scope: null
2728 property var scopeStyle: null
2829
30+ property alias showSignatureLine: header.showSignatureLine
31+
2932 property alias open: previewListView.open
3033 property alias model: previewListView.model
3134 property alias currentIndex: previewListView.currentIndex
@@ -35,6 +38,8 @@ Item {
3538 readonly property bool processing: currentItem && (!currentItem.previewModel.loaded
3639 || currentItem.previewModel.processingAction)
3740
41+ signal backClicked()
42+
3843 PageHeader {
3944 id: header
4045 objectName: "pageHeader"
@@ -44,7 +49,7 @@ Item {
4449 searchEntryEnabled: false
4550 scopeStyle: root.scopeStyle
4651
47− onBackClicked: root.open = false
52+ onBackClicked: root.backClicked()
4853 }
4954
5055 ListView {
@@ -78,6 +83,13 @@ Item {
7883 }
7984 }
8085
86+ onCountChanged: {
87+ if (count > 0 && initialIndex >= 0) {
88+ currentIndex = initialIndex;
89+ initialIndex = -1;
90+ }
91+ }
92+
8193 delegate: Previews.Preview {
8294 id: preview
8395 objectName: "preview" + index
qml/Dash/Previews/PreviewActionButton.qml+1−4
@@ -15,16 +15,13 @@
1515 */
1616
1717import QtQuick 2.0
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
1919
2020Button {
2121 signal triggeredAction(var actionData)
2222
2323 property var data: null
2424 objectName: "button" + (data && data.id || "")
25− color: Theme.palette.selected.foreground
2625 text: data && data.label || ""
27− iconSource: data && data.icon || ""
28− iconPosition: "left"
2926 onClicked: triggeredAction(data)
3027}
qml/Dash/Previews/PreviewActionCombo.qml+4−2
@@ -15,7 +15,7 @@
1515 */
1616
1717import QtQuick 2.0
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
1919
2020Item {
2121 id: root
@@ -25,6 +25,7 @@ Item {
2525 signal triggeredAction(var actionData)
2626
2727 property alias model: actionRepeater.model
28+ property alias strokeColor: moreButton.strokeColor
2829
2930 Button {
3031 id: moreButton
@@ -32,7 +33,7 @@ Item {
3233
3334 objectName: "moreLessButton"
3435 text: !expanded ? i18n.tr("More...") : i18n.tr("Less...")
35− color: Theme.palette.selected.foreground
36+ strokeColor: Theme.palette.normal.baseText
3637 onClicked: expanded = !expanded
3738 width: parent.width
3839 }
@@ -60,6 +61,7 @@ Item {
6061 data: modelData
6162 width: implicitWidth < parent.width ? parent.width : implicitWidth
6263 onClicked: root.triggeredAction(modelData)
64+ strokeColor: moreButton.strokeColor
6365 }
6466 }
6567 }
qml/Dash/Previews/PreviewActions.qml+6
@@ -46,6 +46,11 @@ PreviewWidget {
4646 item.model = row.actions.slice(1);
4747 }
4848 }
49+ Binding {
50+ target: loader.item
51+ property: "strokeColor"
52+ value: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
53+ }
4954 Connections {
5055 target: loader.item
5156 onTriggeredAction: {
@@ -59,6 +64,7 @@ PreviewWidget {
5964 visible: row.actions && row.actions.length > 0
6065 onTriggeredAction: root.triggered(root.widgetId, actionData.id, actionData)
6166 width: (root.width - units.gu(1)) / 2
67+ color: root.scopeStyle ? root.scopeStyle.previewButtonColor : Theme.palette.selected.foreground
6268 }
6369 }
6470}
qml/Dash/Previews/PreviewExpandable.qml+3−2
@@ -59,9 +59,10 @@ PreviewWidget {
5959 }
6060 Icon {
6161 anchors.fill: parent
62− width: 64
63− height: 64
62+ width: units.gu(3)
63+ height: units.gu(3)
6464 name: root.expanded ? "view-collapse" : "view-expand"
65+ color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
6566 }
6667 }
6768
qml/Dash/Previews/PreviewTable.qmlnew file+80
@@ -0,0 +1,80 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtQuick.Layouts 1.1
19+import Ubuntu.Components 0.1
20+import "../../Components"
21+
22+/*! \brief Preview widget for table.
23+
24+ This widget shows two columns contained in widgetData["values"]
25+ as arrays of label,value along with a title that comes from widgetData["title"].
26+
27+ In case the widget is collapsed it only shows 3 lines of values.
28+ */
29+
30+PreviewWidget {
31+ id: root
32+ implicitHeight: column.implicitHeight
33+
34+ readonly property int maximumCollapsedRowCount: 3
35+
36+ Column {
37+ id: column
38+ objectName: "column"
39+ spacing: units.gu(1)
40+
41+ Label {
42+ id: titleLabel
43+ objectName: "titleLabel"
44+ anchors {
45+ left: parent.left
46+ right: parent.right
47+ }
48+ height: visible ? implicitHeight : 0
49+ fontSize: "large"
50+ color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
51+ visible: text !== ""
52+ opacity: .8
53+ text: widgetData["title"] || ""
54+ }
55+
56+ GridLayout {
57+ objectName: "gridLayout"
58+ columns: 2
59+ columnSpacing: units.gu(2)
60+ Repeater {
61+ id: rowsRepeater
62+ model: widgetData["values"]
63+
64+ delegate: Repeater {
65+ id: perRowRepeater
66+ readonly property int rowIndex: index
67+ model: widgetData["values"][index]
68+ delegate: Label {
69+ objectName: "label"+rowIndex+index
70+ fontSize: "small"
71+ text: perRowRepeater.model[index]
72+ visible: root.expanded || rowIndex < maximumCollapsedRowCount
73+ color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
74+ font.bold: index == 0
75+ }
76+ }
77+ }
78+ }
79+ }
80+}
qml/Dash/Previews/PreviewWidgetFactory.qml+2−1
@@ -58,6 +58,7 @@ Loader {
5858 case "payments": return "PreviewPayments.qml";
5959 case "rating-input": return "PreviewRatingInput.qml";
6060 case "reviews": return "PreviewRatingDisplay.qml";
61+ case "table": return "PreviewTable.qml";
6162 case "text": return "PreviewTextSummary.qml";
6263 case "video": return "PreviewVideoPlayback.qml";
6364 default: return "";
@@ -70,7 +71,7 @@ Loader {
7071 item.widgetData = Qt.binding(function() { return root.widgetData } )
7172 item.isCurrentPreview = Qt.binding(function() { return root.isCurrentPreview } )
7273 item.expanded = Qt.binding(function() { return root.expanded } )
73− if (item.hasOwnProperty("scopeStyle")) item.scopeStyle = root.scopeStyle;
74+ item.scopeStyle = Qt.binding(function() { return root.scopeStyle } )
7475 }
7576
7677 Connections {
qml/Dash/ScopeSettings/ScopeSetting.qmlnew file+37
@@ -0,0 +1,37 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+
20+/*! Interface for settings widgets. */
21+
22+Item {
23+ //! The ScopeStyle component.
24+ property var scopeStyle: null
25+
26+ //! Variable used to contain widget's data
27+ property var widgetData: null
28+
29+ /*! \brief This signal should be emitted when a setting action was updated.
30+ *
31+ * \param value the new setting value.
32+ */
33+ signal updated(var value)
34+
35+ //! \internal
36+ readonly property real settingMargins: units.gu(2)
37+}
qml/Dash/ScopeSettings/ScopeSettingBoolean.qmlnew file+61
@@ -0,0 +1,61 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+import Ubuntu.Components.ListItems 1.0 as ListItem
20+
21+ScopeSetting {
22+ id: root
23+ height: listItem.height
24+
25+ ListItem.Empty {
26+ id: listItem
27+
28+ onClicked: {
29+ control.checked = !control.checked;
30+ updated(control.checked);
31+ }
32+
33+ Label {
34+ anchors {
35+ left: parent.left
36+ leftMargin: settingMargins
37+ right: control.left
38+ rightMargin: units.gu(1)
39+ verticalCenter: parent.verticalCenter
40+ }
41+ text: widgetData.displayName
42+ elide: Text.ElideMiddle
43+ maximumLineCount: 2
44+ wrapMode: Text.Wrap
45+ color: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.baseText
46+ }
47+
48+ CheckBox {
49+ id: control
50+ objectName: "control"
51+ anchors {
52+ right: parent.right
53+ rightMargin: settingMargins
54+ verticalCenter: parent.verticalCenter
55+ }
56+ checked: widgetData.value
57+
58+ onTriggered: root.updated(checked)
59+ }
60+ }
61+}
qml/Dash/ScopeSettings/ScopeSettingList.qmlnew file+50
@@ -0,0 +1,50 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+import Ubuntu.Components.ListItems 1.0 as ListItem
20+
21+ScopeSetting {
22+ id: root
23+
24+ // FIXME workaround for: https://bugs.launchpad.net/ubuntu/+source/ubuntu-ui-toolkit/+bug/1355830
25+ height: listItem.currentlyExpanded ? listItem.itemHeight * widgetData.properties["values"].length + units.gu(6) : listItem.height
26+
27+ property int initialValue: -1
28+
29+ ListItem.ItemSelector {
30+ id: listItem
31+ objectName: "control"
32+ anchors {
33+ left: parent.left
34+ right: parent.right
35+ }
36+ text: widgetData.displayName
37+ model: widgetData.properties["values"]
38+
39+ onSelectedIndexChanged: {
40+ if (root.initialValue >= 0) {
41+ var tmpValue = root.initialValue;
42+ root.initialValue = -1;
43+ selectedIndex = tmpValue;
44+ return;
45+ }
46+
47+ root.updated(selectedIndex);
48+ }
49+ }
50+}
qml/Dash/ScopeSettings/ScopeSettingNumber.qmlnew file+21
@@ -0,0 +1,21 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.2
18+
19+ScopeSettingString {
20+ mode: "number"
21+}
qml/Dash/ScopeSettings/ScopeSettingString.qmlnew file+83
@@ -0,0 +1,83 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+import Ubuntu.Components.ListItems 1.0 as ListItem
20+
21+ScopeSetting {
22+ id: root
23+ height: listItem.height
24+
25+ property string mode: "string"
26+
27+ ListItem.Empty {
28+ id: listItem
29+ onClicked: {
30+ control.forceActiveFocus();
31+ control.selectAll();
32+ }
33+
34+ Label {
35+ id: label
36+ anchors {
37+ left: parent.left
38+ leftMargin: settingMargins
39+ right: control.left
40+ rightMargin: units.gu(1)
41+ verticalCenter: parent.verticalCenter
42+ }
43+ text: widgetData.displayName
44+ elide: Text.ElideMiddle
45+ maximumLineCount: 2
46+ wrapMode: Text.Wrap
47+ color: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.baseText
48+ }
49+
50+ TextField {
51+ id: control
52+ objectName: "control"
53+ anchors {
54+ right: parent.right
55+ rightMargin: settingMargins
56+ verticalCenter: parent.verticalCenter
57+ }
58+ width: root.mode == "number" ? units.gu(8) : units.gu(12)
59+ text: widgetData.value
60+ color: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.fieldText
61+ validator: root.mode == "number" ? doubleValidator : null
62+ hasClearButton: root.mode == "number" ? false : true
63+
64+ DoubleValidator {
65+ id: doubleValidator
66+ }
67+
68+ function updateText() {
69+ if (acceptableInput) {
70+ text = displayText;
71+ root.updated(text);
72+ }
73+ }
74+
75+ onAccepted: updateText()
76+ onActiveFocusChanged: {
77+ if (!activeFocus) {
78+ updateText();
79+ }
80+ }
81+ }
82+ }
83+}
qml/Dash/ScopeSettings/ScopeSettingsWidgetFactory.qmlnew file+57
@@ -0,0 +1,57 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.2
18+
19+//! \brief This component loads the widgets based on type.
20+
21+Loader {
22+ id: root
23+
24+ //! The ScopeStyle component.
25+ property var scopeStyle: null
26+
27+ //! Variable used to contain widget's data
28+ property var widgetData: null
29+
30+ //! Triggered signal forwarded from the widgets.
31+ signal updated(var value)
32+
33+ source: widgetSource
34+
35+ //! \cond private
36+ property url widgetSource: {
37+ switch (widgetData.type) {
38+ case "boolean": return "ScopeSettingBoolean.qml";
39+ case "list": return "ScopeSettingList.qml";
40+ case "number": return "ScopeSettingNumber.qml";
41+ case "string": return "ScopeSettingString.qml";
42+ default: return "";
43+ }
44+ }
45+ //! \endcond
46+
47+ onLoaded: {
48+ if (item.hasOwnProperty("initialValue")) item.initialValue = root.widgetData.value;
49+ item.widgetData = Qt.binding(function() { return root.widgetData; } )
50+ item.scopeStyle = Qt.binding(function() { return root.scopeStyle; } )
51+ }
52+
53+ Connections {
54+ target: root.item
55+ onUpdated: if (value !== widgetData.value) root.updated(value)
56+ }
57+}
qml/Dash/ScopeSettingsPage.qmlnew file+62
@@ -0,0 +1,62 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+import Unity 0.2
20+import "../Components"
21+import "ScopeSettings"
22+
23+Item {
24+ id: root
25+
26+ property var scope: null
27+ property var scopeStyle: null
28+
29+ signal backClicked()
30+
31+ PageHeader {
32+ id: header
33+ objectName: "pageHeader"
34+ width: parent.width
35+ title: scope ? scope.name : ""
36+ showBackButton: true
37+ scopeStyle: root.scopeStyle
38+
39+ onBackClicked: root.backClicked()
40+ }
41+
42+ ListView {
43+ id: scopeSettings
44+ objectName: "scopeSettings"
45+ anchors {
46+ top: header.bottom
47+ bottom: parent.bottom
48+ left: parent.left
49+ right: parent.right
50+ }
51+ model: root.scope ? root.scope.settings : null
52+
53+ delegate: ScopeSettingsWidgetFactory {
54+ objectName: "scopeSettingItem" + index
55+ width: root.width
56+ widgetData: model
57+ scopeStyle: root.scopeStyle
58+
59+ onUpdated: model.value = value;
60+ }
61+ }
62+}
qml/Dash/ScopesOverview.qml+22−1
@@ -148,7 +148,12 @@ Item {
148148
149149 ScopeStyle {
150150 id: overviewScopeStyle
151− style: { "foreground-color" : "white", "background-color" : "transparent" }
151+ style: { "foreground-color" : "white",
152+ "background-color" : "transparent",
153+ "page-header": {
154+ "background": "color:///transparent"
155+ }
156+ }
152157 }
153158
154159 DashBackground {
@@ -458,10 +463,13 @@ Item {
458463 objectName: "scopesOverviewPreviewListView"
459464 scope: root.scope
460465 scopeStyle: overviewScopeStyle
466+ showSignatureLine: false
461467 visible: x != width
462468 width: parent.width
463469 height: parent.height
464470 anchors.left: scopesOverviewContent.right
471+
472+ onBackClicked: open = false
465473 }
466474
467475 Item {
@@ -535,6 +543,19 @@ Item {
535543 scopesOverviewXYScaler.opacity = 0;
536544 middleItems.overrideOpacity = -1;
537545 }
546+ // TODO Add tests for these connections
547+ Connections {
548+ target: tempScopeItem.scope
549+ onOpenScope: {
550+ // TODO Animate the newly opened scope into the foreground (stacked on top of the current scope)
551+ tempScopeItem.scope = scope;
552+ }
553+ onGotoScope: {
554+ tempScopeItem.backClicked();
555+ root.currentTab = 0;
556+ root.scope.gotoScope(scopeId);
557+ }
558+ }
538559 }
539560 }
540561}
qml/Dash/graphics/PageHeaderBaseDivider@18.pngdeleted

Binary file not shown.

qml/Dash/graphics/PageHeaderBaseDivider@18.scideleted−5
@@ -1,5 +0,0 @@
1−border.top: 4
2−border.bottom: 4
3−border.left: 0
4−border.right: 0
5−source: PageHeaderBaseDivider@18.png
qml/Dash/graphics/navigation_shadow@18.pngnew file

Binary file not shown.

qml/Dash/graphics/navigation_shadow_light@18.pngnew file

Binary file not shown.

qml/Shell.qml+2−2
@@ -279,8 +279,8 @@ Item {
279279 lockscreen.infoText = i18n.tr("Enter your passphrase")
280280 lockscreen.errorText = i18n.tr("Sorry, incorrect passphrase")
281281 } else {
282− lockscreen.infoText = i18n.tr("Enter your PIN")
283− lockscreen.errorText = i18n.tr("Sorry, incorrect PIN")
282+ lockscreen.infoText = i18n.tr("Enter your passcode")
283+ lockscreen.errorText = i18n.tr("Sorry, incorrect passcode")
284284 }
285285 } else {
286286 lockscreen.infoText = i18n.tr("Enter your %1").arg(text.toLowerCase())
qml/Stages/SpreadDelegate.qml+3−7
@@ -47,14 +47,10 @@ Item {
4747 BorderImage {
4848 anchors {
4949 fill: appWindow
50− leftMargin: -units.gu(2)
51− rightMargin: -units.gu(2)
52− bottomMargin: -units.gu(2)
53− topMargin: -units.gu(2)
50+ margins: -units.gu(2)
5451 }
55− source: "graphics/dropshadow.png"
56− border { left: 50; right: 50; top: 50; bottom: 50 }
57− opacity: root.dropShadow ? .4 : 0
52+ source: "graphics/dropshadow2gu.sci"
53+ opacity: root.dropShadow ? .3 : 0
5854 Behavior on opacity { UbuntuNumberAnimation {} }
5955 }
6056
qml/Stages/graphics/dropshadow.pngdeleted

Binary file not shown.

qml/Stages/graphics/dropshadow2gu@30.pngnew file

Binary file not shown.

qml/Stages/graphics/dropshadow2gu@30.scinew file+5
@@ -0,0 +1,5 @@
1+border.left: 60
2+border.top: 60
3+border.bottom: 60
4+border.right: 60
5+source: dropshadow2gu@30.png
tests/autopilot/unity8/shell/emulators/dash.py+5−5
@@ -140,10 +140,9 @@ class Dash(emulators.UnityEmulatorBase):
140140
141141 def enter_search_query(self, query):
142142 current_header = self._get_current_page_header()
143− self.pointing_device.move(current_header.globalRect.x +
144− current_header.width - current_header.height / 4,
145− current_header.globalRect.y +
146− current_header.height / 4)
143+ search_button = current_header.select_single(objectName="search_header_button")
144+ self.pointing_device.move(search_button.globalRect.x + search_button.width / 2,
145+ search_button.globalRect.y + search_button.height / 2)
147146 self.pointing_device.click()
148147 headerContainer = current_header.select_single(
149148 objectName="headerContainer")
@@ -186,7 +185,8 @@ class GenericScopeView(emulators.UnityEmulatorBase):
186185 # --elopio - 2014-1-14
187186 self.click_scope_item(category, app_name)
188187 preview_list = self.wait_select_single(
189− 'PreviewListView', objectName='previewListView')
188+ 'QQuickLoader', objectName='subPageLoader')
189+ preview_list.subPageShown.wait_for(True)
190190 preview_list.x.wait_for(0)
191191 return preview_list.select_single(
192192 Preview, objectName='preview{}'.format(preview_list.currentIndex))
tests/mocks/GSettings.1.0/fake_gsettings.h+2−2
@@ -27,7 +27,7 @@ class GSettingsSchemaQml: public QObject
2727 Q_PROPERTY(QByteArray id READ id WRITE setId)
2828
2929public:
30− GSettingsSchemaQml(QObject *parent = NULL);
30+ GSettingsSchemaQml(QObject *parent = nullptr);
3131
3232 QByteArray id() const;
3333 void setId(const QByteArray &id);
@@ -48,7 +48,7 @@ class GSettingsQml: public QObject
4848 Q_PROPERTY(QString pictureUri READ pictureUri WRITE setPictureUri NOTIFY pictureUriChanged)
4949
5050public:
51− GSettingsQml(QObject *parent = NULL);
51+ GSettingsQml(QObject *parent = nullptr);
5252 ~GSettingsQml();
5353
5454 GSettingsSchemaQml * schema() const;
tests/mocks/HudClient/fake_libhud_client.cpp+9−9
@@ -48,7 +48,7 @@ static void hud_client_query_class_init(HudClientQueryClass * /*klass*/)
4848 HUD_CLIENT_TYPE_QUERY,
4949 G_SIGNAL_RUN_LAST,
5050 0, /* offset */
51− NULL, NULL, /* Accumulator */
51+ nullptr, nullptr, /* Accumulator */
5252 g_cclosure_marshal_VOID__VOID,
5353 G_TYPE_NONE, 0, G_TYPE_NONE);
5454}
@@ -56,7 +56,7 @@ static void hud_client_query_class_init(HudClientQueryClass * /*klass*/)
5656HudClientQuery *hud_client_query_new(const gchar *query)
5757{
5858 HudClientStub::m_lastSetQuery = QString::fromUtf8(query);
59− HudClientStub::m_query = HUD_CLIENT_QUERY(g_object_new(HUD_CLIENT_TYPE_QUERY, NULL));
59+ HudClientStub::m_query = HUD_CLIENT_QUERY(g_object_new(HUD_CLIENT_TYPE_QUERY, nullptr));
6060 return HudClientStub::m_query;
6161}
6262
@@ -70,13 +70,13 @@ DeeModel *hud_client_query_get_results_model(HudClientQuery *cquery)
7070 GVariant * columns[G_N_ELEMENTS(results_model_schema) + 1];
7171 columns[0] = g_variant_new_variant(g_variant_new_uint64(0));
7272 columns[1] = g_variant_new_string("Help");
73− columns[2] = g_variant_new_array(G_VARIANT_TYPE("(ii)"), NULL, 0);
73+ columns[2] = g_variant_new_array(G_VARIANT_TYPE("(ii)"), nullptr, 0);
7474 columns[3] = g_variant_new_string("Get Help");
75− columns[4] = g_variant_new_array(G_VARIANT_TYPE("(ii)"), NULL, 0);
75+ columns[4] = g_variant_new_array(G_VARIANT_TYPE("(ii)"), nullptr, 0);
7676 columns[5] = g_variant_new_string("");
7777 columns[6] = g_variant_new_uint32(3);
7878 columns[7] = g_variant_new_boolean(false);
79− columns[8] = NULL;
79+ columns[8] = nullptr;
8080 dee_model_append_row(resultsModel, columns);
8181
8282 columns[0] = g_variant_new_variant(g_variant_new_uint64(1));
@@ -153,13 +153,13 @@ HudClientParam *hud_client_query_execute_param_command(HudClientQuery *cquery, G
153153 HudClientStub::m_lastExecutedParametrizedCommandRow = i;
154154 g_variant_unref(it_command);
155155 // No need to create a real HudClientParam since it's always passed down to us
156− return (HudClientParam *)g_object_new(G_TYPE_OBJECT, NULL);
156+ return (HudClientParam *)g_object_new(G_TYPE_OBJECT, nullptr);
157157 }
158158 g_variant_unref(it_command);
159159 }
160160 }
161161 HudClientStub::m_lastExecutedParametrizedCommandRow = -1;
162− return NULL;
162+ return nullptr;
163163}
164164
165165void hud_client_query_voice_query(HudClientQuery *cquery)
@@ -183,7 +183,7 @@ GMenuModel *hud_client_param_get_model(HudClientParam *param)
183183
184184 if (!parametrizedActionModel) {
185185 GMenu *menu = g_menu_new();
186− GMenuItem *item = g_menu_item_new("Item1Label", NULL);
186+ GMenuItem *item = g_menu_item_new("Item1Label", nullptr);
187187 g_menu_item_set_attribute_value(item, "parameter-type", g_variant_new_string("slider"));
188188 g_menu_item_set_attribute_value(item, "min", g_variant_new_double(10));
189189 g_menu_item_set_attribute_value(item, "max", g_variant_new_double(80));
@@ -220,7 +220,7 @@ GActionGroup *hud_client_param_get_actions(HudClientParam *param)
220220 GSimpleAction *action = g_simple_action_new("costAction", G_VARIANT_TYPE_DOUBLE);
221221 g_simple_action_group_insert(actionGroup, G_ACTION(action));
222222
223− g_signal_connect (action, "activate", G_CALLBACK (on_signal_activated), NULL);
223+ g_signal_connect (action, "activate", G_CALLBACK (on_signal_activated), nullptr);
224224
225225
226226 parametrizedActionGroup = G_ACTION_GROUP(actionGroup);
tests/mocks/LightDM/demo/GreeterPrivate.cpp+5−5
@@ -46,7 +46,7 @@ public:
4646 : QObject(parent),
4747 greeter(parent),
4848 greeterPrivate(greeterPrivate),
49− pamHandle(NULL)
49+ pamHandle(nullptr)
5050 {
5151 qRegisterMetaType<QLightDM::GreeterImpl::ResponseFuture>("QLightDM::GreeterImpl::ResponseFuture");
5252
@@ -206,14 +206,14 @@ Q_SIGNALS:
206206private Q_SLOTS:
207207 void finishPam()
208208 {
209− if (pamHandle == NULL) {
209+ if (pamHandle == nullptr) {
210210 return;
211211 }
212212
213213 int pamStatus = futureWatcher.result();
214214
215215 pam_end(pamHandle, pamStatus);
216− pamHandle = NULL;
216+ pamHandle = nullptr;
217217
218218 greeterPrivate->authenticated = (pamStatus == PAM_SUCCESS);
219219 Q_EMIT greeter->authenticationComplete();
@@ -243,9 +243,9 @@ private:
243243 void cancelPam()
244244 {
245245 // Unfortunately we can't simply cancel our QFuture because QtConcurrent::run doesn't support cancel
246− if (pamHandle != NULL) {
246+ if (pamHandle != nullptr) {
247247 pam_handle *handle = pamHandle;
248− pamHandle = NULL; // to disable normal finishPam() handling
248+ pamHandle = nullptr; // to disable normal finishPam() handling
249249 while (respond(QString())); // clear our local queue of QFutures
250250 pam_end(handle, PAM_CONV_ERR);
251251 }
tests/mocks/QMenuModel/unitymenumodel.cpp+4−4
@@ -118,7 +118,7 @@ void UnityMenuModel::setMenuObjectPath(const QByteArray &path)
118118
119119ActionStateParser* UnityMenuModel::actionStateParser() const
120120{
121− return NULL;
121+ return nullptr;
122122}
123123
124124void UnityMenuModel::setActionStateParser(ActionStateParser*)
@@ -190,11 +190,11 @@ QHash<int, QByteArray> UnityMenuModel::roleNames() const
190190QObject * UnityMenuModel::submenu(int position, QQmlComponent*)
191191{
192192 if (position < 0 || m_modelData.count() < position) {
193− return NULL;
193+ return nullptr;
194194 }
195195
196196 while (submenus.count() <= position) {
197− submenus.append(NULL);
197+ submenus.append(nullptr);
198198 }
199199
200200 QVariant submenuData = subMenuData(position);
@@ -209,7 +209,7 @@ QObject * UnityMenuModel::submenu(int position, QQmlComponent*)
209209 return model;
210210 }
211211
212− return NULL;
212+ return nullptr;
213213}
214214
215215bool UnityMenuModel::loadExtendedAttributes(int, const QVariantMap &)
tests/mocks/QMenuModel/unitymenumodel.h+2−2
@@ -35,7 +35,7 @@ class Q_DECL_EXPORT UnityMenuModel : public QAbstractListModel
3535 Q_PROPERTY(QString nameOwner READ nameOwner NOTIFY nameOwnerChanged)
3636
3737public:
38− UnityMenuModel(QObject *parent = NULL);
38+ UnityMenuModel(QObject *parent = nullptr);
3939 virtual ~UnityMenuModel();
4040
4141 Q_INVOKABLE void insertRow(int row, const QVariant& data);
@@ -66,7 +66,7 @@ public:
6666 QModelIndex parent(const QModelIndex &index) const;
6767 QHash<int, QByteArray> roleNames() const;
6868
69− Q_INVOKABLE QObject * submenu(int position, QQmlComponent* actionStateParser = NULL);
69+ Q_INVOKABLE QObject * submenu(int position, QQmlComponent* actionStateParser = nullptr);
7070 Q_INVOKABLE bool loadExtendedAttributes(int position, const QVariantMap &schema);
7171 Q_INVOKABLE QVariant get(int row, const QByteArray &role);
7272
tests/mocks/QtMultimedia/audio.cpp+1−1
@@ -22,7 +22,7 @@ Audio::Audio(QObject* parent):
2222 QObject(parent),
2323 m_playbackState(StoppedState)
2424{
25− qsrand(time(NULL));
25+ qsrand(time(nullptr));
2626 m_timer.setInterval(1000);
2727 connect(&m_timer, SIGNAL(timeout()), SLOT(timerEvent()));
2828}
tests/mocks/Unity/Application/ApplicationInfo.h+3−3
@@ -42,11 +42,11 @@ class ApplicationInfo : public ApplicationInfoInterface {
4242 Q_PROPERTY(bool manualSessionCreation READ manualSessionCreation WRITE setManualSessionCreation NOTIFY manualSessionCreationChanged)
4343
4444public:
45− ApplicationInfo(const QString &appId, QObject *parent = NULL);
46− ApplicationInfo(QObject *parent = NULL);
45+ ApplicationInfo(QObject *parent = nullptr);
46+ ApplicationInfo(const QString &appId, QObject *parent = nullptr);
4747 ~ApplicationInfo();
4848
49− void setIconId(const QString &iconId);
49+ void setIconId(const QString &iconId);
5050 void setScreenshotId(const QString &screenshotId);
5151
5252 void setAppId(const QString &value) { m_appId = value; }
tests/mocks/Unity/Application/ApplicationManager.h+1−1
@@ -46,7 +46,7 @@ class ApplicationManager : public ApplicationManagerInterface {
4646 Q_PROPERTY(bool fake READ fake CONSTANT)
4747
4848 public:
49− ApplicationManager(QObject *parent = NULL);
49+ ApplicationManager(QObject *parent = nullptr);
5050 virtual ~ApplicationManager();
5151
5252 static ApplicationManager *singleton();
tests/mocks/Unity/CMakeLists.txt+2
@@ -24,6 +24,7 @@ set(UnityQML_SOURCES
2424 fake_scope.cpp
2525 fake_scopes.cpp
2626 fake_scopesoverview.cpp
27+ fake_settingsmodel.cpp
2728 fake_categories.cpp
2829 fake_navigation.cpp
2930 fake_resultsmodel.cpp
@@ -39,6 +40,7 @@ set(UnityQML_SOURCES
3940 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ResultsModelInterface.h
4041 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ScopeInterface.h
4142 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ScopesInterface.h
43+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/SettingsModelInterface.h
4244)
4345
4446add_library(FakeUnityQml MODULE ${UnityQML_SOURCES})
tests/mocks/Unity/fake_navigation.cpp+6−1
@@ -32,6 +32,7 @@ Navigation::Navigation(const QString& navigationId, const QString& label, const
3232{
3333 QTimer::singleShot(1500, this, SLOT(slotLoaded()));
3434 connect(scope, SIGNAL(currentNavigationIdChanged()), this, SLOT(slotCurrentNavigationChanged()));
35+ connect(scope, SIGNAL(currentAltNavigationIdChanged()), this, SLOT(slotCurrentNavigationChanged()));
3536}
3637
3738QString Navigation::navigationId() const
@@ -106,7 +107,11 @@ QVariant Navigation::data(const QModelIndex &index, int role) const
106107 case RoleHasChildren:
107108 return m_navigationId == "root" && index.row() != 3;
108109 case RoleIsActive:
109− return m_scope->currentNavigationId() == data(index, RoleNavigationId);
110+ if (m_navigationId.startsWith("alt")) {
111+ return m_scope->currentAltNavigationId() == data(index, RoleNavigationId);
112+ } else {
113+ return m_scope->currentNavigationId() == data(index, RoleNavigationId);
114+ }
110115 }
111116 return QVariant();
112117}
tests/mocks/Unity/fake_scope.cpp+18−3
@@ -22,6 +22,7 @@
2222#include "fake_navigation.h"
2323#include "fake_resultsmodel.h"
2424#include "fake_scopes.h"
25+#include "fake_settingsmodel.h"
2526
2627Scope::Scope(Scopes* parent) : Scope(QString(), QString(), false, parent)
2728{
@@ -39,6 +40,7 @@ Scope::Scope(QString const& id, QString const& name, bool favorite, Scopes* pare
3940 , m_previewRendererName("preview-generic")
4041 , m_categories(new Categories(categories, this))
4142 , m_openScope(nullptr)
43+ , m_settings(new SettingsModel(this))
4244{
4345}
4446
@@ -94,7 +96,7 @@ unity::shell::scopes::CategoriesInterface* Scope::categories() const
9496
9597unity::shell::scopes::SettingsModelInterface* Scope::settings() const
9698{
97− return nullptr;
99+ return m_settings;
98100}
99101
100102QString Scope::noResultsHint() const
@@ -224,9 +226,17 @@ QVariantMap Scope::customizations() const
224226 m["background-color"] = "red";
225227 m["foreground-color"] = "blue";
226228 m["page-header"] = h;
229+ } else if (m_id == "MockScope4") {
230+ h["navigation-background"] = QUrl("../../../tests/qmltests/Dash/artwork/background.png");
231+ m["page-header"] = h;
227232 } else if (m_id == "MockScope5") {
228233 h["background"] = "gradient:///lightgrey/grey";
229234 h["logo"] = QUrl("../../../tests/qmltests/Dash/tst_PageHeader/logo-ubuntu-orange.svg");
235+ h["divider-color"] = "red";
236+ h["navigation-background"] = "color:///black";
237+ m["page-header"] = h;
238+ } else if (m_id == "MockScope6") {
239+ h["navigation-background"] = "gradient:///blue/red";
230240 m["page-header"] = h;
231241 }
232242 return m;
@@ -261,11 +271,16 @@ unity::shell::scopes::NavigationInterface* Scope::getAltNavigation(QString const
261271
262272 QString parentId;
263273 QString parentLabel;
264− if (id.startsWith("altmiddle")) {
274+ if (id != "altroot") {
265275 parentId = "altroot";
266276 parentLabel = "altroot";
267277 }
268− return new Navigation(id, id, "all"+id, parentId, parentLabel, this);
278+ auto result = new Navigation(id, id, "all"+id, parentId, parentLabel, this);
279+ if (id == "altroot") {
280+ m_currentAltNavigationId = "altrootChild1";
281+ Q_EMIT currentAltNavigationIdChanged();
282+ }
283+ return result;
269284}
270285
271286void Scope::setNavigationState(const QString &navigationId, bool isAltNavigation)
tests/mocks/Unity/fake_scope.h+1
@@ -96,6 +96,7 @@ protected:
9696
9797 unity::shell::scopes::CategoriesInterface* m_categories;
9898 unity::shell::scopes::ScopeInterface* m_openScope;
99+ unity::shell::scopes::SettingsModelInterface* m_settings;
99100};
100101
101102#endif // FAKE_SCOPE_H
tests/mocks/Unity/fake_scopes.h+1−1
@@ -56,7 +56,7 @@ public:
5656 // This is used as part of implementation of the other C++ code, not API
5757 QList<Scope*> scopes() const;
5858 QList<Scope*> allScopes() const;
59− Scope* getScopeFromAll(const QString& scope_id) const;
59+ Q_INVOKABLE Scope* getScopeFromAll(const QString& scope_id) const;
6060
6161private Q_SLOTS:
6262 void updateScopes();
tests/mocks/Unity/fake_settingsmodel.cppnew file+95
@@ -0,0 +1,95 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "fake_settingsmodel.h"
18+
19+SettingsModel::SettingsModel(QObject* parent) :
20+ SettingsModelInterface(parent) {
21+ {
22+ QVariantMap parameters;
23+ m_data << QSharedPointer<Data>(new Data("boolean-setting", "Boolean Setting", "boolean", parameters, true));
24+ }
25+ {
26+ QVariantMap parameters;
27+ parameters["values"] = QVariantList() << "First" << "Second" << "Third";
28+ m_data << QSharedPointer<Data>(new Data("list-setting", "List Setting", "list", parameters, 1));
29+ }
30+ {
31+ QVariantMap parameters;
32+ m_data << QSharedPointer<Data>(new Data("number-setting", "Number Setting", "number", parameters, 1.23));
33+ }
34+ {
35+ QVariantMap parameters;
36+ m_data << QSharedPointer<Data>(new Data("string-setting", "String Setting", "string", parameters, "flibble"));
37+ }
38+}
39+
40+QVariant SettingsModel::data(const QModelIndex& index, int role) const {
41+ int row = index.row();
42+ QVariant result;
43+
44+ if (row < m_data.size()) {
45+ auto data = m_data[row];
46+
47+ switch (role) {
48+ case Roles::RoleSettingId:
49+ result = data->id;
50+ break;
51+ case Roles::RoleDisplayName:
52+ result = data->displayName;
53+ break;
54+ case Roles::RoleType:
55+ result = data->type;
56+ break;
57+ case Roles::RoleProperties:
58+ result = data->properties;
59+ break;
60+ case Roles::RoleValue: {
61+ result = data->value;
62+ break;
63+ }
64+ default:
65+ break;
66+ }
67+ }
68+
69+ return result;
70+}
71+
72+bool SettingsModel::setData(const QModelIndex &index, const QVariant &value, int role) {
73+ int row = index.row();
74+ if (row < m_data.size()) {
75+ switch (role) {
76+ case SettingsModelInterface::RoleValue: {
77+ auto data = m_data[row];
78+ data->value = value;
79+ return true;
80+ }
81+ default:
82+ break;
83+ }
84+ }
85+
86+ return false;
87+}
88+
89+int SettingsModel::rowCount(const QModelIndex&) const {
90+ return count();
91+}
92+
93+int SettingsModel::count() const {
94+ return m_data.size();
95+}
tests/mocks/Unity/fake_settingsmodel.hnew file+54
@@ -0,0 +1,54 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef FAKE_SETTINGSMODEL_H
18+#define FAKE_SETTINGSMODEL_H
19+
20+#include <unity/shell/scopes/SettingsModelInterface.h>
21+
22+#include <QList>
23+#include <QSharedPointer>
24+
25+class SettingsModel: public unity::shell::scopes::SettingsModelInterface {
26+Q_OBJECT
27+
28+ struct Data {
29+ QString id;
30+ QString displayName;
31+ QString type;
32+ QVariant properties;
33+ QVariant value;
34+
35+ Data(QString const& id_, QString const& displayName_,
36+ QString const& type_, QVariant const& properties_,
37+ QVariant const& value_) :
38+ id(id_), displayName(displayName_), type(type_),
39+ properties(properties_), value(value_) {}
40+ };
41+
42+public:
43+ explicit SettingsModel(QObject* parent = 0);
44+ ~SettingsModel() = default;
45+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
46+ bool setData(const QModelIndex&index, const QVariant& value, int role = Qt::EditRole) override;
47+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
48+ int count() const override;
49+
50+protected:
51+ QList<QSharedPointer<Data>> m_data;
52+};
53+
54+#endif // FAKE_SETTINGSMODEL_H
tests/mocks/Unity/fake_unity_plugin.cpp+2
@@ -26,6 +26,7 @@
2626#include "fake_previewmodel.h"
2727#include "fake_previewwidgetmodel.h"
2828#include "fake_resultsmodel.h"
29+#include "fake_settingsmodel.h"
2930
3031// External
3132#include <glib-object.h>
@@ -43,6 +44,7 @@ void FakeUnityPlugin::registerTypes(const char *uri)
4344 qmlRegisterType<Scopes>(uri, 0, 2, "Scopes");
4445 qmlRegisterType<Scope>(uri, 0, 2, "MockScope");
4546 qmlRegisterUncreatableType<unity::shell::scopes::ScopeInterface>(uri, 0, 2, "Scope", "Can't create Scope object in QML.");
47+ qmlRegisterUncreatableType<unity::shell::scopes::SettingsModelInterface>(uri, 0, 2, "SettingsModel", "Can't create SettingsModel object in QML.");
4648 qmlRegisterUncreatableType<unity::shell::scopes::NavigationInterface>(uri, 0, 2, "Navigation", "Can't create Navigation object in QML.");
4749 qmlRegisterUncreatableType<unity::shell::scopes::CategoriesInterface>(uri, 0, 2, "Categories", "Can't create Categories object in QML.");
4850 qmlRegisterUncreatableType<unity::shell::scopes::PreviewModelInterface>(uri, 0, 2, "PreviewModel", "Can't create new PreviewModel in QML. Get them from PreviewStack instance.");
tests/plugins/Dash/cardcreator/2.res+3−3
@@ -26,7 +26,7 @@ Loader {
2626 gradientColor: getColor(1) || color;
2727 anchors.fill: parent;
2828 image: backgroundImage.source ? backgroundImage : null;
29− property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
29+ property real luminance: Style.luminance(color);
3030 property Image backgroundImage: Image {
3131 objectName: "backgroundImage";
3232 source: {
@@ -94,7 +94,7 @@ Image {
9494 wrapMode: Text.Wrap;
9595 maximumLineCount: 2;
9696 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
97− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
97+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
9898 visible: showHeader ;
9999 text: root.title;
100100 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -112,7 +112,7 @@ Image {
112112 elide: Text.ElideRight;
113113 fontSize: "small";
114114 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
115− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
115+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
116116 visible: titleLabel.visible && titleLabel.text;
117117 text: cardData && cardData["subtitle"] || "";
118118 font.weight: Font.Light;
tests/plugins/Dash/cardcreator/5.res+5−3
@@ -69,7 +69,7 @@ Loader {
6969 sourceComponent: ShaderEffect {
7070 id: overlay;
7171 height: (fixedHeaderHeight > 0 ? fixedHeaderHeight : headerHeight) + units.gu(2);
72− property real luminance: 0.2126 * overlayColor.r + 0.7152 * overlayColor.g + 0.0722 * overlayColor.b;
72+ property real luminance: Style.luminance(overlayColor);
7373 property color overlayColor: cardData && cardData["overlayColor"] || "#99000000";
7474 property var source: ShaderEffectSource {
7575 id: shaderSource;
@@ -103,6 +103,7 @@ Label {
103103 id: titleLabel;
104104 objectName: "titleLabel";
105105 anchors { right: parent.right;
106+ rightMargin: units.gu(1);
106107 left: parent.left;
107108 leftMargin: units.gu(1);
108109 top: overlayLoader.top;
@@ -113,7 +114,7 @@ Label {
113114 wrapMode: Text.Wrap;
114115 maximumLineCount: 2;
115116 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
116− color: overlayLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
117+ color: root.scopeStyle ? root.scopeStyle.getTextColor(overlayLoader.item.luminance) : (overlayLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
117118 visible: showHeader && overlayLoader.active;
118119 text: root.title;
119120 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -124,6 +125,7 @@ Label {
124125 objectName: "subtitleLabel";
125126 anchors { left: titleLabel.left;
126127 leftMargin: titleLabel.leftMargin;
128+ rightMargin: units.gu(1);
127129 right: titleLabel.right;
128130 top: titleLabel.bottom;
129131 }
@@ -131,7 +133,7 @@ Label {
131133 elide: Text.ElideRight;
132134 fontSize: "small";
133135 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
134− color: overlayLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
136+ color: root.scopeStyle ? root.scopeStyle.getTextColor(overlayLoader.item.luminance) : (overlayLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
135137 visible: titleLabel.visible && titleLabel.text;
136138 text: cardData && cardData["subtitle"] || "";
137139 font.weight: Font.Light;
tests/plugins/Dash/cardcreator/6.res+4−4
@@ -26,7 +26,7 @@ Loader {
2626 gradientColor: getColor(1) || color;
2727 anchors.fill: parent;
2828 image: backgroundImage.source ? backgroundImage : null;
29− property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
29+ property real luminance: Style.luminance(color);
3030 property Image backgroundImage: Image {
3131 objectName: "backgroundImage";
3232 source: {
@@ -70,7 +70,7 @@ Item {
7070 wrapMode: Text.Wrap;
7171 maximumLineCount: 2;
7272 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
73− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
73+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
7474 visible: showHeader ;
7575 text: root.title;
7676 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -88,7 +88,7 @@ Item {
8888 elide: Text.ElideRight;
8989 fontSize: "small";
9090 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
91− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
91+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
9292 visible: titleLabel.visible && titleLabel.text;
9393 text: cardData && cardData["subtitle"] || "";
9494 font.weight: Font.Light;
@@ -103,7 +103,7 @@ Item {
103103 rightMargin: units.gu(1);
104104 }
105105 source: cardData && cardData["emblem"] || "";
106− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
106+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
107107 width: height;
108108 height: source != "" ? titleLabel.font.pixelSize : 0;
109109 }
tests/plugins/Dash/cardcreator/7.res+5−4
@@ -26,7 +26,7 @@ Loader {
2626 gradientColor: getColor(1) || color;
2727 anchors.fill: parent;
2828 image: backgroundImage.source ? backgroundImage : null;
29− property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
29+ property real luminance: Style.luminance(color);
3030 property Image backgroundImage: Image {
3131 objectName: "backgroundImage";
3232 source: {
@@ -95,7 +95,7 @@ Image {
9595 wrapMode: Text.Wrap;
9696 maximumLineCount: 2;
9797 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
98− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
98+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
9999 visible: showHeader ;
100100 text: root.title;
101101 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -113,7 +113,7 @@ Image {
113113 elide: Text.ElideRight;
114114 fontSize: "small";
115115 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
116− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
116+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
117117 visible: titleLabel.visible && titleLabel.text;
118118 text: cardData && cardData["subtitle"] || "";
119119 font.weight: Font.Light;
@@ -127,7 +127,8 @@ Image {
127127 rightMargin: units.gu(1);
128128 top: subtitleLabel.bottom;
129129 }
130− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
130+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
131+ fontScale: root.fontScale;
131132 model: cardData && cardData["attributes"];
132133 }
133134
tests/plugins/Dash/tst_CardAttributes.qml+4−1
@@ -26,7 +26,8 @@ Item {
2626 [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}],
2727 [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"},{"value":"text3"}],
2828 [{"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"}]
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+ [{"value":"text1","icon":"image://theme/ok"},{},{},{},{"value":"text5", "icon": "image://theme/search"}],
3031 ]
3132
3233 CardAttributes {
@@ -49,6 +50,8 @@ Item {
4950 function test_columns(data) {
5051 cardAttributes.model = data;
5152 compare(cardAttributes.columns, 2 + data.length % 2);
53+ var rows = Math.ceil(data.length / 3);
54+ tryCompare(cardAttributes, "height", rows * units.gu(2) + (rows - 1) * cardAttributes.rowSpacing);
5255 }
5356 }
5457}
tests/plugins/Dash/tst_ScopeStyle.qml+73−68
@@ -18,6 +18,7 @@ import QtQuick 2.2
1818import QtTest 1.0
1919import Dash 0.1
2020import Ubuntu.Components 1.1
21+import Utils 0.1
2122
2223Rectangle {
2324 width: units.gu(40)
@@ -35,39 +36,23 @@ Rectangle {
3536 property color color
3637 property var styles: [
3738 {},
38− { "foreground-color": "red", "background-color": "black", "page-header": { "logo": "/foo/bar" } },
39− { "foreground-color": "green", "background-color": "white", "page-header": { "foreground-color": "black" } },
40− { "foreground-color": "blue", "background-color": "darkgrey", "page-header": { "background": "gradient:///white/blue" } },
39+ { "foreground-color": "red", "background-color": "black", "page-header": { "logo": "/foo/bar" },
40+ "preview-button-color": "red"},
41+ { "foreground-color": "green", "background-color": "white",
42+ "page-header": { "foreground-color": "black",
43+ "divider-color": "blue" } },
44+ { "foreground-color": "blue", "background-color": "darkgrey",
45+ "page-header": { "background": "gradient:///white/blue",
46+ "navigation-background": "gradient:///white/blue" } },
4147 ]
4248
4349 function cleanup() {
4450 testCase.color = "transparent";
4551 }
4652
47− function test_luminance_data() {
48− return [
49− { tag: "#F00", luminance: 0.2126 },
50− { tag: "#0F0", luminance: 0.7152 },
51− { tag: "#00F", luminance: 0.0722 },
52− { tag: "white", luminance: 1.0 },
53− { tag: "black", luminance: 0.0 },
54− { tag: "lightgrey", luminance: 0.8275 },
55− { tag: "grey", luminance: 0.5020 },
56− { tag: "darkgrey", luminance: 0.6627 },
57− { tag: "red", luminance: 0.2126 },
58− { tag: "green", luminance: 0.3590 },
59− { tag: "blue", luminance: 0.0722 },
60− ];
61− }
62−
63− function test_luminance(data) {
64− testCase.color = data.tag;
65− compare(tool.luminance(testCase.color).toFixed(4), data.luminance.toFixed(4));
66− }
67−
6853 function test_foreground_data() {
6954 return [
70− { tag: "default", index: 0, foreground: Theme.palette.normal.foreground, luminance: 0.5725 },
55+ { tag: "default", index: 0, foreground: UbuntuColors.darkGrey, luminance: 0.3647 },
7156 { tag: "red on black", index: 1, foreground: "red", luminance: 0.2126 },
7257 { tag: "green on white", index: 2, foreground: "green", luminance: 0.3590 },
7358 { tag: "blue on darkgrey", index: 3, foreground: "blue", luminance: 0.0722 },
@@ -83,7 +68,7 @@ Rectangle {
8368
8469 function test_background_data() {
8570 return [
86− { tag: "default", index: 0, background: "transparent" },
71+ { tag: "default", index: 0, background: "#00f5f5f5", luminance: 0.9608 },
8772 { tag: "red on black", index: 1, background: "black", luminance: 0 },
8873 { tag: "green on white", index: 2, background: "white", luminance: 1 },
8974 { tag: "blue on darkgrey", index: 3, background: "darkgrey", luminance: 0.6627 },
@@ -94,53 +79,35 @@ Rectangle {
9479 tool.style = testCase.styles[data.index];
9580 verify(Qt.colorEqual(tool.background, data.background),
9681 "Background color not equal: %1 != %2".arg(tool.background).arg(data.background));
97− if (data.hasOwnProperty("luminance")) {
98− compare(tool.backgroundLuminance.toFixed(4), data.luminance.toFixed(4));
99− }
82+ compare(tool.backgroundLuminance.toFixed(4), data.luminance.toFixed(4));
10083 }
10184
102− function test_threshold_data() {
85+ function test_getTextColor_data() {
10386 return [
104− { tag: "default", index: 0, threshold: 0.7863 },
105− { tag: "red on black", index: 1, threshold: 0.1063 },
106− { tag: "green on white", index: 2, threshold: 0.6795 },
107− { tag: "blue on darkgrey", index: 3, threshold: 0.3675 },
87+ { tag: "default on black", background: "black", index: 0, textColor: "white" },
88+ { tag: "default on lightgrey", background: "lightgrey", index: 0, textColor: UbuntuColors.darkGrey },
89+ { tag: "default on white", background: "white", index: 0, textColor: UbuntuColors.darkGrey },
90+ { tag: "default on yellow", background: "yellow", index: 0, textColor: UbuntuColors.darkGrey },
91+ { tag: "red/black on black", background: "black", index: 1, textColor: "red" },
92+ { tag: "red/black on lightgrey", background: "lightgrey", index: 1, textColor: "black" },
93+ { tag: "red/black on white", background: "white", index: 1, textColor: "black" },
94+ { tag: "red/black on yellow", background: "yellow", index: 1, textColor: "black" },
95+ { tag: "green/white on black", background: "black", index: 2, textColor: "white" },
96+ { tag: "green/white on lightgrey", background: "lightgrey", index: 2, textColor: "green" },
97+ { tag: "green/white on white", background: "white", index: 2, textColor: "green" },
98+ { tag: "green/white on yellow", background: "yellow", index: 2, textColor: "green" },
99+ { tag: "blue/darkgrey on black", background: "black", index: 3, textColor: "darkgrey" },
100+ { tag: "blue/darkgrey on lightgrey", background: "lightgrey", index: 3, textColor: "blue" },
101+ { tag: "blue/darkgrey on white", background: "white", index: 3, textColor: "blue" },
102+ { tag: "blue/darkgrey on yellow", background: "yellow", index: 3, textColor: "blue" },
108103 ];
109104 }
110105
111− function test_threshold(data) {
106+ function test_getTextColor(data) {
112107 tool.style = testCase.styles[data.index];
113− compare(tool.threshold.toFixed(4), data.threshold.toFixed(4), "Luminance threshold was incorrect.");
114− }
115−
116− function test_light_data() {
117− return [
118− { tag: "default", index: 0, light: "white" },
119− { tag: "red on black", index: 1, light: "red" },
120− { tag: "green on white", index: 2, light: "white" },
121− { tag: "blue on darkgrey", index: 3, light: "darkgrey" },
122− ];
123− }
124−
125− function test_light(data) {
126− tool.style = testCase.styles[data.index];
127− verify(Qt.colorEqual(tool.light, data.light),
128− "Light color not equal: %1 != %2".arg(tool.light).arg(data.light));
129− }
130−
131− function test_dark_data() {
132− return [
133− { tag: "default", index: 0, dark: Theme.palette.normal.baseText },
134− { tag: "red on black", index: 1, dark: "black" },
135− { tag: "green on white", index: 2, dark: "green" },
136− { tag: "blue on darkgrey", index: 3, dark: "blue" },
137− ];
138− }
139−
140− function test_dark(data) {
141− tool.style = testCase.styles[data.index];
142− verify(Qt.colorEqual(tool.dark, data.dark),
143− "Dark color not equal: %1 != %2".arg(tool.dark).arg(data.dark));
108+ var textColor = tool.getTextColor(Style.luminance(data.background));
109+ verify(Qt.colorEqual(textColor, data.textColor),
110+ "TextColor not equal: %1 != %2".arg(textColor).arg(data.textColor));
144111 }
145112
146113 function test_headerLogo_data() {
@@ -157,7 +124,7 @@ Rectangle {
157124
158125 function test_headerForeground_data() {
159126 return [
160− { tag: "default", index: 0, headerForeground: Theme.palette.normal.baseText },
127+ { tag: "default", index: 0, headerForeground: UbuntuColors.darkGrey },
161128 { tag: "black", index: 2, headerForeground: "black" },
162129 ];
163130 }
@@ -170,7 +137,7 @@ Rectangle {
170137
171138 function test_headerBackground_data() {
172139 return [
173− { tag: "default", index: 0, headerBackground: "" },
140+ { tag: "default", index: 0, headerBackground: "color:///#f5f5f5" },
174141 { tag: "black", index: 3, headerBackground: "gradient:///white/blue" },
175142 ];
176143 }
@@ -179,5 +146,43 @@ Rectangle {
179146 tool.style = testCase.styles[data.index];
180147 compare(tool.headerBackground, data.headerBackground, "Header background was incorrect.");
181148 }
149+
150+ function test_headerDividerColor_data() {
151+ return [
152+ { tag: "default", index: 0, headerDividerColor: "#e0e0e0" },
153+ { tag: "blue", index: 2, headerDividerColor: "blue" },
154+ ];
155+ }
156+
157+ function test_headerDividerColor(data) {
158+ tool.style = testCase.styles[data.index];
159+ verify(Qt.colorEqual(tool.headerDividerColor, data.headerDividerColor),
160+ "Header divider color not equal: %1 != %2".arg(tool.headerDividerColor).arg(data.headerDividerColor));
161+ }
162+
163+ function test_navigationBackground_data() {
164+ return [
165+ { tag: "default", index: 0, navigationBackground: "color:///#f5f5f5" },
166+ { tag: "black", index: 3, navigationBackground: "gradient:///white/blue" },
167+ ];
168+ }
169+
170+ function test_navigationBackground(data) {
171+ tool.style = testCase.styles[data.index];
172+ compare(tool.navigationBackground, data.navigationBackground, "Navigation background was incorrect.");
173+ }
174+
175+ function test_previewButtonColor_data() {
176+ return [
177+ { tag: "default", index: 0, previewButtonColor: Theme.palette.selected.foreground },
178+ { tag: "red", index: 1, previewButtonColor: "red" },
179+ ];
180+ }
181+
182+ function test_previewButtonColor(data) {
183+ tool.style = testCase.styles[data.index];
184+ verify(Qt.colorEqual(tool.previewButtonColor, data.previewButtonColor),
185+ "Preview button color not equal: %1 != %2".arg(tool.previewButtonColor).arg(data.previewButtonColor));
186+ }
182187 }
183188}
tests/plugins/Unity/Indicators/rootactionstatetest.cpp+4−4
@@ -50,7 +50,7 @@ private Q_SLOTS:
5050
5151 QCOMPARE(rootState->menu(), menuModel);
5252 delete menuModel;
53− QVERIFY(rootState->menu() == NULL);
53+ QVERIFY(rootState->menu() == nullptr);
5454 delete rootState;
5555 }
5656
@@ -60,8 +60,8 @@ private Q_SLOTS:
6060 g_variant_builder_init(&builderIcons, G_VARIANT_TYPE("av"));
6161 for (int i = 0; i < 3; i ++) {
6262
63− GIcon* icon = NULL;
64− icon = g_icon_new_for_string (QString("testIcon%1").arg(i).toUtf8().constData(), NULL);
63+ GIcon* icon = nullptr;
64+ icon = g_icon_new_for_string (QString("testIcon%1").arg(i).toUtf8().constData(), nullptr);
6565
6666 g_variant_builder_add(&builderIcons,
6767 "v",
@@ -73,7 +73,7 @@ private Q_SLOTS:
7373 g_variant_builder_init(&builderParams, G_VARIANT_TYPE_ARRAY);
7474 GVariant* icons = g_variant_builder_end (&builderIcons);
7575 g_variant_ref_sink (icons);
76− g_variant_builder_add (&builderParams, "{sv}", g_strdup ("icons"), icons, NULL);
76+ g_variant_builder_add (&builderParams, "{sv}", g_strdup ("icons"), icons, nullptr);
7777
7878 GVariant* params = g_variant_builder_end (&builderParams);
7979
tests/plugins/Unity/Indicators/unitymenumodelstacktest.cpp+1−1
@@ -42,7 +42,7 @@ private Q_SLOTS:
4242 void cleanup()
4343 {
4444 delete m_model;
45− m_model = NULL;
45+ m_model = nullptr;
4646 }
4747
4848 void testHeadOnSetHead()
tests/plugins/Utils/CMakeLists.txt+5
@@ -27,3 +27,8 @@ run_tests(
2727 qsortfilterproxymodeltest
2828 timeformattertest
2929 )
30+
31+# plain qml test
32+set(qmltest_DEFAULT_TARGETS qmltests)
33+set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal")
34+add_qml_test(. UtilsStyle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
tests/plugins/Utils/tst_UtilsStyle.qmlnew file+47
@@ -0,0 +1,47 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.2
18+import QtTest 1.0
19+import Utils 0.1
20+
21+TestCase {
22+ id: testCase
23+ name: "UtilsStyle"
24+
25+ property color color
26+
27+ function test_luminance_data() {
28+ return [
29+ { tag: "#F00", luminance: 0.2126 },
30+ { tag: "#0F0", luminance: 0.7152 },
31+ { tag: "#00F", luminance: 0.0722 },
32+ { tag: "white", luminance: 1.0 },
33+ { tag: "black", luminance: 0.0 },
34+ { tag: "lightgrey", luminance: 0.8275 },
35+ { tag: "grey", luminance: 0.5020 },
36+ { tag: "darkgrey", luminance: 0.6627 },
37+ { tag: "red", luminance: 0.2126 },
38+ { tag: "green", luminance: 0.3590 },
39+ { tag: "blue", luminance: 0.0722 },
40+ ];
41+ }
42+
43+ function test_luminance(data) {
44+ testCase.color = data.tag;
45+ compare(Style.luminance(testCase.color).toFixed(4), data.luminance.toFixed(4));
46+ }
47+}
tests/qmltests/CMakeLists.txt+6
@@ -50,10 +50,16 @@ add_qml_test(Dash/Previews PreviewPayments)
5050add_qml_test(Dash/Previews PreviewProgress)
5151add_qml_test(Dash/Previews PreviewRatingDisplay)
5252add_qml_test(Dash/Previews PreviewRatingInput)
53+add_qml_test(Dash/Previews PreviewTable)
5354add_qml_test(Dash/Previews PreviewTextSummary)
5455add_qml_test(Dash/Previews PreviewVideoPlayback)
5556add_qml_test(Dash/Previews PreviewWidgetFactory)
5657add_qml_test(Dash/Previews PreviewZoomableImage)
58+add_qml_test(Dash/ScopeSettings ScopeSettingBoolean)
59+add_qml_test(Dash/ScopeSettings ScopeSettingList)
60+add_qml_test(Dash/ScopeSettings ScopeSettingNumber)
61+add_qml_test(Dash/ScopeSettings ScopeSettingString)
62+add_qml_test(Dash/ScopeSettings ScopeSettingsWidgetFactory)
5763add_qml_test(Greeter Lockscreen ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
5864add_qml_test(Greeter MultiGreeter ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
5965add_qml_test(Greeter SingleGreeter ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
tests/qmltests/Components/tst_Background.qml+5−4
@@ -46,10 +46,10 @@ Rectangle {
4646
4747 function test_style_data() {
4848 return [
49− { tag: "empty", style: "" },
50− { tag: "solid", style: "color:///black" },
51− { tag: "gradient", style: "gradient:///black/red" },
52− { tag: "image", style: "/some/path" },
49+ { tag: "empty", style: "", luminance: 0.5 },
50+ { tag: "solid", style: "color:///black", luminance: 0 },
51+ { tag: "gradient", style: "gradient:///black/red", luminance: 0.1063 },
52+ { tag: "image", style: "/some/path", luminance: 0.5 },
5353 ];
5454 }
5555
@@ -58,6 +58,7 @@ Rectangle {
5858 expectFail("empty", "Empty style should not create a background.");
5959 loadedSpy.wait();
6060 compare(background.item.objectName, data.tag, "Background should be %1".arg(data.style));
61+ compare(background.luminance, data.luminance);
6162 }
6263
6364 function test_solid() {
tests/qmltests/Dash/Previews/tst_PreviewActions.qml+8−8
@@ -27,21 +27,21 @@ Rectangle {
2727 height: units.gu(80)
2828
2929 property var actionDataOneAction: {
30− "actions": [{"label": "Some Label", "icon": "../graphics/play_button.png", "id": "someid"}]
30+ "actions": [{"label": "Some Label", "id": "someid"}]
3131 }
3232
3333 property var actionDataTwoActions: {
34− "actions": [{"label": "Some Label A", "icon": "../graphics/icon_clear.png", "id": "someid1"},
35− {"label": "Some Label B", "icon": "../graphics/play_button.png", "id": "someid2"}
34+ "actions": [{"label": "Some Label A", "id": "someid1"},
35+ {"label": "Some Label B", "id": "someid2"}
3636 ]
3737 }
3838
3939 property var actionDataFiveActions: {
40− "actions": [{"label": "Some Label C", "icon": "../graphics/play_button.png", "id": "someid3"},
41− {"label": "Some Label D", "icon": "../graphics/icon_clear.png", "id": "someid4"},
42− {"label": "Some Label E", "icon": "../graphics/play_button.png", "id": "someid5"},
43− {"label": "Some Label F", "icon": "../graphics/icon_clear.png", "id": "someid6"},
44− {"label": "Some Label G", "icon": "../graphics/play_button.png", "id": "someid7"}
40+ "actions": [{"label": "Some Label C", "id": "someid3"},
41+ {"label": "Some Label D", "id": "someid4"},
42+ {"label": "Some Label E", "id": "someid5"},
43+ {"label": "Some Label F", "id": "someid6"},
44+ {"label": "Some Label G", "id": "someid7"}
4545 ]
4646 }
4747
tests/qmltests/Dash/Previews/tst_PreviewExpandable.qml+5−1
@@ -30,6 +30,10 @@ Rectangle {
3030 property string longText2: "This is a very very very long text. 1 This is a very very very long text. 2 This is a very very very long text. 3 This is a very very very long text. 4 This is a very very very long text. 5 This is a very very very long text. 6 This is a very very very long text. 7 This is a very very very long text. 8 This is a very very very long text. 9 This is a very very very long text. 10 This is a very very very long text. 11 This is a very very very long text."
3131 property string shortText: "This is a short text :)"
3232
33+ property var tableData: {
34+ "values": [ [ "Long Label 1", "Value 1"], [ "Label 2", "Long Value 2"], [ "Label 3", "Value 3"], [ "Label 4", "Value 4"], [ "Label 5", "Value 5"] ]
35+ }
36+
3337 ListModel {
3438 id: widgetsModel
3539 }
@@ -42,7 +46,7 @@ Rectangle {
4246
4347 Component.onCompleted: {
4448 widgetsModel.append({"type": "text", "widgetId": "text1", "properties": { "text": longText }});
45− widgetsModel.append({"type": "text", "widgetId": "text2", "properties": { "text": longText2 }});
49+ widgetsModel.append({"type": "text", "widgetId": "table1", "properties": { "text": tableData }});
4650 widgetsModel.append({"type": "text", "widgetId": "text3", "properties": { "text": shortText }});
4751 widgetsModel.append({"type": "text", "widgetId": "text4", "properties": { "text": longText }});
4852 }
tests/qmltests/Dash/Previews/tst_PreviewTable.qmlnew file+114
@@ -0,0 +1,114 @@
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.0
18+import QtTest 1.0
19+import Ubuntu.Components 0.1
20+import "../../../../qml/Dash/Previews"
21+import Unity.Test 0.1 as UT
22+
23+Rectangle {
24+ id: root
25+ width: units.gu(40)
26+ height: units.gu(80)
27+ color: Theme.palette.selected.background
28+
29+ property var widgetDataComplete: {
30+ "title": "Title here",
31+ "values": [ [ "Long Label 1", "Value 1"], [ "Label 2", "Long Value 2"], [ "Label 3", "Value 3"], [ "Label 4", "Value 4"], [ "Label 5", "Value 5"] ]
32+ }
33+
34+ property var widgetDataNoTitle: {
35+ "values": [ [ "Long Label 1", "Value 1"], [ "Label 2", "Long Value 2"], [ "Label 3", "Value 3"], [ "Label 4", "Value 4"], [ "Label 5", "Value 5"] ]
36+ }
37+
38+ PreviewTable {
39+ id: previewTable
40+ anchors { left: parent.left; right: parent.right }
41+ widgetData: widgetDataComplete
42+
43+ Rectangle {
44+ color: "red"
45+ anchors.fill: parent
46+ opacity: 0.5
47+ }
48+ }
49+
50+ UT.UnityTestCase {
51+ name: "PreviewTableTest"
52+ when: windowShown
53+
54+ function init() {
55+ previewTable.widgetData = widgetDataComplete;
56+ }
57+
58+ function test_values() {
59+ compare(findChild(previewTable, "label00").text, "Long Label 1");
60+ compare(findChild(previewTable, "label01").text, "Value 1");
61+ compare(findChild(previewTable, "label10").text, "Label 2");
62+ compare(findChild(previewTable, "label11").text, "Long Value 2");
63+ compare(findChild(previewTable, "label20").text, "Label 3");
64+ compare(findChild(previewTable, "label21").text, "Value 3");
65+ compare(findChild(previewTable, "label30").text, "Label 4");
66+ compare(findChild(previewTable, "label31").text, "Value 4");
67+ compare(findChild(previewTable, "label40").text, "Label 5");
68+ compare(findChild(previewTable, "label41").text, "Value 5");
69+ }
70+
71+ function test_optional_title() {
72+ var titleLabel = findChild(previewTable, "titleLabel");
73+ compare(titleLabel.visible, true);
74+ var titleHeight = titleLabel.height;
75+ var prevHeight = previewTable.height;
76+
77+ previewTable.widgetData = widgetDataNoTitle;
78+ var column = findChild(previewTable, "column");
79+ tryCompare(previewTable, "height", prevHeight - titleHeight - column.spacing );
80+ }
81+
82+ function test_show_collapsed() {
83+ verify(findChild(previewTable, "label00").visible);
84+ verify(findChild(previewTable, "label01").visible);
85+ verify(findChild(previewTable, "label10").visible);
86+ verify(findChild(previewTable, "label11").visible);
87+ verify(findChild(previewTable, "label20").visible);
88+ verify(findChild(previewTable, "label21").visible);
89+ verify(findChild(previewTable, "label30").visible);
90+ verify(findChild(previewTable, "label31").visible);
91+ verify(findChild(previewTable, "label40").visible);
92+ verify(findChild(previewTable, "label41").visible);
93+
94+ waitForRendering(previewTable);
95+ var prevHeight = previewTable.height;
96+ previewTable.expanded = false;
97+
98+ verify(findChild(previewTable, "label00").visible);
99+ verify(findChild(previewTable, "label01").visible);
100+ verify(findChild(previewTable, "label10").visible);
101+ verify(findChild(previewTable, "label11").visible);
102+ verify(findChild(previewTable, "label20").visible);
103+ verify(findChild(previewTable, "label21").visible);
104+ verify(!findChild(previewTable, "label30").visible);
105+ verify(!findChild(previewTable, "label31").visible);
106+ verify(!findChild(previewTable, "label40").visible);
107+ verify(!findChild(previewTable, "label41").visible);
108+
109+ var labelHeight = findChild(previewTable, "label00").height;
110+ var gridLayout = findChild(previewTable, "gridLayout");
111+ tryCompare(previewTable, "height", prevHeight - labelHeight * 2 - gridLayout.rowSpacing * 2);
112+ }
113+ }
114+}
tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qml+2−1
@@ -84,6 +84,7 @@ Rectangle {
8484 { tag: "Progress", type: "progress", source: "PreviewProgress.qml" },
8585 { tag: "Rating Input", type: "rating-input", source: "PreviewRatingInput.qml" },
8686 { tag: "Rating Display", type: "reviews", source: "PreviewRatingDisplay.qml" },
87+ { tag: "Table", type: "table", source: "PreviewTable.qml" },
8788 { tag: "Text", type: "text", source: "PreviewTextSummary.qml" },
8889 { tag: "Video", type: "video", source: "PreviewVideoPlayback.qml" },
8990 ];
@@ -93,7 +94,7 @@ Rectangle {
9394 factory.widgetData = { type: data.type };
9495 factory.widgetType = data.type;
9596
96− verify(("" + factory.source).indexOf(data.source) != -1);
97+ verify((String(factory.source)).indexOf(data.source) != -1);
9798 }
9899 }
99100}
tests/qmltests/Dash/ScopeSettings/tst_ScopeSettingBoolean.qmlnew file+62
@@ -0,0 +1,62 @@
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.0
18+import Ubuntu.Components 1.1
19+import QtTest 1.0
20+import "../../../../qml/Dash/ScopeSettings"
21+import Unity.Test 0.1 as UT
22+
23+Rectangle {
24+ id: root
25+ width: units.gu(40)
26+ height: units.gu(80)
27+
28+ property var settingData: {
29+ "displayName" : "Mock boolean setting",
30+ "value": true
31+ }
32+
33+ ScopeSettingBoolean {
34+ id: scopeSetting
35+ widgetData: settingData
36+ width: parent.width
37+ }
38+
39+ SignalSpy {
40+ id: spy
41+ target: scopeSetting
42+ signalName: "updated"
43+ }
44+
45+ UT.UnityTestCase {
46+ id: testCase
47+ name: "ScopeSettingBoolean"
48+ when: windowShown
49+
50+ function test_updated() {
51+ var control = findChild(scopeSetting, "control");
52+ mouseClick(control, control.width / 2, control.height / 2);
53+ spy.wait();
54+ compare(spy.signalArguments[0][0], false);
55+
56+ spy.clear();
57+ mouseClick(scopeSetting, scopeSetting.width / 2, scopeSetting.height / 2);
58+ spy.wait();
59+ compare(spy.signalArguments[0][0], true);
60+ }
61+ }
62+}
tests/qmltests/Dash/ScopeSettings/tst_ScopeSettingList.qmlnew file+84
@@ -0,0 +1,84 @@
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.0
18+import Ubuntu.Components 1.1
19+import QtTest 1.0
20+import "../../../../qml/Dash/ScopeSettings"
21+import Unity.Test 0.1 as UT
22+
23+Rectangle {
24+ id: root
25+ width: units.gu(40)
26+ height: units.gu(80)
27+
28+ property var settingData: {
29+ "displayName" : "Mock list setting",
30+ "properties": { "values" : [ "first", "second", "third" ] }
31+ }
32+
33+ ScopeSettingList {
34+ id: scopeSetting
35+ width: parent.width
36+ widgetData: settingData
37+ }
38+
39+ SignalSpy {
40+ id: spy
41+ target: scopeSetting
42+ signalName: "updated"
43+ }
44+
45+ UT.UnityTestCase {
46+ id: testCase
47+ name: "ScopeSettingList"
48+ when: windowShown
49+
50+ property var control: findChild(scopeSetting, "control")
51+
52+ function cleanup() {
53+ control.selectedIndex = 0;
54+ spy.clear();
55+ }
56+
57+ function test_initialValue() {
58+ // there's no initialIndex, so we workaround, testing is difficult too https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1362031
59+ var newObject = Qt.createQmlObject('import "../../../../qml/Dash/ScopeSettings" \n\
60+ ScopeSettingList { initialValue: 2; }', root, "dynamicScopeSettingList");
61+ newObject.widgetData = settingData;
62+ var controlDynamic = findChild(newObject, "control");
63+ tryCompare(controlDynamic, "selectedIndex", 2);
64+ }
65+
66+ function test_updated_data() {
67+ return [
68+ { tag: "current", index: 0, updated: false },
69+ { tag: "second", index: 1, updated: true },
70+ { tag: "third", index: 2, updated: true }
71+ ]
72+ }
73+
74+ function test_updated(data) {
75+ control.selectedIndex = data.index;
76+ if (data.updated) {
77+ spy.wait();
78+ compare(spy.signalArguments[0][0], data.index);
79+ } else {
80+ compare(spy.count, 0);
81+ }
82+ }
83+ }
84+}
tests/qmltests/Dash/ScopeSettings/tst_ScopeSettingNumber.qmlnew file+98
@@ -0,0 +1,98 @@
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.0
18+import Ubuntu.Components 1.1
19+import QtTest 1.0
20+import "../../../../qml/Dash/ScopeSettings"
21+import Unity.Test 0.1 as UT
22+
23+Rectangle {
24+ id: root
25+ width: units.gu(40)
26+ height: units.gu(80)
27+
28+ property var settingData: {
29+ "displayName" : "Mock number setting",
30+ "value": 0.2
31+ }
32+
33+ ScopeSettingNumber {
34+ id: scopeSetting
35+ widgetData: settingData
36+ width: parent.width
37+ }
38+
39+ SignalSpy {
40+ id: spy
41+ target: scopeSetting
42+ signalName: "updated"
43+ }
44+
45+ UT.UnityTestCase {
46+ id: testCase
47+ name: "ScopeSettingNumber"
48+ when: windowShown
49+
50+ property var control: findChild(scopeSetting, "control")
51+ property real newNumber: 11.7
52+
53+ function cleanup() {
54+ control.focus = false;
55+ control.text = settingData.value;
56+ spy.clear();
57+ }
58+
59+ function test_updated_on_unfocus() {
60+ mouseClick(control, control.width / 2, control.height / 2);
61+ control.selectAll();
62+ control.cut();
63+ control.insert(0, newNumber);
64+ control.focus = false;
65+ spy.wait();
66+ verify(spy.signalArguments[0][0] == newNumber);
67+ }
68+
69+ function test_updated_on_accepted() {
70+ mouseClick(control, control.width / 2, control.height / 2);
71+ control.selectAll();
72+ control.cut();
73+ control.insert(0, newNumber);
74+ control.accepted();
75+ spy.wait();
76+ verify(spy.signalArguments[0][0] == newNumber);
77+ }
78+
79+ function test_selection_on_listitem_click() {
80+ mouseClick(scopeSetting, 0, scopeSetting.height / 2);
81+ compare(control.focus, true);
82+ // we're checking that selectAll() is being called by omitting it here
83+ control.cut();
84+ control.insert(0, newNumber);
85+ verify(control.displayText == newNumber);
86+ }
87+
88+ function test_unacceptable_input() {
89+ mouseClick(control, control.width / 2, control.height / 2);
90+ control.selectAll();
91+ control.cut();
92+ control.insert(0, "not valid");
93+ control.accepted();
94+ compare(spy.count, 0);
95+ compare(control.displayText, "");
96+ }
97+ }
98+}
tests/qmltests/Dash/ScopeSettings/tst_ScopeSettingString.qmlnew file+88
@@ -0,0 +1,88 @@
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.0
18+import Ubuntu.Components 1.1
19+import QtTest 1.0
20+import "../../../../qml/Dash/ScopeSettings"
21+import Unity.Test 0.1 as UT
22+
23+Rectangle {
24+ id: root
25+ width: units.gu(40)
26+ height: units.gu(80)
27+
28+ property var settingData: {
29+ "displayName" : "Mock string setting",
30+ "value": "mock string"
31+ }
32+
33+ ScopeSettingString {
34+ id: scopeSetting
35+ widgetData: settingData
36+ width: parent.width
37+ }
38+
39+ SignalSpy {
40+ id: spy
41+ target: scopeSetting
42+ signalName: "updated"
43+ }
44+
45+ UT.UnityTestCase {
46+ id: testCase
47+ name: "ScopeSettingString"
48+ when: windowShown
49+
50+ property var control: findChild(scopeSetting, "control")
51+ property string newText: "new text to compare"
52+
53+ function cleanup() {
54+ control.focus = false;
55+ control.text = settingData.value;
56+ spy.clear();
57+ }
58+
59+ function test_updated_on_unfocus() {
60+ mouseClick(control, control.width / 2, control.height / 2);
61+ control.selectAll();
62+ control.cut();
63+ control.insert(0, newText);
64+ control.focus = false;
65+ spy.wait();
66+ compare(spy.signalArguments[0][0], newText);
67+ }
68+
69+ function test_updated_on_accepted() {
70+ mouseClick(control, control.width / 2, control.height / 2);
71+ control.selectAll();
72+ control.cut();
73+ control.insert(0, newText);
74+ control.accepted();
75+ spy.wait();
76+ compare(spy.signalArguments[0][0], newText);
77+ }
78+
79+ function test_selection_on_listitem_click() {
80+ mouseClick(scopeSetting, 0, scopeSetting.height / 2);
81+ compare(control.focus, true);
82+ // we're checking that selectAll() is being called by omitting it here
83+ control.cut();
84+ control.insert(0, newText);
85+ compare(control.displayText, newText);
86+ }
87+ }
88+}
tests/qmltests/Dash/ScopeSettings/tst_ScopeSettingsWidgetFactory.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.0
18+import Ubuntu.Components 1.1
19+import QtTest 1.0
20+import "../../../../qml/Dash/ScopeSettings"
21+import Unity.Test 0.1 as UT
22+
23+Rectangle {
24+ id: root
25+ width: units.gu(40)
26+ height: units.gu(80)
27+
28+ property var settingData: {
29+ "type": "",
30+ "displayName" : "Mock setting",
31+ "value": "1",
32+ "properties": { "values" : [ "first", "second", "third" ] }
33+ }
34+
35+ ScopeSettingsWidgetFactory {
36+ id: scopeSettingsWidgetFactory
37+ anchors {
38+ left: parent.left
39+ right: parent.right
40+ }
41+ }
42+
43+ UT.UnityTestCase {
44+ id: testCase
45+ name: "ScopeSettingWidgetFactory"
46+ when: windowShown
47+
48+ function test_mapping_data() {
49+ return [
50+ { tag: "Boolean", type: "boolean", source: "ScopeSettingBoolean.qml" },
51+ { tag: "List", type: "list", source: "ScopeSettingList.qml" },
52+ { tag: "Number", type: "number", source: "ScopeSettingNumber.qml" },
53+ { tag: "String", type: "string", source: "ScopeSettingString.qml" }
54+ ];
55+ }
56+
57+ function test_mapping(data) {
58+ var newSettingData = settingData;
59+ newSettingData.type = data.type;
60+ scopeSettingsWidgetFactory.widgetData = newSettingData;
61+
62+ verify((String(scopeSettingsWidgetFactory.source)).indexOf(data.source) != -1);
63+ }
64+ }
65+}
tests/qmltests/Dash/artwork/background.pngnew file

Binary file not shown.

tests/qmltests/Dash/tst_Card.qml+62−1
@@ -96,6 +96,11 @@ Rectangle {
9696 "layout": { "template": { "card-background": Qt.resolvedUrl("artwork/checkers.png") },
9797 "components": JSON.parse(Helpers.fullMapping) }
9898 },
99+ {
100+ "name": "Art, title - overlaid",
101+ "layout": { "template": { "overlay": true },
102+ "components": { "art": "art", "title": "title" } }
103+ },
99104 ]
100105
101106 CardTool {
@@ -217,6 +222,7 @@ Rectangle {
217222
218223 function test_card_binding(data) {
219224 selector.selectedIndex = data.index;
225+ waitForRendering(selector);
220226 waitForRendering(card);
221227
222228 tryCompareFunction(function() { return testCase[data.object] !== null }, true);
@@ -247,6 +253,8 @@ Rectangle {
247253
248254 function test_card_size(data) {
249255 selector.selectedIndex = data.index;
256+ waitForRendering(selector);
257+ waitForRendering(card);
250258
251259 if (data.hasOwnProperty("card_layout")) {
252260 cardTool.template['card-layout'] = data.card_layout;
@@ -279,6 +287,7 @@ Rectangle {
279287
280288 function test_art_size(data) {
281289 selector.selectedIndex = data.index;
290+ waitForRendering(selector);
282291 if (data.hasOwnProperty("size")) {
283292 cardTool.template['card-size'] = data.size;
284293 cardTool.templateChanged();
@@ -310,6 +319,7 @@ Rectangle {
310319
311320 function test_art_shape_fixed_size() {
312321 selector.selectedIndex = 6;
322+ waitForRendering(selector);
313323 card.fixedArtShapeSize = Qt.size( units.gu(8), units.gu(4) );
314324 waitForRendering(card);
315325 tryCompare(art, "width", units.gu(8));
@@ -326,6 +336,7 @@ Rectangle {
326336
327337 function test_art_layout(data) {
328338 selector.selectedIndex = data.index;
339+ waitForRendering(selector);
329340 waitForRendering(card);
330341
331342 tryCompare(headerRow, "x", data.left());
@@ -344,6 +355,7 @@ Rectangle {
344355
345356 function test_header_layout(data) {
346357 selector.selectedIndex = data.index;
358+ waitForRendering(selector);
347359 waitForRendering(card);
348360
349361 tryCompareFunction(function() { return testCase.headerRow.y === data.top() }, true);
@@ -359,7 +371,7 @@ Rectangle {
359371
360372 function test_summary_layout(data) {
361373 selector.selectedIndex = data.index;
362−
374+ waitForRendering(selector);
363375 waitForRendering(card);
364376
365377 tryCompareFunction(function() { return art.height > 0 && testCase.summary.y === data.top() }, true);
@@ -367,6 +379,8 @@ Rectangle {
367379
368380 function test_art_visibility() {
369381 selector.selectedIndex = 8;
382+ waitForRendering(selector);
383+ waitForRendering(card);
370384
371385 compare(testCase.artImage, null);
372386 compare(testCase.art, null);
@@ -389,6 +403,7 @@ Rectangle {
389403
390404 function test_background(data) {
391405 selector.selectedIndex = data.index;
406+ waitForRendering(selector);
392407
393408 if (data.hasOwnProperty("background")) {
394409 card.cardData["background"] = data.background;
@@ -425,6 +440,8 @@ Rectangle {
425440
426441 function test_font_weights(data) {
427442 selector.selectedIndex = data.index;
443+ waitForRendering(selector);
444+ waitForRendering(card);
428445
429446 tryCompare(testCase.title.font, "weight", data.weight);
430447 }
@@ -448,6 +465,7 @@ Rectangle {
448465
449466 function test_fontColor(data) {
450467 selector.selectedIndex = 10;
468+ waitForRendering(selector);
451469 waitForRendering(card);
452470
453471 background.color = data.tag;
@@ -470,6 +488,7 @@ Rectangle {
470488
471489 function test_emblemIcon(data) {
472490 selector.selectedIndex = data.index;
491+ waitForRendering(selector);
473492 waitForRendering(card);
474493
475494 var emblemIcon = findChild(card, "emblemIcon");
@@ -486,6 +505,7 @@ Rectangle {
486505
487506 function test_mascotShape(data) {
488507 selector.selectedIndex = data.index;
508+ waitForRendering(selector);
489509 waitForRendering(card);
490510
491511 var shape = findChild(card, "mascotShapeLoader");
@@ -496,6 +516,8 @@ Rectangle {
496516
497517 function test_touchdown_visibility() {
498518 selector.selectedIndex = 0;
519+ waitForRendering(selector);
520+ waitForRendering(card);
499521
500522 var touchdown = findChild(card, "touchdown");
501523
@@ -505,5 +527,44 @@ Rectangle {
505527 mouseRelease(card, card.width/2, card.height/2);
506528 compare(touchdown.visible, false);
507529 }
530+
531+ function test_paddings_data() {
532+ return [
533+ { tag: "Art and summary", index: 0 },
534+ { tag: "No Summary", index: 6 },
535+ { tag: "No header", index: 7 },
536+ { tag: "Header only", index: 8 },
537+ { tag: "Art, header, summary - overlaid", index: 9 },
538+ { tag: "Art, title - overlaid", index: 13 },
539+ ];
540+ }
541+
542+ function test_paddings(data) {
543+ selector.selectedIndex = data.index;
544+ waitForRendering(selector);
545+ waitForRendering(card);
546+
547+ if (title) var titleToCard = title.mapToItem(card, 0, 0, title.width, title.height);
548+
549+ // left margin
550+ if (mascotImage) {
551+ var mascotToCard = mascotImage.mapToItem(card, 0, 0, mascotImage.width, mascotImage.height);
552+ verify(mascotToCard.x === units.gu(1));
553+ if (title) {
554+ verify((titleToCard.x - mascotToCard.x - mascotToCard.width) === units.gu(1));
555+ }
556+ } else if (title) {
557+ verify(titleToCard.x === units.gu(1));
558+ }
559+
560+ // right margin
561+ var emblemIcon = findChild(card, "emblemIcon");
562+ if (emblemIcon) {
563+ var emblemToCard = emblemIcon.mapToItem(card, 0, 0, emblemIcon.width, emblemIcon.height);
564+ verify((card.width - emblemToCard.x - emblemToCard.width) === units.gu(1));
565+ } else if (title) {
566+ verify((card.width - titleToCard.x - titleToCard.width) === units.gu(1));
567+ }
568+ }
508569 }
509570}
tests/qmltests/Dash/tst_DashContent.qml+152−48
@@ -200,8 +200,12 @@ Item {
200200 dashContent.setCurrentScopeAtIndex(data.index, true, false);
201201 tryCompareFunction(get_current_item_object_name, data.objectName)
202202 var scopeView = findChild(dashContent, data.objectName);
203− var pageHeader = findChild(scopeView, "innerPageHeader");
204− compare(pageHeader.title, scopesModel.getScope(data.index).name);
203+ verify(scopeView, "Could not find the scope view.");
204+ var pageHeader = findChild(scopeView, "scopePageHeader");
205+ verify(pageHeader, "Could not find the scope page header.");
206+ var innerHeader = findChild(pageHeader, "innerPageHeader");
207+ verify(innerHeader, "Could not find the scope page header.");
208+ compare(innerHeader.title, scopesModel.getScope(data.index).name);
205209 }
206210
207211 function test_is_active_data() {
@@ -266,56 +270,57 @@ Item {
266270 verify(carouselLV.tileWidth / carouselLV.tileHeight == cardTool.components["art"]["aspect-ratio"]);
267271 }
268272
269− function test_navigations() {
273+ function test_mainNavigation() {
270274 var dashContentList = findChild(dashContent, "dashContentList");
271275 tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
272276 var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
273277 tryCompare(dashNavigation, "visible", true);
274− compare(dashNavigation.showList, false);
275− waitForRendering(dashNavigation);
276− mouseClick(dashNavigation, 0, 0);
277− compare(dashNavigation.showList, true);
278+ var dashNavigationButton = findChild(dashContentList.currentItem, "navigationButton");
279+ compare(dashNavigationButton.showList, false);
280+ waitForRendering(dashNavigationButton);
281+ mouseClick(dashNavigationButton, 0, 0);
282+ compare(dashNavigationButton.showList, true);
278283
279− var navigationListView = findChild(dashNavigation, "navigationListView");
284+ var navigationListView = findChild(dashNavigationButton, "navigationListView");
280285 compare(navigationListView.count, 1);
281286 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
282287
283288 waitForRendering(navigationListView);
284289 waitForRendering(navigationListView.currentItem);
285290
286− var allButton = findChild(dashNavigation, "allButton");
291+ var allButton = findChild(dashNavigationButton, "allButton");
287292 compare(allButton.visible, false);
288293
289− var navigation = findChild(dashNavigation, "navigation0child3");
294+ var navigation = findChild(dashNavigationButton, "navigation0child3");
290295 mouseClick(navigation, 0, 0);
291− compare(dashNavigation.showList, false);
292− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle3");
296+ compare(dashNavigationButton.showList, false);
297+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle3");
293298 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
294299
295− mouseClick(dashNavigation, 0, 0);
296− compare(dashNavigation.showList, true);
300+ mouseClick(dashNavigationButton, 0, 0);
301+ compare(dashNavigationButton.showList, true);
297302 waitForRendering(navigationListView);
298303 waitForRendering(navigationListView.currentItem);
299304 compare(allButton.visible, true);
300305
301306 mouseClick(allButton, 0, 0);
302− compare(dashNavigation.showList, false);
303− tryCompare(dashNavigation.currentNavigation, "navigationId", "root");
307+ compare(dashNavigationButton.showList, false);
308+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "root");
304309 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
305310
306− mouseClick(dashNavigation, 0, 0);
307− compare(dashNavigation.showList, true);
311+ mouseClick(dashNavigationButton, 0, 0);
312+ compare(dashNavigationButton.showList, true);
308313 waitForRendering(navigationListView);
309314 waitForRendering(navigationListView.currentItem);
310315 compare(allButton.visible, false);
311316
312− navigation = findChild(dashNavigation, "navigation0child2");
317+ navigation = findChild(dashNavigationButton, "navigation0child2");
313318 mouseClick(navigation, 0, 0);
314− compare(dashNavigation.showList, true);
315− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
319+ compare(dashNavigationButton.showList, true);
320+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
316321 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
317322
318− var navigationList1 = findChild(dashNavigation, "navigation1");
323+ var navigationList1 = findChild(dashNavigationButton, "navigation1");
319324 allButton = findChild(navigationList1, "allButton");
320325 var backButton = findChild(findChild(navigationList1, "navigation1"), "backButton");
321326 compare(allButton.visible, true);
@@ -324,61 +329,160 @@ Item {
324329 tryCompare(navigationListView, "contentX", navigationList1.x);
325330 waitForRendering(navigationListView);
326331 mouseClick(allButton, 0, 0);
327− compare(dashNavigation.showList, false);
328− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
332+ compare(dashNavigationButton.showList, false);
333+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
329334 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
330335
331− mouseClick(dashNavigation, 0, 0);
332− compare(dashNavigation.showList, true);
336+ mouseClick(dashNavigationButton, 0, 0);
337+ compare(dashNavigationButton.showList, true);
333338 waitForRendering(navigationListView);
334339 waitForRendering(navigationListView.currentItem);
335340 compare(allButton.visible, true);
336341 compare(backButton.visible, true);
337342
338343 tryCompare(navigationList1.navigation, "loaded", true);
339− navigation = findChild(dashNavigation, "navigation1child2");
344+ navigation = findChild(dashNavigationButton, "navigation1child2");
340345 mouseClick(navigation, 0, 0);
341− compare(dashNavigation.showList, false);
342− tryCompare(dashNavigation.currentNavigation, "navigationId", "childmiddle22");
346+ compare(dashNavigationButton.showList, false);
347+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "childmiddle22");
343348 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
344349
345− mouseClick(dashNavigation, 0, 0);
346− compare(dashNavigation.showList, true);
350+ mouseClick(dashNavigationButton, 0, 0);
351+ compare(dashNavigationButton.showList, true);
347352 waitForRendering(navigationListView);
348353 waitForRendering(navigationListView.currentItem);
349354
350355 tryCompare(navigationList1.navigation, "loaded", true);
351− navigation = findChild(dashNavigation, "navigation1child3");
356+ navigation = findChild(dashNavigationButton, "navigation1child3");
352357 mouseClick(navigation, 0, 0);
353− compare(dashNavigation.showList, false);
354− tryCompare(dashNavigation.currentNavigation, "navigationId", "childmiddle23");
358+ compare(dashNavigationButton.showList, false);
359+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "childmiddle23");
355360 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
356361
357− mouseClick(dashNavigation, 0, 0);
358− compare(dashNavigation.showList, true);
362+ mouseClick(dashNavigationButton, 0, 0);
363+ compare(dashNavigationButton.showList, true);
359364 waitForRendering(navigationListView);
360365 waitForRendering(navigationListView.currentItem);
361366 mouseClick(backButton, 0, 0);
362367
363− tryCompare(dashNavigation.currentNavigation, "navigationId", "root");
368+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "root");
364369 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
365− compare(dashNavigation.showList, true);
366− mouseClick(dashNavigation, 0, 0);
367− compare(dashNavigation.showList, false);
370+ compare(dashNavigationButton.showList, true);
371+ mouseClick(dashNavigationButton, 0, 0);
372+ compare(dashNavigationButton.showList, false);
368373
369− mouseClick(dashNavigation, 0, 0);
370− compare(dashNavigation.showList, true);
374+ mouseClick(dashNavigationButton, 0, 0);
375+ compare(dashNavigationButton.showList, true);
371376 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
372− navigation = findChild(dashNavigation, "navigation0child2");
377+ navigation = findChild(dashNavigationButton, "navigation0child2");
373378 mouseClick(navigation, 0, 0);
374− compare(dashNavigation.showList, true);
375− navigationList1 = findChild(dashNavigation, "navigation1");
379+ compare(dashNavigationButton.showList, true);
380+ navigationList1 = findChild(dashNavigationButton, "navigation1");
376381 compare(navigationList1.navigation.loaded, false);
377− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
382+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
378383 allButton = findChild(navigationList1, "allButton");
379− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
384+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
380385 mouseClick(allButton, 0, 0);
381− tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
386+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
387+ }
388+
389+ function test_altNavigation() {
390+ var dashContentList = findChild(dashContent, "dashContentList");
391+ tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
392+ var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
393+ tryCompare(dashNavigation, "visible", true);
394+ var dashNavigationButton = findChild(dashContentList.currentItem, "altNavigationButton");
395+ verify(dashNavigationButton, "Can't find navigation button");
396+
397+ compare(dashNavigationButton.showList, false);
398+ waitForRendering(dashNavigationButton);
399+ mouseClick(dashNavigationButton, 0, 0);
400+ compare(dashNavigationButton.showList, true);
401+
402+ var navigationListView = findChild(dashNavigationButton, "navigationListView");
403+ compare(navigationListView.count, 1);
404+ tryCompare(navigationListView.currentItem.navigation, "loaded", true);
405+
406+ waitForRendering(navigationListView);
407+ waitForRendering(navigationListView.currentItem);
408+
409+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "altrootChild1");
410+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "altroot");
411+
412+ var allButton = findChild(dashNavigationButton, "allButton");
413+ compare(allButton.visible, false);
414+ var backButton = findChild(navigationListView, "backButton");
415+ compare(backButton.visible, false);
416+
417+ var navigation = findChild(dashNavigationButton, "navigation0child3");
418+ mouseClick(navigation, 0, 0);
419+ compare(dashNavigationButton.showList, false);
420+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "altrootChild3");
421+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "altroot");
422+
423+ mouseClick(dashNavigationButton, 0, 0);
424+ compare(dashNavigationButton.showList, true);
425+ waitForRendering(navigationListView);
426+ waitForRendering(navigationListView.currentItem);
427+ compare(allButton.visible, false);
428+
429+ navigation = findChild(dashNavigationButton, "navigation0child2");
430+ mouseClick(navigation, 0, 0);
431+ compare(dashNavigationButton.showList, false);
432+ tryCompare(dashNavigationButton.currentNavigation, "navigationId", "altrootChild2");
433+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "altroot");
434+
435+ mouseClick(dashNavigationButton, 0, 0);
436+ compare(dashNavigationButton.showList, true);
437+ waitForRendering(navigationListView);
438+ waitForRendering(navigationListView.currentItem);
439+ verify(!backButton.visible, "Back button should not be visible");
440+
441+ compare(navigationListView.count, 1, "There should be no second-level navigation");
442+
443+ mouseClick(dashNavigationButton, 0, 0);
444+ compare(dashNavigationButton.showList, false);
445+ }
446+
447+ function test_navigationsClosing() {
448+ var dashContentList = findChild(dashContent, "dashContentList");
449+ tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
450+ var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
451+ tryCompare(dashNavigation, "visible", true);
452+ var dashNavigationButton = findChild(dashContentList.currentItem, "navigationButton");
453+ verify(dashNavigationButton, "Can't find navigation button");
454+ var dashAltNavigationButton = findChild(dashContentList.currentItem, "altNavigationButton");
455+ verify(dashAltNavigationButton, "Can't find alternative navigation button");
456+
457+ waitForRendering(dashNavigationButton);
458+ waitForRendering(dashAltNavigationButton);
459+
460+ compare(dashNavigationButton.showList, false);
461+ compare(dashAltNavigationButton.showList, false);
462+
463+ mouseClick(dashNavigation, 0, 0);
464+ compare(dashNavigationButton.showList, false);
465+ compare(dashAltNavigationButton.showList, true);
466+
467+ var navigationListView = findChild(dashAltNavigationButton, "navigationListView");
468+ tryCompare(navigationListView.currentItem.navigation, "loaded", true);
469+
470+ mouseClick(dashNavigation, dashNavigationButton.x, 0);
471+ compare(dashNavigationButton.showList, false);
472+ compare(dashAltNavigationButton.showList, false);
473+
474+ tryCompare(navigationListView, "visible", false);
475+
476+ mouseClick(dashNavigation, dashNavigationButton.x, 0);
477+ compare(dashNavigationButton.showList, true);
478+ compare(dashAltNavigationButton.showList, false);
479+
480+ navigationListView = findChild(dashNavigationButton, "navigationListView");
481+ tryCompare(navigationListView.currentItem.navigation, "loaded", true);
482+
483+ mouseClick(dashNavigation, 0, 0);
484+ compare(dashNavigationButton.showList, false);
485+ compare(dashAltNavigationButton.showList, false);
382486 }
383487
384488 function test_searchHint() {
tests/qmltests/Dash/tst_GenericScopeView.qml+102−39
@@ -20,7 +20,7 @@ import Unity 0.2
2020import ".."
2121import "../../../qml/Dash"
2222import "../../../qml/Components"
23−import Ubuntu.Components 0.1
23+import Ubuntu.Components 1.1
2424import Unity.Test 0.1 as UT
2525
2626Item {
@@ -39,6 +39,8 @@ Item {
3939
4040 Scopes {
4141 id: scopes
42+ // for tryGenericScopeView
43+ onLoadedChanged: if (loaded) genericScopeView.scope = scopes.getScope(2);
4244 }
4345
4446 SignalSpy {
@@ -59,7 +61,7 @@ Item {
5961 name: "GenericScopeView"
6062 when: scopes.loaded && windowShown
6163
62− property Item previewListView: findChild(genericScopeView, "previewListView")
64+ property Item subPageLoader: findChild(genericScopeView, "subPageLoader")
6365 property Item header: findChild(genericScopeView, "scopePageHeader")
6466
6567 function init() {
@@ -94,24 +96,24 @@ Item {
9496 tryCompare(genericScopeView.scope, "isActive", false)
9597 genericScopeView.isCurrent = true
9698 tryCompare(genericScopeView.scope, "isActive", true)
97− testCase.previewListView.open = true
99+ testCase.subPageLoader.open = true
98100 tryCompare(genericScopeView.scope, "isActive", false)
99− testCase.previewListView.open = false
101+ testCase.subPageLoader.open = false
100102 tryCompare(genericScopeView.scope, "isActive", true)
101103 genericScopeView.isCurrent = false
102104 tryCompare(genericScopeView.scope, "isActive", false)
103105 }
104106
105107 function test_showDash() {
106− testCase.previewListView.open = true;
108+ testCase.subPageLoader.open = true;
107109 genericScopeView.scope.showDash();
108− tryCompare(testCase.previewListView, "open", false);
110+ tryCompare(testCase.subPageLoader, "open", false);
109111 }
110112
111113 function test_hideDash() {
112− testCase.previewListView.open = true;
114+ testCase.subPageLoader.open = true;
113115 genericScopeView.scope.hideDash();
114− tryCompare(testCase.previewListView, "open", false);
116+ tryCompare(testCase.subPageLoader, "open", false);
115117 }
116118
117119 function test_searchQuery() {
@@ -214,7 +216,7 @@ Item {
214216
215217 openPreview(4, 0);
216218
217− compare(testCase.previewListView.count, 12, "There should only be 12 items in preview.");
219+ compare(testCase.subPageLoader.count, 12, "There should only be 12 items in preview.");
218220
219221 closePreview();
220222 }
@@ -227,11 +229,15 @@ Item {
227229 shell.width = units.gu(20)
228230 var categoryListView = findChild(genericScopeView, "categoryListView");
229231 categoryListView.contentY = units.gu(20);
230− var seeAll = findChild(category, "seeAll")
232+ var seeAll = findChild(category, "seeAll");
233+ var floatingSeeLess = findChild(genericScopeView, "floatingSeeLess");
231234 mouseClick(seeAll, seeAll.width / 2, seeAll.height / 2);
232235 tryCompare(category, "expanded", true);
233− tryCompareFunction(function() { return category.item.height == genericScopeView.height - category.item.displayMarginBeginning - category.item.displayMarginEnd; }, true);
234− mouseClick(seeAll, seeAll.width / 2, seeAll.height / 2);
236+ tryCompareFunction(function() {
237+ return category.item.height + floatingSeeLess.height ==
238+ genericScopeView.height - category.item.displayMarginBeginning - category.item.displayMarginEnd;
239+ }, true);
240+ mouseClick(floatingSeeLess, floatingSeeLess.width / 2, floatingSeeLess.height / 2);
235241 tryCompare(category, "expanded", false);
236242 }
237243
@@ -272,19 +278,21 @@ Item {
272278 true);
273279 var tile = findChild(findChild(genericScopeView, category), "delegate"+delegate);
274280 mouseClick(tile, tile.width / 2, tile.height / 2);
275− tryCompare(testCase.previewListView, "open", true);
276− tryCompare(testCase.previewListView, "x", 0);
281+ tryCompare(testCase.subPageLoader, "open", true);
282+ tryCompare(testCase.subPageLoader, "x", 0);
277283 }
278284
279285 function closePreview() {
280− var closePreviewMouseArea = findChild(genericScopeView, "innerPageHeader");
286+ var closePreviewMouseArea = findChild(subPageLoader.item, "pageHeader");
281287 mouseClick(closePreviewMouseArea, units.gu(2), units.gu(2));
282288
283− tryCompare(testCase.previewListView, "open", false);
289+ tryCompare(testCase.subPageLoader, "open", false);
290+ tryCompare(testCase.subPageLoader, "visible", false);
284291 }
285292
286293 function test_previewOpenClose() {
287− tryCompare(testCase.previewListView, "open", false);
294+ tryCompare(testCase.subPageLoader, "open", false);
295+ tryCompare(testCase.subPageLoader, "visible", false);
288296
289297 var categoryListView = findChild(genericScopeView, "categoryListView");
290298 categoryListView.positionAtBeginning();
@@ -296,7 +304,7 @@ Item {
296304 function test_showPreviewCarousel() {
297305 var category = scrollToCategory("dashCategory1");
298306
299− tryCompare(testCase.previewListView, "open", false);
307+ tryCompare(testCase.subPageLoader, "open", false);
300308
301309 var tile = findChild(category, "carouselDelegate1");
302310 verify(tile, "Could not find delegate");
@@ -304,14 +312,14 @@ Item {
304312 mouseClick(tile, tile.width / 2, tile.height / 2);
305313 tryCompare(tile, "explicitlyScaled", true);
306314 mouseClick(tile, tile.width / 2, tile.height / 2);
307− tryCompare(testCase.previewListView, "open", true);
308− tryCompare(testCase.previewListView, "x", 0);
315+ tryCompare(testCase.subPageLoader, "open", true);
316+ tryCompare(testCase.subPageLoader, "x", 0);
309317
310318 closePreview();
311319
312320 mousePress(tile, tile.width / 2, tile.height / 2);
313− tryCompare(testCase.previewListView, "open", true);
314− tryCompare(testCase.previewListView, "x", 0);
321+ tryCompare(testCase.subPageLoader, "open", true);
322+ tryCompare(testCase.subPageLoader, "x", 0);
315323 mouseRelease(tile, tile.width / 2, tile.height / 2);
316324
317325 closePreview();
@@ -320,20 +328,20 @@ Item {
320328 function test_showPreviewHorizontalList() {
321329 var category = scrollToCategory("dashCategory18");
322330
323− tryCompare(testCase.previewListView, "open", false);
331+ tryCompare(testCase.subPageLoader, "open", false);
324332
325333 var tile = findChild(category, "delegate1");
326334 verify(tile, "Could not find delegate");
327335
328336 mouseClick(tile, tile.width / 2, tile.height / 2);
329− tryCompare(testCase.previewListView, "open", true);
330− tryCompare(testCase.previewListView, "x", 0);
337+ tryCompare(testCase.subPageLoader, "open", true);
338+ tryCompare(testCase.subPageLoader, "x", 0);
331339
332340 closePreview();
333341
334342 mousePress(tile, tile.width / 2, tile.height / 2);
335− tryCompare(testCase.previewListView, "open", true);
336− tryCompare(testCase.previewListView, "x", 0);
343+ tryCompare(testCase.subPageLoader, "open", true);
344+ tryCompare(testCase.subPageLoader, "x", 0);
337345 mouseRelease(tile, tile.width / 2, tile.height / 2);
338346
339347 closePreview();
@@ -343,30 +351,57 @@ Item {
343351 var categoryListView = findChild(genericScopeView, "categoryListView");
344352 categoryListView.positionAtBeginning();
345353
346− tryCompare(testCase.previewListView, "open", false);
347− var previewListViewList = findChild(previewListView, "listView");
354+ tryCompare(testCase.subPageLoader, "open", false);
348355
349356 openPreview();
357+ var previewListViewList = findChild(subPageLoader.item, "listView");
350358
351359 // flick to the next previews
352− tryCompare(testCase.previewListView, "count", 15);
353− for (var i = 1; i < testCase.previewListView.count; ++i) {
354− mouseFlick(testCase.previewListView, testCase.previewListView.width - units.gu(1),
355− testCase.previewListView.height / 2,
360+ tryCompare(testCase.subPageLoader, "count", 15);
361+ for (var i = 1; i < testCase.subPageLoader.count; ++i) {
362+ mouseFlick(testCase.subPageLoader.item, testCase.subPageLoader.width - units.gu(1),
363+ testCase.subPageLoader.height / 2,
356364 units.gu(2),
357− testCase.previewListView.height / 2);
365+ testCase.subPageLoader.height / 2);
358366 tryCompare(previewListViewList, "moving", false);
359− tryCompare(testCase.previewListView.currentItem, "objectName", "preview" + i);
360−
367+ tryCompare(testCase.subPageLoader.currentItem, "objectName", "preview" + i);
361368 }
362369 closePreview();
363370 }
364371
372+ function test_settingsOpenClose() {
373+ waitForRendering(genericScopeView);
374+ verify(header, "Could not find the header.");
375+ var innerHeader = findChild(header, "innerPageHeader");
376+ verify(innerHeader, "Could not find the inner header");
377+
378+ // open
379+ tryCompare(testCase.subPageLoader, "open", false);
380+ tryCompare(testCase.subPageLoader, "visible", false);
381+ var settings = findChild(innerHeader, "settings_header_button");
382+ mouseClick(settings, settings.width / 2, settings.height / 2);
383+ tryCompare(testCase.subPageLoader, "open", true);
384+ tryCompareFunction(function() { return (String(subPageLoader.source)).indexOf("ScopeSettingsPage.qml") != -1; }, true);
385+ tryCompare(genericScopeView, "subPageShown", true);
386+ compare(testCase.subPageLoader.subPage, "settings");
387+ tryCompare(testCase.subPageLoader, "x", 0);
388+
389+ // close
390+ var settingsHeader = findChild(testCase.subPageLoader.item, "pageHeader");
391+ mouseClick(settingsHeader, units.gu(2), units.gu(2));
392+ tryCompare(testCase.subPageLoader, "open", false);
393+ tryCompare(genericScopeView, "subPageShown", false);
394+ var categoryListView = findChild(genericScopeView, "categoryListView");
395+ tryCompare(categoryListView, "x", 0);
396+ tryCompare(testCase.subPageLoader, "visible", false);
397+ tryCompare(testCase.subPageLoader, "source", "");
398+ }
399+
365400 function test_header_style_data() {
366401 return [
367− { tag: "Default", index: 0, foreground: Theme.palette.normal.baseText, background: "", logo: "" },
368− { tag: "Foreground", index: 1, foreground: "yellow", background: "", logo: "" },
369− { tag: "Logo+Background", index: 2, foreground: Theme.palette.normal.baseText, background: "gradient:///lightgrey/grey",
402+ { tag: "Default", index: 0, foreground: UbuntuColors.darkGrey, background: "color:///#f5f5f5", logo: "" },
403+ { tag: "Foreground", index: 1, foreground: "yellow", background: "color:///#f5f5f5", logo: "" },
404+ { tag: "Logo+Background", index: 2, foreground: UbuntuColors.darkGrey, background: "gradient:///lightgrey/grey",
370405 logo: Qt.resolvedUrl("../Dash/tst_PageHeader/logo-ubuntu-orange.svg") },
371406 ];
372407 }
@@ -390,6 +425,34 @@ Item {
390425 verify(image, "Could not find the title image.");
391426 compare(image.source, data.logo, "Title image has the wrong source");
392427 }
428+
429+ function test_favorite_data() {
430+ return [
431+ { tag: "People", id: "MockScope1", favorite: true },
432+ { tag: "Music", id: "MockScope2", favorite: false },
433+ { tag: "Apps", id: "clickscope", favorite: true },
434+ ];
435+ }
436+
437+ function test_favorite(data) {
438+ genericScopeView.scope = scopes.getScopeFromAll(data.id);
439+ waitForRendering(genericScopeView);
440+ verify(header, "Could not find the header.");
441+
442+ compare(genericScopeView.scope.favorite, data.favorite, "Unexpected initial favorite value");
443+
444+ var innerHeader = findChild(header, "innerPageHeader");
445+ verify(innerHeader, "Could not find the inner header");
446+
447+ expectFail("Apps", "Click scope should not have a favorite button");
448+ var favoriteAction = findChild(innerHeader, "favorite_header_button");
449+ verify(favoriteAction, "Could not find the favorite action.");
450+ mouseClick(favoriteAction, favoriteAction.width / 2, favoriteAction.height / 2);
451+
452+ tryCompare(genericScopeView.scope, "favorite", !data.favorite);
453+
454+ genericScopeView.scope = !genericScopeView.scope;
455+ }
393456 }
394457 }
395458}
tests/qmltests/Dash/tst_PageHeader.qml+3−1
@@ -94,7 +94,7 @@ Item {
9494 typeString("humppa1")
9595 pageHeader.resetSearch()
9696
97− compare(pageHeader.searchHistory.count, 1)
97+ tryCompare(pageHeader.searchHistory, "count", 1)
9898 compare(pageHeader.searchHistory.get(0).query, "humppa1")
9999
100100 pageHeader.triggerSearch()
@@ -155,6 +155,8 @@ Item {
155155 compare(searchTextField.focus, true);
156156
157157 var recentSearches = findChild(headerContainer.popover, "recentSearches");
158+ verify(recentSearches, "Could not find recent searches in the popover");
159+ waitForRendering(recentSearches);
158160 mouseClick(recentSearches.itemAt(0), 0, 0);
159161
160162 compare(pageHeader.searchQuery, "Search2");
tests/qmltests/tst_ShellWithPin.qml+2−2
@@ -220,10 +220,10 @@ Item {
220220 tryCompare(placeHolder, "text", "")
221221
222222 enterPin("1111")
223− tryCompare(placeHolder, "text", "Sorry, incorrect PIN")
223+ tryCompare(placeHolder, "text", "Sorry, incorrect passcode")
224224
225225 enterPin("1111")
226− tryCompare(placeHolder, "text", "Sorry, incorrect PIN")
226+ tryCompare(placeHolder, "text", "Sorry, incorrect passcode")
227227
228228 enterPin("1111")
229229 tryCompare(placeHolder, "text", "Too many incorrect attempts")
tests/uqmlscene/main.cpp+1−1
@@ -495,7 +495,7 @@ int main(int argc, char ** argv)
495495 if (!window) {
496496 QQuickItem *contentItem = qobject_cast<QQuickItem *>(topLevel);
497497 if (contentItem) {
498− qxView = new QQuickView(&engine, NULL);
498+ qxView = new QQuickView(&engine, nullptr);
499499 window = qxView;
500500 // Set window default properties; the qml can still override them
501501 QString oname = contentItem->objectName();