on xenial help sign in
Merge ~aacid/unity8/seeLessStuckBottom
commite9c7da07caf14f9a39ab7134449b977d0075b5ad authorAlbert Astals <albert.astals@canonical.com> dateFri 8 Aug 2014, 09:28 (12 years ago) parent440b8d834fa939
46 files changed+2247−235
data/unity8-dash.conf+1−1 plugins/Dash/CardCreator.js+3−2 plugins/Dash/ScopeStyle.qml+3−2 plugins/Dash/listviewwithpageheader.cpp+2−2 po/hu.po+69−33 po/sl.po+67−31 po/unity8.pot+62−30 qml/Components/PageHeader.qml+4−1 qml/Components/ResponsiveGridView.qml+3−3 qml/Dash/CardCarousel.qml+2−2 qml/Dash/CardGrid.qml+14−3 qml/Dash/CardVerticalJournal.qml+2−2 qml/Dash/Dash.qml+158−12 qml/Dash/DashBackground.qml+24 qml/Dash/DashContent.qml+8−4 qml/Dash/DashRenderer.qml+5−2 qml/Dash/GenericScopeView.qml+148−47 qml/Dash/PreviewListView.qml+1−1 qml/Dash/Previews/PreviewTable.qml+80 qml/Dash/Previews/PreviewWidgetFactory.qml+1 qml/Dash/ScopesOverview.qml+523 qml/Dash/ScopesOverviewAll.qml+54 qml/Dash/ScopesOverviewFavorites.qml+73 qml/Dash/ScopesOverviewTab.qml+74 qml/Dash/graphics/apps-scope.png qml/Dash/graphics/dark_background.jpg tests/autopilot/unity8/shell/tests/test_emulators.py+1−1 tests/mocks/Unity/CMakeLists.txt+1 tests/mocks/Unity/fake_resultsmodel.cpp+2−1 tests/mocks/Unity/fake_scope.cpp+18−6 tests/mocks/Unity/fake_scope.h+6−3 tests/mocks/Unity/fake_scopes.cpp+45−6 tests/mocks/Unity/fake_scopes.h+8−1 tests/mocks/Unity/fake_scopesoverview.cpp+281 tests/mocks/Unity/fake_scopesoverview.h+104 tests/plugins/Dash/cardcreator/2.res+2−2 tests/plugins/Dash/cardcreator/5.res+8−6 tests/plugins/Dash/cardcreator/7.res+4−4 tests/plugins/Dash/tst_ScopeStyle.qml+1−1 tests/qmltests/CMakeLists.txt+1 tests/qmltests/Dash/Previews/tst_PreviewExpandable.qml+5−1 tests/qmltests/Dash/Previews/tst_PreviewTable.qml+114 tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qml+1 tests/qmltests/Dash/tst_Dash.qml+229−7 tests/qmltests/Dash/tst_DashContent.qml+19−6 tests/qmltests/Dash/tst_GenericScopeView.qml+16−12
data/unity8-dash.conf+1−1
@@ -13,7 +13,7 @@ respawn
1313pre-start script
1414 if [ -z "$UNITY_SCOPES_LIST" ]; then
1515 # 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"
1717 fi
1818
1919 initctl emit scope-ui-starting
plugins/Dash/CardCreator.js+3−2
@@ -108,6 +108,7 @@ var kOverlayLoaderCode = 'Loader { \n\
108108 sourceComponent: ShaderEffect { \n\
109109 id: overlay; \n\
110110 height: (fixedHeaderHeight > 0 ? fixedHeaderHeight : headerHeight) + units.gu(2); \n\
111+ property real luminance: 0.2126 * overlayColor.r + 0.7152 * overlayColor.g + 0.0722 * overlayColor.b; \n\
111112 property color overlayColor: cardData && cardData["overlayColor"] || "#99000000"; \n\
112113 property var source: ShaderEffectSource { \n\
113114 id: shaderSource; \n\
@@ -446,13 +447,13 @@ function cardString(template, components) {
446447 mascotCode = kMascotImageCode.arg(anchors).arg(mascotImageVisible);
447448 }
448449
449− var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey")';
450+ 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")';
450451
451452 var titleSubtitleCode = "";
452453 if (hasTitle) {
453454 var color;
454455 if (headerAsOverlay) {
455− color = '"white"';
456+ color = 'overlayLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : "grey")';
456457 } else if (hasSummary) {
457458 color = 'summary.color';
458459 } else if (hasBackground) {
plugins/Dash/ScopeStyle.qml+3−2
@@ -49,9 +49,10 @@ QtObject {
4949
5050 /*! \brief Luminance threshold for switching between fore and background color
5151
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.
5353 */
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
5556
5657 /*! \brief The lighter of foreground and background colors
5758
plugins/Dash/listviewwithpageheader.cpp+2−2
@@ -822,8 +822,8 @@ ListViewWithPageHeader::ListItem *ListViewWithPageHeader::createItem(int modelIn
822822 ListItem *nextItem = itemAtIndex(modelIndex + 1);
823823 if (nextItem) {
824824 listItem->setY(nextItem->y() - listItem->height());
825− } else if (modelIndex == 0 && m_headerItem) {
826− listItem->setY(m_headerItem->height());
825+ } else if (modelIndex == 0) {
826+ listItem->setY(m_headerItem ? m_headerItem->height() : 0);
827827 } else if (!m_visibleItems.isEmpty()) {
828828 lostItem = true;
829829 }
po/hu.po+69−33
@@ -6,11 +6,12 @@
66msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
9−"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2014-07-11 14:59+0200\n"
9+"Report-Msgid-Bugs-To: \n"
10+"POT-Creation-Date: 2014-08-05 19:54+0200\n"
1111"PO-Revision-Date: 2014-08-06 07:41+0000\n"
1212"Last-Translator: Richard Somlói <ricsipontaz@gmail.com>\n"
1313"Language-Team: Hungarian <hu@li.org>\n"
14+"Language: hu\n"
1415"MIME-Version: 1.0\n"
1516"Content-Type: text/plain; charset=UTF-8\n"
1617"Content-Transfer-Encoding: 8bit\n"
@@ -26,70 +27,86 @@ msgstr "Indító rögzítése"
2627msgid "Unpin shortcut"
2728msgstr "Indító feloldása"
2829
30+#: qml/Components/Dialogs.qml:142
31+msgid "Are you sure you would like to turn power off?"
32+msgstr ""
33+
34+#: qml/Components/Dialogs.qml:144
35+msgid "Power off"
36+msgstr ""
37+
38+#: qml/Components/Dialogs.qml:153
39+msgid "Restart"
40+msgstr ""
41+
42+#: qml/Components/Dialogs.qml:162
43+msgid "Cancel"
44+msgstr ""
45+
2946#: qml/Components/EdgeDemoOverlay.qml:151
3047msgid "Skip intro"
3148msgstr "A bemutató kihagyása"
3249
33−#: qml/Components/EdgeDemo.qml:113
50+#: qml/Components/EdgeDemo.qml:112
3451msgid "Right edge"
3552msgstr "Jobb szél"
3653
37−#: qml/Components/EdgeDemo.qml:114
54+#: qml/Components/EdgeDemo.qml:113
3855msgid "Try swiping from the right edge to unlock the phone"
3956msgstr "A telefon feloldásához csúsztassa ujját a jobb szélről"
4057
41−#: qml/Components/EdgeDemo.qml:145
58+#: qml/Components/EdgeDemo.qml:144
4259msgid "Top edge"
4360msgstr "Felső szél"
4461
45−#: qml/Components/EdgeDemo.qml:146
62+#: qml/Components/EdgeDemo.qml:145
4663msgid "Try swiping from the top edge to access the indicators"
4764msgstr "Az állapotmenük eléréséhez csúsztassa ujját a felső szélről"
4865
49−#: qml/Components/EdgeDemo.qml:171
66+#: qml/Components/EdgeDemo.qml:170
5067msgid "Close"
5168msgstr "Bezárás"
5269
53−#: qml/Components/EdgeDemo.qml:172
70+#: qml/Components/EdgeDemo.qml:171
5471msgid "Swipe up again to close the settings screen"
5572msgstr "A beállítások bezárásához csúsztassa ujját újra felfelé"
5673
57−#: qml/Components/EdgeDemo.qml:199
74+#: qml/Components/EdgeDemo.qml:198
5875msgid "Left edge"
5976msgstr "Bal szél"
6077
61−#: qml/Components/EdgeDemo.qml:200
78+#: qml/Components/EdgeDemo.qml:199
6279msgid "Swipe from the left to reveal the launcher for quick access to apps"
6380msgstr ""
6481"Csúsztassa ujját a bal szélről az indítón található alkalmazások gyors "
6582"eléréséhez"
6683
67−#: qml/Components/EdgeDemo.qml:227
84+#: qml/Components/EdgeDemo.qml:226
6885msgid "Well done"
6986msgstr "Rendben"
7087
71−#: qml/Components/EdgeDemo.qml:228
88+#: qml/Components/EdgeDemo.qml:227
7289msgid ""
7390"You have now mastered the edge gestures and can start using the "
7491"phone<br><br>Tap on the screen to start"
7592msgstr ""
76−"Elsajátította a gesztusokat, és megkezdheti a telefon "
77−"használatát.<br><br>Koppintson a képernyőre a kezdéshez."
93+"Elsajátította a gesztusokat, és megkezdheti a telefon használatát."
94+"<br><br>Koppintson a képernyőre a kezdéshez."
7895
79−#: qml/Components/Lockscreen.qml:214
96+#: qml/Components/Lockscreen.qml:220
8097msgid "Emergency Call"
8198msgstr "Vészhívás"
8299
83−#: qml/Components/Lockscreen.qml:231
100+#: qml/Components/Lockscreen.qml:243
84101msgid "OK"
85102msgstr "Ok"
86103
87−#: qml/Components/PassphraseLockscreen.qml:60
104+#: qml/Components/PassphraseLockscreen.qml:62
88105#, qt-format
89106msgid "Hello %1"
90107msgstr "Üdv %1"
91108
92−#: qml/Components/PassphraseLockscreen.qml:60
109+#: qml/Components/PassphraseLockscreen.qml:62
93110msgid "Hello"
94111msgstr "Üdvözöljük"
95112
@@ -105,15 +122,11 @@ msgstr "KÉSZ"
105122msgid "See more"
106123msgstr "Több"
107124
108−#: qml/Components/SeeMore.qml:58 qml/Dash/GenericScopeView.qml:330
125+#: qml/Components/SeeMore.qml:58 qml/Dash/GenericScopeView.qml:354
109126msgid "See less"
110127msgstr "Kevesebb"
111128
112−#: qml/Dash/DashApps.qml:37
113−msgid "Recent"
114−msgstr "Legutóbbi"
115−
116−#: qml/Dash/GenericScopeView.qml:330
129+#: qml/Dash/GenericScopeView.qml:354
117130msgid "See all"
118131msgstr "Összes megtekintése"
119132
@@ -137,6 +150,26 @@ msgstr "Értékelés hozzáadása"
137150msgid "Send"
138151msgstr "Küldés"
139152
153+#: qml/Dash/ScopesOverview.qml:187
154+msgid "Manage Dash"
155+msgstr ""
156+
157+#: qml/Dash/ScopesOverview.qml:392
158+msgid "Done"
159+msgstr ""
160+
161+#: qml/Dash/ScopesOverview.qml:418
162+msgid "Store"
163+msgstr ""
164+
165+#: qml/Dash/ScopesOverviewTab.qml:36
166+msgid "Favorites"
167+msgstr ""
168+
169+#: qml/Dash/ScopesOverviewTab.qml:54
170+msgid "All"
171+msgstr ""
172+
140173#: qml/Greeter/Greeter.qml:157
141174msgid "Swipe to unlock"
142175msgstr "Csúsztasson a feloldáshoz"
@@ -161,11 +194,11 @@ msgstr "Most beszéljen…"
161194msgid "Speaking..."
162195msgstr "Beszéd…"
163196
164−#: qml/Notifications/NotificationMenuItemFactory.qml:91
197+#: qml/Notifications/NotificationMenuItemFactory.qml:97
165198msgid "Show password"
166199msgstr "Jelszó megjelenítése"
167200
168−#: qml/Notifications/NotificationMenuItemFactory.qml:103
201+#: qml/Notifications/NotificationMenuItemFactory.qml:112
169202msgid "Please enter SIM PIN"
170203msgstr "Adja meg a SIM PIN kódját"
171204
@@ -177,27 +210,27 @@ msgstr "Érintse meg, hogy visszatérjen a híváshoz..."
177210msgid "Conference"
178211msgstr "Konferencia"
179212
180−#: qml/Panel/Indicators/MenuItemFactory.qml:583
213+#: qml/Panel/Indicators/MenuItemFactory.qml:577
181214msgid "In queue…"
182215msgstr "Sorban…"
183216
184−#: qml/Panel/Indicators/MenuItemFactory.qml:587
217+#: qml/Panel/Indicators/MenuItemFactory.qml:581
185218msgid "Downloading"
186219msgstr "Letöltés"
187220
188−#: qml/Panel/Indicators/MenuItemFactory.qml:589
221+#: qml/Panel/Indicators/MenuItemFactory.qml:583
189222msgid "Paused, tap to resume"
190223msgstr "Szüneteltetve, koppintson a folytatáshoz"
191224
192−#: qml/Panel/Indicators/MenuItemFactory.qml:591
225+#: qml/Panel/Indicators/MenuItemFactory.qml:585
193226msgid "Canceled"
194227msgstr "Megszakítva"
195228
196−#: qml/Panel/Indicators/MenuItemFactory.qml:593
229+#: qml/Panel/Indicators/MenuItemFactory.qml:587
197230msgid "Finished"
198231msgstr "Befejeződött"
199232
200−#: qml/Panel/Indicators/MenuItemFactory.qml:595
233+#: qml/Panel/Indicators/MenuItemFactory.qml:589
201234msgid "Failed, tap to retry"
202235msgstr "Sikertelen, koppintson az újrapróbáláshoz"
203236
@@ -205,7 +238,10 @@ msgstr "Sikertelen, koppintson az újrapróbáláshoz"
205238msgid "Search"
206239msgstr "Keresés"
207240
208−#: qml/Shell.qml:359
241+#: qml/Shell.qml:256
209242#, qt-format
210243msgid "Please enter %1"
211244msgstr "Adja meg a következőt: %1"
245+
246+#~ msgid "Recent"
247+#~ msgstr "Legutóbbi"
po/sl.po+67−31
@@ -6,11 +6,12 @@
66msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
9−"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2014-07-11 14:59+0200\n"
9+"Report-Msgid-Bugs-To: \n"
10+"POT-Creation-Date: 2014-08-05 19:54+0200\n"
1111"PO-Revision-Date: 2014-08-06 06:11+0000\n"
1212"Last-Translator: Damir Jerovšek <Unknown>\n"
1313"Language-Team: Slovenian <sl@li.org>\n"
14+"Language: sl\n"
1415"MIME-Version: 1.0\n"
1516"Content-Type: text/plain; charset=UTF-8\n"
1617"Content-Transfer-Encoding: 8bit\n"
@@ -26,49 +27,65 @@ msgstr "Pripni bližnjico"
2627msgid "Unpin shortcut"
2728msgstr "Odpni bližnjico"
2829
30+#: qml/Components/Dialogs.qml:142
31+msgid "Are you sure you would like to turn power off?"
32+msgstr ""
33+
34+#: qml/Components/Dialogs.qml:144
35+msgid "Power off"
36+msgstr ""
37+
38+#: qml/Components/Dialogs.qml:153
39+msgid "Restart"
40+msgstr ""
41+
42+#: qml/Components/Dialogs.qml:162
43+msgid "Cancel"
44+msgstr ""
45+
2946#: qml/Components/EdgeDemoOverlay.qml:151
3047msgid "Skip intro"
3148msgstr "Preskoči uvod"
3249
33−#: qml/Components/EdgeDemo.qml:113
50+#: qml/Components/EdgeDemo.qml:112
3451msgid "Right edge"
3552msgstr "Desni rob"
3653
37−#: qml/Components/EdgeDemo.qml:114
54+#: qml/Components/EdgeDemo.qml:113
3855msgid "Try swiping from the right edge to unlock the phone"
3956msgstr "Poskusite povleči od desnega roba za odklep telefona"
4057
41−#: qml/Components/EdgeDemo.qml:145
58+#: qml/Components/EdgeDemo.qml:144
4259msgid "Top edge"
4360msgstr "Zgornji rob"
4461
45−#: qml/Components/EdgeDemo.qml:146
62+#: qml/Components/EdgeDemo.qml:145
4663msgid "Try swiping from the top edge to access the indicators"
4764msgstr "Poskusite povleči od zgornjega roba za dostop do kazalnikov"
4865
49−#: qml/Components/EdgeDemo.qml:171
66+#: qml/Components/EdgeDemo.qml:170
5067msgid "Close"
5168msgstr "Zapri"
5269
53−#: qml/Components/EdgeDemo.qml:172
70+#: qml/Components/EdgeDemo.qml:171
5471msgid "Swipe up again to close the settings screen"
5572msgstr "Povlecite ponovno navzgor za zaprtje zaslona z nastavitvami"
5673
57−#: qml/Components/EdgeDemo.qml:199
74+#: qml/Components/EdgeDemo.qml:198
5875msgid "Left edge"
5976msgstr "Levi rob"
6077
61−#: qml/Components/EdgeDemo.qml:200
78+#: qml/Components/EdgeDemo.qml:199
6279msgid "Swipe from the left to reveal the launcher for quick access to apps"
6380msgstr ""
6481"Poskusite povleči od leve za razkritje zaganjalnika za hiter dostop do "
6582"programov"
6683
67−#: qml/Components/EdgeDemo.qml:227
84+#: qml/Components/EdgeDemo.qml:226
6885msgid "Well done"
6986msgstr "Odlično"
7087
71−#: qml/Components/EdgeDemo.qml:228
88+#: qml/Components/EdgeDemo.qml:227
7289msgid ""
7390"You have now mastered the edge gestures and can start using the "
7491"phone<br><br>Tap on the screen to start"
@@ -76,20 +93,20 @@ msgstr ""
7693"Sedaj, ko ste osvojili poteze robov, lahko začnete uporabljati "
7794"telefon<br><br>Tapnite na zaslon za začetek"
7895
79−#: qml/Components/Lockscreen.qml:214
96+#: qml/Components/Lockscreen.qml:220
8097msgid "Emergency Call"
8198msgstr "Klic v sili"
8299
83−#: qml/Components/Lockscreen.qml:231
100+#: qml/Components/Lockscreen.qml:243
84101msgid "OK"
85102msgstr "V REDU"
86103
87−#: qml/Components/PassphraseLockscreen.qml:60
104+#: qml/Components/PassphraseLockscreen.qml:62
88105#, qt-format
89106msgid "Hello %1"
90107msgstr "Pozdravljeni, %1"
91108
92−#: qml/Components/PassphraseLockscreen.qml:60
109+#: qml/Components/PassphraseLockscreen.qml:62
93110msgid "Hello"
94111msgstr "Pozdravljeni"
95112
@@ -105,15 +122,11 @@ msgstr "KONČANO"
105122msgid "See more"
106123msgstr "Pokaži več"
107124
108−#: qml/Components/SeeMore.qml:58 qml/Dash/GenericScopeView.qml:330
125+#: qml/Components/SeeMore.qml:58 qml/Dash/GenericScopeView.qml:354
109126msgid "See less"
110127msgstr "Pokaži manj"
111128
112−#: qml/Dash/DashApps.qml:37
113−msgid "Recent"
114−msgstr "Nedavno"
115−
116−#: qml/Dash/GenericScopeView.qml:330
129+#: qml/Dash/GenericScopeView.qml:354
117130msgid "See all"
118131msgstr "Ogled vsega"
119132
@@ -137,6 +150,26 @@ msgstr "Dodaj oceno"
137150msgid "Send"
138151msgstr "Pošlji"
139152
153+#: qml/Dash/ScopesOverview.qml:187
154+msgid "Manage Dash"
155+msgstr ""
156+
157+#: qml/Dash/ScopesOverview.qml:392
158+msgid "Done"
159+msgstr ""
160+
161+#: qml/Dash/ScopesOverview.qml:418
162+msgid "Store"
163+msgstr ""
164+
165+#: qml/Dash/ScopesOverviewTab.qml:36
166+msgid "Favorites"
167+msgstr ""
168+
169+#: qml/Dash/ScopesOverviewTab.qml:54
170+msgid "All"
171+msgstr ""
172+
140173#: qml/Greeter/Greeter.qml:157
141174msgid "Swipe to unlock"
142175msgstr "Podrsajte za odklenitev"
@@ -161,11 +194,11 @@ msgstr "Govorite zdaj ..."
161194msgid "Speaking..."
162195msgstr "Govorjenje ..."
163196
164−#: qml/Notifications/NotificationMenuItemFactory.qml:91
197+#: qml/Notifications/NotificationMenuItemFactory.qml:97
165198msgid "Show password"
166199msgstr "Pokaži geslo"
167200
168−#: qml/Notifications/NotificationMenuItemFactory.qml:103
201+#: qml/Notifications/NotificationMenuItemFactory.qml:112
169202msgid "Please enter SIM PIN"
170203msgstr "Vnesite PIN kartice SIM"
171204
@@ -177,27 +210,27 @@ msgstr "Tapnite za vrnitev v klic ..."
177210msgid "Conference"
178211msgstr "Konferenca"
179212
180−#: qml/Panel/Indicators/MenuItemFactory.qml:583
213+#: qml/Panel/Indicators/MenuItemFactory.qml:577
181214msgid "In queue…"
182215msgstr "V vrsti ..."
183216
184−#: qml/Panel/Indicators/MenuItemFactory.qml:587
217+#: qml/Panel/Indicators/MenuItemFactory.qml:581
185218msgid "Downloading"
186219msgstr "Prejemanje"
187220
188−#: qml/Panel/Indicators/MenuItemFactory.qml:589
221+#: qml/Panel/Indicators/MenuItemFactory.qml:583
189222msgid "Paused, tap to resume"
190223msgstr "Premor, tapnite za nadaljevanje"
191224
192−#: qml/Panel/Indicators/MenuItemFactory.qml:591
225+#: qml/Panel/Indicators/MenuItemFactory.qml:585
193226msgid "Canceled"
194227msgstr "Preklicano"
195228
196−#: qml/Panel/Indicators/MenuItemFactory.qml:593
229+#: qml/Panel/Indicators/MenuItemFactory.qml:587
197230msgid "Finished"
198231msgstr "Končano"
199232
200−#: qml/Panel/Indicators/MenuItemFactory.qml:595
233+#: qml/Panel/Indicators/MenuItemFactory.qml:589
201234msgid "Failed, tap to retry"
202235msgstr "Spodletelo, tapnite za ponovni poskus"
203236
@@ -205,7 +238,10 @@ msgstr "Spodletelo, tapnite za ponovni poskus"
205238msgid "Search"
206239msgstr "Iskanje"
207240
208−#: qml/Shell.qml:359
241+#: qml/Shell.qml:256
209242#, qt-format
210243msgid "Please enter %1"
211244msgstr "Vnesite %1"
245+
246+#~ msgid "Recent"
247+#~ msgstr "Nedavno"
po/unity8.pot+62−30
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2014-07-11 14:59+0200\n"
11+"POT-Creation-Date: 2014-08-05 19:54+0200\n"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -26,66 +26,82 @@ msgstr ""
2626msgid "Unpin shortcut"
2727msgstr ""
2828
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+
2945#: qml/Components/EdgeDemoOverlay.qml:151
3046msgid "Skip intro"
3147msgstr ""
3248
33−#: qml/Components/EdgeDemo.qml:113
49+#: qml/Components/EdgeDemo.qml:112
3450msgid "Right edge"
3551msgstr ""
3652
37−#: qml/Components/EdgeDemo.qml:114
53+#: qml/Components/EdgeDemo.qml:113
3854msgid "Try swiping from the right edge to unlock the phone"
3955msgstr ""
4056
41−#: qml/Components/EdgeDemo.qml:145
57+#: qml/Components/EdgeDemo.qml:144
4258msgid "Top edge"
4359msgstr ""
4460
45−#: qml/Components/EdgeDemo.qml:146
61+#: qml/Components/EdgeDemo.qml:145
4662msgid "Try swiping from the top edge to access the indicators"
4763msgstr ""
4864
49−#: qml/Components/EdgeDemo.qml:171
65+#: qml/Components/EdgeDemo.qml:170
5066msgid "Close"
5167msgstr ""
5268
53−#: qml/Components/EdgeDemo.qml:172
69+#: qml/Components/EdgeDemo.qml:171
5470msgid "Swipe up again to close the settings screen"
5571msgstr ""
5672
57−#: qml/Components/EdgeDemo.qml:199
73+#: qml/Components/EdgeDemo.qml:198
5874msgid "Left edge"
5975msgstr ""
6076
61−#: qml/Components/EdgeDemo.qml:200
77+#: qml/Components/EdgeDemo.qml:199
6278msgid "Swipe from the left to reveal the launcher for quick access to apps"
6379msgstr ""
6480
65−#: qml/Components/EdgeDemo.qml:227
81+#: qml/Components/EdgeDemo.qml:226
6682msgid "Well done"
6783msgstr ""
6884
69−#: qml/Components/EdgeDemo.qml:228
85+#: qml/Components/EdgeDemo.qml:227
7086msgid ""
7187"You have now mastered the edge gestures and can start using the "
7288"phone<br><br>Tap on the screen to start"
7389msgstr ""
7490
75−#: qml/Components/Lockscreen.qml:214
91+#: qml/Components/Lockscreen.qml:220
7692msgid "Emergency Call"
7793msgstr ""
7894
79−#: qml/Components/Lockscreen.qml:231
95+#: qml/Components/Lockscreen.qml:243
8096msgid "OK"
8197msgstr ""
8298
83−#: qml/Components/PassphraseLockscreen.qml:60
99+#: qml/Components/PassphraseLockscreen.qml:62
84100#, qt-format
85101msgid "Hello %1"
86102msgstr ""
87103
88−#: qml/Components/PassphraseLockscreen.qml:60
104+#: qml/Components/PassphraseLockscreen.qml:62
89105msgid "Hello"
90106msgstr ""
91107
@@ -101,15 +117,11 @@ msgstr ""
101117msgid "See more"
102118msgstr ""
103119
104−#: qml/Components/SeeMore.qml:58 qml/Dash/GenericScopeView.qml:330
120+#: qml/Components/SeeMore.qml:58 qml/Dash/GenericScopeView.qml:354
105121msgid "See less"
106122msgstr ""
107123
108−#: qml/Dash/DashApps.qml:37
109−msgid "Recent"
110−msgstr ""
111−
112−#: qml/Dash/GenericScopeView.qml:330
124+#: qml/Dash/GenericScopeView.qml:354
113125msgid "See all"
114126msgstr ""
115127
@@ -133,6 +145,26 @@ msgstr ""
133145msgid "Send"
134146msgstr ""
135147
148+#: qml/Dash/ScopesOverview.qml:187
149+msgid "Manage Dash"
150+msgstr ""
151+
152+#: qml/Dash/ScopesOverview.qml:392
153+msgid "Done"
154+msgstr ""
155+
156+#: qml/Dash/ScopesOverview.qml:418
157+msgid "Store"
158+msgstr ""
159+
160+#: qml/Dash/ScopesOverviewTab.qml:36
161+msgid "Favorites"
162+msgstr ""
163+
164+#: qml/Dash/ScopesOverviewTab.qml:54
165+msgid "All"
166+msgstr ""
167+
136168#: qml/Greeter/Greeter.qml:157
137169msgid "Swipe to unlock"
138170msgstr ""
@@ -157,11 +189,11 @@ msgstr ""
157189msgid "Speaking..."
158190msgstr ""
159191
160−#: qml/Notifications/NotificationMenuItemFactory.qml:91
192+#: qml/Notifications/NotificationMenuItemFactory.qml:97
161193msgid "Show password"
162194msgstr ""
163195
164−#: qml/Notifications/NotificationMenuItemFactory.qml:103
196+#: qml/Notifications/NotificationMenuItemFactory.qml:112
165197msgid "Please enter SIM PIN"
166198msgstr ""
167199
@@ -173,27 +205,27 @@ msgstr ""
173205msgid "Conference"
174206msgstr ""
175207
176−#: qml/Panel/Indicators/MenuItemFactory.qml:583
208+#: qml/Panel/Indicators/MenuItemFactory.qml:577
177209msgid "In queue…"
178210msgstr ""
179211
180−#: qml/Panel/Indicators/MenuItemFactory.qml:587
212+#: qml/Panel/Indicators/MenuItemFactory.qml:581
181213msgid "Downloading"
182214msgstr ""
183215
184−#: qml/Panel/Indicators/MenuItemFactory.qml:589
216+#: qml/Panel/Indicators/MenuItemFactory.qml:583
185217msgid "Paused, tap to resume"
186218msgstr ""
187219
188−#: qml/Panel/Indicators/MenuItemFactory.qml:591
220+#: qml/Panel/Indicators/MenuItemFactory.qml:585
189221msgid "Canceled"
190222msgstr ""
191223
192−#: qml/Panel/Indicators/MenuItemFactory.qml:593
224+#: qml/Panel/Indicators/MenuItemFactory.qml:587
193225msgid "Finished"
194226msgstr ""
195227
196−#: qml/Panel/Indicators/MenuItemFactory.qml:595
228+#: qml/Panel/Indicators/MenuItemFactory.qml:589
197229msgid "Failed, tap to retry"
198230msgstr ""
199231
@@ -201,7 +233,7 @@ msgstr ""
201233msgid "Search"
202234msgstr ""
203235
204−#: qml/Shell.qml:359
236+#: qml/Shell.qml:256
205237#, qt-format
206238msgid "Please enter %1"
207239msgstr ""
qml/Components/PageHeader.qml+4−1
@@ -24,7 +24,7 @@ import "SearchHistoryModel"
2424Item {
2525 id: root
2626 objectName: "pageHeader"
27− implicitHeight: headerContainer.height + units.gu(2) + bottomContainer.height
27+ implicitHeight: headerContainer.height + bottomContainer.height + (showSignatureLine ? units.gu(2) : 0)
2828
2929 property bool showBackButton: false
3030 property string title
@@ -32,6 +32,8 @@ Item {
3232 property bool searchEntryEnabled: false
3333 property ListModel searchHistory: SearchHistoryModel
3434 property alias searchQuery: searchTextField.text
35+ property alias searchHint: searchTextField.placeholderText
36+ property alias showSignatureLine: bottomBorder.visible
3537
3638 property alias bottomItem: bottomContainer.children
3739 property int paginationCount: 0
@@ -251,6 +253,7 @@ Item {
251253
252254 actions: [
253255 Action {
256+ objectName: "search"
254257 iconName: "search"
255258 visible: root.searchEntryEnabled
256259 onTriggered: {
qml/Components/ResponsiveGridView.qml+3−3
@@ -34,7 +34,7 @@ Item {
3434 readonly property int cellWidth: gridView.cellWidth
3535 readonly property int cellHeight: gridView.cellHeight
3636 readonly property int totalContentHeight: {
37− return contentHeightForRows(Math.ceil(gridView.model.count / columns))
37+ return contentHeightForRows(Math.ceil(gridView.model.count / columns), cellHeight)
3838 }
3939 property alias interactive: gridView.interactive
4040 readonly property alias flicking: gridView.flicking
@@ -47,8 +47,8 @@ Item {
4747 property alias cacheBuffer: gridView.cacheBuffer
4848 readonly property alias currentItem: gridView.currentItem
4949
50− function contentHeightForRows(rows) {
51− return rows * cellHeight;
50+ function contentHeightForRows(rows, height) {
51+ return rows * height
5252 }
5353
5454 GridView {
qml/Dash/CardCarousel.qml+2−2
@@ -48,8 +48,8 @@ DashRenderer {
4848
4949 objectName: "carouselDelegate" + index
5050
51− function clicked() { cardCarousel.clicked(index, model.result) }
52− function pressAndHold() { cardCarousel.pressAndHold(index, model.result) }
51+ function clicked() { cardCarousel.clicked(index, model.result, loader.item, model) }
52+ function pressAndHold() { cardCarousel.pressAndHold(index, model) }
5353
5454 sourceComponent: cardTool.cardComponent
5555 onLoaded: {
qml/Dash/CardGrid.qml+14−3
@@ -26,10 +26,20 @@ DashRenderer {
2626 }
2727
2828 expandedHeight: grid.totalContentHeight
29− collapsedHeight: Math.min(grid.contentHeightForRows(collapsedRows), expandedHeight)
29+ collapsedHeight: Math.min(grid.contentHeightForRows(collapsedRows, grid.cellHeight), expandedHeight)
3030 collapsedItemCount: collapsedRows * grid.columns
3131 originY: grid.originY
3232
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+
3343 ResponsiveGridView {
3444 id: grid
3545 anchors.fill: parent
@@ -53,6 +63,7 @@ DashRenderer {
5363 item.objectName = "delegate" + index;
5464 item.width = Qt.binding(function() { return cardTool.cardWidth; });
5565 item.height = Qt.binding(function() { return cardTool.cardHeight; });
66+ item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight; });
5667 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
5768 item.cardData = Qt.binding(function() { return model; });
5869 item.template = Qt.binding(function() { return cardTool.template; });
@@ -62,8 +73,8 @@ DashRenderer {
6273 }
6374 Connections {
6475 target: loader.item
65− onClicked: root.clicked(index, result)
66− onPressAndHold: root.pressAndHold(index)
76+ onClicked: root.clicked(index, result, loader.item, model)
77+ onPressAndHold: root.pressAndHold(index, model)
6778 }
6879 }
6980 }
qml/Dash/CardVerticalJournal.qml+2−2
@@ -61,8 +61,8 @@ DashRenderer {
6161 }
6262 Connections {
6363 target: loader.item
64− onClicked: root.clicked(index, result)
65− onPressAndHold: root.pressAndHold(index)
64+ onClicked: root.clicked(index, result, loader.item, model)
65+ onPressAndHold: root.pressAndHold(index, model)
6666 }
6767 }
6868 }
qml/Dash/Dash.qml+158−12
@@ -16,6 +16,7 @@
1616
1717import QtQuick 2.2
1818import Ubuntu.Components 0.1
19+import Ubuntu.Gestures 0.1
1920import Unity 0.2
2021import Utils 0.1
2122import Unity.DashCommunicator 0.1
@@ -73,26 +74,104 @@ Showable {
7374 id: scopes
7475 }
7576
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"
7792 anchors.fill: parent
78− source: parent.width > parent.height ? "graphics/paper_landscape.png" : "graphics/paper_portrait.png"
79− fillMode: Image.PreserveAspectCrop
80− horizontalAlignment: Image.AlignRight
81− verticalAlignment: Image.AlignTop
93+ scope: scopes.overviewScope
94+ progress: overviewController.progress
95+ scopeScale: scopeItem.scope ? 0.4 : (1 - overviewController.progress * 0.6)
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
154+ live: false
82155 }
83156
84157 DashContent {
85158 id: dashContent
159+
160+ property var scopeThatOpenedScope: null
161+
86162 objectName: "dashContent"
87− width: parent.width
88− height: parent.height
163+ width: dash.width
164+ height: dash.height
89165 scopes: scopes
90− visible: x != -width
166+ visible: !scopesOverview.showingNonFavoriteScope && x != -width
91167 onGotoScope: {
92168 dash.setCurrentScope(scopeId, true, false);
93169 }
94170 onOpenScope: {
171+ scopeThatOpenedScope = currentScope;
95172 scopeItem.scope = scope;
173+ scopesOverview.currentTab = 1;
174+ scopesOverview.ensureAllScopeVisible(scope.id);
96175 x = -width;
97176 }
98177 onScopeLoaded: {
@@ -102,25 +181,63 @@ Showable {
102181 }
103182 }
104183 scale: dash.contentScale
105− clip: scale != 1.0 || scopeItem.visible
184+ clip: scale != 1.0 || scopeItem.visible || overviewController.progress != 0
106185 Behavior on x {
107186 UbuntuNumberAnimation {
187+ duration: overviewController.progress != 0 ? 0 : UbuntuAnimation.FastDuration
108188 onRunningChanged: {
109189 if (!running && dashContent.x == 0) {
110− dashContent.closeScope(scopeItem.scope);
190+ dashContent.scopeThatOpenedScope.closeScope(scopeItem.scope);
111191 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+ }
112196 }
113197 }
114198 }
115199 }
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
116212 }
117213
118214 GenericScopeView {
119215 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
121236 width: parent.width
122237 height: parent.height
123− scale: dash.contentScale
238+ scale: dash.contentScale * overviewProgressScale
239+ enabled: opacity == 1
240+ opacity: 1 - overviewController.progress
124241 clip: scale != 1.0
125242 visible: scope != null
126243 hasBackAction: true
@@ -207,4 +324,33 @@ Showable {
207324 }
208325 }
209326
327+ EdgeDragArea {
328+ id: overviewDragHandle
329+ objectName: "overviewDragHandle"
330+ z: 1
331+ direction: Direction.Upwards
332+ enabled: !dashContent.previewShown &&
333+ dashContent.currentScope &&
334+ dashContent.currentScope.searchQuery == "" &&
335+ (overviewController.progress == 0 || dragging)
336+
337+ readonly property real fullMovement: units.gu(20)
338+
339+ anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
340+ height: units.gu(2)
341+
342+ onSceneDistanceChanged: {
343+ if (overviewController.enableAnimation) {
344+ dashContentCache.scheduleUpdate();
345+ }
346+ overviewController.enableAnimation = false;
347+ overviewController.progress = Math.max(0, Math.min(1, sceneDistance / fullMovement));
348+ }
349+
350+ onDraggingChanged: {
351+ overviewController.enableAnimation = true;
352+ overviewController.progress = (overviewController.progress > 0.7) ? 1 : 0;
353+ }
354+ }
355+
210356}
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+8−4
@@ -25,6 +25,10 @@ Item {
2525
2626 property alias scopes: dashContentList.model
2727 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
2832 readonly property bool processing: dashContentList.currentItem && dashContentList.currentItem.item
2933 && dashContentList.currentItem.item.processing || false
3034
@@ -77,15 +81,15 @@ Item {
7781 }
7882 }
7983
80− function closeScope(scope) {
81− dashContentList.currentItem.theScope.closeScope(scope)
82− }
83−
8484 Item {
8585 id: dashContentListHolder
8686
8787 anchors.fill: parent
8888
89+ DashBackground {
90+ anchors.fill: parent
91+ }
92+
8993 ListView {
9094 id: dashContentList
9195 objectName: "dashContentList"
qml/Dash/DashRenderer.qml+5−2
@@ -41,9 +41,12 @@ Item {
4141 /// Emitted when the user clicked on an item
4242 /// @param index is the index of the clicked item
4343 /// @param result result model of the cliked item, used for activation
44− signal clicked(int index, var result)
44+ /// @param item item that has been clicked
45+ /// @param itemModel model of the item
46+ signal clicked(int index, var result, var item, var itemModel)
4547
4648 /// Emitted when the user pressed and held on an item
4749 /// @param index is the index of the held item
48− signal pressAndHold(int index)
50+ /// @param itemModel model of the item
51+ signal pressAndHold(int index, var itemModel)
4952}
qml/Dash/GenericScopeView.qml+148−47
@@ -25,7 +25,7 @@ import "../Components/ListItems" as ListItems
2525FocusScope {
2626 id: scopeView
2727
28− readonly property alias navigationShown: dashNavigation.showList
28+ readonly property bool navigationShown: pageHeaderLoader.item ? pageHeaderLoader.item.bottomItem[0].showList : false
2929 property var scope: null
3030 property SortFilterProxyModel categories: categoryFilter
3131 property bool isCurrent: false
@@ -33,8 +33,10 @@ FocusScope {
3333 property bool hasBackAction: false
3434 property bool enableHeightBehaviorOnNextCreation: false
3535 property var categoryView: categoryView
36− property alias paginationCount: pageHeader.paginationCount
37− property alias paginationIndex: pageHeader.paginationIndex
36+ property bool showPageHeader: true
37+ readonly property alias previewShown: previewListView.open
38+ property int paginationCount: 0
39+ property int paginationIndex: 0
3840
3941 property var scopeStyle: ScopeStyle {
4042 style: scope ? scope.customizations : {}
@@ -44,6 +46,11 @@ FocusScope {
4446
4547 signal backClicked()
4648
49+ onScopeChanged: {
50+ floatingSeeLess.companionTo = null;
51+ floatingSeeLess.companionBase = null;
52+ }
53+
4754 function positionAtBeginning() {
4855 categoryView.positionAtBeginning()
4956 }
@@ -56,6 +63,36 @@ FocusScope {
5663 previewListView.open = false;
5764 }
5865
66+ function itemClicked(index, result, item, itemModel, resultsModel, limitedCategoryItemCount) {
67+ if (itemModel.uri.indexOf("scope://") === 0 || scope.id === "clickscope") {
68+ // TODO Technically it is possible that calling activate() will make the scope emit
69+ // previewRequested so that we show a preview but there's no scope that does that yet
70+ // so it's not implemented
71+ scope.activate(result)
72+ } else {
73+ openPreview(index, resultsModel, limitedCategoryItemCount);
74+ }
75+ }
76+
77+ function itemPressedAndHeld(index, itemModel, resultsModel, limitedCategoryItemCount) {
78+ if (itemModel.uri.indexOf("scope://") !== 0) {
79+ openPreview(index, resultsModel, limitedCategoryItemCount);
80+ }
81+ }
82+
83+ function openPreview(index, resultsModel, limitedCategoryItemCount) {
84+ if (limitedCategoryItemCount > 0) {
85+ previewLimitModel.model = resultsModel;
86+ previewLimitModel.limit = limitedCategoryItemCount;
87+ previewListView.model = previewLimitModel;
88+ } else {
89+ previewListView.model = resultsModel;
90+ }
91+ previewListView.currentIndex = -1;
92+ previewListView.currentIndex = index;
93+ previewListView.open = true;
94+ }
95+
5996 Binding {
6097 target: scope
6198 property: "isActive"
@@ -72,22 +109,24 @@ FocusScope {
72109 }
73110
74111 onIsCurrentChanged: {
75− pageHeader.resetSearch();
112+ if (showPageHeader) {
113+ pageHeaderLoader.item.resetSearch();
114+ }
76115 previewListView.open = false;
77116 }
78117
79118 Binding {
80119 target: scopeView.scope
81120 property: "searchQuery"
82− value: pageHeader.searchQuery
83− when: isCurrent
121+ value: pageHeaderLoader.item ? pageHeaderLoader.item.searchQuery : ""
122+ when: isCurrent && showPageHeader
84123 }
85124
86125 Binding {
87− target: pageHeader
126+ target: pageHeaderLoader.item
88127 property: "searchQuery"
89128 value: scopeView.scope ? scopeView.scope.searchQuery : ""
90− when: isCurrent
129+ when: isCurrent && showPageHeader
91130 }
92131
93132 Connections {
@@ -109,12 +148,13 @@ FocusScope {
109148 x: previewListView.open ? -width : 0
110149 Behavior on x { UbuntuNumberAnimation { } }
111150 width: parent.width
112− height: parent.height
151+ height: floatingSeeLess.visible ? parent.height - floatingSeeLess.height + floatingSeeLess.yOffset : parent.height
152+ clip: height != parent.height
113153
114154 model: scopeView.categories
115155 forceNoClip: previewListView.open
116156 pixelAligned: true
117− interactive: !dashNavigation.showList
157+ interactive: !navigationShown
118158
119159 property string expandedCategoryId: ""
120160
@@ -223,28 +263,19 @@ FocusScope {
223263 Connections {
224264 target: rendererLoader.item
225265 onClicked: {
226− if (scopeView.scope.id === "scopes" || scopeView.scope.id == "clickscope") {
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− }
266+ scopeView.itemClicked(index, result, item, itemModel, target.model, categoryItemCount());
234267 }
235− onPressAndHold: openPreview(index)
236268
237− function openPreview(index) {
269+ onPressAndHold: {
270+ scopeView.itemPressedAndHeld(index, itemModel, target.model, categoryItemCount());
271+ }
272+
273+ function categoryItemCount() {
274+ var categoryItemCount = -1;
238275 if (!rendererLoader.expanded && !seeAllLabel.visible && target.collapsedItemCount > 0) {
239− previewLimitModel.model = target.model;
240− previewLimitModel.limit = target.collapsedItemCount;
241− previewListView.model = previewLimitModel;
242− } else {
243− previewListView.model = target.model;
276+ categoryItemCount = target.collapsedItemCount;
244277 }
245− previewListView.currentIndex = -1;
246− previewListView.currentIndex = index;
247− previewListView.open = true;
278+ return categoryItemCount;
248279 }
249280 }
250281 Connections {
@@ -316,11 +347,13 @@ FocusScope {
316347 left: parent.left
317348 right: parent.right
318349 }
319− height: seeAllLabel.visible ? seeAllLabel.font.pixelSize + units.gu(6) : 0
350+ height: seeAllLabel.visible ? seeAllLabel.font.pixelSize + units.gu(4) : 0
320351
321352 onClicked: {
322353 if (categoryView.expandedCategoryId != baseItem.category) {
323354 categoryView.expandedCategoryId = baseItem.category;
355+ floatingSeeLess.companionTo = seeAll;
356+ floatingSeeLess.companionBase = baseItem;
324357 } else {
325358 categoryView.expandedCategoryId = "";
326359 }
@@ -380,26 +413,92 @@ FocusScope {
380413 }
381414 }
382415
383− pageHeader: PageHeader {
384− id: pageHeader
385− objectName: "scopePageHeader"
416+ pageHeader: scopeView.showPageHeader ? pageHeaderLoader : null
417+ Loader {
418+ id: pageHeaderLoader
386419 width: parent.width
387− title: scopeView.scope ? scopeView.scope.name : ""
388− showBackButton: scopeView.hasBackAction
389− searchEntryEnabled: true
390− scopeStyle: scopeView.scopeStyle
391−
392− bottomItem: DashNavigation {
393− id: dashNavigation
394− scope: scopeView.scope
395− width: parent.width <= units.gu(60) ? parent.width : units.gu(40)
396− anchors.right: parent.right
397− windowHeight: scopeView.height
398− windowWidth: scopeView.width
399− scopeStyle: scopeView.scopeStyle
420+ sourceComponent: scopeView.showPageHeader ? pageHeaderComponent : undefined
421+ Component {
422+ id: pageHeaderComponent
423+ PageHeader {
424+ objectName: "scopePageHeader"
425+ width: parent.width
426+ title: scopeView.scope ? scopeView.scope.name : ""
427+ searchHint: scopeView.scope && scopeView.scope.searchHint || i18n.tr("Search")
428+ showBackButton: scopeView.hasBackAction
429+ searchEntryEnabled: true
430+ scopeStyle: scopeView.scopeStyle
431+ paginationCount: scopeView.paginationCount
432+ paginationIndex: scopeView.paginationIndex
433+
434+ bottomItem: DashNavigation {
435+ scope: scopeView.scope
436+ width: parent.width <= units.gu(60) ? parent.width : units.gu(40)
437+ anchors.right: parent.right
438+ windowHeight: scopeView.height
439+ windowWidth: scopeView.width
440+ scopeStyle: scopeView.scopeStyle
441+ }
442+
443+ onBackClicked: scopeView.backClicked()
444+ }
445+ }
446+ }
447+ }
448+
449+ AbstractButton {
450+ id: floatingSeeLess
451+ objectName: "floatingSeeLess"
452+
453+ property var companionTo: null
454+ property var companionBase: null
455+ property bool showBecausePosition: false
456+ property real yOffset: 0
457+
458+ anchors {
459+ left: categoryView.left
460+ right: categoryView.right
461+ }
462+ y: parent.height - height + yOffset
463+ height: seeLessLabel.font.pixelSize + units.gu(4)
464+ visible: companionTo && showBecausePosition
465+
466+ onClicked: categoryView.expandedCategoryId = "";
467+
468+ function updateVisibility() {
469+ var companionPos = companionTo.mapToItem(floatingSeeLess, 0, 0);
470+ showBecausePosition = companionPos.y > 0;
471+
472+ var posToBase = floatingSeeLess.mapToItem(companionBase, 0, -yOffset).y;
473+ yOffset = Math.max(0, companionBase.item.collapsedHeight - posToBase);
474+ yOffset = Math.min(yOffset, height);
475+
476+ if (!showBecausePosition && categoryView.expandedCategoryId === "") {
477+ companionTo = null;
478+ companionBase = null;
479+ }
480+ }
481+
482+ Label {
483+ id: seeLessLabel
484+ text: i18n.tr("See less")
485+ anchors {
486+ centerIn: parent
487+ verticalCenterOffset: units.gu(-0.5)
400488 }
489+ fontSize: "small"
490+ font.weight: Font.Bold
491+ color: scopeStyle ? scopeStyle.foreground : "grey"
492+ }
401493
402− onBackClicked: scopeView.backClicked()
494+ Connections {
495+ target: floatingSeeLess.companionTo ? categoryView : null
496+ onContentYChanged: floatingSeeLess.updateVisibility();
497+ }
498+
499+ Connections {
500+ target: floatingSeeLess.companionTo ? floatingSeeLess.companionTo : null
501+ onYChanged: floatingSeeLess.updateVisibility();
403502 }
404503 }
405504
@@ -418,7 +517,9 @@ FocusScope {
418517 anchors.left: categoryView.right
419518
420519 onOpenChanged: {
421− pageHeader.unfocus();
520+ if (showPageHeader) {
521+ pageHeaderLoader.item.unfocus();
522+ }
422523 }
423524 }
424525
qml/Dash/PreviewListView.qml+1−1
@@ -108,7 +108,7 @@ Item {
108108 anchors {
109109 left: parent.left
110110 right: parent.right
111− top: pageHeader.bottom
111+ top: header.bottom
112112 bottom: parent.bottom
113113 }
114114
qml/Dash/Previews/PreviewTable.qmlnew file+80
@@ -0,0 +1,80 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtQuick.Layouts 1.1
19+import Ubuntu.Components 0.1
20+import "../../Components"
21+
22+/*! \brief Preview widget for table.
23+
24+ This widget shows two columns contained in widgetData["values"]
25+ as arrays of label,value along with a title that comes from widgetData["title"].
26+
27+ In case the widget is collapsed it only shows 3 lines of values.
28+ */
29+
30+PreviewWidget {
31+ id: root
32+ implicitHeight: column.implicitHeight
33+
34+ readonly property int maximumCollapsedRowCount: 3
35+
36+ Column {
37+ id: column
38+ objectName: "column"
39+ spacing: units.gu(1)
40+
41+ Label {
42+ id: titleLabel
43+ objectName: "titleLabel"
44+ anchors {
45+ left: parent.left
46+ right: parent.right
47+ }
48+ height: visible ? implicitHeight : 0
49+ fontSize: "large"
50+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey"
51+ visible: text !== ""
52+ opacity: .8
53+ text: widgetData["title"] || ""
54+ }
55+
56+ GridLayout {
57+ objectName: "gridLayout"
58+ columns: 2
59+ columnSpacing: units.gu(2)
60+ Repeater {
61+ id: rowsRepeater
62+ model: widgetData["values"]
63+
64+ delegate: Repeater {
65+ id: perRowRepeater
66+ readonly property int rowIndex: index
67+ model: widgetData["values"][index]
68+ delegate: Label {
69+ objectName: "label"+rowIndex+index
70+ fontSize: "small"
71+ text: perRowRepeater.model[index]
72+ visible: root.expanded || rowIndex < maximumCollapsedRowCount
73+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey"
74+ font.bold: index == 0
75+ }
76+ }
77+ }
78+ }
79+ }
80+}
qml/Dash/Previews/PreviewWidgetFactory.qml+1
@@ -58,6 +58,7 @@ Loader {
5858 case "payments": return "PreviewPayments.qml";
5959 case "rating-input": return "PreviewRatingInput.qml";
6060 case "reviews": return "PreviewRatingDisplay.qml";
61+ case "table": return "PreviewTable.qml";
6162 case "text": return "PreviewTextSummary.qml";
6263 case "video": return "PreviewVideoPlayback.qml";
6364 default: return "";
qml/Dash/ScopesOverview.qmlnew file+523
@@ -0,0 +1,523 @@
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+ property bool growingDashFromPos: false
36+ readonly property bool searching: scope && scope.searchQuery == ""
37+ readonly property bool showingNonFavoriteScope: tempScopeItem.scope != null
38+ readonly property var dashItemEater: {
39+ if (!forceXYScalerEater && tabBar.currentTab == 0 && middleItems.count > 0) {
40+ var loaderItem = middleItems.itemAt(0).item;
41+ return loaderItem && loaderItem.currentItem ? loaderItem.currentItem : null;
42+ }
43+ return scopesOverviewXYScaler;
44+ }
45+ readonly property size allCardSize: {
46+ if (middleItems.count > 1) {
47+ var loaderItem = middleItems.itemAt(1).item;
48+ if (loaderItem) {
49+ var cardTool = loaderItem.cardTool;
50+ return Qt.size(cardTool.cardWidth, cardTool.cardHeight);
51+ }
52+ }
53+ return Qt.size(0, 0);
54+ }
55+
56+ // Internal properties
57+ property bool forceXYScalerEater: false
58+
59+ signal done()
60+ signal favoriteSelected(var scopeId)
61+ signal allFavoriteSelected(var scopeId)
62+ signal searchSelected(var scopeId, var result, var pos, var size)
63+
64+ Connections {
65+ target: scope
66+ onOpenScope: {
67+ var itemPos = scopesOverviewXYScaler.restorePosition;
68+ var itemSize = scopesOverviewXYScaler.restoreSize;
69+ scopesOverviewXYScaler.scale = itemSize.width / scopesOverviewXYScaler.width;
70+ if (itemPos) {
71+ scopesOverviewXYScaler.x = itemPos.x -(scopesOverviewXYScaler.width - scopesOverviewXYScaler.width * scopesOverviewXYScaler.scale) / 2;
72+ scopesOverviewXYScaler.y = itemPos.y -(scopesOverviewXYScaler.height - scopesOverviewXYScaler.height * scopesOverviewXYScaler.scale) / 2;
73+ } else {
74+ scopesOverviewXYScaler.x = 0;
75+ scopesOverviewXYScaler.y = 0;
76+ }
77+ scopesOverviewXYScaler.opacity = 0;
78+ tempScopeItem.scope = scope;
79+ middleItems.overrideOpacity = 0;
80+ scopesOverviewXYScaler.scale = 1;
81+ scopesOverviewXYScaler.x = 0;
82+ scopesOverviewXYScaler.y = 0;
83+ scopesOverviewXYScaler.opacity = 1;
84+ }
85+ }
86+
87+ Binding {
88+ target: scope
89+ property: "isActive"
90+ value: progress === 1
91+ }
92+
93+ function animateDashFromAll(scopeId) {
94+ var currentScopePos = allScopeCardPosition(scopeId);
95+ if (currentScopePos) {
96+ showDashFromPos(currentScopePos, allCardSize);
97+ } else {
98+ console.log("Warning: Could not find Dash OverView All card position for scope", dashContent.currentScopeId);
99+ }
100+ }
101+
102+ function showDashFromPos(itemPos, itemSize) {
103+ scopesOverviewXYScaler.scale = itemSize.width / scopesOverviewXYScaler.width;
104+ scopesOverviewXYScaler.x = itemPos.x -(scopesOverviewXYScaler.width - scopesOverviewXYScaler.width * scopesOverviewXYScaler.scale) / 2;
105+ scopesOverviewXYScaler.y = itemPos.y -(scopesOverviewXYScaler.height - scopesOverviewXYScaler.height * scopesOverviewXYScaler.scale) / 2;
106+ scopesOverviewXYScaler.opacity = 0;
107+ root.growingDashFromPos = true;
108+ scopesOverviewXYScaler.scale = 1;
109+ scopesOverviewXYScaler.x = 0;
110+ scopesOverviewXYScaler.y = 0;
111+ scopesOverviewXYScaler.opacity = 1;
112+ }
113+
114+ function allScopeCardPosition(scopeId) {
115+ if (middleItems.count > 1) {
116+ var loaderItem = middleItems.itemAt(1).item;
117+ if (loaderItem) {
118+ var pos = loaderItem.scopeCardPosition(scopeId);
119+ return loaderItem.mapToItem(null, pos.x, pos.y);
120+ }
121+ }
122+ }
123+
124+ function ensureAllScopeVisible(scopeId) {
125+ if (middleItems.count > 1) {
126+ var loaderItem = middleItems.itemAt(1).item;
127+ if (loaderItem) {
128+ var pos = loaderItem.scopeCardPosition(scopeId);
129+ loaderItem.contentY = Math.min(pos.y, loaderItem.contentHeight - loaderItem.height);
130+ }
131+ }
132+ }
133+
134+ onProgressChanged: {
135+ if (progress == 0) {
136+ pageHeader.resetSearch();
137+ pageHeader.unfocus(); // Shouldn't the previous call do this too?
138+ }
139+ }
140+
141+ ScopeStyle {
142+ id: overviewScopeStyle
143+ style: { "foreground-color" : "white", "background-color" : "transparent" }
144+ }
145+
146+ DashBackground {
147+ anchors.fill: parent
148+ source: "graphics/dark_background.jpg"
149+ }
150+
151+ Connections {
152+ target: pageHeader
153+ onSearchQueryChanged: {
154+ // Need this in order, otherwise something gets unhappy in rendering
155+ // of the overlay in carousels because the parent of the dash dies for
156+ // a moment, this way we make sure it's reparented first
157+ // by forceXYScalerEater making dashItemEater return scopesOverviewXYScaler
158+ // before we kill the previous parent by scope.searchQuery
159+ root.forceXYScalerEater = true;
160+ root.scope.searchQuery = pageHeader.searchQuery;
161+ root.forceXYScalerEater = false;
162+ }
163+ }
164+
165+ Binding {
166+ target: pageHeader
167+ property: "searchQuery"
168+ value: scope ? scope.searchQuery : ""
169+ }
170+
171+ Item {
172+ id: scopesOverviewContent
173+ x: previewListView.open ? -width : 0
174+ Behavior on x { UbuntuNumberAnimation { } }
175+ width: parent.width
176+ height: parent.height
177+
178+ PageHeader {
179+ id: pageHeader
180+ objectName: "scopesOverviewPageHeader"
181+
182+ readonly property real yDisplacement: pageHeader.height + tabBar.height + tabBar.anchors.margins
183+
184+ y: {
185+ if (root.progress < 0.5) {
186+ return -yDisplacement;
187+ } else {
188+ return -yDisplacement + (root.progress - 0.5) * yDisplacement * 2;
189+ }
190+ }
191+ width: parent.width
192+ clip: true
193+ title: i18n.tr("Manage Dash")
194+ scopeStyle: overviewScopeStyle
195+ showSignatureLine: false
196+ searchEntryEnabled: true
197+ }
198+
199+ ScopesOverviewTab {
200+ id: tabBar
201+ anchors {
202+ left: parent.left
203+ right: parent.right
204+ top: pageHeader.bottom
205+ margins: units.gu(2)
206+ }
207+ height: units.gu(4)
208+
209+ enabled: opacity == 1
210+ opacity: !scope || scope.searchQuery == "" ? 1 : 0
211+ Behavior on opacity { UbuntuNumberAnimation { } }
212+ }
213+
214+ Repeater {
215+ id: middleItems
216+ objectName: "scopesOverviewRepeater"
217+ property real overrideOpacity: -1
218+ model: scope && scope.searchQuery == "" ? scope.categories : null
219+ delegate: Loader {
220+ id: loader
221+ objectName: "scopesOverviewRepeaterChild" + index
222+
223+ height: {
224+ if (index == 0) {
225+ return root.height;
226+ } else {
227+ return root.height - pageHeader.height - tabBar.height - tabBar.anchors.margins - units.gu(2);
228+ }
229+ }
230+ width: {
231+ if (index == 0) {
232+ return root.width / scopeScale;
233+ } else {
234+ return root.width;
235+ }
236+ }
237+ x: {
238+ if (index == 0) {
239+ return (root.width - width) / 2;
240+ } else {
241+ return 0;
242+ }
243+ }
244+ anchors {
245+ bottom: scopesOverviewContent.bottom
246+ }
247+
248+ scale: index == 0 ? scopeScale : 1
249+
250+ opacity: {
251+ if (middleItems.overrideOpacity >= 0)
252+ return middleItems.overrideOpacity;
253+
254+ if (tabBar.currentTab != index)
255+ return 0;
256+
257+ return index == 0 ? 1 : root.progress;
258+ }
259+ Behavior on opacity {
260+ enabled: root.progress == 1
261+ UbuntuNumberAnimation { }
262+ }
263+ enabled: opacity == 1
264+
265+ clip: index == 1
266+
267+ CardTool {
268+ id: cardTool
269+ objectName: "cardTool"
270+ count: results.count
271+ template: model.renderer
272+ components: model.components
273+ viewWidth: parent.width
274+ }
275+
276+ source: {
277+ if (index == 0 && categoryId == "favorites") return "ScopesOverviewFavorites.qml";
278+ else if (index == 1 && categoryId == "all") return "ScopesOverviewAll.qml";
279+ else return "";
280+ }
281+
282+ onLoaded: {
283+ item.model = Qt.binding(function() { return results; });
284+ item.cardTool = cardTool;
285+ if (index == 0) {
286+ item.scopeWidth = Qt.binding(function() { return root.width; });
287+ item.scopeHeight = Qt.binding(function() { return root.height; });
288+ item.appliedScale = Qt.binding(function() { return loader.scale });
289+ item.currentIndex = Qt.binding(function() { return root.currentIndex });
290+ } else if (index == 1) {
291+ item.extraHeight = bottomBar.height;
292+ }
293+ }
294+
295+ Connections {
296+ target: loader.item
297+ onClicked: {
298+ if (tabBar.currentTab == 0) {
299+ root.favoriteSelected(itemModel.scopeId);
300+ } else {
301+ var favoriteScopesItem = middleItems.itemAt(0).item;
302+ var scopeIndex = favoriteScopesItem.model.scopeIndex(itemModel.scopeId);
303+ if (scopeIndex >= 0) {
304+ root.allFavoriteSelected(itemModel.scopeId);
305+ } else {
306+ // Will result in an openScope from root.scope
307+ scopesOverviewXYScaler.restorePosition = item.mapToItem(null, 0, 0);
308+ scopesOverviewXYScaler.restoreSize = allCardSize;
309+ root.scope.activate(result);
310+ }
311+ }
312+ }
313+ onPressAndHold: {
314+ // Preview can call openScope so make sure restorePosition and restoreSize are set
315+ scopesOverviewXYScaler.restorePosition = undefined;
316+ scopesOverviewXYScaler.restoreSize = allCardSize;
317+
318+ previewListView.model = target.model;
319+ previewListView.currentIndex = -1;
320+ previewListView.currentIndex = index;
321+ previewListView.open = true;
322+ }
323+ }
324+ }
325+ }
326+
327+ GenericScopeView {
328+ id: searchResultsViewer
329+ objectName: "searchResultsViewer"
330+ anchors {
331+ top: pageHeader.bottom
332+ right: parent.right
333+ left: parent.left
334+ bottom: parent.bottom
335+ }
336+ scope: root.scope && root.scope.searchQuery != "" ? root.scope : null
337+ scopeStyle: overviewScopeStyle
338+ enabled: opacity == 1
339+ showPageHeader: false
340+ clip: true
341+ opacity: searchResultsViewer.scope ? 1 : 0
342+ isCurrent: true
343+ Behavior on opacity { UbuntuNumberAnimation { } }
344+
345+ function itemClicked(index, result, item, itemModel, resultsModel, limitedCategoryItemCount) {
346+ pageHeader.closePopup();
347+ if (itemModel.scopeId) {
348+ // This can end up in openScope so save restorePosition and restoreSize
349+ scopesOverviewXYScaler.restorePosition = item.mapToItem(null, 0, 0);
350+ scopesOverviewXYScaler.restoreSize = Qt.size(item.width, item.height);
351+ root.searchSelected(itemModel.scopeId, result, item.mapToItem(null, 0, 0), Qt.size(item.width, item.height));
352+ } else {
353+ // Not a scope, just activate it
354+ searchResultsViewer.scope.activate(result);
355+ }
356+ }
357+
358+ function itemPressedAndHeld(index, itemModel, resultsModel, limitedCategoryItemCount) {
359+ // Do nothing
360+ }
361+ }
362+
363+ Rectangle {
364+ id: bottomBar
365+ color: "black"
366+ height: units.gu(8)
367+ width: parent.width
368+ enabled: opacity == 1
369+ opacity: scope && scope.searchQuery == "" ? 1 : 0
370+ Behavior on opacity { UbuntuNumberAnimation { } }
371+ y: {
372+ if (root.progress < 0.5) {
373+ return parent.height;
374+ } else {
375+ return parent.height - (root.progress - 0.5) * height * 2;
376+ }
377+ }
378+
379+ AbstractButton {
380+ objectName: "scopesOverviewDoneButton"
381+ width: Math.max(label.width + units.gu(2), units.gu(10))
382+ height: units.gu(4)
383+ anchors {
384+ left: parent.left
385+ leftMargin: units.gu(2)
386+ verticalCenter: parent.verticalCenter
387+ }
388+ Rectangle {
389+ anchors.fill: parent
390+ border.color: "white"
391+ border.width: units.dp(1)
392+ radius: units.dp(10)
393+ color: parent.pressed ? "gray" : "transparent"
394+ }
395+ Label {
396+ id: label
397+ anchors.centerIn: parent
398+ text: i18n.tr("Done")
399+ color: parent.pressed ? "black" : "white"
400+ }
401+ onClicked: root.done();
402+ }
403+
404+ AbstractButton {
405+ objectName: "scopesOverviewStoreButton"
406+ width: Math.max(storeLabel.width, units.gu(10))
407+ height: units.gu(4)
408+ anchors {
409+ right: parent.right
410+ verticalCenter: parent.verticalCenter
411+ }
412+ Image {
413+ id: storeImage
414+ source: "graphics/apps-scope.png"
415+ anchors.horizontalCenter: parent.horizontalCenter
416+ width: units.gu(2)
417+ height: units.gu(2)
418+ fillMode: Image.Stretch
419+ }
420+ Label {
421+ id: storeLabel
422+ anchors.horizontalCenter: parent.horizontalCenter
423+ anchors.top: storeImage.bottom
424+ text: i18n.tr("Store")
425+ color: "white"
426+ }
427+ onClicked: {
428+ // Just zoom from the middle
429+ scopesOverviewXYScaler.restorePosition = undefined;
430+ scopesOverviewXYScaler.restoreSize = allCardSize;
431+ scope.performQuery("scope://com.canonical.scopes.clickstore");
432+ }
433+ }
434+ }
435+ }
436+
437+ PreviewListView {
438+ id: previewListView
439+ objectName: "scopesOverviewPreviewListView"
440+ scope: root.scope
441+ scopeStyle: overviewScopeStyle
442+ visible: x != width
443+ width: parent.width
444+ height: parent.height
445+ anchors.left: scopesOverviewContent.right
446+ }
447+
448+
449+
450+ Item {
451+ id: scopesOverviewXYScaler
452+ width: parent.width
453+ height: parent.height
454+
455+ clip: scale != 1.0
456+ enabled: scale == 1
457+
458+ property bool animationsEnabled: root.showingNonFavoriteScope || root.growingDashFromPos
459+
460+ property var restorePosition
461+ property var restoreSize
462+
463+ Behavior on x {
464+ enabled: scopesOverviewXYScaler.animationsEnabled
465+ UbuntuNumberAnimation { }
466+ }
467+ Behavior on y {
468+ enabled: scopesOverviewXYScaler.animationsEnabled
469+ UbuntuNumberAnimation { }
470+ }
471+ Behavior on opacity {
472+ enabled: scopesOverviewXYScaler.animationsEnabled
473+ UbuntuNumberAnimation { }
474+ }
475+ Behavior on scale {
476+ enabled: scopesOverviewXYScaler.animationsEnabled
477+ UbuntuNumberAnimation {
478+ onRunningChanged: {
479+ if (!running) {
480+ if (root.showingNonFavoriteScope && scopesOverviewXYScaler.scale != 1) {
481+ root.scope.closeScope(tempScopeItem.scope);
482+ tempScopeItem.scope = null;
483+ } else if (root.growingDashFromPos) {
484+ root.growingDashFromPos = false;
485+ }
486+ }
487+ }
488+ }
489+ }
490+
491+ DashBackground {
492+ anchors.fill: tempScopeItem
493+ visible: tempScopeItem.visible
494+ parent: tempScopeItem.parent
495+ }
496+
497+ GenericScopeView {
498+ id: tempScopeItem
499+ objectName: "scopesOverviewTempScopeItem"
500+
501+ width: parent.width
502+ height: parent.height
503+ scale: dash.contentScale
504+ clip: scale != 1.0
505+ visible: scope != null
506+ hasBackAction: true
507+ isCurrent: visible
508+ onBackClicked: {
509+ var v = scopesOverviewXYScaler.restoreSize.width / tempScopeItem.width;
510+ scopesOverviewXYScaler.scale = v;
511+ if (scopesOverviewXYScaler.restorePosition) {
512+ scopesOverviewXYScaler.x = scopesOverviewXYScaler.restorePosition.x -(tempScopeItem.width - tempScopeItem.width * v) / 2;
513+ scopesOverviewXYScaler.y = scopesOverviewXYScaler.restorePosition.y -(tempScopeItem.height - tempScopeItem.height * v) / 2;
514+ } else {
515+ scopesOverviewXYScaler.x = 0;
516+ scopesOverviewXYScaler.y = 0;
517+ }
518+ scopesOverviewXYScaler.opacity = 0;
519+ middleItems.overrideOpacity = -1;
520+ }
521+ }
522+ }
523+}
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/apps-scope.pngnew file

Binary file not shown.

qml/Dash/graphics/dark_background.jpgnew file

Binary file not shown.

tests/autopilot/unity8/shell/tests/test_emulators.py+1−1
@@ -122,7 +122,7 @@ class DashEmulatorTestCase(tests.DashBaseTestCase):
122122 self._assert_scope_is_opened(scope, scope_id)
123123
124124 def test_open_generic_scope(self):
125− scope_id = 'scopes'
125+ scope_id = 'musicaggregator'
126126 scope = self.dash.open_scope(scope_id)
127127 self._assert_scope_is_opened(scope, scope_id)
128128 self.assertIsInstance(scope, dash_emulators.GenericScopeView)
tests/mocks/Unity/CMakeLists.txt+1
@@ -22,6 +22,7 @@ add_definitions(-DQT_NO_KEYWORDS)
2222set(UnityQML_SOURCES
2323 fake_scope.cpp
2424 fake_scopes.cpp
25+ fake_scopesoverview.cpp
2526 fake_categories.cpp
2627 fake_navigation.cpp
2728 fake_resultsmodel.cpp
tests/mocks/Unity/fake_resultsmodel.cpp+2−1
@@ -58,8 +58,9 @@ ResultsModel::data(const QModelIndex& index, int role) const
5858 case RoleUri:
5959 case RoleCategoryId:
6060 case RoleDndUri:
61− case RoleResult:
6261 return QString();
62+ case RoleResult:
63+ return QString("Result.%1.%2").arg(m_categoryId).arg(index.row());
6364 case RoleTitle:
6465 return QString("Title.%1.%2").arg(m_categoryId).arg(index.row());
6566 case RoleArt:
tests/mocks/Unity/fake_scope.cpp+18−6
@@ -18,14 +18,16 @@
1818#include <QUrl>
1919
2020#include "fake_scope.h"
21+
2122#include "fake_navigation.h"
2223#include "fake_resultsmodel.h"
24+#include "fake_scopes.h"
2325
24−Scope::Scope(QObject* parent) : Scope(QString(), QString(), false, parent)
26+Scope::Scope(Scopes* parent) : Scope(QString(), QString(), false, parent)
2527{
2628}
2729
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)
2931 : unity::shell::scopes::ScopeInterface(parent)
3032 , m_id(id)
3133 , m_name(name)
@@ -36,6 +38,7 @@ Scope::Scope(QString const& id, QString const& name, bool favorite, QObject* par
3638 , m_currentAltNavigationId("altroot")
3739 , m_previewRendererName("preview-generic")
3840 , m_categories(new Categories(categories, this))
41+ , m_openScope(nullptr)
3942{
4043}
4144
@@ -66,7 +69,7 @@ QString Scope::description() const
6669
6770QString Scope::searchHint() const
6871{
69− return QString("");
72+ return QString("Search %1").arg(m_name);
7073}
7174
7275QString Scope::shortcut() const
@@ -158,7 +161,12 @@ void Scope::setNoResultsHint(const QString& str)
158161
159162void Scope::activate(QVariant const& result)
160163{
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+ }
162170}
163171
164172PreviewStack* Scope::preview(QVariant const& result)
@@ -174,9 +182,13 @@ void Scope::cancelActivation()
174182{
175183}
176184
177−void Scope::closeScope(unity::shell::scopes::ScopeInterface* /*scope*/)
185+void Scope::closeScope(unity::shell::scopes::ScopeInterface* scope)
178186{
179− qFatal("Scope::closeScope is not implemented");
187+ if (scope != m_openScope) {
188+ qDebug() << scope << m_openScope;
189+ qFatal("Scope::closeScope got wrong scope in closeScope");
190+ }
191+ m_openScope = nullptr;
180192}
181193
182194QString Scope::currentNavigationId() const
tests/mocks/Unity/fake_scope.h+6−3
@@ -24,13 +24,15 @@
2424
2525#include <QTimer>
2626
27+class Scopes;
28+
2729class Scope : public unity::shell::scopes::ScopeInterface
2830{
2931 Q_OBJECT
3032
3133public:
32− Scope(QObject* parent = 0);
33− Scope(QString const& id, QString const& name, bool favorite, QObject* parent = 0, int categories = 20);
34+ Scope(Scopes* parent = 0);
35+ Scope(QString const& id, QString const& name, bool favorite, Scopes* parent = 0, int categories = 20);
3436
3537 /* getters */
3638 QString id() const override;
@@ -92,7 +94,8 @@ protected:
9294
9395 QString m_previewRendererName;
9496
95− Categories* m_categories;
97+ unity::shell::scopes::CategoriesInterface* m_categories;
98+ unity::shell::scopes::ScopeInterface* m_openScope;
9699};
97100
98101#endif // FAKE_SCOPE_H
tests/mocks/Unity/fake_scopes.cpp+45−6
@@ -18,6 +18,7 @@
1818
1919// Self
2020#include "fake_scopes.h"
21+#include "fake_scopesoverview.h"
2122
2223// TODO: Implement remaining pieces, like Categories (i.e. LensView now gives warnings)
2324
@@ -26,6 +27,7 @@
2627
2728Scopes::Scopes(QObject *parent)
2829 : unity::shell::scopes::ScopesInterface(parent)
30+ , m_scopesOverview(nullptr)
2931 , m_loaded(false)
3032 , timer(this)
3133{
@@ -52,10 +54,17 @@ void Scopes::updateScopes()
5254 addScope(new Scope("clickscope", "Apps", true, this));
5355 addScope(new Scope("MockScope5", "Videos", true, this));
5456 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);
5563
5664 if (!m_loaded) {
5765 m_loaded = true;
5866 Q_EMIT loadedChanged();
67+ Q_EMIT overviewScopeChanged();
5968 }
6069}
6170
@@ -64,10 +73,13 @@ void Scopes::clear()
6473 timer.stop();
6574 if (m_scopes.size() > 0) {
6675 beginRemoveRows(QModelIndex(), 0, m_scopes.count()-1);
67− qDeleteAll(m_scopes);
76+ qDeleteAll(m_allScopes);
77+ m_allScopes.clear();
6878 m_scopes.clear();
6979 endRemoveRows();
7080 }
81+ delete m_scopesOverview;
82+ m_scopesOverview = nullptr;
7183
7284 if (m_loaded) {
7385 m_loaded = false;
@@ -113,8 +125,22 @@ unity::shell::scopes::ScopeInterface* Scopes::getScope(int row) const
113125 return m_scopes[row];
114126}
115127
116−unity::shell::scopes::ScopeInterface* Scopes::getScope(QString const&) const
128+unity::shell::scopes::ScopeInterface* Scopes::getScope(QString const &scope_id) const
117129{
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+ }
118144 return nullptr;
119145}
120146
@@ -135,13 +161,26 @@ int Scopes::count() const
135161
136162unity::shell::scopes::ScopeInterface* Scopes::overviewScope() const
137163{
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;
139175}
140176
141177void Scopes::addScope(Scope* scope)
142178{
143179 int index = rowCount();
144− beginInsertRows(QModelIndex(), index, index);
145− m_scopes.append(scope);
146− endInsertRows();
180+ if (scope->favorite()) {
181+ beginInsertRows(QModelIndex(), index, index);
182+ m_scopes.append(scope);
183+ endInsertRows();
184+ }
185+ m_allScopes.append(scope);
147186}
tests/mocks/Unity/fake_scopes.h+8−1
@@ -53,11 +53,18 @@ public:
5353 int count() const override;
5454 unity::shell::scopes::ScopeInterface* overviewScope() const override;
5555
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+
5661private Q_SLOTS:
5762 void updateScopes();
5863
5964private:
60− QList<Scope*> m_scopes;
65+ QList<Scope*> m_scopes; // the favorite ones
66+ QList<Scope*> m_allScopes;
67+ Scope *m_scopesOverview;
6168 bool m_loaded;
6269 QTimer timer;
6370};
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
@@ -89,7 +89,7 @@ left: parent.left;
8989 wrapMode: Text.Wrap;
9090 maximumLineCount: 2;
9191 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
92− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
92+ 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");
9393 visible: showHeader ;
9494 text: root.title;
9595 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -104,7 +104,7 @@ left: parent.left;
104104 elide: Text.ElideRight;
105105 fontSize: "small";
106106 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
107− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
107+ 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");
108108 visible: titleLabel.visible && titleLabel.text;
109109 text: cardData && cardData["subtitle"] || "";
110110 font.weight: Font.Light;
tests/plugins/Dash/cardcreator/5.res+8−6
@@ -68,8 +68,9 @@ Loader {
6868 visible: showHeader && status == Loader.Ready;
6969 sourceComponent: ShaderEffect {
7070 id: overlay;
71− height: (fixedHeaderHeight > 0 ? fixedHeaderHeight : headerHeight) + units.gu(2);
72− opacity: 0.6;
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;
73+ property color overlayColor: cardData && cardData["overlayColor"] || "#99000000";
7374 property var source: ShaderEffectSource {
7475 id: shaderSource;
7576 sourceItem: artShapeLoader.item;
@@ -90,15 +91,16 @@ Loader {
9091 varying highp vec2 coord;
9192 uniform sampler2D source;
9293 uniform lowp float qt_Opacity;
94+ uniform highp vec4 overlayColor;
9395 void main() {
9496 lowp vec4 tex = texture2D(source, coord);
95− gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity;
97+ gl_FragColor = vec4(overlayColor.r, overlayColor.g, overlayColor.b, 1) * qt_Opacity * overlayColor.a * tex.a;
9698 }";
9799 }
98100 }
99101readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
100102Label {
101− id: titleLabel;
103+ id: titleLabel;
102104 objectName: "titleLabel";
103105 anchors { left: parent.left;
104106 leftMargin: units.gu(1);
@@ -112,7 +114,7 @@ Label {
112114 wrapMode: Text.Wrap;
113115 maximumLineCount: 2;
114116 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
115− 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");
116118 visible: showHeader && overlayLoader.active;
117119 text: root.title;
118120 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -131,7 +133,7 @@ Label {
131133 elide: Text.ElideRight;
132134 fontSize: "small";
133135 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
134− 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");
135137 visible: titleLabel.visible && titleLabel.text;
136138 text: cardData && cardData["subtitle"] || "";
137139 font.weight: Font.Light;
tests/plugins/Dash/cardcreator/7.res+4−4
@@ -90,7 +90,7 @@ left: parent.left;
9090 wrapMode: Text.Wrap;
9191 maximumLineCount: 2;
9292 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
93− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
93+ 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");
9494 visible: showHeader ;
9595 text: root.title;
9696 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -104,7 +104,7 @@ Label {
104104 elide: Text.ElideRight;
105105 fontSize: "small";
106106 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
107− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
107+ 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");
108108 visible: titleLabel.visible && titleLabel.text;
109109 text: cardData && cardData["subtitle"] || "";
110110 font.weight: Font.Light;
@@ -115,8 +115,8 @@ CardAttributes {
115115 id: attributesRow;
116116 objectName: "attributesRow";
117117 anchors { left: parent.left; right: parent.right }
118− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
119− model: cardData["attributes"];
118+ 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");
119+ model: cardData["attributes"];
120120 }
121121
122122 ]
tests/plugins/Dash/tst_ScopeStyle.qml+1−1
@@ -101,7 +101,7 @@ Rectangle {
101101
102102 function test_threshold_data() {
103103 return [
104− { tag: "default", index: 0, threshold: 0.5020 },
104+ { tag: "default", index: 0, threshold: 0.7510 },
105105 { tag: "red on black", index: 1, threshold: 0.1063 },
106106 { tag: "green on white", index: 2, threshold: 0.6795 },
107107 { tag: "blue on darkgrey", index: 3, threshold: 0.3675 },
tests/qmltests/CMakeLists.txt+1
@@ -49,6 +49,7 @@ add_qml_test(Dash/Previews PreviewPayments)
4949add_qml_test(Dash/Previews PreviewProgress)
5050add_qml_test(Dash/Previews PreviewRatingDisplay)
5151add_qml_test(Dash/Previews PreviewRatingInput)
52+add_qml_test(Dash/Previews PreviewTable)
5253add_qml_test(Dash/Previews PreviewTextSummary)
5354add_qml_test(Dash/Previews PreviewVideoPlayback)
5455add_qml_test(Dash/Previews PreviewWidgetFactory)
tests/qmltests/Dash/Previews/tst_PreviewExpandable.qml+5−1
@@ -30,12 +30,16 @@ Rectangle {
3030 property string longText2: "This is a very very very long text. 1 This is a very very very long text. 2 This is a very very very long text. 3 This is a very very very long text. 4 This is a very very very long text. 5 This is a very very very long text. 6 This is a very very very long text. 7 This is a very very very long text. 8 This is a very very very long text. 9 This is a very very very long text. 10 This is a very very very long text. 11 This is a very very very long text."
3131 property string shortText: "This is a short text :)"
3232
33+ property var tableData: {
34+ "values": [ [ "Long Label 1", "Value 1"], [ "Label 2", "Long Value 2"], [ "Label 3", "Value 3"], [ "Label 4", "Value 4"], [ "Label 5", "Value 5"] ]
35+ }
36+
3337 property var widgetData: {
3438 "title": "Title here",
3539 "collapsed-widgets": 2,
3640 "widgets": [
3741 { "type": "text", "widgetId" : "text1", "properties" : { "text": longText } },
38− { "type": "text", "widgetId" : "text2", "properties" : { "text": longText2 } },
42+ { "type": "table", "widgetId" : "table1", "properties" : tableData },
3943 { "type": "text", "widgetId" : "text3", "properties" : { "text": shortText } },
4044 { "type": "text", "widgetId" : "text4", "properties" : { "text": longText } }
4145 ]
tests/qmltests/Dash/Previews/tst_PreviewTable.qmlnew file+114
@@ -0,0 +1,114 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtTest 1.0
19+import Ubuntu.Components 0.1
20+import "../../../../qml/Dash/Previews"
21+import Unity.Test 0.1 as UT
22+
23+Rectangle {
24+ id: root
25+ width: units.gu(40)
26+ height: units.gu(80)
27+ color: Theme.palette.selected.background
28+
29+ property var widgetDataComplete: {
30+ "title": "Title here",
31+ "values": [ [ "Long Label 1", "Value 1"], [ "Label 2", "Long Value 2"], [ "Label 3", "Value 3"], [ "Label 4", "Value 4"], [ "Label 5", "Value 5"] ]
32+ }
33+
34+ property var widgetDataNoTitle: {
35+ "values": [ [ "Long Label 1", "Value 1"], [ "Label 2", "Long Value 2"], [ "Label 3", "Value 3"], [ "Label 4", "Value 4"], [ "Label 5", "Value 5"] ]
36+ }
37+
38+ PreviewTable {
39+ id: previewTable
40+ anchors { left: parent.left; right: parent.right }
41+ widgetData: widgetDataComplete
42+
43+ Rectangle {
44+ color: "red"
45+ anchors.fill: parent
46+ opacity: 0.5
47+ }
48+ }
49+
50+ UT.UnityTestCase {
51+ name: "PreviewTableTest"
52+ when: windowShown
53+
54+ function init() {
55+ previewTable.widgetData = widgetDataComplete;
56+ }
57+
58+ function test_values() {
59+ compare(findChild(previewTable, "label00").text, "Long Label 1");
60+ compare(findChild(previewTable, "label01").text, "Value 1");
61+ compare(findChild(previewTable, "label10").text, "Label 2");
62+ compare(findChild(previewTable, "label11").text, "Long Value 2");
63+ compare(findChild(previewTable, "label20").text, "Label 3");
64+ compare(findChild(previewTable, "label21").text, "Value 3");
65+ compare(findChild(previewTable, "label30").text, "Label 4");
66+ compare(findChild(previewTable, "label31").text, "Value 4");
67+ compare(findChild(previewTable, "label40").text, "Label 5");
68+ compare(findChild(previewTable, "label41").text, "Value 5");
69+ }
70+
71+ function test_optional_title() {
72+ var titleLabel = findChild(previewTable, "titleLabel");
73+ compare(titleLabel.visible, true);
74+ var titleHeight = titleLabel.height;
75+ var prevHeight = previewTable.height;
76+
77+ previewTable.widgetData = widgetDataNoTitle;
78+ var column = findChild(previewTable, "column");
79+ tryCompare(previewTable, "height", prevHeight - titleHeight - column.spacing );
80+ }
81+
82+ function test_show_collapsed() {
83+ verify(findChild(previewTable, "label00").visible);
84+ verify(findChild(previewTable, "label01").visible);
85+ verify(findChild(previewTable, "label10").visible);
86+ verify(findChild(previewTable, "label11").visible);
87+ verify(findChild(previewTable, "label20").visible);
88+ verify(findChild(previewTable, "label21").visible);
89+ verify(findChild(previewTable, "label30").visible);
90+ verify(findChild(previewTable, "label31").visible);
91+ verify(findChild(previewTable, "label40").visible);
92+ verify(findChild(previewTable, "label41").visible);
93+
94+ waitForRendering(previewTable);
95+ var prevHeight = previewTable.height;
96+ previewTable.expanded = false;
97+
98+ verify(findChild(previewTable, "label00").visible);
99+ verify(findChild(previewTable, "label01").visible);
100+ verify(findChild(previewTable, "label10").visible);
101+ verify(findChild(previewTable, "label11").visible);
102+ verify(findChild(previewTable, "label20").visible);
103+ verify(findChild(previewTable, "label21").visible);
104+ verify(!findChild(previewTable, "label30").visible);
105+ verify(!findChild(previewTable, "label31").visible);
106+ verify(!findChild(previewTable, "label40").visible);
107+ verify(!findChild(previewTable, "label41").visible);
108+
109+ var labelHeight = findChild(previewTable, "label00").height;
110+ var gridLayout = findChild(previewTable, "gridLayout");
111+ tryCompare(previewTable, "height", prevHeight - labelHeight * 2 - gridLayout.rowSpacing * 2);
112+ }
113+ }
114+}
tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qml+1
@@ -84,6 +84,7 @@ Rectangle {
8484 { tag: "Progress", type: "progress", source: "PreviewProgress.qml" },
8585 { tag: "Rating Input", type: "rating-input", source: "PreviewRatingInput.qml" },
8686 { tag: "Rating Display", type: "reviews", source: "PreviewRatingDisplay.qml" },
87+ { tag: "Table", type: "table", source: "PreviewTable.qml" },
8788 { tag: "Text", type: "text", source: "PreviewTextSummary.qml" },
8889 { tag: "Video", type: "video", source: "PreviewVideoPlayback.qml" },
8990 ];
tests/qmltests/Dash/tst_Dash.qml+229−7
@@ -20,8 +20,6 @@ import "../../../qml/Dash"
2020import Ubuntu.Components 0.1
2121import Unity.Test 0.1 as UT
2222
23−// TODO We don't have any tests for the overlay scope functionality.
24−
2523Item {
2624 id: shell
2725 width: units.gu(40)
@@ -61,9 +59,9 @@ Item {
6159 function get_scope_data() {
6260 return [
6361 { tag: "MockScope1", visualIndex: 0 },
64− { tag: "MockScope2", visualIndex: 1 },
65− { tag: "clickscope", visualIndex: 2 },
66− { tag: "MockScope5", visualIndex: 3 },
62+ { tag: "MockScope2", visualIndex: -1 },
63+ { tag: "clickscope", visualIndex: 1 },
64+ { tag: "MockScope5", visualIndex: 2 },
6765 ]
6866 }
6967
@@ -79,10 +77,234 @@ Item {
7977 tryCompare(dashContentList, "count", 0);
8078 scopes.load();
8179 tryCompare(scopes, "loaded", true);
82− tryCompare(dashContentList, "count", 5);
80+ tryCompare(dashContentList, "count", 6);
8381
8482 verify(dashContentList != undefined);
85− tryCompare(dashContentList, "currentIndex", data.visualIndex);
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 {
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);
86308 }
87309
88310 function test_setCurrentScope() {
tests/qmltests/Dash/tst_DashContent.qml+19−6
@@ -70,7 +70,7 @@ Item {
7070 function loadScopes() {
7171 scopeLoadedSpy.clear();
7272 scopesModel.load();
73− tryCompare(scopeLoadedSpy, "count", 4);
73+ tryCompare(scopeLoadedSpy, "count", 6);
7474 }
7575
7676 function init() {
@@ -114,7 +114,8 @@ Item {
114114
115115 loadScopes();
116116
117− verify(dashContentList.currentIndex >= 0 && dashContentList.currentIndex < 5);
117+ compare(dashContentList.count, 6);
118+ verify(dashContentList.currentIndex >= 0 && dashContentList.currentIndex < dashContentList.count);
118119 }
119120
120121 function test_show_header_on_list_movement() {
@@ -170,7 +171,7 @@ Item {
170171
171172 // test greater than scope count.
172173 var currentScopeIndex = dashContent.currentIndex;
173− dashContent.setCurrentScopeAtIndex(8, true, false);
174+ dashContent.setCurrentScopeAtIndex(18, true, false);
174175 compare(dashContent.currentIndex, currentScopeIndex, "Scope should not change if changing to greater index than count");
175176 }
176177
@@ -189,9 +190,9 @@ Item {
189190 function test_scope_mapping_data() {
190191 return [
191192 {tag: "index0", index: 0, objectName: "MockScope1"},
192− {tag: "index1", index: 1, objectName: "MockScope2"},
193− {tag: "index2", index: 2, objectName: "clickscope"},
194− {tag: "index3", index: 3, objectName: "MockScope5"}
193+ {tag: "index1", index: 1, objectName: "clickscope"},
194+ {tag: "index2", index: 2, objectName: "MockScope5"},
195+ {tag: "index3", index: 3, objectName: "SingleCategoryScope"}
195196 ]
196197 }
197198
@@ -380,5 +381,17 @@ Item {
380381 mouseClick(allButton, 0, 0);
381382 tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
382383 }
384+
385+ function test_searchHint() {
386+ var dashContentList = findChild(dashContent, "dashContentList");
387+ verify(dashContentList !== null);
388+ var scope = findChild(dashContent, "MockScope1 loader");
389+ waitForRendering(scope);
390+
391+ var categoryListView = findChild(scope, "categoryListView");
392+ waitForRendering(categoryListView);
393+
394+ compare(categoryListView.pageHeader.searchHint, "Search People");
395+ }
383396 }
384397}
tests/qmltests/Dash/tst_GenericScopeView.qml+16−12
@@ -61,8 +61,8 @@ Item {
6161 property Item header: findChild(genericScopeView, "scopePageHeader")
6262
6363 function init() {
64− genericScopeView.scope = scopes.getScope(1)
65− shell.width = units.gu(120)
64+ genericScopeView.scope = scopes.getScope(2);
65+ shell.width = units.gu(120);
6666 genericScopeView.categoryView.positionAtBeginning();
6767 waitForRendering(genericScopeView.categoryView);
6868 }
@@ -88,13 +88,13 @@ Item {
8888
8989 function test_showDash() {
9090 testCase.previewListView.open = true;
91− scopes.getScope(1).showDash();
91+ genericScopeView.scope.showDash();
9292 tryCompare(testCase.previewListView, "open", false);
9393 }
9494
9595 function test_hideDash() {
9696 testCase.previewListView.open = true;
97− scopes.getScope(1).hideDash();
97+ genericScopeView.scope.hideDash();
9898 tryCompare(testCase.previewListView, "open", false);
9999 }
100100
@@ -111,8 +111,9 @@ Item {
111111
112112 function test_changeScope() {
113113 genericScopeView.scope.searchQuery = "test"
114− genericScopeView.scope = scopes.getScope(2)
115− genericScopeView.scope = scopes.getScope(1)
114+ var originalScopeId = genericScopeView.scope.id;
115+ genericScopeView.scope = scopes.getScope(originalScopeId + 1)
116+ genericScopeView.scope = scopes.getScope(originalScopeId)
116117 tryCompare(genericScopeView.scope, "searchQuery", "test")
117118 }
118119
@@ -198,6 +199,8 @@ Item {
198199 openPreview(4, 0);
199200
200201 compare(testCase.previewListView.count, 12, "There should only be 12 items in preview.");
202+
203+ closePreview();
201204 }
202205
203206 function test_narrow_delegate_ranges_expand() {
@@ -208,11 +211,12 @@ Item {
208211 shell.width = units.gu(20)
209212 var categoryListView = findChild(genericScopeView, "categoryListView");
210213 categoryListView.contentY = units.gu(20);
211− var seeAll = findChild(category, "seeAll")
214+ var seeAll = findChild(category, "seeAll");
215+ var floatingSeeLess = findChild(genericScopeView, "floatingSeeLess");
212216 mouseClick(seeAll, seeAll.width / 2, seeAll.height / 2);
213217 tryCompare(category, "expanded", true);
214− tryCompareFunction(function() { return category.item.height == genericScopeView.height - category.item.displayMarginBeginning - category.item.displayMarginEnd; }, true);
215− mouseClick(seeAll, seeAll.width / 2, seeAll.height / 2);
218+ tryCompareFunction(function() { return category.item.height + floatingSeeLess.height == genericScopeView.height - category.item.displayMarginBeginning - category.item.displayMarginEnd; }, true);
219+ mouseClick(floatingSeeLess, floatingSeeLess.width / 2, floatingSeeLess.height / 2);
216220 tryCompare(category, "expanded", false);
217221 }
218222
@@ -232,7 +236,7 @@ Item {
232236 }
233237
234238 function test_single_category_expansion() {
235− genericScopeView.scope = scopes.getScope(4);
239+ genericScopeView.scope = scopes.getScope(3);
236240
237241 tryCompareFunction(function() { return findChild(genericScopeView, "dashCategory0") != undefined; }, true);
238242 var category = findChild(genericScopeView, "dashCategory0")
@@ -329,8 +333,8 @@ Item {
329333 function test_header_style_data() {
330334 return [
331335 { tag: "Default", index: 0, foreground: "grey", background: "", logo: "" },
332− { tag: "Foreground", index: 2, foreground: "yellow", background: "", logo: "" },
333− { tag: "Logo+Background", index: 3, foreground: "grey", background: "gradient:///lightgrey/grey",
336+ { tag: "Foreground", index: 1, foreground: "yellow", background: "", logo: "" },
337+ { tag: "Logo+Background", index: 2, foreground: "grey", background: "gradient:///lightgrey/grey",
334338 logo: Qt.resolvedUrl("../Components/tst_PageHeader/logo-ubuntu-orange.svg") },
335339 ];
336340 }