on xenial help sign in
Add scope settings UI

Accessible through the page header in scope pages on supported scopes. Approved by: Michał Sawicz, PS Jenkins bot

commit624ac8c7623cbf74e20d4709b69a464970b2e0e0 authorMichał Sawicz <michal.sawicz@canonical.com> dateWed 27 Aug 2014, 15:35 (12 years ago) parentf539346d8d287b
28 files changed+1093−74
qml/Dash/Dash.qml+1−1 qml/Dash/DashContent.qml+4−4 qml/Dash/GenericScopeView.qml+67−26 qml/Dash/PageHeader.qml+9−3 qml/Dash/PreviewListView.qml+11−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+2 tests/autopilot/unity8/shell/emulators/dash.py+5−5 tests/mocks/Unity/CMakeLists.txt+2 tests/mocks/Unity/fake_scope.cpp+3−1 tests/mocks/Unity/fake_scope.h+1 tests/mocks/Unity/fake_settingsmodel.cpp+95 tests/mocks/Unity/fake_settingsmodel.h+54 tests/mocks/Unity/fake_unity_plugin.cpp+2 tests/qmltests/CMakeLists.txt+5 tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qml+1−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/tst_GenericScopeView.qml+63−32
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+4−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
@@ -96,8 +96,7 @@ Item {
9696 id: dashContentList
9797 objectName: "dashContentList"
9898
99− interactive: dashContent.scopes.loaded && currentItem && !currentItem.moving && !currentItem.navigationShown
100−
99+ interactive: dashContent.scopes.loaded && currentItem && !currentItem.moving && !currentItem.navigationShown && !currentItem.subPageShown
101100 anchors.fill: parent
102101 orientation: ListView.Horizontal
103102 boundsBehavior: Flickable.DragAndOvershootBounds
@@ -137,6 +136,7 @@ Item {
137136
138137 readonly property bool moving: item ? item.moving : false
139138 readonly property bool navigationShown: item ? item.navigationShown : false
139+ readonly property bool subPageShown: item ? item.subPageShown : false
140140 readonly property var categoryView: item ? item.categoryView : null
141141 readonly property var theScope: scope
142142
qml/Dash/GenericScopeView.qml+67−26
@@ -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,7 +141,7 @@ 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
147147 height: floatingSeeLess.visible ? parent.height - floatingSeeLess.height + floatingSeeLess.yOffset
@@ -149,7 +149,7 @@ FocusScope {
149149 clip: height != parent.height
150150
151151 model: scopeView.categories
152− forceNoClip: previewListView.open
152+ forceNoClip: subPageLoader.open
153153 pixelAligned: true
154154 interactive: !navigationShown
155155
@@ -294,10 +294,10 @@ FocusScope {
294294 // If the filter animation will be seen start it, otherwise, just flip the switch
295295 var shrinkingVisible = !shouldExpand && y + item.collapsedHeight + seeAll.height < categoryView.height;
296296 var growingVisible = shouldExpand && y + height < categoryView.height;
297− if (!previewListView.open || shouldExpand) {
297+ if (!subPageLoader.open || shouldExpand) {
298298 var animate = shrinkingVisible || growingVisible;
299299 baseItem.expand(shouldExpand, animate)
300− if (shouldExpand && !previewListView.open) {
300+ if (shouldExpand && !subPageLoader.open) {
301301 categoryView.maximizeVisibleArea(index, item.expandedHeight + seeAll.height);
302302 }
303303 }
@@ -428,6 +428,7 @@ FocusScope {
428428 searchHint: scopeView.scope && scopeView.scope.searchHint || i18n.tr("Search")
429429 showBackButton: scopeView.hasBackAction
430430 searchEntryEnabled: true
431+ settingsEnabled: scopeView.scope && scopeView.scope.settings && scopeView.scope.settings.count > 0 || false
431432 scopeStyle: scopeView.scopeStyle
432433 paginationCount: scopeView.paginationCount
433434 paginationIndex: scopeView.paginationIndex
@@ -441,6 +442,7 @@ FocusScope {
441442 }
442443
443444 onBackClicked: scopeView.backClicked()
445+ onSettingsClicked: subPageLoader.openSubPage("settings")
444446 }
445447 }
446448 }
@@ -501,21 +503,60 @@ FocusScope {
501503 id: previewLimitModel
502504 }
503505
504− PreviewListView {
505− id: previewListView
506− objectName: "previewListView"
506+ Loader {
507+ id: subPageLoader
508+ objectName: "subPageLoader"
507509 visible: x != width
508− scope: scopeView.scope
509− scopeStyle: scopeView.scopeStyle
510510 width: parent.width
511511 height: parent.height
512512 anchors.left: categoryView.right
513513
514− onOpenChanged: {
515− if (showPageHeader) {
516− pageHeaderLoader.item.unfocus();
514+ property bool open: false
515+ property var scope: scopeView.scope
516+ property var scopeStyle: scopeView.scopeStyle
517+ property int initialIndex: -1
518+ property var model: null
519+
520+ readonly property bool processing: item && item.processing || false
521+ readonly property int count: item && item.count || 0
522+ readonly property int currentIndex: item && item.currentIndex || 0
523+ readonly property var currentItem: item && item.currentItem || null
524+
525+ property string subPage: ""
526+ readonly property bool subPageShown: visible && status === Loader.Ready
527+
528+ function openSubPage(page) {
529+ subPage = page;
530+ }
531+
532+ function closeSubPage() {
533+ open = false;
534+ }
535+
536+ source: switch(subPage) {
537+ case "preview": return "PreviewListView.qml";
538+ case "settings": return "ScopeSettingsPage.qml";
539+ default: return "";
540+ }
541+
542+ onLoaded: {
543+ item.scope = Qt.binding(function() { return subPageLoader.scope; } )
544+ item.scopeStyle = Qt.binding(function() { return subPageLoader.scopeStyle; } )
545+ if (subPage == "preview") {
546+ item.open = Qt.binding(function() { return subPageLoader.open; } )
547+ item.initialIndex = Qt.binding(function() { return subPageLoader.initialIndex; } )
548+ item.model = Qt.binding(function() { return subPageLoader.model; } )
517549 }
550+ open = true;
518551 }
519− }
520552
553+ onOpenChanged: pageHeaderLoader.item.unfocus()
554+
555+ onVisibleChanged: if (!visible) subPage = ""
556+
557+ Connections {
558+ target: subPageLoader.item
559+ onBackClicked: subPageLoader.closeSubPage()
560+ }
561+ }
521562}
qml/Dash/PageHeader.qml+9−3
@@ -31,6 +31,7 @@ Item {
3131 property string title
3232
3333 property bool searchEntryEnabled: false
34+ property bool settingsEnabled: false
3435 property ListModel searchHistory: SearchHistoryModel
3536 property alias searchQuery: searchTextField.text
3637 property alias searchHint: searchTextField.placeholderText
@@ -43,6 +44,7 @@ Item {
4344 property var scopeStyle: null
4445
4546 signal backClicked()
47+ signal settingsClicked()
4648
4749 onScopeStyleChanged: refreshLogo()
4850 onSearchQueryChanged: {
@@ -245,9 +247,7 @@ Item {
245247 backAction: Action {
246248 iconName: "back"
247249 visible: root.showBackButton
248− onTriggered: {
249− root.backClicked();
250− }
250+ onTriggered: root.backClicked()
251251 }
252252
253253 actions: [
@@ -259,6 +259,12 @@ Item {
259259 headerContainer.showSearch = true;
260260 searchTextField.forceActiveFocus();
261261 }
262+ },
263+ Action {
264+ objectName: "settings"
265+ iconName: "settings"
266+ visible: root.settingsEnabled
267+ onTriggered: root.settingsClicked()
262268 }
263269 ]
264270 }
qml/Dash/PreviewListView.qml+11−1
@@ -23,6 +23,7 @@ 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
@@ -37,6 +38,8 @@ Item {
3738 readonly property bool processing: currentItem && (!currentItem.previewModel.loaded
3839 || currentItem.previewModel.processingAction)
3940
41+ signal backClicked()
42+
4043 PageHeader {
4144 id: header
4245 objectName: "pageHeader"
@@ -46,7 +49,7 @@ Item {
4649 searchEntryEnabled: false
4750 scopeStyle: root.scopeStyle
4851
49− onBackClicked: root.open = false
52+ onBackClicked: root.backClicked()
5053 }
5154
5255 ListView {
@@ -80,6 +83,13 @@ Item {
8083 }
8184 }
8285
86+ onCountChanged: {
87+ if (count > 0 && initialIndex >= 0) {
88+ currentIndex = initialIndex;
89+ initialIndex = -1;
90+ }
91+ }
92+
8393 delegate: Previews.Preview {
8494 id: preview
8595 objectName: "preview" + index
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+2
@@ -468,6 +468,8 @@ Item {
468468 width: parent.width
469469 height: parent.height
470470 anchors.left: scopesOverviewContent.right
471+
472+ onBackClicked: open = false
471473 }
472474
473475 Item {
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/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_scope.cpp+3−1
@@ -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
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_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/qmltests/CMakeLists.txt+5
@@ -55,6 +55,11 @@ add_qml_test(Dash/Previews PreviewTextSummary)
5555add_qml_test(Dash/Previews PreviewVideoPlayback)
5656add_qml_test(Dash/Previews PreviewWidgetFactory)
5757add_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)
5863add_qml_test(Greeter Lockscreen ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
5964add_qml_test(Greeter MultiGreeter ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
6065add_qml_test(Greeter SingleGreeter ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qml+1−1
@@ -94,7 +94,7 @@ Rectangle {
9494 factory.widgetData = { type: data.type };
9595 factory.widgetType = data.type;
9696
97− verify(("" + factory.source).indexOf(data.source) != -1);
97+ verify((String(factory.source)).indexOf(data.source) != -1);
9898 }
9999 }
100100}
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/tst_GenericScopeView.qml+63−32
@@ -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 }
@@ -276,19 +278,21 @@ Item {
276278 true);
277279 var tile = findChild(findChild(genericScopeView, category), "delegate"+delegate);
278280 mouseClick(tile, tile.width / 2, tile.height / 2);
279− tryCompare(testCase.previewListView, "open", true);
280− tryCompare(testCase.previewListView, "x", 0);
281+ tryCompare(testCase.subPageLoader, "open", true);
282+ tryCompare(testCase.subPageLoader, "x", 0);
281283 }
282284
283285 function closePreview() {
284− var closePreviewMouseArea = findChild(genericScopeView, "innerPageHeader");
286+ var closePreviewMouseArea = findChild(subPageLoader.item, "pageHeader");
285287 mouseClick(closePreviewMouseArea, units.gu(2), units.gu(2));
286288
287− tryCompare(testCase.previewListView, "open", false);
289+ tryCompare(testCase.subPageLoader, "open", false);
290+ tryCompare(testCase.subPageLoader, "visible", false);
288291 }
289292
290293 function test_previewOpenClose() {
291− tryCompare(testCase.previewListView, "open", false);
294+ tryCompare(testCase.subPageLoader, "open", false);
295+ tryCompare(testCase.subPageLoader, "visible", false);
292296
293297 var categoryListView = findChild(genericScopeView, "categoryListView");
294298 categoryListView.positionAtBeginning();
@@ -300,7 +304,7 @@ Item {
300304 function test_showPreviewCarousel() {
301305 var category = scrollToCategory("dashCategory1");
302306
303− tryCompare(testCase.previewListView, "open", false);
307+ tryCompare(testCase.subPageLoader, "open", false);
304308
305309 var tile = findChild(category, "carouselDelegate1");
306310 verify(tile, "Could not find delegate");
@@ -308,14 +312,14 @@ Item {
308312 mouseClick(tile, tile.width / 2, tile.height / 2);
309313 tryCompare(tile, "explicitlyScaled", true);
310314 mouseClick(tile, tile.width / 2, tile.height / 2);
311− tryCompare(testCase.previewListView, "open", true);
312− tryCompare(testCase.previewListView, "x", 0);
315+ tryCompare(testCase.subPageLoader, "open", true);
316+ tryCompare(testCase.subPageLoader, "x", 0);
313317
314318 closePreview();
315319
316320 mousePress(tile, tile.width / 2, tile.height / 2);
317− tryCompare(testCase.previewListView, "open", true);
318− tryCompare(testCase.previewListView, "x", 0);
321+ tryCompare(testCase.subPageLoader, "open", true);
322+ tryCompare(testCase.subPageLoader, "x", 0);
319323 mouseRelease(tile, tile.width / 2, tile.height / 2);
320324
321325 closePreview();
@@ -324,20 +328,20 @@ Item {
324328 function test_showPreviewHorizontalList() {
325329 var category = scrollToCategory("dashCategory18");
326330
327− tryCompare(testCase.previewListView, "open", false);
331+ tryCompare(testCase.subPageLoader, "open", false);
328332
329333 var tile = findChild(category, "delegate1");
330334 verify(tile, "Could not find delegate");
331335
332336 mouseClick(tile, tile.width / 2, tile.height / 2);
333− tryCompare(testCase.previewListView, "open", true);
334− tryCompare(testCase.previewListView, "x", 0);
337+ tryCompare(testCase.subPageLoader, "open", true);
338+ tryCompare(testCase.subPageLoader, "x", 0);
335339
336340 closePreview();
337341
338342 mousePress(tile, tile.width / 2, tile.height / 2);
339− tryCompare(testCase.previewListView, "open", true);
340− tryCompare(testCase.previewListView, "x", 0);
343+ tryCompare(testCase.subPageLoader, "open", true);
344+ tryCompare(testCase.subPageLoader, "x", 0);
341345 mouseRelease(tile, tile.width / 2, tile.height / 2);
342346
343347 closePreview();
@@ -347,25 +351,52 @@ Item {
347351 var categoryListView = findChild(genericScopeView, "categoryListView");
348352 categoryListView.positionAtBeginning();
349353
350− tryCompare(testCase.previewListView, "open", false);
351− var previewListViewList = findChild(previewListView, "listView");
354+ tryCompare(testCase.subPageLoader, "open", false);
352355
353356 openPreview();
357+ var previewListViewList = findChild(subPageLoader.item, "listView");
354358
355359 // flick to the next previews
356− tryCompare(testCase.previewListView, "count", 15);
357− for (var i = 1; i < testCase.previewListView.count; ++i) {
358− mouseFlick(testCase.previewListView, testCase.previewListView.width - units.gu(1),
359− 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,
360364 units.gu(2),
361− testCase.previewListView.height / 2);
365+ testCase.subPageLoader.height / 2);
362366 tryCompare(previewListViewList, "moving", false);
363− tryCompare(testCase.previewListView.currentItem, "objectName", "preview" + i);
364−
367+ tryCompare(testCase.subPageLoader.currentItem, "objectName", "preview" + i);
365368 }
366369 closePreview();
367370 }
368371
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+
369400 function test_header_style_data() {
370401 return [
371402 { tag: "Default", index: 0, foreground: UbuntuColors.darkGrey, background: "color:///#f5f5f5", logo: "" },