on xenial help sign in
merged overview
38f4f43Andrea Cimitan
data/unity8-dash.conf+1−1
@@ -13,7 +13,7 @@ respawn
13pre-start script
13pre-start script
14 if [ -z "$UNITY_SCOPES_LIST" ]; then
14 if [ -z "$UNITY_SCOPES_LIST" ]; then
15 # FIXME: remove once we have this in dconf
15 # FIXME: remove once we have this in dconf
16− initctl set-env UNITY_SCOPES_LIST="scopes;clickscope;musicaggregator;videoaggregator"
16+ initctl set-env UNITY_SCOPES_LIST="clickscope;musicaggregator;videoaggregator"
17 fi
17 fi
18
18
19 initctl emit scope-ui-starting
19 initctl emit scope-ui-starting
plugins/Dash/CardCreator.js+3−2
@@ -108,6 +108,7 @@ var kOverlayLoaderCode = 'Loader { \n\
108 sourceComponent: ShaderEffect { \n\
108 sourceComponent: ShaderEffect { \n\
109 id: overlay; \n\
109 id: overlay; \n\
110 height: (fixedHeaderHeight > 0 ? fixedHeaderHeight : headerHeight) + units.gu(2); \n\
110 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 color overlayColor: cardData && cardData["overlayColor"] || "#99000000"; \n\
112 property color overlayColor: cardData && cardData["overlayColor"] || "#99000000"; \n\
112 property var source: ShaderEffectSource { \n\
113 property var source: ShaderEffectSource { \n\
113 id: shaderSource; \n\
114 id: shaderSource; \n\
@@ -436,14 +437,14 @@ function cardString(template, components) {
436 mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible);
437 mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible);
437 }
438 }
438
439
439− var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey")';
440+ 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 : "grey")';
440
441
441 var hasTitleContainer = hasTitle && (hasEmblem || (hasMascot && (hasSubtitle || hasAttributes)));
442 var hasTitleContainer = hasTitle && (hasEmblem || (hasMascot && (hasSubtitle || hasAttributes)));
442 var titleSubtitleCode = '';
443 var titleSubtitleCode = '';
443 if (hasTitle) {
444 if (hasTitle) {
444 var titleColor;
445 var titleColor;
445 if (headerAsOverlay) {
446 if (headerAsOverlay) {
446− titleColor = '"white"';
447+ titleColor = 'overlayLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : "grey")';
447 } else if (hasSummary) {
448 } else if (hasSummary) {
448 titleColor = 'summary.color';
449 titleColor = 'summary.color';
449 } else if (hasBackground) {
450 } else if (hasBackground) {
plugins/Dash/ScopeStyle.qml+3−2
@@ -49,9 +49,10 @@ QtObject {
49
49
50 /*! \brief Luminance threshold for switching between fore and background color
50 /*! \brief Luminance threshold for switching between fore and background color
51
51
52− \note If background colour is not fully opaque, it's not taken into account.
52+ \note If background colour is not fully opaque, the defaultLightLuminance it's taken into account instead of it.
53 */
53 */
54− readonly property real threshold: background.a !== 1.0 ? foregroundLuminance : (foregroundLuminance + backgroundLuminance) / 2
54+ readonly property real threshold: background.a !== 1.0 ? (foregroundLuminance + d.defaultLightLuminance) / 2
55+ : (foregroundLuminance + backgroundLuminance) / 2
55
56
56 /*! \brief The lighter of foreground and background colors
57 /*! \brief The lighter of foreground and background colors
57
58
plugins/Dash/listviewwithpageheader.cpp+10−2
@@ -330,6 +330,11 @@ int ListViewWithPageHeader::stickyHeaderHeight() const
330 return m_topSectionItem ? m_topSectionItem->height() : 0;
330 return m_topSectionItem ? m_topSectionItem->height() : 0;
331}
331}
332
332
333+qreal ListViewWithPageHeader::headerItemShownHeight() const
334+{
335+ return m_headerItemShownHeight;
336+}
337+
333void ListViewWithPageHeader::positionAtBeginning()
338void ListViewWithPageHeader::positionAtBeginning()
334{
339{
335 if (m_delegateModel->count() <= 0)
340 if (m_delegateModel->count() <= 0)
@@ -396,6 +401,7 @@ void ListViewWithPageHeader::showHeader()
396 firstItem->setY(firstItem->y() - m_headerItemShownHeight);
401 firstItem->setY(firstItem->y() - m_headerItemShownHeight);
397 layout();
402 layout();
398 }
403 }
404+ Q_EMIT headerItemShownHeightChanged();
399 }
405 }
400 m_contentYAnimation->setTo(to);
406 m_contentYAnimation->setTo(to);
401 contentYAnimationType = ContentYAnimationShowHeader;
407 contentYAnimationType = ContentYAnimationShowHeader;
@@ -544,6 +550,7 @@ void ListViewWithPageHeader::adjustHeader(qreal diff)
544 m_headerItem->setY(-m_minYExtent);
550 m_headerItem->setY(-m_minYExtent);
545 }
551 }
546 }
552 }
553+ Q_EMIT headerItemShownHeightChanged();
547 } else {
554 } else {
548 // Stick the header item to the top when dragging down
555 // Stick the header item to the top when dragging down
549 m_headerItem->setY(contentY());
556 m_headerItem->setY(contentY());
@@ -822,8 +829,8 @@ ListViewWithPageHeader::ListItem *ListViewWithPageHeader::createItem(int modelIn
822 ListItem *nextItem = itemAtIndex(modelIndex + 1);
829 ListItem *nextItem = itemAtIndex(modelIndex + 1);
823 if (nextItem) {
830 if (nextItem) {
824 listItem->setY(nextItem->y() - listItem->height());
831 listItem->setY(nextItem->y() - listItem->height());
825− } else if (modelIndex == 0 && m_headerItem) {
832+ } else if (modelIndex == 0) {
826− listItem->setY(m_headerItem->height());
833+ listItem->setY(m_headerItem ? m_headerItem->height() : 0);
827 } else if (!m_visibleItems.isEmpty()) {
834 } else if (!m_visibleItems.isEmpty()) {
828 lostItem = true;
835 lostItem = true;
829 }
836 }
@@ -1096,6 +1103,7 @@ void ListViewWithPageHeader::headerHeightChanged(qreal newHeaderHeight, qreal ol
1096 m_headerItemShownHeight = qBound(static_cast<qreal>(0.), m_headerItemShownHeight, newHeaderHeight);
1103 m_headerItemShownHeight = qBound(static_cast<qreal>(0.), m_headerItemShownHeight, newHeaderHeight);
1097 updateClipItem();
1104 updateClipItem();
1098 adjustMinYExtent();
1105 adjustMinYExtent();
1106+ Q_EMIT headerItemShownHeightChanged();
1099 } else {
1107 } else {
1100 if (oldHeaderY + oldHeaderHeight > contentY()) {
1108 if (oldHeaderY + oldHeaderHeight > contentY()) {
1101 // If the header is shown because its position
1109 // If the header is shown because its position
plugins/Dash/listviewwithpageheader.h+3
@@ -53,6 +53,7 @@ class ListViewWithPageHeader : public QQuickFlickable, public QQuickItemChangeLi
53 Q_PROPERTY(QString sectionProperty READ sectionProperty WRITE setSectionProperty NOTIFY sectionPropertyChanged)
53 Q_PROPERTY(QString sectionProperty READ sectionProperty WRITE setSectionProperty NOTIFY sectionPropertyChanged)
54 Q_PROPERTY(bool forceNoClip READ forceNoClip WRITE setForceNoClip NOTIFY forceNoClipChanged)
54 Q_PROPERTY(bool forceNoClip READ forceNoClip WRITE setForceNoClip NOTIFY forceNoClipChanged)
55 Q_PROPERTY(int stickyHeaderHeight READ stickyHeaderHeight NOTIFY stickyHeaderHeightChanged)
55 Q_PROPERTY(int stickyHeaderHeight READ stickyHeaderHeight NOTIFY stickyHeaderHeightChanged)
56+ Q_PROPERTY(qreal headerItemShownHeight READ headerItemShownHeight NOTIFY headerItemShownHeightChanged)
56
57
57 friend class ListViewWithPageHeaderTest;
58 friend class ListViewWithPageHeaderTest;
58 friend class ListViewWithPageHeaderTestSection;
59 friend class ListViewWithPageHeaderTestSection;
@@ -81,6 +82,7 @@ public:
81 void setForceNoClip(bool noClip);
82 void setForceNoClip(bool noClip);
82
83
83 int stickyHeaderHeight() const;
84 int stickyHeaderHeight() const;
85+ qreal headerItemShownHeight() const;
84
86
85 Q_INVOKABLE void positionAtBeginning();
87 Q_INVOKABLE void positionAtBeginning();
86 Q_INVOKABLE void showHeader();
88 Q_INVOKABLE void showHeader();
@@ -100,6 +102,7 @@ Q_SIGNALS:
100 void sectionPropertyChanged();
102 void sectionPropertyChanged();
101 void forceNoClipChanged();
103 void forceNoClipChanged();
102 void stickyHeaderHeightChanged();
104 void stickyHeaderHeightChanged();
105+ void headerItemShownHeightChanged();
103
106
104protected:
107protected:
105 void componentComplete() override;
108 void componentComplete() override;
po/hu.po+6−5
@@ -6,16 +6,17 @@
6msgid ""
6msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9−"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9+"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2014-07-11 14:59+0200\n"
10"POT-Creation-Date: 2014-07-11 14:59+0200\n"
11"PO-Revision-Date: 2014-08-06 07:41+0000\n"
11"PO-Revision-Date: 2014-08-06 07:41+0000\n"
12"Last-Translator: Richard Somlói <ricsipontaz@gmail.com>\n"
12"Last-Translator: Richard Somlói <ricsipontaz@gmail.com>\n"
13"Language-Team: Hungarian <hu@li.org>\n"
13"Language-Team: Hungarian <hu@li.org>\n"
14+"Language: hu\n"
14"MIME-Version: 1.0\n"
15"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-08-08 07:07+0000\n"
18+"X-Launchpad-Export-Date: 2014-08-07 06:27+0000\n"
18−"X-Generator: Launchpad (build 17156)\n"
19+"X-Generator: Launchpad (build 17147)\n"
19
20
20#: plugins/Unity/Launcher/launcheritem.cpp:43
21#: plugins/Unity/Launcher/launcheritem.cpp:43
21#: plugins/Unity/Launcher/launcheritem.cpp:73
22#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -73,8 +74,8 @@ msgid ""
73"You have now mastered the edge gestures and can start using the "
74"You have now mastered the edge gestures and can start using the "
74"phone<br><br>Tap on the screen to start"
75"phone<br><br>Tap on the screen to start"
75msgstr ""
76msgstr ""
76−"Elsajátította a gesztusokat, és megkezdheti a telefon "
77+"Elsajátította a gesztusokat, és megkezdheti a telefon használatát."
77−"használatát.<br><br>Koppintson a képernyőre a kezdéshez."
78+"<br><br>Koppintson a képernyőre a kezdéshez."
78
79
79#: qml/Components/Lockscreen.qml:214
80#: qml/Components/Lockscreen.qml:214
80msgid "Emergency Call"
81msgid "Emergency Call"
po/sl.po+4−3
@@ -6,16 +6,17 @@
6msgid ""
6msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9−"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9+"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2014-07-11 14:59+0200\n"
10"POT-Creation-Date: 2014-07-11 14:59+0200\n"
11"PO-Revision-Date: 2014-08-06 06:11+0000\n"
11"PO-Revision-Date: 2014-08-06 06:11+0000\n"
12"Last-Translator: Damir Jerovšek <Unknown>\n"
12"Last-Translator: Damir Jerovšek <Unknown>\n"
13"Language-Team: Slovenian <sl@li.org>\n"
13"Language-Team: Slovenian <sl@li.org>\n"
14+"Language: sl\n"
14"MIME-Version: 1.0\n"
15"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-08-08 07:07+0000\n"
18+"X-Launchpad-Export-Date: 2014-08-07 06:27+0000\n"
18−"X-Generator: Launchpad (build 17156)\n"
19+"X-Generator: Launchpad (build 17147)\n"
19
20
20#: plugins/Unity/Launcher/launcheritem.cpp:43
21#: plugins/Unity/Launcher/launcheritem.cpp:43
21#: plugins/Unity/Launcher/launcheritem.cpp:73
22#: plugins/Unity/Launcher/launcheritem.cpp:73
po/unity8.pot+70−34
@@ -8,7 +8,7 @@ msgid ""
8msgstr ""
8msgstr ""
9"Project-Id-Version: unity8\n"
9"Project-Id-Version: unity8\n"
10"Report-Msgid-Bugs-To: \n"
10"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2014-07-11 14:59+0200\n"
11+"POT-Creation-Date: 2014-08-08 11:17+0200\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -26,66 +26,82 @@ msgstr ""
26msgid "Unpin shortcut"
26msgid "Unpin shortcut"
27msgstr ""
27msgstr ""
28
28
29+#: qml/Components/Dialogs.qml:142
30+msgid "Are you sure you would like to turn power off?"
31+msgstr ""
32+
33+#: qml/Components/Dialogs.qml:144
34+msgid "Power off"
35+msgstr ""
36+
37+#: qml/Components/Dialogs.qml:153
38+msgid "Restart"
39+msgstr ""
40+
41+#: qml/Components/Dialogs.qml:162
42+msgid "Cancel"
43+msgstr ""
44+
29#: qml/Components/EdgeDemoOverlay.qml:151
45#: qml/Components/EdgeDemoOverlay.qml:151
30msgid "Skip intro"
46msgid "Skip intro"
31msgstr ""
47msgstr ""
32
48
33−#: qml/Components/EdgeDemo.qml:113
49+#: qml/Components/EdgeDemo.qml:112
34msgid "Right edge"
50msgid "Right edge"
35msgstr ""
51msgstr ""
36
52
37−#: qml/Components/EdgeDemo.qml:114
53+#: qml/Components/EdgeDemo.qml:113
38msgid "Try swiping from the right edge to unlock the phone"
54msgid "Try swiping from the right edge to unlock the phone"
39msgstr ""
55msgstr ""
40
56
41−#: qml/Components/EdgeDemo.qml:145
57+#: qml/Components/EdgeDemo.qml:144
42msgid "Top edge"
58msgid "Top edge"
43msgstr ""
59msgstr ""
44
60
45−#: qml/Components/EdgeDemo.qml:146
61+#: qml/Components/EdgeDemo.qml:145
46msgid "Try swiping from the top edge to access the indicators"
62msgid "Try swiping from the top edge to access the indicators"
47msgstr ""
63msgstr ""
48
64
49−#: qml/Components/EdgeDemo.qml:171
65+#: qml/Components/EdgeDemo.qml:170
50msgid "Close"
66msgid "Close"
51msgstr ""
67msgstr ""
52
68
53−#: qml/Components/EdgeDemo.qml:172
69+#: qml/Components/EdgeDemo.qml:171
54msgid "Swipe up again to close the settings screen"
70msgid "Swipe up again to close the settings screen"
55msgstr ""
71msgstr ""
56
72
57−#: qml/Components/EdgeDemo.qml:199
73+#: qml/Components/EdgeDemo.qml:198
58msgid "Left edge"
74msgid "Left edge"
59msgstr ""
75msgstr ""
60
76
61−#: qml/Components/EdgeDemo.qml:200
77+#: qml/Components/EdgeDemo.qml:199
62msgid "Swipe from the left to reveal the launcher for quick access to apps"
78msgid "Swipe from the left to reveal the launcher for quick access to apps"
63msgstr ""
79msgstr ""
64
80
65−#: qml/Components/EdgeDemo.qml:227
81+#: qml/Components/EdgeDemo.qml:226
66msgid "Well done"
82msgid "Well done"
67msgstr ""
83msgstr ""
68
84
69−#: qml/Components/EdgeDemo.qml:228
85+#: qml/Components/EdgeDemo.qml:227
70msgid ""
86msgid ""
71"You have now mastered the edge gestures and can start using the "
87"You have now mastered the edge gestures and can start using the "
72"phone<br><br>Tap on the screen to start"
88"phone<br><br>Tap on the screen to start"
73msgstr ""
89msgstr ""
74
90
75−#: qml/Components/Lockscreen.qml:214
91+#: qml/Components/Lockscreen.qml:220
76msgid "Emergency Call"
92msgid "Emergency Call"
77msgstr ""
93msgstr ""
78
94
79−#: qml/Components/Lockscreen.qml:231
95+#: qml/Components/Lockscreen.qml:243
80msgid "OK"
96msgid "OK"
81msgstr ""
97msgstr ""
82
98
83−#: qml/Components/PassphraseLockscreen.qml:60
99+#: qml/Components/PassphraseLockscreen.qml:62
84#, qt-format
100#, qt-format
85msgid "Hello %1"
101msgid "Hello %1"
86msgstr ""
102msgstr ""
87
103
88−#: qml/Components/PassphraseLockscreen.qml:60
104+#: qml/Components/PassphraseLockscreen.qml:62
89msgid "Hello"
105msgid "Hello"
90msgstr ""
106msgstr ""
91
107
@@ -97,19 +113,11 @@ msgstr ""
97msgid "DONE"
113msgid "DONE"
98msgstr ""
114msgstr ""
99
115
100−#: qml/Components/SeeMore.qml:35
101−msgid "See more"
102−msgstr ""
103−
104−#: qml/Components/SeeMore.qml:58 qml/Dash/GenericScopeView.qml:330
116+#: qml/Dash/GenericScopeView.qml:358
105msgid "See less"
117msgid "See less"
106msgstr ""
118msgstr ""
107
119
108−#: qml/Dash/DashApps.qml:37
109−msgid "Recent"
110−msgstr ""
111−
112−#: qml/Dash/GenericScopeView.qml:330
120+#: qml/Dash/GenericScopeView.qml:358
113msgid "See all"
121msgid "See all"
114msgstr ""
122msgstr ""
115
123
@@ -133,6 +141,26 @@ msgstr ""
133msgid "Send"
141msgid "Send"
134msgstr ""
142msgstr ""
135
143
144+#: qml/Dash/ScopesOverview.qml:200
145+msgid "Manage Dash"
146+msgstr ""
147+
148+#: qml/Dash/ScopesOverview.qml:405
149+msgid "Done"
150+msgstr ""
151+
152+#: qml/Dash/ScopesOverview.qml:431
153+msgid "Store"
154+msgstr ""
155+
156+#: qml/Dash/ScopesOverviewTab.qml:36
157+msgid "Favorites"
158+msgstr ""
159+
160+#: qml/Dash/ScopesOverviewTab.qml:54
161+msgid "All"
162+msgstr ""
163+
136#: qml/Greeter/Greeter.qml:157
164#: qml/Greeter/Greeter.qml:157
137msgid "Swipe to unlock"
165msgid "Swipe to unlock"
138msgstr ""
166msgstr ""
@@ -157,11 +185,11 @@ msgstr ""
157msgid "Speaking..."
185msgid "Speaking..."
158msgstr ""
186msgstr ""
159
187
160−#: qml/Notifications/NotificationMenuItemFactory.qml:91
188+#: qml/Notifications/NotificationMenuItemFactory.qml:100
161msgid "Show password"
189msgid "Show password"
162msgstr ""
190msgstr ""
163
191
164−#: qml/Notifications/NotificationMenuItemFactory.qml:103
192+#: qml/Notifications/NotificationMenuItemFactory.qml:115
165msgid "Please enter SIM PIN"
193msgid "Please enter SIM PIN"
166msgstr ""
194msgstr ""
167
195
@@ -173,35 +201,43 @@ msgstr ""
173msgid "Conference"
201msgid "Conference"
174msgstr ""
202msgstr ""
175
203
176−#: qml/Panel/Indicators/MenuItemFactory.qml:583
204+#: qml/Panel/Indicators/MenuItemFactory.qml:651
177msgid "In queue…"
205msgid "In queue…"
178msgstr ""
206msgstr ""
179
207
180−#: qml/Panel/Indicators/MenuItemFactory.qml:587
208+#: qml/Panel/Indicators/MenuItemFactory.qml:655
181msgid "Downloading"
209msgid "Downloading"
182msgstr ""
210msgstr ""
183
211
184−#: qml/Panel/Indicators/MenuItemFactory.qml:589
212+#: qml/Panel/Indicators/MenuItemFactory.qml:657
185msgid "Paused, tap to resume"
213msgid "Paused, tap to resume"
186msgstr ""
214msgstr ""
187
215
188−#: qml/Panel/Indicators/MenuItemFactory.qml:591
216+#: qml/Panel/Indicators/MenuItemFactory.qml:659
189msgid "Canceled"
217msgid "Canceled"
190msgstr ""
218msgstr ""
191
219
192−#: qml/Panel/Indicators/MenuItemFactory.qml:593
220+#: qml/Panel/Indicators/MenuItemFactory.qml:661
193msgid "Finished"
221msgid "Finished"
194msgstr ""
222msgstr ""
195
223
196−#: qml/Panel/Indicators/MenuItemFactory.qml:595
224+#: qml/Panel/Indicators/MenuItemFactory.qml:663
197msgid "Failed, tap to retry"
225msgid "Failed, tap to retry"
198msgstr ""
226msgstr ""
199
227
228+#: qml/Panel/Indicators/ModemInfoItem.qml:105
229+msgid "Unlock SIM"
230+msgstr ""
231+
232+#: qml/Panel/Indicators/RoamingIndication.qml:27
233+msgid "Roaming"
234+msgstr ""
235+
200#: qml/Panel/SearchIndicator.qml:27
236#: qml/Panel/SearchIndicator.qml:27
201msgid "Search"
237msgid "Search"
202msgstr ""
238msgstr ""
203
239
204−#: qml/Shell.qml:359
240+#: qml/Shell.qml:256
205#, qt-format
241#, qt-format
206msgid "Please enter %1"
242msgid "Please enter %1"
207msgstr ""
243msgstr ""
qml/Components/PageHeader.qml+3−1
@@ -24,7 +24,7 @@ import "SearchHistoryModel"
24Item {
24Item {
25 id: root
25 id: root
26 objectName: "pageHeader"
26 objectName: "pageHeader"
27− implicitHeight: headerContainer.height + units.gu(2) + bottomContainer.height
27+ implicitHeight: headerContainer.height + bottomContainer.height + (showSignatureLine ? units.gu(2) : 0)
28
28
29 property bool showBackButton: false
29 property bool showBackButton: false
30 property string title
30 property string title
@@ -32,6 +32,7 @@ Item {
32 property bool searchEntryEnabled: false
32 property bool searchEntryEnabled: false
33 property ListModel searchHistory: SearchHistoryModel
33 property ListModel searchHistory: SearchHistoryModel
34 property alias searchQuery: searchTextField.text
34 property alias searchQuery: searchTextField.text
35+ property alias showSignatureLine: bottomBorder.visible
35
36
36 property alias bottomItem: bottomContainer.children
37 property alias bottomItem: bottomContainer.children
37 property int paginationCount: 0
38 property int paginationCount: 0
@@ -251,6 +252,7 @@ Item {
251
252
252 actions: [
253 actions: [
253 Action {
254 Action {
255+ objectName: "search"
254 iconName: "search"
256 iconName: "search"
255 visible: root.searchEntryEnabled
257 visible: root.searchEntryEnabled
256 onTriggered: {
258 onTriggered: {
qml/Components/ResponsiveGridView.qml+3−3
@@ -34,7 +34,7 @@ Item {
34 readonly property int cellWidth: gridView.cellWidth
34 readonly property int cellWidth: gridView.cellWidth
35 readonly property int cellHeight: gridView.cellHeight
35 readonly property int cellHeight: gridView.cellHeight
36 readonly property int totalContentHeight: {
36 readonly property int totalContentHeight: {
37− return contentHeightForRows(Math.ceil(gridView.model.count / columns))
37+ return contentHeightForRows(Math.ceil(gridView.model.count / columns), cellHeight)
38 }
38 }
39 property alias interactive: gridView.interactive
39 property alias interactive: gridView.interactive
40 readonly property alias flicking: gridView.flicking
40 readonly property alias flicking: gridView.flicking
@@ -47,8 +47,8 @@ Item {
47 property alias cacheBuffer: gridView.cacheBuffer
47 property alias cacheBuffer: gridView.cacheBuffer
48 readonly property alias currentItem: gridView.currentItem
48 readonly property alias currentItem: gridView.currentItem
49
49
50− function contentHeightForRows(rows) {
50+ function contentHeightForRows(rows, height) {
51− return rows * cellHeight;
51+ return rows * height
52 }
52 }
53
53
54 GridView {
54 GridView {
qml/Dash/CardCarousel.qml+2−2
@@ -48,8 +48,8 @@ DashRenderer {
48
48
49 objectName: "carouselDelegate" + index
49 objectName: "carouselDelegate" + index
50
50
51− function clicked() { cardCarousel.clicked(index, model.result) }
51+ function clicked() { cardCarousel.clicked(index, model.result, loader.item, model) }
52− function pressAndHold() { cardCarousel.pressAndHold(index, model.result) }
52+ function pressAndHold() { cardCarousel.pressAndHold(index) }
53
53
54 sourceComponent: cardTool.cardComponent
54 sourceComponent: cardTool.cardComponent
55 onLoaded: {
55 onLoaded: {
qml/Dash/CardGrid.qml+13−2
@@ -26,10 +26,20 @@ DashRenderer {
26 }
26 }
27
27
28 expandedHeight: grid.totalContentHeight
28 expandedHeight: grid.totalContentHeight
29− collapsedHeight: Math.min(grid.contentHeightForRows(collapsedRows), expandedHeight)
29+ collapsedHeight: Math.min(grid.contentHeightForRows(collapsedRows, grid.cellHeight), expandedHeight)
30 collapsedItemCount: collapsedRows * grid.columns
30 collapsedItemCount: collapsedRows * grid.columns
31 originY: grid.originY
31 originY: grid.originY
32
32
33+ function cardPosition(index) {
34+ var pos = {};
35+ var row = Math.floor(index / grid.columns);
36+ var column = index % grid.columns;
37+ // Bit sad this is not symmetrical
38+ pos.x = column * grid.cellWidth + grid.margins;
39+ pos.y = row * grid.cellHeight;
40+ return pos;
41+ }
42+
33 ResponsiveGridView {
43 ResponsiveGridView {
34 id: grid
44 id: grid
35 anchors.fill: parent
45 anchors.fill: parent
@@ -53,6 +63,7 @@ DashRenderer {
53 item.objectName = "delegate" + index;
63 item.objectName = "delegate" + index;
54 item.width = Qt.binding(function() { return cardTool.cardWidth; });
64 item.width = Qt.binding(function() { return cardTool.cardWidth; });
55 item.height = Qt.binding(function() { return cardTool.cardHeight; });
65 item.height = Qt.binding(function() { return cardTool.cardHeight; });
66+ item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight; });
56 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
67 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
57 item.cardData = Qt.binding(function() { return model; });
68 item.cardData = Qt.binding(function() { return model; });
58 item.template = Qt.binding(function() { return cardTool.template; });
69 item.template = Qt.binding(function() { return cardTool.template; });
@@ -62,7 +73,7 @@ DashRenderer {
62 }
73 }
63 Connections {
74 Connections {
64 target: loader.item
75 target: loader.item
65− onClicked: root.clicked(index, result)
76+ onClicked: root.clicked(index, result, loader.item, model)
66 onPressAndHold: root.pressAndHold(index)
77 onPressAndHold: root.pressAndHold(index)
67 }
78 }
68 }
79 }
qml/Dash/CardVerticalJournal.qml+1−1
@@ -61,7 +61,7 @@ DashRenderer {
61 }
61 }
62 Connections {
62 Connections {
63 target: loader.item
63 target: loader.item
64− onClicked: root.clicked(index, result)
64+ onClicked: root.clicked(index, result, loader.item, model)
65 onPressAndHold: root.pressAndHold(index)
65 onPressAndHold: root.pressAndHold(index)
66 }
66 }
67 }
67 }
qml/Dash/Dash.qml+171−13
@@ -16,6 +16,7 @@
16
16
17import QtQuick 2.2
17import QtQuick 2.2
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19+import Ubuntu.Gestures 0.1
19import Unity 0.2
20import Unity 0.2
20import Utils 0.1
21import Utils 0.1
21import Unity.DashCommunicator 0.1
22import Unity.DashCommunicator 0.1
@@ -73,26 +74,104 @@ Showable {
73 id: scopes
74 id: scopes
74 }
75 }
75
76
76− Image {
77+ QtObject {
78+ id: overviewController
79+ objectName: "overviewController"
80+
81+ property alias enableAnimation: progressAnimation.enabled
82+ property real progress: 0
83+ Behavior on progress {
84+ id: progressAnimation
85+ UbuntuNumberAnimation { }
86+ }
87+ }
88+
89+ ScopesOverview {
90+ id: scopesOverview
91+ objectName: "scopesOverview"
77 anchors.fill: parent
92 anchors.fill: parent
78− source: parent.width > parent.height ? "graphics/paper_landscape.png" : "graphics/paper_portrait.png"
93+ scope: scopes.overviewScope
79− fillMode: Image.PreserveAspectCrop
94+ progress: overviewController.progress
80− horizontalAlignment: Image.AlignRight
95+ scopeScale: scopeItem.scope ? 0.4 : (1 - overviewController.progress * 0.6)
81− verticalAlignment: Image.AlignTop
96+ visible: scopeScale != 1
97+ currentIndex: dashContent.currentIndex
98+ onDone: {
99+ if (currentTab == 1) {
100+ animateDashFromAll(dashContent.currentScopeId);
101+ }
102+ hide();
103+ }
104+ onFavoriteSelected: {
105+ setCurrentScope(scopeId, false, false);
106+ dashContentCache.scheduleUpdate();
107+ hide();
108+ }
109+ onAllFavoriteSelected: {
110+ setCurrentScope(scopeId, false, false);
111+ dashContentCache.scheduleUpdate();
112+ animateDashFromAll(dashContent.currentScopeId);
113+ hide();
114+ }
115+ onSearchSelected: {
116+ var scopeIndex = -1;
117+ for (var i = 0; i < scopes.count; ++i) {
118+ if (scopes.getScope(i).id == scopeId) {
119+ scopeIndex = i;
120+ break;
121+ }
122+ }
123+ if (scopeIndex >= 0) {
124+ // Is a favorite one
125+ setCurrentScope(scopeId, false, false);
126+ dashContentCache.scheduleUpdate();
127+ showDashFromPos(pos, size);
128+ hide();
129+ } else {
130+ // Is not a favorite one, activate and get openScope
131+ scope.activate(result);
132+ }
133+ }
134+ function hide() {
135+ overviewController.enableAnimation = true;
136+ overviewController.progress = 0;
137+ }
138+ onProgressChanged: {
139+ if (progress == 0) {
140+ currentTab = scopeItem.scope ? 1 : 0;
141+ }
142+ }
143+ }
144+
145+ ShaderEffectSource {
146+ id: dashContentCache
147+ parent: scopesOverview.dashItemEater
148+ z: 1
149+ sourceItem: dashContent
150+ height: sourceItem.height
151+ width: sourceItem.width
152+ opacity: 1 - overviewController.progress
153+ visible: overviewController.progress != 0 && scopeItem.scope === null
154+ live: false
82 }
155 }
83
156
84 DashContent {
157 DashContent {
85 id: dashContent
158 id: dashContent
159+
160+ property var scopeThatOpenedScope: null
161+
86 objectName: "dashContent"
162 objectName: "dashContent"
87− width: parent.width
163+ width: dash.width
88− height: parent.height
164+ height: dash.height
89 scopes: scopes
165 scopes: scopes
90− visible: x != -width
166+ visible: !scopesOverview.showingNonFavoriteScope && x != -width
91 onGotoScope: {
167 onGotoScope: {
92 dash.setCurrentScope(scopeId, true, false);
168 dash.setCurrentScope(scopeId, true, false);
93 }
169 }
94 onOpenScope: {
170 onOpenScope: {
171+ scopeThatOpenedScope = currentScope;
95 scopeItem.scope = scope;
172 scopeItem.scope = scope;
173+ scopesOverview.currentTab = 1;
174+ scopesOverview.ensureAllScopeVisible(scope.id);
96 x = -width;
175 x = -width;
97 }
176 }
98 onScopeLoaded: {
177 onScopeLoaded: {
@@ -102,25 +181,63 @@ Showable {
102 }
181 }
103 }
182 }
104 scale: dash.contentScale
183 scale: dash.contentScale
105− clip: scale != 1.0 || scopeItem.visible
184+ clip: scale != 1.0 || scopeItem.visible || overviewController.progress != 0
106 Behavior on x {
185 Behavior on x {
107 UbuntuNumberAnimation {
186 UbuntuNumberAnimation {
187+ duration: overviewController.progress != 0 ? 0 : UbuntuAnimation.FastDuration
108 onRunningChanged: {
188 onRunningChanged: {
109 if (!running && dashContent.x == 0) {
189 if (!running && dashContent.x == 0) {
110− dashContent.closeScope(scopeItem.scope);
190+ dashContent.scopeThatOpenedScope.closeScope(scopeItem.scope);
111 scopeItem.scope = null;
191 scopeItem.scope = null;
192+ if (overviewController.progress == 0) {
193+ // Set tab to Favorites only if we are not showing the overview
194+ scopesOverview.currentTab = 0;
195+ }
112 }
196 }
113 }
197 }
114 }
198 }
115 }
199 }
200+
201+ enabled: overviewController.progress == 0
202+ opacity: enabled ? 1 : 0
203+ }
204+
205+ DashBackground
206+ {
207+ anchors.fill: scopeItem
208+ visible: scopeItem.visible
209+ parent: scopeItem.parent
210+ scale: scopeItem.scale
211+ opacity: scopeItem.opacity
116 }
212 }
117
213
118 GenericScopeView {
214 GenericScopeView {
119 id: scopeItem
215 id: scopeItem
120− anchors.left: dashContent.right
216+ objectName: "dashTempScopeItem"
217+
218+ readonly property real targetOverviewScale: {
219+ if (scopesOverview.currentTab == 0) {
220+ return 0.4;
221+ } else {
222+ return scopesOverview.allCardSize.width / scopeItem.width;
223+ }
224+ }
225+ readonly property real overviewProgressScale: (1 - overviewController.progress * (1 - targetOverviewScale))
226+ readonly property var targetOverviewPosition: scope ? scopesOverview.allScopeCardPosition(scope.id) : null
227+ readonly property real overviewProgressX: scope && scopesOverview.currentTab == 1 && targetOverviewPosition ?
228+ overviewController.progress * (targetOverviewPosition.x - (width - scopesOverview.allCardSize.width) / 2)
229+ : 0
230+ readonly property real overviewProgressY: scope && scopesOverview.currentTab == 1 && targetOverviewPosition ?
231+ overviewController.progress * (targetOverviewPosition.y - (height - scopesOverview.allCardSize.height) / 2)
232+ : 0
233+
234+ x: overviewController.progress == 0 ? dashContent.x + width : overviewProgressX
235+ y: overviewController.progress == 0 ? dashContent.y : overviewProgressY
121 width: parent.width
236 width: parent.width
122 height: parent.height
237 height: parent.height
123− scale: dash.contentScale
238+ scale: dash.contentScale * overviewProgressScale
239+ enabled: opacity == 1
240+ opacity: 1 - overviewController.progress
124 clip: scale != 1.0
241 clip: scale != 1.0
125 visible: scope != null
242 visible: scope != null
126 hasBackAction: true
243 hasBackAction: true
@@ -154,7 +271,7 @@ Showable {
154 opacity: 0
271 opacity: 0
155 visible: opacity > 0
272 visible: opacity > 0
156
273
157− readonly property bool processing: dashContent.processing || scopeItem.processing
274+ readonly property bool processing: dashContent.processing || scopeItem.processing || scopesOverview.processing
158
275
159 Behavior on opacity {
276 Behavior on opacity {
160 UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
277 UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
@@ -207,4 +324,45 @@ Showable {
207 }
324 }
208 }
325 }
209
326
327+ Image {
328+ source: "graphics/overview_hint.png"
329+ anchors.horizontalCenter: parent.horizontalCenter
330+ opacity: (scopeItem.scope ? scopeItem.pageHeaderTotallyVisible : dashContent.pageHeaderTotallyVisible) &&
331+ (overviewDragHandle.enabled || overviewController.progress != 0) ? 1 : 0
332+ Behavior on opacity {
333+ enabled: overviewController.progress == 0
334+ UbuntuNumberAnimation {}
335+ }
336+ y: parent.height - height * (1 - overviewController.progress * 4)
337+ }
338+
339+ EdgeDragArea {
340+ id: overviewDragHandle
341+ objectName: "overviewDragHandle"
342+ z: 1
343+ direction: Direction.Upwards
344+ enabled: !dashContent.previewShown &&
345+ dashContent.currentScope &&
346+ dashContent.currentScope.searchQuery == "" &&
347+ (overviewController.progress == 0 || dragging)
348+
349+ readonly property real fullMovement: units.gu(20)
350+
351+ anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
352+ height: units.gu(2)
353+
354+ onSceneDistanceChanged: {
355+ if (overviewController.enableAnimation) {
356+ dashContentCache.scheduleUpdate();
357+ }
358+ overviewController.enableAnimation = false;
359+ overviewController.progress = Math.max(0, Math.min(1, sceneDistance / fullMovement));
360+ }
361+
362+ onDraggingChanged: {
363+ overviewController.enableAnimation = true;
364+ overviewController.progress = (overviewController.progress > 0.7) ? 1 : 0;
365+ }
366+ }
367+
210}
368}
qml/Dash/DashBackground.qmlnew file+24
@@ -0,0 +1,24 @@
1+/*
2+ * Copyright (C) 2013, 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+
19+Image {
20+ source: anchors.fill.width > anchors.fill.height ? "graphics/paper_landscape.png" : "graphics/paper_portrait.png"
21+ fillMode: Image.PreserveAspectCrop
22+ horizontalAlignment: Image.AlignRight
23+ verticalAlignment: Image.AlignTop
24+}
qml/Dash/DashContent.qml+10−4
@@ -25,8 +25,14 @@ Item {
25
25
26 property alias scopes: dashContentList.model
26 property alias scopes: dashContentList.model
27 readonly property alias currentIndex: dashContentList.currentIndex
27 readonly property alias currentIndex: dashContentList.currentIndex
28+ readonly property string currentScopeId: dashContentList.currentItem ? dashContentList.currentItem.scopeId : ""
29+ 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
28 readonly property bool processing: dashContentList.currentItem && dashContentList.currentItem.item
32 readonly property bool processing: dashContentList.currentItem && dashContentList.currentItem.item
29 && dashContentList.currentItem.item.processing || false
33 && dashContentList.currentItem.item.processing || false
34+ readonly property bool pageHeaderTotallyVisible: dashContentList.currentItem && dashContentList.currentItem.item
35+ && dashContentList.currentItem.item.pageHeaderTotallyVisible || false
30
36
31 signal scopeLoaded(string scopeId)
37 signal scopeLoaded(string scopeId)
32 signal gotoScope(string scopeId)
38 signal gotoScope(string scopeId)
@@ -77,15 +83,15 @@ Item {
77 }
83 }
78 }
84 }
79
85
80− function closeScope(scope) {
81− dashContentList.currentItem.theScope.closeScope(scope)
82− }
83−
84 Item {
86 Item {
85 id: dashContentListHolder
87 id: dashContentListHolder
86
88
87 anchors.fill: parent
89 anchors.fill: parent
88
90
91+ DashBackground {
92+ anchors.fill: parent
93+ }
94+
89 ListView {
95 ListView {
90 id: dashContentList
96 id: dashContentList
91 objectName: "dashContentList"
97 objectName: "dashContentList"
qml/Dash/DashRenderer.qml+3−1
@@ -41,7 +41,9 @@ Item {
41 /// Emitted when the user clicked on an item
41 /// Emitted when the user clicked on an item
42 /// @param index is the index of the clicked item
42 /// @param index is the index of the clicked item
43 /// @param result result model of the cliked item, used for activation
43 /// @param result result model of the cliked item, used for activation
44+ /// @param item item that has been clicked
45+ /// @param itemModel model of the item
44− signal clicked(int index, var result)
46+ signal clicked(int index, var result, var item, var itemModel)
45
47
46 /// Emitted when the user pressed and held on an item
48 /// Emitted when the user pressed and held on an item
47 /// @param index is the index of the held item
49 /// @param index is the index of the held item
qml/Dash/GenericScopeView.qml+84−46
@@ -25,7 +25,7 @@ import "../Components/ListItems" as ListItems
25FocusScope {
25FocusScope {
26 id: scopeView
26 id: scopeView
27
27
28− readonly property alias navigationShown: dashNavigation.showList
28+ readonly property bool navigationShown: pageHeaderLoader.item ? pageHeaderLoader.item.bottomItem[0].showList : false
29 property var scope: null
29 property var scope: null
30 property SortFilterProxyModel categories: categoryFilter
30 property SortFilterProxyModel categories: categoryFilter
31 property bool isCurrent: false
31 property bool isCurrent: false
@@ -33,8 +33,11 @@ FocusScope {
33 property bool hasBackAction: false
33 property bool hasBackAction: false
34 property bool enableHeightBehaviorOnNextCreation: false
34 property bool enableHeightBehaviorOnNextCreation: false
35 property var categoryView: categoryView
35 property var categoryView: categoryView
36− property alias paginationCount: pageHeader.paginationCount
36+ property bool showPageHeader: true
37+ readonly property alias previewShown: previewListView.open
38+ property int paginationCount: 0
37− property alias paginationIndex: pageHeader.paginationIndex
39+ property int paginationIndex: 0
40+ property alias pageHeaderTotallyVisible: categoryView.pageHeaderTotallyVisible
38
41
39 property var scopeStyle: ScopeStyle {
42 property var scopeStyle: ScopeStyle {
40 style: scope ? scope.customizations : {}
43 style: scope ? scope.customizations : {}
@@ -56,6 +59,34 @@ FocusScope {
56 previewListView.open = false;
59 previewListView.open = false;
57 }
60 }
58
61
62+ function itemClicked(index, result, item, itemModel, resultsModel, limitedCategoryItemCount) {
63+ if (scope.id === "scopes" || scope.id == "clickscope") {
64+ // TODO Technically it is possible that calling activate() will make the scope emit
65+ // previewRequested so that we show a preview but there's no scope that does that yet
66+ // so it's not implemented
67+ scope.activate(result)
68+ } else {
69+ openPreview(index, resultsModel, limitedCategoryItemCount);
70+ }
71+ }
72+
73+ function itemPressedAndHeld(index, resultsModel, limitedCategoryItemCount) {
74+ openPreview(index, resultsModel, limitedCategoryItemCount);
75+ }
76+
77+ function openPreview(index, resultsModel, limitedCategoryItemCount) {
78+ if (limitedCategoryItemCount > 0) {
79+ previewLimitModel.model = resultsModel;
80+ previewLimitModel.limit = limitedCategoryItemCount;
81+ previewListView.model = previewLimitModel;
82+ } else {
83+ previewListView.model = resultsModel;
84+ }
85+ previewListView.currentIndex = -1;
86+ previewListView.currentIndex = index;
87+ previewListView.open = true;
88+ }
89+
59 Binding {
90 Binding {
60 target: scope
91 target: scope
61 property: "isActive"
92 property: "isActive"
@@ -72,22 +103,24 @@ FocusScope {
72 }
103 }
73
104
74 onIsCurrentChanged: {
105 onIsCurrentChanged: {
106+ if (showPageHeader) {
75− pageHeader.resetSearch();
107+ pageHeaderLoader.item.resetSearch();
108+ }
76 previewListView.open = false;
109 previewListView.open = false;
77 }
110 }
78
111
79 Binding {
112 Binding {
80 target: scopeView.scope
113 target: scopeView.scope
81 property: "searchQuery"
114 property: "searchQuery"
82− value: pageHeader.searchQuery
115+ value: pageHeaderLoader.item ? pageHeaderLoader.item.searchQuery : ""
83− when: isCurrent
116+ when: isCurrent && showPageHeader
84 }
117 }
85
118
86 Binding {
119 Binding {
87− target: pageHeader
120+ target: pageHeaderLoader.item
88 property: "searchQuery"
121 property: "searchQuery"
89 value: scopeView.scope ? scopeView.scope.searchQuery : ""
122 value: scopeView.scope ? scopeView.scope.searchQuery : ""
90− when: isCurrent
123+ when: isCurrent && showPageHeader
91 }
124 }
92
125
93 Connections {
126 Connections {
@@ -114,10 +147,13 @@ FocusScope {
114 model: scopeView.categories
147 model: scopeView.categories
115 forceNoClip: previewListView.open
148 forceNoClip: previewListView.open
116 pixelAligned: true
149 pixelAligned: true
117− interactive: !dashNavigation.showList
150+ interactive: !navigationShown
118
151
119 property string expandedCategoryId: ""
152 property string expandedCategoryId: ""
120
153
154+ readonly property bool pageHeaderTotallyVisible: scopeView.showPageHeader &&
155+ ((headerItemShownHeight == 0 && categoryView.contentY <= categoryView.originY) || (headerItemShownHeight == pageHeaderLoader.item.height))
156+
121 delegate: ListItems.Base {
157 delegate: ListItems.Base {
122 id: baseItem
158 id: baseItem
123 objectName: "dashCategory" + category
159 objectName: "dashCategory" + category
@@ -223,28 +259,19 @@ FocusScope {
223 Connections {
259 Connections {
224 target: rendererLoader.item
260 target: rendererLoader.item
225 onClicked: {
261 onClicked: {
226− if (scopeView.scope.id === "scopes" || scopeView.scope.id == "clickscope") {
262+ scopeView.itemClicked(index, result, item, itemModel, target.model, categoryItemCount());
227− // TODO Technically it is possible that calling activate() will make the scope emit
228− // previewRequested so that we show a preview but there's no scope that does that yet
229− // so it's not implemented
230− scopeView.scope.activate(result)
231− } else {
232− openPreview(index);
233− }
234 }
263 }
235− onPressAndHold: openPreview(index)
236
264
237− function openPreview(index) {
265+ onPressAndHold: {
266+ scopeView.itemPressedAndHeld(index, target.model, categoryItemCount());
267+ }
268+
269+ function categoryItemCount() {
270+ var categoryItemCount = -1;
238 if (!rendererLoader.expanded && !seeAllLabel.visible && target.collapsedItemCount > 0) {
271 if (!rendererLoader.expanded && !seeAllLabel.visible && target.collapsedItemCount > 0) {
239− previewLimitModel.model = target.model;
240− previewLimitModel.limit = target.collapsedItemCount;
272+ categoryItemCount = target.collapsedItemCount;
241− previewListView.model = previewLimitModel;
242− } else {
243− previewListView.model = target.model;
244 }
273 }
245− previewListView.currentIndex = -1;
274+ return categoryItemCount;
246− previewListView.currentIndex = index;
247− previewListView.open = true;
248 }
275 }
249 }
276 }
250 Connections {
277 Connections {
@@ -380,26 +407,35 @@ FocusScope {
380 }
407 }
381 }
408 }
382
409
383− pageHeader: PageHeader {
410+ pageHeader: scopeView.showPageHeader ? pageHeaderLoader : null
384− id: pageHeader
411+ Loader {
385− objectName: "scopePageHeader"
412+ id: pageHeaderLoader
386 width: parent.width
413 width: parent.width
414+ sourceComponent: scopeView.showPageHeader ? pageHeaderComponent : undefined
415+ Component {
416+ id: pageHeaderComponent
417+ PageHeader {
418+ objectName: "scopePageHeader"
419+ width: parent.width
387− title: scopeView.scope ? scopeView.scope.name : ""
420+ title: scopeView.scope ? scopeView.scope.name : ""
388− showBackButton: scopeView.hasBackAction
421+ showBackButton: scopeView.hasBackAction
389− searchEntryEnabled: true
422+ searchEntryEnabled: true
390− scopeStyle: scopeView.scopeStyle
423+ scopeStyle: scopeView.scopeStyle
391−
424+ paginationCount: scopeView.paginationCount
425+ paginationIndex: scopeView.paginationIndex
426+
392− bottomItem: DashNavigation {
427+ bottomItem: DashNavigation {
393− id: dashNavigation
394− scope: scopeView.scope
428+ scope: scopeView.scope
395− width: parent.width <= units.gu(60) ? parent.width : units.gu(40)
429+ width: parent.width <= units.gu(60) ? parent.width : units.gu(40)
396− anchors.right: parent.right
430+ anchors.right: parent.right
397− windowHeight: scopeView.height
431+ windowHeight: scopeView.height
398− windowWidth: scopeView.width
432+ windowWidth: scopeView.width
399− scopeStyle: scopeView.scopeStyle
433+ scopeStyle: scopeView.scopeStyle
400− }
434+ }
401
435
402− onBackClicked: scopeView.backClicked()
436+ onBackClicked: scopeView.backClicked()
437+ }
438+ }
403 }
439 }
404 }
440 }
405
441
@@ -418,7 +454,9 @@ FocusScope {
418 anchors.left: categoryView.right
454 anchors.left: categoryView.right
419
455
420 onOpenChanged: {
456 onOpenChanged: {
421− pageHeader.unfocus();
457+ if (showPageHeader) {
458+ pageHeaderLoader.item.unfocus();
459+ }
422 }
460 }
423 }
461 }
424
462
qml/Dash/PreviewListView.qml+1−1
@@ -108,7 +108,7 @@ Item {
108 anchors {
108 anchors {
109 left: parent.left
109 left: parent.left
110 right: parent.right
110 right: parent.right
111− top: pageHeader.bottom
111+ top: header.bottom
112 bottom: parent.bottom
112 bottom: parent.bottom
113 }
113 }
114
114
qml/Dash/ScopesOverview.qmlnew file+531
@@ -0,0 +1,531 @@
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 Dash 0.1
19+import Ubuntu.Components 0.1
20+import "../Components"
21+
22+Item {
23+ id: root
24+
25+ // Properties set by parent
26+ property real progress: 0
27+ property var scope: null
28+ property int currentIndex: 0
29+ property real scopeScale: 1
30+
31+ // Properties set and used by parent
32+ property alias currentTab: tabBar.currentTab
33+
34+ // Properties used by parent
35+ readonly property bool processing: searchResultsViewer.processing || tempScopeItem.processing
36+ property bool growingDashFromPos: false
37+ readonly property bool searching: scope && scope.searchQuery == ""
38+ readonly property bool showingNonFavoriteScope: tempScopeItem.scope != null
39+ readonly property var dashItemEater: {
40+ if (!forceXYScalerEater && tabBar.currentTab == 0 && middleItems.count > 0) {
41+ var loaderItem = middleItems.itemAt(0).item;
42+ return loaderItem && loaderItem.currentItem ? loaderItem.currentItem : null;
43+ }
44+ return scopesOverviewXYScaler;
45+ }
46+ readonly property size allCardSize: {
47+ if (middleItems.count > 1) {
48+ var loaderItem = middleItems.itemAt(1).item;
49+ if (loaderItem) {
50+ var cardTool = loaderItem.cardTool;
51+ return Qt.size(cardTool.cardWidth, cardTool.cardHeight);
52+ }
53+ }
54+ return Qt.size(0, 0);
55+ }
56+
57+ // Internal properties
58+ property bool forceXYScalerEater: false
59+
60+ signal done()
61+ signal favoriteSelected(var scopeId)
62+ signal allFavoriteSelected(var scopeId)
63+ signal searchSelected(var scopeId, var result, var pos, var size)
64+
65+ Connections {
66+ target: scope
67+ onOpenScope: {
68+ var itemPos = scopesOverviewXYScaler.restorePosition;
69+ var itemSize = scopesOverviewXYScaler.restoreSize;
70+ scopesOverviewXYScaler.scale = itemSize.width / scopesOverviewXYScaler.width;
71+ if (itemPos) {
72+ scopesOverviewXYScaler.x = itemPos.x -(scopesOverviewXYScaler.width - scopesOverviewXYScaler.width * scopesOverviewXYScaler.scale) / 2;
73+ scopesOverviewXYScaler.y = itemPos.y -(scopesOverviewXYScaler.height - scopesOverviewXYScaler.height * scopesOverviewXYScaler.scale) / 2;
74+ } else {
75+ scopesOverviewXYScaler.x = 0;
76+ scopesOverviewXYScaler.y = 0;
77+ }
78+ scopesOverviewXYScaler.opacity = 0;
79+ tempScopeItem.scope = scope;
80+ middleItems.overrideOpacity = 0;
81+ scopesOverviewXYScaler.scale = 1;
82+ scopesOverviewXYScaler.x = 0;
83+ scopesOverviewXYScaler.y = 0;
84+ scopesOverviewXYScaler.opacity = 1;
85+ }
86+ onGotoScope: {
87+ if (tabBar.currentTab == 0) {
88+ root.favoriteSelected(scopeId);
89+ } else {
90+ root.allFavoriteSelected(scopeId);
91+ }
92+ }
93+ }
94+
95+ Binding {
96+ target: scope
97+ property: "isActive"
98+ value: progress === 1
99+ }
100+
101+ function animateDashFromAll(scopeId) {
102+ var currentScopePos = allScopeCardPosition(scopeId);
103+ if (currentScopePos) {
104+ showDashFromPos(currentScopePos, allCardSize);
105+ } else {
106+ console.log("Warning: Could not find Dash OverView All card position for scope", dashContent.currentScopeId);
107+ }
108+ }
109+
110+ function showDashFromPos(itemPos, itemSize) {
111+ scopesOverviewXYScaler.scale = itemSize.width / scopesOverviewXYScaler.width;
112+ scopesOverviewXYScaler.x = itemPos.x -(scopesOverviewXYScaler.width - scopesOverviewXYScaler.width * scopesOverviewXYScaler.scale) / 2;
113+ scopesOverviewXYScaler.y = itemPos.y -(scopesOverviewXYScaler.height - scopesOverviewXYScaler.height * scopesOverviewXYScaler.scale) / 2;
114+ scopesOverviewXYScaler.opacity = 0;
115+ root.growingDashFromPos = true;
116+ scopesOverviewXYScaler.scale = 1;
117+ scopesOverviewXYScaler.x = 0;
118+ scopesOverviewXYScaler.y = 0;
119+ scopesOverviewXYScaler.opacity = 1;
120+ }
121+
122+ function allScopeCardPosition(scopeId) {
123+ if (middleItems.count > 1) {
124+ var loaderItem = middleItems.itemAt(1).item;
125+ if (loaderItem) {
126+ var pos = loaderItem.scopeCardPosition(scopeId);
127+ return loaderItem.mapToItem(null, pos.x, pos.y);
128+ }
129+ }
130+ }
131+
132+ function ensureAllScopeVisible(scopeId) {
133+ if (middleItems.count > 1) {
134+ var loaderItem = middleItems.itemAt(1).item;
135+ if (loaderItem) {
136+ var pos = loaderItem.scopeCardPosition(scopeId);
137+ loaderItem.contentY = Math.min(pos.y, loaderItem.contentHeight - loaderItem.height);
138+ }
139+ }
140+ }
141+
142+ onProgressChanged: {
143+ if (progress == 0) {
144+ pageHeader.resetSearch();
145+ pageHeader.unfocus(); // Shouldn't the previous call do this too?
146+ }
147+ }
148+
149+ ScopeStyle {
150+ id: overviewScopeStyle
151+ style: { "foreground-color" : "white", "background-color" : "transparent" }
152+ }
153+
154+ DashBackground {
155+ anchors.fill: parent
156+ source: "graphics/dark_background.jpg"
157+ }
158+
159+ Connections {
160+ target: pageHeader
161+ onSearchQueryChanged: {
162+ // Need this in order, otherwise something gets unhappy in rendering
163+ // of the overlay in carousels because the parent of the dash dies for
164+ // a moment, this way we make sure it's reparented first
165+ // by forceXYScalerEater making dashItemEater return scopesOverviewXYScaler
166+ // before we kill the previous parent by scope.searchQuery
167+ root.forceXYScalerEater = true;
168+ root.scope.searchQuery = pageHeader.searchQuery;
169+ root.forceXYScalerEater = false;
170+ }
171+ }
172+
173+ Binding {
174+ target: pageHeader
175+ property: "searchQuery"
176+ value: scope ? scope.searchQuery : ""
177+ }
178+
179+ Item {
180+ id: scopesOverviewContent
181+ x: previewListView.open ? -width : 0
182+ Behavior on x { UbuntuNumberAnimation { } }
183+ width: parent.width
184+ height: parent.height
185+
186+ PageHeader {
187+ id: pageHeader
188+ objectName: "scopesOverviewPageHeader"
189+
190+ readonly property real yDisplacement: pageHeader.height + tabBar.height + tabBar.anchors.margins
191+
192+ y: {
193+ if (root.progress < 0.5) {
194+ return -yDisplacement;
195+ } else {
196+ return -yDisplacement + (root.progress - 0.5) * yDisplacement * 2;
197+ }
198+ }
199+ width: parent.width
200+ clip: true
201+ title: i18n.tr("Manage Dash")
202+ scopeStyle: overviewScopeStyle
203+ showSignatureLine: false
204+ searchEntryEnabled: true
205+ }
206+
207+ ScopesOverviewTab {
208+ id: tabBar
209+ anchors {
210+ left: parent.left
211+ right: parent.right
212+ top: pageHeader.bottom
213+ margins: units.gu(2)
214+ }
215+ height: units.gu(4)
216+
217+ enabled: opacity == 1
218+ opacity: !scope || scope.searchQuery == "" ? 1 : 0
219+ Behavior on opacity { UbuntuNumberAnimation { } }
220+ }
221+
222+ Repeater {
223+ id: middleItems
224+ objectName: "scopesOverviewRepeater"
225+ property real overrideOpacity: -1
226+ model: scope && scope.searchQuery == "" ? scope.categories : null
227+ delegate: Loader {
228+ id: loader
229+ objectName: "scopesOverviewRepeaterChild" + index
230+
231+ height: {
232+ if (index == 0) {
233+ return root.height;
234+ } else {
235+ return root.height - pageHeader.height - tabBar.height - tabBar.anchors.margins - units.gu(2);
236+ }
237+ }
238+ width: {
239+ if (index == 0) {
240+ return root.width / scopeScale;
241+ } else {
242+ return root.width;
243+ }
244+ }
245+ x: {
246+ if (index == 0) {
247+ return (root.width - width) / 2;
248+ } else {
249+ return 0;
250+ }
251+ }
252+ anchors {
253+ bottom: scopesOverviewContent.bottom
254+ }
255+
256+ scale: index == 0 ? scopeScale : 1
257+
258+ opacity: {
259+ if (middleItems.overrideOpacity >= 0)
260+ return middleItems.overrideOpacity;
261+
262+ if (tabBar.currentTab != index)
263+ return 0;
264+
265+ return index == 0 ? 1 : root.progress;
266+ }
267+ Behavior on opacity {
268+ enabled: root.progress == 1
269+ UbuntuNumberAnimation { }
270+ }
271+ enabled: opacity == 1
272+
273+ clip: index == 1
274+
275+ CardTool {
276+ id: cardTool
277+ objectName: "cardTool"
278+ count: results.count
279+ template: model.renderer
280+ components: model.components
281+ viewWidth: parent.width
282+ }
283+
284+ source: {
285+ if (index == 0 && categoryId == "favorites") return "ScopesOverviewFavorites.qml";
286+ else if (index == 1 && categoryId == "all") return "ScopesOverviewAll.qml";
287+ else return "";
288+ }
289+
290+ onLoaded: {
291+ item.model = Qt.binding(function() { return results; });
292+ item.cardTool = cardTool;
293+ if (index == 0) {
294+ item.scopeWidth = Qt.binding(function() { return root.width; });
295+ item.scopeHeight = Qt.binding(function() { return root.height; });
296+ item.appliedScale = Qt.binding(function() { return loader.scale });
297+ item.currentIndex = Qt.binding(function() { return root.currentIndex });
298+ } else if (index == 1) {
299+ item.extraHeight = bottomBar.height;
300+ }
301+ }
302+
303+ Connections {
304+ target: loader.item
305+ onClicked: {
306+ if (tabBar.currentTab == 0) {
307+ root.favoriteSelected(itemModel.scopeId);
308+ } else {
309+ var favoriteScopesItem = middleItems.itemAt(0).item;
310+ var scopeIndex = favoriteScopesItem.model.scopeIndex(itemModel.scopeId);
311+ if (scopeIndex >= 0) {
312+ root.allFavoriteSelected(itemModel.scopeId);
313+ } else {
314+ // Will result in an openScope from root.scope
315+ scopesOverviewXYScaler.restorePosition = item.mapToItem(null, 0, 0);
316+ scopesOverviewXYScaler.restoreSize = allCardSize;
317+ root.scope.activate(result);
318+ }
319+ }
320+ }
321+ onPressAndHold: {
322+ // Preview can call openScope so make sure restorePosition and restoreSize are set
323+ scopesOverviewXYScaler.restorePosition = undefined;
324+ scopesOverviewXYScaler.restoreSize = allCardSize;
325+
326+ previewListView.model = target.model;
327+ previewListView.currentIndex = -1;
328+ previewListView.currentIndex = index;
329+ previewListView.open = true;
330+ }
331+ }
332+ }
333+ }
334+
335+ GenericScopeView {
336+ id: searchResultsViewer
337+ objectName: "searchResultsViewer"
338+ anchors {
339+ top: pageHeader.bottom
340+ right: parent.right
341+ left: parent.left
342+ bottom: parent.bottom
343+ }
344+ scope: root.scope && root.scope.searchQuery != "" ? root.scope : null
345+ scopeStyle: overviewScopeStyle
346+ enabled: opacity == 1
347+ showPageHeader: false
348+ clip: true
349+ opacity: searchResultsViewer.scope ? 1 : 0
350+ isCurrent: true
351+ Behavior on opacity { UbuntuNumberAnimation { } }
352+
353+ function itemClicked(index, result, item, itemModel, resultsModel, limitedCategoryItemCount) {
354+ pageHeader.closePopup();
355+ if (itemModel.scopeId) {
356+ // This can end up in openScope so save restorePosition and restoreSize
357+ scopesOverviewXYScaler.restorePosition = item.mapToItem(null, 0, 0);
358+ scopesOverviewXYScaler.restoreSize = Qt.size(item.width, item.height);
359+ root.searchSelected(itemModel.scopeId, result, item.mapToItem(null, 0, 0), Qt.size(item.width, item.height));
360+ } else {
361+ // Not a scope, just activate it
362+ searchResultsViewer.scope.activate(result);
363+ }
364+ }
365+
366+ function itemPressedAndHeld(index, resultsModel, limitedCategoryItemCount) {
367+ // Do nothing
368+ }
369+ }
370+
371+ Rectangle {
372+ id: bottomBar
373+ color: "black"
374+ height: units.gu(8)
375+ width: parent.width
376+ enabled: opacity == 1
377+ opacity: scope && scope.searchQuery == "" ? 1 : 0
378+ Behavior on opacity { UbuntuNumberAnimation { } }
379+ y: {
380+ if (root.progress < 0.5) {
381+ return parent.height;
382+ } else {
383+ return parent.height - (root.progress - 0.5) * height * 2;
384+ }
385+ }
386+
387+ AbstractButton {
388+ objectName: "scopesOverviewDoneButton"
389+ width: Math.max(label.width + units.gu(2), units.gu(10))
390+ height: units.gu(4)
391+ anchors {
392+ left: parent.left
393+ leftMargin: units.gu(2)
394+ verticalCenter: parent.verticalCenter
395+ }
396+ Rectangle {
397+ anchors.fill: parent
398+ border.color: "white"
399+ border.width: units.dp(1)
400+ radius: units.dp(10)
401+ color: parent.pressed ? "gray" : "transparent"
402+ }
403+ Label {
404+ id: label
405+ anchors.centerIn: parent
406+ text: i18n.tr("Done")
407+ color: parent.pressed ? "black" : "white"
408+ }
409+ onClicked: root.done();
410+ }
411+
412+ AbstractButton {
413+ objectName: "scopesOverviewStoreButton"
414+ width: Math.max(storeLabel.width, units.gu(10))
415+ height: units.gu(4)
416+ anchors {
417+ right: parent.right
418+ verticalCenter: parent.verticalCenter
419+ }
420+ Icon {
421+ id: storeImage
422+ name: "ubuntu-store-symbolic"
423+ color: "white"
424+ anchors.horizontalCenter: parent.horizontalCenter
425+ width: units.gu(2)
426+ height: units.gu(2)
427+ }
428+ Label {
429+ id: storeLabel
430+ anchors.horizontalCenter: parent.horizontalCenter
431+ anchors.top: storeImage.bottom
432+ text: i18n.tr("Store")
433+ color: "white"
434+ }
435+ onClicked: {
436+ // Just zoom from the middle
437+ scopesOverviewXYScaler.restorePosition = undefined;
438+ scopesOverviewXYScaler.restoreSize = allCardSize;
439+ scope.performQuery("scope://com.canonical.scopes.clickstore");
440+ }
441+ }
442+ }
443+ }
444+
445+ PreviewListView {
446+ id: previewListView
447+ objectName: "scopesOverviewPreviewListView"
448+ scope: root.scope
449+ scopeStyle: overviewScopeStyle
450+ visible: x != width
451+ width: parent.width
452+ height: parent.height
453+ anchors.left: scopesOverviewContent.right
454+ }
455+
456+
457+
458+ Item {
459+ id: scopesOverviewXYScaler
460+ width: parent.width
461+ height: parent.height
462+
463+ clip: scale != 1.0
464+ enabled: scale == 1
465+
466+ property bool animationsEnabled: root.showingNonFavoriteScope || root.growingDashFromPos
467+
468+ property var restorePosition
469+ property var restoreSize
470+
471+ Behavior on x {
472+ enabled: scopesOverviewXYScaler.animationsEnabled
473+ UbuntuNumberAnimation { }
474+ }
475+ Behavior on y {
476+ enabled: scopesOverviewXYScaler.animationsEnabled
477+ UbuntuNumberAnimation { }
478+ }
479+ Behavior on opacity {
480+ enabled: scopesOverviewXYScaler.animationsEnabled
481+ UbuntuNumberAnimation { }
482+ }
483+ Behavior on scale {
484+ enabled: scopesOverviewXYScaler.animationsEnabled
485+ UbuntuNumberAnimation {
486+ onRunningChanged: {
487+ if (!running) {
488+ if (root.showingNonFavoriteScope && scopesOverviewXYScaler.scale != 1) {
489+ root.scope.closeScope(tempScopeItem.scope);
490+ tempScopeItem.scope = null;
491+ } else if (root.growingDashFromPos) {
492+ root.growingDashFromPos = false;
493+ }
494+ }
495+ }
496+ }
497+ }
498+
499+ DashBackground {
500+ anchors.fill: tempScopeItem
501+ visible: tempScopeItem.visible
502+ parent: tempScopeItem.parent
503+ }
504+
505+ GenericScopeView {
506+ id: tempScopeItem
507+ objectName: "scopesOverviewTempScopeItem"
508+
509+ width: parent.width
510+ height: parent.height
511+ scale: dash.contentScale
512+ clip: scale != 1.0
513+ visible: scope != null
514+ hasBackAction: true
515+ isCurrent: visible
516+ onBackClicked: {
517+ var v = scopesOverviewXYScaler.restoreSize.width / tempScopeItem.width;
518+ scopesOverviewXYScaler.scale = v;
519+ if (scopesOverviewXYScaler.restorePosition) {
520+ scopesOverviewXYScaler.x = scopesOverviewXYScaler.restorePosition.x -(tempScopeItem.width - tempScopeItem.width * v) / 2;
521+ scopesOverviewXYScaler.y = scopesOverviewXYScaler.restorePosition.y -(tempScopeItem.height - tempScopeItem.height * v) / 2;
522+ } else {
523+ scopesOverviewXYScaler.x = 0;
524+ scopesOverviewXYScaler.y = 0;
525+ }
526+ scopesOverviewXYScaler.opacity = 0;
527+ middleItems.overrideOpacity = -1;
528+ }
529+ }
530+ }
531+}
qml/Dash/ScopesOverviewAll.qmlnew 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+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+
20+Flickable {
21+ id: root
22+
23+ property alias model: cardGrid.model
24+ property alias cardTool: cardGrid.cardTool
25+
26+ property real extraHeight: 0
27+
28+ signal clicked(int index, var result, var item, var itemModel)
29+ signal pressAndHold(int index)
30+
31+ contentHeight: cardGrid.expandedHeight + extraHeight
32+ contentWidth: cardGrid.width
33+ flickableDirection: Flickable.VerticalFlick
34+
35+ function scopeCardPosition(scopeId) {
36+ var index = model.scopeIndex(scopeId);
37+ var pos = cardGrid.cardPosition(index);
38+ pos.y = pos.y - root.contentY;
39+ return pos;
40+ }
41+
42+ CardGrid {
43+ id: cardGrid
44+ width: root.width
45+ height: parent.height
46+
47+ onClicked: {
48+ root.clicked(index, result, item, itemModel);
49+ }
50+ onPressAndHold: {
51+ root.pressAndHold(index);
52+ }
53+ }
54+}
qml/Dash/ScopesOverviewFavorites.qmlnew file+73
@@ -0,0 +1,73 @@
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+
19+Flickable {
20+ id: root
21+
22+ signal clicked(int index, var result, var itemModel)
23+ signal pressAndHold(int index)
24+
25+ property var cardTool: null
26+ property real scopeHeight: 0
27+ property real scopeWidth: 0
28+ property real appliedScale: 1
29+ property int currentIndex: -1
30+ property var currentItem: currentIndex < repeater.count ? repeater.itemAt(currentIndex) : null
31+
32+ property alias model: repeater.model
33+
34+ contentHeight: height
35+ contentWidth: repeater.count * root.scopeWidth + units.gu(2) / appliedScale * (repeater.count - 1)
36+
37+ contentX: {
38+ var indexX = currentIndex * scopeWidth + units.gu(2) / appliedScale * currentIndex;
39+ var newContentX = indexX - (width - scopeWidth) / 2;
40+ newContentX = Math.min(Math.max(newContentX, 0), contentWidth - width);
41+ return newContentX;
42+ }
43+
44+ Repeater {
45+ id: repeater
46+ objectName: "scopesOverviewFavoritesRepeater"
47+
48+ delegate: Loader {
49+ id: loader
50+
51+ x: index * root.scopeWidth + units.gu(2) / appliedScale * index
52+ asynchronous: true
53+
54+ sourceComponent: cardTool.cardComponent
55+ onLoaded: {
56+ item.fixedArtShapeSize = Qt.binding(function() { return Qt.size(root.scopeWidth, root.scopeHeight); });
57+ item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight / appliedScale; });
58+ item.fontScale = Qt.binding(function() { return 1 / appliedScale; });
59+ item.height = Qt.binding(function() { return root.scopeHeight; });
60+ item.width = Qt.binding(function() { return root.scopeWidth; });
61+ item.cardData = Qt.binding(function() { return model; });
62+ item.template = Qt.binding(function() { return cardTool.template; });
63+ item.components = Qt.binding(function() { return cardTool.components; });
64+ item.headerAlignment = Qt.binding(function() { return cardTool.headerAlignment; });
65+ }
66+
67+ Connections {
68+ target: loader.item
69+ onClicked: root.clicked(index, result, model)
70+ }
71+ }
72+ }
73+}
qml/Dash/ScopesOverviewTab.qmlnew file+74
@@ -0,0 +1,74 @@
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.3
18+import Ubuntu.Components 0.1
19+
20+Item {
21+ id: root
22+
23+ property int currentTab: 0
24+
25+ AbstractButton {
26+ id: tab1
27+ height: parent.height
28+ width: parent.width / 2
29+ Rectangle {
30+ anchors.fill: parent
31+ color: root.currentTab == 0 && root.enabled ? "white" : "transparent"
32+ radius: units.dp(10)
33+ }
34+ Label {
35+ anchors.centerIn: parent
36+ text: i18n.tr("Favorites")
37+ color: root.currentTab == 0 && root.enabled ? "black" : "white"
38+ }
39+ onClicked: root.currentTab = 0
40+ }
41+ AbstractButton {
42+ id: tab2
43+ objectName: "scopesOverviewAllTabButton"
44+ x: width
45+ height: parent.height
46+ width: parent.width / 2
47+ Rectangle {
48+ anchors.fill: parent
49+ color: root.currentTab == 1 && root.enabled ? "white" : "transparent"
50+ radius: units.dp(10)
51+ }
52+ Label {
53+ anchors.centerIn: parent
54+ text: i18n.tr("All")
55+ color: root.currentTab == 1 && root.enabled ? "black" : "white"
56+ }
57+ onClicked: root.currentTab = 1
58+ }
59+ Rectangle {
60+ id: centerPiece
61+ width: root.enabled ? units.dp(10) : units.dp(1)
62+ height: parent.height
63+ color: "white"
64+ x: root.currentTab == 1 ? tab2.x : tab2.x - width
65+ }
66+ Rectangle {
67+ id: border
68+ anchors.fill: parent
69+ radius: units.dp(10)
70+ color: "transparent"
71+ border.color: centerPiece.color
72+ border.width: units.dp(1)
73+ }
74+}
qml/Dash/graphics/dark_background.jpgnew file

Binary file not shown.

qml/Dash/graphics/overview_hint@27.pngnew file

Binary file not shown.

tests/autopilot/unity8/shell/tests/test_emulators.py+1−1
@@ -122,7 +122,7 @@ class DashEmulatorTestCase(tests.DashBaseTestCase):
122 self._assert_scope_is_opened(scope, scope_id)
122 self._assert_scope_is_opened(scope, scope_id)
123
123
124 def test_open_generic_scope(self):
124 def test_open_generic_scope(self):
125− scope_id = 'scopes'
125+ scope_id = 'musicaggregator'
126 scope = self.dash.open_scope(scope_id)
126 scope = self.dash.open_scope(scope_id)
127 self._assert_scope_is_opened(scope, scope_id)
127 self._assert_scope_is_opened(scope, scope_id)
128 self.assertIsInstance(scope, dash_emulators.GenericScopeView)
128 self.assertIsInstance(scope, dash_emulators.GenericScopeView)
tests/mocks/Unity/CMakeLists.txt+1
@@ -22,6 +22,7 @@ add_definitions(-DQT_NO_KEYWORDS)
22set(UnityQML_SOURCES
22set(UnityQML_SOURCES
23 fake_scope.cpp
23 fake_scope.cpp
24 fake_scopes.cpp
24 fake_scopes.cpp
25+ fake_scopesoverview.cpp
25 fake_categories.cpp
26 fake_categories.cpp
26 fake_navigation.cpp
27 fake_navigation.cpp
27 fake_resultsmodel.cpp
28 fake_resultsmodel.cpp
tests/mocks/Unity/fake_resultsmodel.cpp+2−1
@@ -58,8 +58,9 @@ ResultsModel::data(const QModelIndex& index, int role) const
58 case RoleUri:
58 case RoleUri:
59 case RoleCategoryId:
59 case RoleCategoryId:
60 case RoleDndUri:
60 case RoleDndUri:
61− case RoleResult:
62 return QString();
61 return QString();
62+ case RoleResult:
63+ return QString("Result.%1.%2").arg(m_categoryId).arg(index.row());
63 case RoleTitle:
64 case RoleTitle:
64 return QString("Title.%1.%2").arg(m_categoryId).arg(index.row());
65 return QString("Title.%1.%2").arg(m_categoryId).arg(index.row());
65 case RoleArt:
66 case RoleArt:
tests/mocks/Unity/fake_scope.cpp+17−5
@@ -18,14 +18,16 @@
18#include <QUrl>
18#include <QUrl>
19
19
20#include "fake_scope.h"
20#include "fake_scope.h"
21+
21#include "fake_navigation.h"
22#include "fake_navigation.h"
22#include "fake_resultsmodel.h"
23#include "fake_resultsmodel.h"
24+#include "fake_scopes.h"
23
25
24−Scope::Scope(QObject* parent) : Scope(QString(), QString(), false, parent)
26+Scope::Scope(Scopes* parent) : Scope(QString(), QString(), false, parent)
25{
27{
26}
28}
27
29
28−Scope::Scope(QString const& id, QString const& name, bool favorite, QObject* parent, int categories)
30+Scope::Scope(QString const& id, QString const& name, bool favorite, Scopes* parent, int categories)
29 : unity::shell::scopes::ScopeInterface(parent)
31 : unity::shell::scopes::ScopeInterface(parent)
30 , m_id(id)
32 , m_id(id)
31 , m_name(name)
33 , m_name(name)
@@ -36,6 +38,7 @@ Scope::Scope(QString const& id, QString const& name, bool favorite, QObject* par
36 , m_currentAltNavigationId("altroot")
38 , m_currentAltNavigationId("altroot")
37 , m_previewRendererName("preview-generic")
39 , m_previewRendererName("preview-generic")
38 , m_categories(new Categories(categories, this))
40 , m_categories(new Categories(categories, this))
41+ , m_openScope(nullptr)
39{
42{
40}
43}
41
44
@@ -158,7 +161,12 @@ void Scope::setNoResultsHint(const QString& str)
158
161
159void Scope::activate(QVariant const& result)
162void Scope::activate(QVariant const& result)
160{
163{
161− Q_UNUSED(result);
164+ qDebug() << "Called activate on scope" << m_id << "with result" << result;
165+ if (result.toString() == "Result.2.2") {
166+ Scopes *scopes = dynamic_cast<Scopes*>(parent());
167+ m_openScope = scopes->getScopeFromAll("MockScope9");
168+ Q_EMIT openScope(m_openScope);
169+ }
162}
170}
163
171
164PreviewStack* Scope::preview(QVariant const& result)
172PreviewStack* Scope::preview(QVariant const& result)
@@ -174,9 +182,13 @@ void Scope::cancelActivation()
174{
182{
175}
183}
176
184
177−void Scope::closeScope(unity::shell::scopes::ScopeInterface* /*scope*/)
185+void Scope::closeScope(unity::shell::scopes::ScopeInterface* scope)
178{
186{
187+ if (scope != m_openScope) {
188+ qDebug() << scope << m_openScope;
179− qFatal("Scope::closeScope is not implemented");
189+ qFatal("Scope::closeScope got wrong scope in closeScope");
190+ }
191+ m_openScope = nullptr;
180}
192}
181
193
182QString Scope::currentNavigationId() const
194QString Scope::currentNavigationId() const
tests/mocks/Unity/fake_scope.h+6−3
@@ -24,13 +24,15 @@
24
24
25#include <QTimer>
25#include <QTimer>
26
26
27+class Scopes;
28+
27class Scope : public unity::shell::scopes::ScopeInterface
29class Scope : public unity::shell::scopes::ScopeInterface
28{
30{
29 Q_OBJECT
31 Q_OBJECT
30
32
31public:
33public:
32− Scope(QObject* parent = 0);
34+ Scope(Scopes* parent = 0);
33− Scope(QString const& id, QString const& name, bool favorite, QObject* parent = 0, int categories = 20);
35+ Scope(QString const& id, QString const& name, bool favorite, Scopes* parent = 0, int categories = 20);
34
36
35 /* getters */
37 /* getters */
36 QString id() const override;
38 QString id() const override;
@@ -92,7 +94,8 @@ protected:
92
94
93 QString m_previewRendererName;
95 QString m_previewRendererName;
94
96
95− Categories* m_categories;
97+ unity::shell::scopes::CategoriesInterface* m_categories;
98+ unity::shell::scopes::ScopeInterface* m_openScope;
96};
99};
97
100
98#endif // FAKE_SCOPE_H
101#endif // FAKE_SCOPE_H
tests/mocks/Unity/fake_scopes.cpp+45−6
@@ -18,6 +18,7 @@
18
18
19// Self
19// Self
20#include "fake_scopes.h"
20#include "fake_scopes.h"
21+#include "fake_scopesoverview.h"
21
22
22// TODO: Implement remaining pieces, like Categories (i.e. LensView now gives warnings)
23// TODO: Implement remaining pieces, like Categories (i.e. LensView now gives warnings)
23
24
@@ -26,6 +27,7 @@
26
27
27Scopes::Scopes(QObject *parent)
28Scopes::Scopes(QObject *parent)
28 : unity::shell::scopes::ScopesInterface(parent)
29 : unity::shell::scopes::ScopesInterface(parent)
30+ , m_scopesOverview(nullptr)
29 , m_loaded(false)
31 , m_loaded(false)
30 , timer(this)
32 , timer(this)
31{
33{
@@ -52,10 +54,17 @@ void Scopes::updateScopes()
52 addScope(new Scope("clickscope", "Apps", true, this));
54 addScope(new Scope("clickscope", "Apps", true, this));
53 addScope(new Scope("MockScope5", "Videos", true, this));
55 addScope(new Scope("MockScope5", "Videos", true, this));
54 addScope(new Scope("SingleCategoryScope", "Single", true, this, 1));
56 addScope(new Scope("SingleCategoryScope", "Single", true, this, 1));
57+ addScope(new Scope("MockScope4", "MS4", true, this));
58+ addScope(new Scope("MockScope6", "MS6", true, this));
59+ addScope(new Scope("MockScope7", "MS7", false, this));
60+ addScope(new Scope("MockScope8", "MS8", false, this));
61+ addScope(new Scope("MockScope9", "MS9", false, this));
62+ m_scopesOverview = new ScopesOverview(this);
55
63
56 if (!m_loaded) {
64 if (!m_loaded) {
57 m_loaded = true;
65 m_loaded = true;
58 Q_EMIT loadedChanged();
66 Q_EMIT loadedChanged();
67+ Q_EMIT overviewScopeChanged();
59 }
68 }
60}
69}
61
70
@@ -64,10 +73,13 @@ void Scopes::clear()
64 timer.stop();
73 timer.stop();
65 if (m_scopes.size() > 0) {
74 if (m_scopes.size() > 0) {
66 beginRemoveRows(QModelIndex(), 0, m_scopes.count()-1);
75 beginRemoveRows(QModelIndex(), 0, m_scopes.count()-1);
67− qDeleteAll(m_scopes);
76+ qDeleteAll(m_allScopes);
77+ m_allScopes.clear();
68 m_scopes.clear();
78 m_scopes.clear();
69 endRemoveRows();
79 endRemoveRows();
70 }
80 }
81+ delete m_scopesOverview;
82+ m_scopesOverview = nullptr;
71
83
72 if (m_loaded) {
84 if (m_loaded) {
73 m_loaded = false;
85 m_loaded = false;
@@ -113,8 +125,22 @@ unity::shell::scopes::ScopeInterface* Scopes::getScope(int row) const
113 return m_scopes[row];
125 return m_scopes[row];
114}
126}
115
127
116−unity::shell::scopes::ScopeInterface* Scopes::getScope(QString const&) const
128+unity::shell::scopes::ScopeInterface* Scopes::getScope(QString const &scope_id) const
117{
129{
130+ // According to mh3 Scopes::getScope should only return favorite scopes (i.e the ones in the model)
131+ for (Scope *scope : m_scopes) {
132+ if (scope->id() == scope_id)
133+ return scope;
134+ }
135+ return nullptr;
136+}
137+
138+Scope* Scopes::getScopeFromAll(const QString& scope_id) const
139+{
140+ for (Scope *scope : m_allScopes) {
141+ if (scope->id() == scope_id)
142+ return scope;
143+ }
118 return nullptr;
144 return nullptr;
119}
145}
120
146
@@ -135,13 +161,26 @@ int Scopes::count() const
135
161
136unity::shell::scopes::ScopeInterface* Scopes::overviewScope() const
162unity::shell::scopes::ScopeInterface* Scopes::overviewScope() const
137{
163{
138− return nullptr;
164+ return m_scopesOverview;
165+}
166+
167+QList<Scope*> Scopes::scopes() const
168+{
169+ return m_scopes;
170+}
171+
172+QList<Scope*> Scopes::allScopes() const
173+{
174+ return m_allScopes;
139}
175}
140
176
141void Scopes::addScope(Scope* scope)
177void Scopes::addScope(Scope* scope)
142{
178{
143 int index = rowCount();
179 int index = rowCount();
180+ if (scope->favorite()) {
144− beginInsertRows(QModelIndex(), index, index);
181+ beginInsertRows(QModelIndex(), index, index);
145− m_scopes.append(scope);
182+ m_scopes.append(scope);
146− endInsertRows();
183+ endInsertRows();
184+ }
185+ m_allScopes.append(scope);
147}
186}
tests/mocks/Unity/fake_scopes.h+8−1
@@ -53,11 +53,18 @@ public:
53 int count() const override;
53 int count() const override;
54 unity::shell::scopes::ScopeInterface* overviewScope() const override;
54 unity::shell::scopes::ScopeInterface* overviewScope() const override;
55
55
56+ // This is used as part of implementation of the other C++ code, not API
57+ QList<Scope*> scopes() const;
58+ QList<Scope*> allScopes() const;
59+ Scope* getScopeFromAll(const QString& scope_id) const;
60+
56private Q_SLOTS:
61private Q_SLOTS:
57 void updateScopes();
62 void updateScopes();
58
63
59private:
64private:
60− QList<Scope*> m_scopes;
65+ QList<Scope*> m_scopes; // the favorite ones
66+ QList<Scope*> m_allScopes;
67+ Scope *m_scopesOverview;
61 bool m_loaded;
68 bool m_loaded;
62 QTimer timer;
69 QTimer timer;
63};
70};
tests/mocks/Unity/fake_scopesoverview.cppnew file+281
@@ -0,0 +1,281 @@
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_scopesoverview.h"
18+
19+#include "fake_scopes.h"
20+
21+#include <paths.h>
22+
23+ScopesOverview::ScopesOverview(Scopes* parent)
24+ : Scope("scopesOverview", "Scopes Overview", false, parent)
25+{
26+ delete m_categories; // delete the usual categories, we're not going to use it
27+ m_scopesOverviewCategories = new ScopesOverviewCategories(parent, this);
28+ m_searchCategories = new ScopesOverviewSearchCategories(parent, this);
29+ m_categories = m_scopesOverviewCategories;
30+}
31+
32+void ScopesOverview::setSearchQuery(const QString& search_query)
33+{
34+ Scope::setSearchQuery(search_query);
35+
36+ auto origCategories = m_categories;
37+ if (search_query.isEmpty()) m_categories = m_scopesOverviewCategories;
38+ else m_categories = m_searchCategories;
39+
40+ if (m_categories != origCategories)
41+ Q_EMIT categoriesChanged();
42+}
43+
44+Q_INVOKABLE void ScopesOverview::activate(QVariant const& result)
45+{
46+ Scopes *scopes = dynamic_cast<Scopes*>(parent());
47+ m_openScope = scopes->getScopeFromAll(result.toString());
48+ Q_EMIT openScope(m_openScope);
49+}
50+
51+ScopesOverviewCategories::ScopesOverviewCategories(Scopes *scopes, QObject* parent)
52+ : unity::shell::scopes::CategoriesInterface(parent)
53+ , m_scopes(scopes)
54+{
55+}
56+
57+int ScopesOverviewCategories::rowCount(const QModelIndex& /*parent*/) const
58+{
59+ return 2;
60+}
61+
62+void ScopesOverviewCategories::addSpecialCategory(QString const&, QString const&, QString const&, QString const&, QObject*)
63+{
64+ qFatal("Using un-implemented ScopesOverviewCategories::addSpecialCategory");
65+}
66+
67+bool ScopesOverviewCategories::overrideCategoryJson(QString const& /* categoryId */, QString const& /* json */)
68+{
69+ qFatal("Using un-implemented ScopesOverviewCategories::overrideCategoryJson");
70+}
71+
72+QVariant
73+ScopesOverviewCategories::data(const QModelIndex& index, int role) const
74+{
75+ if (!index.isValid()) {
76+ return QVariant();
77+ }
78+
79+ const QString categoryId = index.row() == 0 ? "favorites" : "all";
80+
81+ unity::shell::scopes::ResultsModelInterface *resultsModel = m_resultsModels[index.row()];
82+ if (!resultsModel) {
83+ QObject *that = const_cast<ScopesOverviewCategories*>(this);
84+ QList<Scope*> scopes = index.row() == 0 ? m_scopes->scopes() : m_scopes->allScopes();
85+ resultsModel = new ScopesOverviewResultsModel(scopes, categoryId, that);
86+ m_resultsModels[index.row()] = resultsModel;
87+ }
88+ switch (role) {
89+ case RoleCategoryId:
90+ return categoryId;
91+ case RoleName:
92+ return index.row() == 0 ? "Favorites" : "All";
93+ case RoleIcon:
94+ return QVariant();
95+ case RoleRawRendererTemplate:
96+ qFatal("Using un-implemented RoleRawRendererTemplate Categories role");
97+ return QVariant();
98+ case RoleRenderer:
99+ {
100+ QVariantMap map;
101+ map["category-layout"] = "grid";
102+ map["card-size"] = "small";
103+ map["overlay"] = true;
104+ return map;
105+ }
106+ case RoleComponents:
107+ {
108+ QVariantMap map, artMap;
109+ artMap["aspect-ratio"] = "0.5";
110+ artMap["field"] = "art";
111+ map["art"] = artMap;
112+ map["title"] = "HOLA";
113+ return map;
114+ }
115+ case RoleResults:
116+ return QVariant::fromValue(resultsModel);
117+ case RoleCount:
118+ return resultsModel->rowCount();
119+ case RoleHeaderLink:
120+ return QString();
121+ default:
122+ qFatal("Using un-implemented Categories role");
123+ return QVariant();
124+ }
125+}
126+
127+
128+
129+ScopesOverviewSearchCategories::ScopesOverviewSearchCategories(Scopes *scopes, QObject* parent)
130+ : unity::shell::scopes::CategoriesInterface(parent)
131+ , m_scopes(scopes)
132+{
133+}
134+
135+int ScopesOverviewSearchCategories::rowCount(const QModelIndex& /*parent*/) const
136+{
137+ return 2;
138+}
139+
140+void ScopesOverviewSearchCategories::addSpecialCategory(QString const&, QString const&, QString const&, QString const&, QObject*)
141+{
142+ qFatal("Using un-implemented ScopesOverviewSearchCategories::addSpecialCategory");
143+}
144+
145+bool ScopesOverviewSearchCategories::overrideCategoryJson(QString const& /* categoryId */, QString const& /* json */)
146+{
147+ qFatal("Using un-implemented ScopesOverviewSearchCategories::overrideCategoryJson");
148+}
149+
150+QVariant
151+ScopesOverviewSearchCategories::data(const QModelIndex& index, int role) const
152+{
153+ if (!index.isValid()) {
154+ return QVariant();
155+ }
156+
157+ const QString categoryId = index.row() == 0 ? "searchA" : "searchB";
158+
159+ unity::shell::scopes::ResultsModelInterface *resultsModel = m_resultsModels[index.row()];
160+ if (!resultsModel) {
161+ QObject *that = const_cast<ScopesOverviewSearchCategories*>(this);
162+ QList<Scope *> scopes;
163+ if (index.row() == 0) {
164+ scopes << m_scopes->getScopeFromAll("clickscope") << nullptr << m_scopes->getScopeFromAll("MockScope2");
165+ } else {
166+ scopes << nullptr << m_scopes->getScopeFromAll("MockScope7") << nullptr << m_scopes->getScopeFromAll("MockScope1");
167+ }
168+ resultsModel = new ScopesOverviewResultsModel(scopes, categoryId, that);
169+ m_resultsModels[index.row()] = resultsModel;
170+ }
171+ switch (role) {
172+ case RoleCategoryId:
173+ return categoryId;
174+ case RoleName:
175+ return index.row() == 0 ? "SearchA" : "SearchB";
176+ case RoleIcon:
177+ return QVariant();
178+ case RoleRawRendererTemplate:
179+ qFatal("Using un-implemented RoleRawRendererTemplate Categories role");
180+ return QVariant();
181+ case RoleRenderer:
182+ {
183+ QVariantMap map;
184+ map["category-layout"] = "grid";
185+ map["card-size"] = "small";
186+ map["overlay"] = true;
187+ return map;
188+ }
189+ case RoleComponents:
190+ {
191+ QVariantMap map, artMap;
192+ artMap["aspect-ratio"] = "1";
193+ artMap["field"] = "art";
194+ map["art"] = artMap;
195+ map["title"] = "HOLA";
196+ return map;
197+ }
198+ case RoleResults:
199+ return QVariant::fromValue(resultsModel);
200+ case RoleCount:
201+ return resultsModel->rowCount();
202+ case RoleHeaderLink:
203+ return QString();
204+ default:
205+ qFatal("Using un-implemented Categories role");
206+ return QVariant();
207+ }
208+}
209+
210+
211+ScopesOverviewResultsModel::ScopesOverviewResultsModel(const QList<Scope *> &scopes, const QString &categoryId, QObject* parent)
212+ : unity::shell::scopes::ResultsModelInterface(parent)
213+ , m_scopes(scopes)
214+ , m_categoryId(categoryId)
215+{
216+}
217+
218+QString ScopesOverviewResultsModel::categoryId() const
219+{
220+ return m_categoryId;
221+}
222+
223+void ScopesOverviewResultsModel::setCategoryId(QString const& /*id*/)
224+{
225+ qFatal("Calling un-implemented ScopesOverviewResultsModel::setCategoryId");
226+}
227+
228+int ScopesOverviewResultsModel::scopeIndex(QString const& id) const
229+{
230+ const int scopeCount = count();
231+ for (int i = 0; i < scopeCount; ++i) {
232+ if (m_scopes[i]->id() == id)
233+ return i;
234+ }
235+ return -1;
236+}
237+
238+QHash<int, QByteArray> ScopesOverviewResultsModel::roleNames() const
239+{
240+ QHash<int, QByteArray> roles = unity::shell::scopes::ResultsModelInterface::roleNames();
241+ roles[RoleBackground + 1] = "scopeId";
242+ return roles;
243+}
244+
245+int ScopesOverviewResultsModel::rowCount(const QModelIndex& parent) const
246+{
247+ Q_UNUSED(parent);
248+
249+ return m_scopes.count();
250+}
251+
252+int ScopesOverviewResultsModel::count() const
253+{
254+ return rowCount();
255+}
256+
257+QVariant
258+ScopesOverviewResultsModel::data(const QModelIndex& index, int role) const
259+{
260+ unity::shell::scopes::ScopeInterface *scope = m_scopes[index.row()];
261+ switch (role) {
262+ case RoleUri:
263+ case RoleCategoryId:
264+ case RoleDndUri:
265+ return QString();
266+ case RoleResult:
267+ return scope ? scope->id() : QString("Result.%1.%2").arg(categoryId()).arg(index.row());
268+ case RoleTitle:
269+ return scope ? scope->name() : QString("Title.%1.%2").arg(categoryId()).arg(index.row());
270+ case RoleArt:
271+ return qmlDirectory() + "graphics/applicationIcons/dash.png";
272+ case RoleMascot:
273+ case RoleEmblem:
274+ case RoleSummary:
275+ case RoleBackground + 1: // scopeId
276+ return scope ? scope->id() : nullptr;
277+ break;
278+ default:
279+ return QVariant();
280+ }
281+}
tests/mocks/Unity/fake_scopesoverview.hnew file+104
@@ -0,0 +1,104 @@
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_SCOPESOVERVIEW_H
18+#define FAKE_SCOPESOVERVIEW_H
19+
20+#include "fake_scope.h"
21+#include <unity/shell/scopes/ResultsModelInterface.h>
22+
23+class Scopes;
24+
25+class ScopesOverview : public Scope
26+{
27+ Q_OBJECT
28+
29+public:
30+ ScopesOverview(Scopes* parent = 0);
31+
32+ void setSearchQuery(const QString& search_query) override;
33+ Q_INVOKABLE void activate(QVariant const& result) override;
34+
35+private:
36+ unity::shell::scopes::CategoriesInterface *m_scopesOverviewCategories;
37+ unity::shell::scopes::CategoriesInterface *m_searchCategories;
38+};
39+
40+class ScopesOverviewCategories : public unity::shell::scopes::CategoriesInterface
41+{
42+ Q_OBJECT
43+
44+public:
45+ ScopesOverviewCategories(Scopes *scopes, QObject* parent = 0);
46+
47+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
48+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
49+
50+ Q_INVOKABLE void addSpecialCategory(QString const& categoryId, QString const& name, QString const& icon, QString const& rawTemplate, QObject* countObject) override;
51+ Q_INVOKABLE bool overrideCategoryJson(QString const& categoryId, QString const& json) override;
52+
53+private:
54+ mutable QHash<int, unity::shell::scopes::ResultsModelInterface*> m_resultsModels;
55+
56+ Scopes *m_scopes;
57+};
58+
59+class ScopesOverviewSearchCategories : public unity::shell::scopes::CategoriesInterface
60+{
61+ Q_OBJECT
62+
63+public:
64+ ScopesOverviewSearchCategories(Scopes *scopes, QObject* parent = 0);
65+
66+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
67+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
68+
69+ Q_INVOKABLE void addSpecialCategory(QString const& categoryId, QString const& name, QString const& icon, QString const& rawTemplate, QObject* countObject) override;
70+ Q_INVOKABLE bool overrideCategoryJson(QString const& categoryId, QString const& json) override;
71+
72+private:
73+ mutable QHash<int, unity::shell::scopes::ResultsModelInterface*> m_resultsModels;
74+
75+ Scopes *m_scopes;
76+};
77+
78+class ScopesOverviewResultsModel : public unity::shell::scopes::ResultsModelInterface
79+{
80+ Q_OBJECT
81+
82+public:
83+ explicit ScopesOverviewResultsModel(const QList<Scope *> &scopes, const QString &categoryId, QObject* parent = 0);
84+
85+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
86+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
87+
88+ /* getters */
89+ QString categoryId() const override;
90+ int count() const override;
91+
92+ /* setters */
93+ void setCategoryId(QString const& id) override;
94+
95+ /* Special API */
96+ Q_INVOKABLE int scopeIndex(QString const& id) const;
97+ QHash<int, QByteArray> roleNames() const override;
98+
99+private:
100+ QList<Scope *> m_scopes;
101+ QString m_categoryId;
102+};
103+
104+#endif // FAKE_SCOPESOVERVIEW_H
tests/plugins/Dash/cardcreator/2.res+2−2
@@ -94,7 +94,7 @@ Image {
94 wrapMode: Text.Wrap;
94 wrapMode: Text.Wrap;
95 maximumLineCount: 2;
95 maximumLineCount: 2;
96 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
96 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
97− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
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 : "grey");
98 visible: showHeader ;
98 visible: showHeader ;
99 text: root.title;
99 text: root.title;
100 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
100 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -112,7 +112,7 @@ Image {
112 elide: Text.ElideRight;
112 elide: Text.ElideRight;
113 fontSize: "small";
113 fontSize: "small";
114 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
114 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
115− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
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 : "grey");
116 visible: titleLabel.visible && titleLabel.text;
116 visible: titleLabel.visible && titleLabel.text;
117 text: cardData && cardData["subtitle"] || "";
117 text: cardData && cardData["subtitle"] || "";
118 font.weight: Font.Light;
118 font.weight: Font.Light;
tests/plugins/Dash/cardcreator/5.res+6−5
@@ -68,7 +68,8 @@ Loader {
68 visible: showHeader && status == Loader.Ready;
68 visible: showHeader && status == Loader.Ready;
69 sourceComponent: ShaderEffect {
69 sourceComponent: ShaderEffect {
70 id: overlay;
70 id: overlay;
71− height: (fixedHeaderHeight > 0 ? fixedHeaderHeight : headerHeight) + units.gu(2);
71+ 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 color overlayColor: cardData && cardData["overlayColor"] || "#99000000";
73 property color overlayColor: cardData && cardData["overlayColor"] || "#99000000";
73 property var source: ShaderEffectSource {
74 property var source: ShaderEffectSource {
74 id: shaderSource;
75 id: shaderSource;
@@ -90,7 +91,7 @@ Loader {
90 varying highp vec2 coord;
91 varying highp vec2 coord;
91 uniform sampler2D source;
92 uniform sampler2D source;
92 uniform lowp float qt_Opacity;
93 uniform lowp float qt_Opacity;
93− uniform highp vec4 overlayColor;
94+ uniform highp vec4 overlayColor;
94 void main() {
95 void main() {
95 lowp vec4 tex = texture2D(source, coord);
96 lowp vec4 tex = texture2D(source, coord);
96 gl_FragColor = vec4(overlayColor.r, overlayColor.g, overlayColor.b, 1) * qt_Opacity * overlayColor.a * tex.a;
97 gl_FragColor = vec4(overlayColor.r, overlayColor.g, overlayColor.b, 1) * qt_Opacity * overlayColor.a * tex.a;
@@ -99,7 +100,7 @@ Loader {
99 }
100 }
100readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
101readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
101Label {
102Label {
102− id: titleLabel;
103+ id: titleLabel;
103 objectName: "titleLabel";
104 objectName: "titleLabel";
104 anchors { right: parent.right;
105 anchors { right: parent.right;
105 rightMargin: units.gu(1);
106 rightMargin: units.gu(1);
@@ -113,7 +114,7 @@ Label {
113 wrapMode: Text.Wrap;
114 wrapMode: Text.Wrap;
114 maximumLineCount: 2;
115 maximumLineCount: 2;
115 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
116 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
116− color: "white";
117+ color: overlayLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : "grey");
117 visible: showHeader && overlayLoader.active;
118 visible: showHeader && overlayLoader.active;
118 text: root.title;
119 text: root.title;
119 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
120 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -132,7 +133,7 @@ Label {
132 elide: Text.ElideRight;
133 elide: Text.ElideRight;
133 fontSize: "small";
134 fontSize: "small";
134 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
135 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
135− color: "white";
136+ color: overlayLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : "grey");
136 visible: titleLabel.visible && titleLabel.text;
137 visible: titleLabel.visible && titleLabel.text;
137 text: cardData && cardData["subtitle"] || "";
138 text: cardData && cardData["subtitle"] || "";
138 font.weight: Font.Light;
139 font.weight: Font.Light;
tests/plugins/Dash/cardcreator/6.res+2−2
@@ -70,7 +70,7 @@ Item {
70 wrapMode: Text.Wrap;
70 wrapMode: Text.Wrap;
71 maximumLineCount: 2;
71 maximumLineCount: 2;
72 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
72 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
73− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
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 : "grey");
74 visible: showHeader ;
74 visible: showHeader ;
75 text: root.title;
75 text: root.title;
76 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
76 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -88,7 +88,7 @@ Item {
88 elide: Text.ElideRight;
88 elide: Text.ElideRight;
89 fontSize: "small";
89 fontSize: "small";
90 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
90 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
91− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
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 : "grey");
92 visible: titleLabel.visible && titleLabel.text;
92 visible: titleLabel.visible && titleLabel.text;
93 text: cardData && cardData["subtitle"] || "";
93 text: cardData && cardData["subtitle"] || "";
94 font.weight: Font.Light;
94 font.weight: Font.Light;
tests/plugins/Dash/cardcreator/7.res+3−3
@@ -95,7 +95,7 @@ Image {
95 wrapMode: Text.Wrap;
95 wrapMode: Text.Wrap;
96 maximumLineCount: 2;
96 maximumLineCount: 2;
97 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
97 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
98− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
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 : "grey");
99 visible: showHeader ;
99 visible: showHeader ;
100 text: root.title;
100 text: root.title;
101 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
101 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -113,7 +113,7 @@ Image {
113 elide: Text.ElideRight;
113 elide: Text.ElideRight;
114 fontSize: "small";
114 fontSize: "small";
115 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
115 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
116− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
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 : "grey");
117 visible: titleLabel.visible && titleLabel.text;
117 visible: titleLabel.visible && titleLabel.text;
118 text: cardData && cardData["subtitle"] || "";
118 text: cardData && cardData["subtitle"] || "";
119 font.weight: Font.Light;
119 font.weight: Font.Light;
@@ -127,7 +127,7 @@ Image {
127 rightMargin: units.gu(1);
127 rightMargin: units.gu(1);
128 top: subtitleLabel.bottom;
128 top: subtitleLabel.bottom;
129 }
129 }
130− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
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 : "grey");
131 model: cardData && cardData["attributes"];
131 model: cardData && cardData["attributes"];
132 }
132 }
133
133
tests/plugins/Dash/tst_ScopeStyle.qml+1−1
@@ -101,7 +101,7 @@ Rectangle {
101
101
102 function test_threshold_data() {
102 function test_threshold_data() {
103 return [
103 return [
104− { tag: "default", index: 0, threshold: 0.5020 },
104+ { tag: "default", index: 0, threshold: 0.7510 },
105 { tag: "red on black", index: 1, threshold: 0.1063 },
105 { tag: "red on black", index: 1, threshold: 0.1063 },
106 { tag: "green on white", index: 2, threshold: 0.6795 },
106 { tag: "green on white", index: 2, threshold: 0.6795 },
107 { tag: "blue on darkgrey", index: 3, threshold: 0.3675 },
107 { tag: "blue on darkgrey", index: 3, threshold: 0.3675 },
tests/qmltests/Dash/tst_Dash.qml+229−7
@@ -20,8 +20,6 @@ import "../../../qml/Dash"
20import Ubuntu.Components 0.1
20import Ubuntu.Components 0.1
21import Unity.Test 0.1 as UT
21import Unity.Test 0.1 as UT
22
22
23−// TODO We don't have any tests for the overlay scope functionality.
24−
25Item {
23Item {
26 id: shell
24 id: shell
27 width: units.gu(40)
25 width: units.gu(40)
@@ -61,9 +59,9 @@ Item {
61 function get_scope_data() {
59 function get_scope_data() {
62 return [
60 return [
63 { tag: "MockScope1", visualIndex: 0 },
61 { tag: "MockScope1", visualIndex: 0 },
64− { tag: "MockScope2", visualIndex: 1 },
62+ { tag: "MockScope2", visualIndex: -1 },
65− { tag: "clickscope", visualIndex: 2 },
63+ { tag: "clickscope", visualIndex: 1 },
66− { tag: "MockScope5", visualIndex: 3 },
64+ { tag: "MockScope5", visualIndex: 2 },
67 ]
65 ]
68 }
66 }
69
67
@@ -79,10 +77,234 @@ Item {
79 tryCompare(dashContentList, "count", 0);
77 tryCompare(dashContentList, "count", 0);
80 scopes.load();
78 scopes.load();
81 tryCompare(scopes, "loaded", true);
79 tryCompare(scopes, "loaded", true);
82− tryCompare(dashContentList, "count", 5);
80+ tryCompare(dashContentList, "count", 6);
83
81
84 verify(dashContentList != undefined);
82 verify(dashContentList != undefined);
83+ if (data.visualIndex == -1) {
84+ tryCompare(dashContentList, "currentIndex", 0);
85+ expectFail(data.tag, "non favorite scopes should not be visble in the scopes model");
86+ compare(dashContentList.currentItem.scopeId, data.tag); // this should fail
87+ } else {
85− tryCompare(dashContentList, "currentIndex", data.visualIndex);
88+ tryCompare(dashContentList, "currentIndex", data.visualIndex);
89+ compare(dashContentList.currentItem.scopeId, data.tag);
90+ }
91+ }
92+
93+ function test_dash_overview_show_select_same_favorite() {
94+ // Wait for stuff to be loaded
95+ tryCompare(scopes, "loaded", true);
96+ var dashContentList = findChild(dash, "dashContentList");
97+ tryCompare(dashContentList, "count", 6);
98+ var mockScope1Loader = findChild(dash, "MockScope1 loader");
99+ tryCompareFunction(function() { return mockScope1Loader.item != null; }, true);
100+
101+ // Show the overview
102+ touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, dash.height - units.gu(18));
103+ var overviewController = findInvisibleChild(dash, "overviewController");
104+ tryCompare(overviewController, "progress", 1);
105+
106+ // Make sure tab is where it should
107+ var scopesOverview = findChild(dash, "scopesOverview");
108+ compare(scopesOverview.currentTab, 0);
109+
110+ // Make sure stuff is loaded
111+ var scopesOverviewFavoritesRepeater = findChild(dash, "scopesOverviewFavoritesRepeater");
112+ tryCompare(scopesOverviewFavoritesRepeater, "count", 6);
113+ tryCompareFunction(function() { return scopesOverviewFavoritesRepeater.itemAt(0).item != null; }, true);
114+ waitForRendering(scopesOverviewFavoritesRepeater.itemAt(0).item);
115+
116+ // Click in first item
117+ mouseClick(scopesOverviewFavoritesRepeater.itemAt(0).item, 0, 0);
118+
119+ // Make sure animation went back
120+ tryCompare(overviewController, "progress", 0);
121+ compare(dashContentList.currentIndex, 0);
122+ }
123+
124+ function test_dash_overview_show_select_different_favorite() {
125+ // Wait for stuff to be loaded
126+ tryCompare(scopes, "loaded", true);
127+ var dashContentList = findChild(dash, "dashContentList");
128+ tryCompare(dashContentList, "count", 6);
129+ var mockScope1Loader = findChild(dash, "MockScope1 loader");
130+ tryCompareFunction(function() { return mockScope1Loader.item != null; }, true);
131+
132+ // Show the overview
133+ touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, dash.height - units.gu(18));
134+ var overviewController = findInvisibleChild(dash, "overviewController");
135+ tryCompare(overviewController, "progress", 1);
136+
137+ // Make sure tab is where it should
138+ var scopesOverview = findChild(dash, "scopesOverview");
139+ compare(scopesOverview.currentTab, 0);
140+
141+ // Make sure stuff is loaded
142+ var scopesOverviewFavoritesRepeater = findChild(dash, "scopesOverviewFavoritesRepeater");
143+ tryCompare(scopesOverviewFavoritesRepeater, "count", 6);
144+ tryCompareFunction(function() { return scopesOverviewFavoritesRepeater.itemAt(0).item != null; }, true);
145+ waitForRendering(scopesOverviewFavoritesRepeater.itemAt(1).item);
146+
147+ // Click in first item
148+ mouseClick(scopesOverviewFavoritesRepeater.itemAt(1).item, 0, 0);
149+
150+ // Make sure animation went back
151+ tryCompare(overviewController, "progress", 0);
152+ compare(dashContentList.currentIndex, 1);
153+ }
154+
155+ function test_dash_overview_all_temp_scope_done_from_all() {
156+ // Wait for stuff to be loaded
157+ tryCompare(scopes, "loaded", true);
158+ var dashContentList = findChild(dash, "dashContentList");
159+ tryCompare(dashContentList, "count", 6);
160+ var mockScope1Loader = findChild(dash, "MockScope1 loader");
161+ tryCompareFunction(function() { return mockScope1Loader.item != null; }, true);
162+
163+ // Show the overview
164+ touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, dash.height - units.gu(18));
165+ var overviewController = findInvisibleChild(dash, "overviewController");
166+ tryCompare(overviewController, "progress", 1);
167+
168+ // Make sure tab is where it should
169+ var scopesOverview = findChild(dash, "scopesOverview");
170+ compare(scopesOverview.currentTab, 0);
171+
172+ // Make sure stuff is loaded
173+ var scopesOverviewFavoritesRepeater = findChild(dash, "scopesOverviewFavoritesRepeater");
174+ tryCompare(scopesOverviewFavoritesRepeater, "count", 6);
175+ tryCompareFunction(function() { return scopesOverviewFavoritesRepeater.itemAt(0).item != null; }, true);
176+ waitForRendering(scopesOverviewFavoritesRepeater.itemAt(1).item);
177+
178+ // Click on the all tab
179+ var scopesOverviewAllTabButton = findChild(dash, "scopesOverviewAllTabButton");
180+ mouseClick(scopesOverviewAllTabButton, 0, 0);
181+
182+ // Wait for all tab to be enabled (animation finish)
183+ var scopesOverviewAllView = findChild(dash, "scopesOverviewRepeaterChild1");
184+ tryCompare(scopesOverviewAllView, "enabled", true);
185+
186+ // Click on a temp scope
187+ var tempScopeCard = findChild(scopesOverviewAllView, "delegate1");
188+ mouseClick(tempScopeCard, 0, 0);
189+
190+ // Check the bottom edge (overview) is disabled from temp scope
191+ var overviewDragHandle = findChild(dash, "overviewDragHandle");
192+ compare(overviewDragHandle.enabled, false);
193+
194+ // Check temp scope is there
195+ var scopesOverviewTempScopeItem = findChild(dash, "scopesOverviewTempScopeItem");
196+ tryCompareFunction( function() { return scopesOverviewTempScopeItem.scope != null; }, true);
197+ tryCompare(scopesOverviewTempScopeItem, "enabled", true);
198+
199+ // Go back
200+ var scopesOverviewTempScopeItemHeader = findChild(scopesOverviewTempScopeItem, "scopePageHeader");
201+ var backButton = findChild(findChild(scopesOverviewTempScopeItemHeader, "innerPageHeader"), "backButton");
202+ mouseClick(backButton, 0, 0);
203+
204+ // Check temp scope is gone
205+ var scopesOverviewTempScopeItem = findChild(dash, "scopesOverviewTempScopeItem");
206+ tryCompareFunction( function() { return scopesOverviewTempScopeItem.scope == null; }, true);
207+ tryCompare(scopesOverviewTempScopeItem, "enabled", false);
208+
209+ // Press on done
210+ var scopesOverviewDoneButton = findChild(scopesOverview, "scopesOverviewDoneButton");
211+ mouseClick(scopesOverviewDoneButton, 0, 0);
212+
213+ // Check the dash overview is gone
214+ tryCompare(overviewController, "progress", 0);
215+
216+ // Original list is still on 0
217+ compare(dashContentList.currentIndex, 0);
218+ }
219+
220+ function test_temp_scope_dash_overview_all_search_temp_scope_favorite_from_all() {
221+ // Wait for stuff to be loaded
222+ tryCompare(scopes, "loaded", true);
223+ var dashContentList = findChild(dash, "dashContentList");
224+ tryCompare(dashContentList, "count", 6);
225+ var mockScope1Loader = findChild(dash, "MockScope1 loader");
226+ tryCompareFunction(function() { return mockScope1Loader.item != null; }, true);
227+
228+ // Swipe right to Apps scope
229+ touchFlick(dash, dash.width - 1, units.gu(1), dash.width - units.gu(10), units.gu(1));
230+ tryCompare(dashContentList, "contentX", dashContentList.width);
231+ tryCompare(dashContentList, "currentIndex", 1);
232+
233+ // Click on card that opens temp scope
234+ var categoryListView = findChild(dashContentList.currentItem, "categoryListView");
235+ var dashCategory2 = findChild(categoryListView, "dashCategory2");
236+ tryCompareFunction(function() {
237+ var d = findChild(dashCategory2, "delegate2");
238+ if (d) return true;
239+ categoryListView.contentY += 100;
240+ return false;
241+ }, true);
242+ var card2 = findChild(dashCategory2, "delegate2");
243+ waitForRendering(card2);
244+ mouseClick(card2, card2.width / 2, card2.height / 2);
245+
246+ // Wait for temp scope to be there
247+ var dashTempScopeItem = findChild(dash, "dashTempScopeItem");
248+ tryCompare(dashTempScopeItem, "x", 0);
249+
250+ // Show the overview
251+ touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, dash.height - units.gu(18));
252+ var overviewController = findInvisibleChild(dash, "overviewController");
253+ tryCompare(overviewController, "progress", 1);
254+
255+ // Make sure tab is where it should
256+ var scopesOverview = findChild(dash, "scopesOverview");
257+ compare(scopesOverview.currentTab, 1);
258+
259+ // Do a search
260+ var scopesOverviewPageHeader = findChild(scopesOverview, "scopesOverviewPageHeader");
261+ var searchButton = findChild(scopesOverviewPageHeader, "search_header_button");
262+ mouseClick(searchButton, 0, 0);
263+
264+ // Type something
265+ keyClick(Qt.Key_H);
266+
267+ // Check results grid is there and the other lists are not
268+ var searchResultsViewer = findChild(scopesOverview, "searchResultsViewer");
269+ var scopesOverviewRepeater = findChild(dash, "scopesOverviewRepeater");
270+ tryCompare(searchResultsViewer, "opacity", 1);
271+ tryCompare(scopesOverviewRepeater, "count", 0);
272+
273+ // Click on a temp scope in the search
274+ var dashCategorysearchA = findChild(searchResultsViewer, "dashCategorysearchA");
275+ var cardTempScope = findChild(dashCategorysearchA, "delegate2");
276+ waitForRendering(cardTempScope);
277+ mouseClick(cardTempScope, cardTempScope.width / 2, cardTempScope.height / 2);
278+
279+ // Check the bottom edge (overview) is disabled from temp scope
280+ var overviewDragHandle = findChild(dash, "overviewDragHandle");
281+ compare(overviewDragHandle.enabled, false);
282+
283+ // Check temp scope is there
284+ var scopesOverviewTempScopeItem = findChild(dash, "scopesOverviewTempScopeItem");
285+ tryCompareFunction( function() { return scopesOverviewTempScopeItem.scope != null; }, true);
286+ tryCompare(scopesOverviewTempScopeItem, "enabled", true);
287+
288+ // Go back
289+ var scopesOverviewTempScopeItemHeader = findChild(scopesOverviewTempScopeItem, "scopePageHeader");
290+ var backButton = findChild(findChild(scopesOverviewTempScopeItemHeader, "innerPageHeader"), "backButton");
291+ mouseClick(backButton, 0, 0);
292+
293+ // Check temp scope is gone
294+ var scopesOverviewTempScopeItem = findChild(dash, "scopesOverviewTempScopeItem");
295+ tryCompareFunction( function() { return scopesOverviewTempScopeItem.scope == null; }, true);
296+ tryCompare(scopesOverviewTempScopeItem, "enabled", false);
297+
298+ // Press on a favorite
299+ var dashCategorysearchB = findChild(searchResultsViewer, "dashCategorysearchB");
300+ var cardFavSearch = findChild(dashCategorysearchB, "delegate3");
301+ mouseClick(cardFavSearch, 0, 0);
302+
303+ // Check the dash overview is gone
304+ tryCompare(overviewController, "progress", 0);
305+
306+ // Original list went to the favorite
307+ compare(dashContentList.currentIndex, 0);
86 }
308 }
87
309
88 function test_setCurrentScope() {
310 function test_setCurrentScope() {
tests/qmltests/Dash/tst_DashContent.qml+7−6
@@ -70,7 +70,7 @@ Item {
70 function loadScopes() {
70 function loadScopes() {
71 scopeLoadedSpy.clear();
71 scopeLoadedSpy.clear();
72 scopesModel.load();
72 scopesModel.load();
73− tryCompare(scopeLoadedSpy, "count", 4);
73+ tryCompare(scopeLoadedSpy, "count", 6);
74 }
74 }
75
75
76 function init() {
76 function init() {
@@ -114,7 +114,8 @@ Item {
114
114
115 loadScopes();
115 loadScopes();
116
116
117+ compare(dashContentList.count, 6);
117− verify(dashContentList.currentIndex >= 0 && dashContentList.currentIndex < 5);
118+ verify(dashContentList.currentIndex >= 0 && dashContentList.currentIndex < dashContentList.count);
118 }
119 }
119
120
120 function test_show_header_on_list_movement() {
121 function test_show_header_on_list_movement() {
@@ -170,7 +171,7 @@ Item {
170
171
171 // test greater than scope count.
172 // test greater than scope count.
172 var currentScopeIndex = dashContent.currentIndex;
173 var currentScopeIndex = dashContent.currentIndex;
173− dashContent.setCurrentScopeAtIndex(8, true, false);
174+ dashContent.setCurrentScopeAtIndex(18, true, false);
174 compare(dashContent.currentIndex, currentScopeIndex, "Scope should not change if changing to greater index than count");
175 compare(dashContent.currentIndex, currentScopeIndex, "Scope should not change if changing to greater index than count");
175 }
176 }
176
177
@@ -189,9 +190,9 @@ Item {
189 function test_scope_mapping_data() {
190 function test_scope_mapping_data() {
190 return [
191 return [
191 {tag: "index0", index: 0, objectName: "MockScope1"},
192 {tag: "index0", index: 0, objectName: "MockScope1"},
192− {tag: "index1", index: 1, objectName: "MockScope2"},
193+ {tag: "index1", index: 1, objectName: "clickscope"},
193− {tag: "index2", index: 2, objectName: "clickscope"},
194+ {tag: "index2", index: 2, objectName: "MockScope5"},
194− {tag: "index3", index: 3, objectName: "MockScope5"}
195+ {tag: "index3", index: 3, objectName: "SingleCategoryScope"}
195 ]
196 ]
196 }
197 }
197
198
tests/qmltests/Dash/tst_GenericScopeView.qml+12−9
@@ -61,8 +61,8 @@ Item {
61 property Item header: findChild(genericScopeView, "scopePageHeader")
61 property Item header: findChild(genericScopeView, "scopePageHeader")
62
62
63 function init() {
63 function init() {
64− genericScopeView.scope = scopes.getScope(1)
64+ genericScopeView.scope = scopes.getScope(2);
65− shell.width = units.gu(120)
65+ shell.width = units.gu(120);
66 genericScopeView.categoryView.positionAtBeginning();
66 genericScopeView.categoryView.positionAtBeginning();
67 waitForRendering(genericScopeView.categoryView);
67 waitForRendering(genericScopeView.categoryView);
68 }
68 }
@@ -88,13 +88,13 @@ Item {
88
88
89 function test_showDash() {
89 function test_showDash() {
90 testCase.previewListView.open = true;
90 testCase.previewListView.open = true;
91− scopes.getScope(1).showDash();
91+ genericScopeView.scope.showDash();
92 tryCompare(testCase.previewListView, "open", false);
92 tryCompare(testCase.previewListView, "open", false);
93 }
93 }
94
94
95 function test_hideDash() {
95 function test_hideDash() {
96 testCase.previewListView.open = true;
96 testCase.previewListView.open = true;
97− scopes.getScope(1).hideDash();
97+ genericScopeView.scope.hideDash();
98 tryCompare(testCase.previewListView, "open", false);
98 tryCompare(testCase.previewListView, "open", false);
99 }
99 }
100
100
@@ -111,8 +111,9 @@ Item {
111
111
112 function test_changeScope() {
112 function test_changeScope() {
113 genericScopeView.scope.searchQuery = "test"
113 genericScopeView.scope.searchQuery = "test"
114− genericScopeView.scope = scopes.getScope(2)
114+ var originalScopeId = genericScopeView.scope.id;
115− genericScopeView.scope = scopes.getScope(1)
115+ genericScopeView.scope = scopes.getScope(originalScopeId + 1)
116+ genericScopeView.scope = scopes.getScope(originalScopeId)
116 tryCompare(genericScopeView.scope, "searchQuery", "test")
117 tryCompare(genericScopeView.scope, "searchQuery", "test")
117 }
118 }
118
119
@@ -198,6 +199,8 @@ Item {
198 openPreview(4, 0);
199 openPreview(4, 0);
199
200
200 compare(testCase.previewListView.count, 12, "There should only be 12 items in preview.");
201 compare(testCase.previewListView.count, 12, "There should only be 12 items in preview.");
202+
203+ closePreview();
201 }
204 }
202
205
203 function test_narrow_delegate_ranges_expand() {
206 function test_narrow_delegate_ranges_expand() {
@@ -232,7 +235,7 @@ Item {
232 }
235 }
233
236
234 function test_single_category_expansion() {
237 function test_single_category_expansion() {
235− genericScopeView.scope = scopes.getScope(4);
238+ genericScopeView.scope = scopes.getScope(3);
236
239
237 tryCompareFunction(function() { return findChild(genericScopeView, "dashCategory0") != undefined; }, true);
240 tryCompareFunction(function() { return findChild(genericScopeView, "dashCategory0") != undefined; }, true);
238 var category = findChild(genericScopeView, "dashCategory0")
241 var category = findChild(genericScopeView, "dashCategory0")
@@ -329,8 +332,8 @@ Item {
329 function test_header_style_data() {
332 function test_header_style_data() {
330 return [
333 return [
331 { tag: "Default", index: 0, foreground: "grey", background: "", logo: "" },
334 { tag: "Default", index: 0, foreground: "grey", background: "", logo: "" },
332− { tag: "Foreground", index: 2, foreground: "yellow", background: "", logo: "" },
335+ { tag: "Foreground", index: 1, foreground: "yellow", background: "", logo: "" },
333− { tag: "Logo+Background", index: 3, foreground: "grey", background: "gradient:///lightgrey/grey",
336+ { tag: "Logo+Background", index: 2, foreground: "grey", background: "gradient:///lightgrey/grey",
334 logo: Qt.resolvedUrl("../Components/tst_PageHeader/logo-ubuntu-orange.svg") },
337 logo: Qt.resolvedUrl("../Components/tst_PageHeader/logo-ubuntu-orange.svg") },
335 ];
338 ];
336 }
339 }