on xenial help sign in
Merged with trunk
commitf62e3c89f7f7b7c981b494a6d93c94009b513f5d authorNick Dedekind <nicholas.dedekind@gmail.com> dateMon 29 Jul 2013, 08:59 (13 years ago) parentf7e72c4dd41cd4
177 files changed+3328−2679
ApplicationArguments.h+49 Applications/applications.js+2−2 CMakeLists.txt+1 Components/ApplicationManagerWrapper.qml+8−1 Components/ApplicationScreenshot.qml+1−1 Components/Carousel.qml+38−1 Components/FilterGrid.qml+2−2 Components/ListItems/Header.qml+1−1 Components/PageHeader.qml+1−1 Components/Stage.qml+14 Components/Tile.qml+1−1 Dash/Apps/RunningApplicationTile.qml+1−1 Dash/Dash.qml−3 Dash/DashApps.qml+1−1 Dash/DashBar.qml+1−1 Dash/DashContent.qml+5−2 Dash/DashHome.qml+1 Dash/DashMusic.qml+2−2 Dash/DashPreview.qml+1−1 Dash/DashVideos.qml+2−3 Dash/Generic/GenericCarousel.qml+58 Dash/Generic/GenericFilterGrid.qml+59 Panel/Menus/IndicatorMenuWindow.qml → Dash/Generic/GenericFilterGridPotrait.qml+9−14 Dash/GenericScopeView.qml+55−41 Dash/Music/AlbumTile.qml+2−2 Dash/Video/VideoPreview.qml+3−3 Notifications/Notification.qml+6−5 Notifications/Notifications.qml+1 Panel/IndicatorItem.qml+6−1 Panel/IndicatorRow.qml+34−49 Panel/Indicators.qml+102−137 Panel/Indicators/DatetimeIndicatorPage.qml+2−2 Panel/Indicators/DatetimeIndicatorWidget.qml+1−1 Panel/Indicators/DefaultIndicatorWidget.qml+1−1 Panel/MenuContent.qml+22−71 Panel/Menus/Overview.qml−70 Panel/Menus/Overview/FlightModeWidget.qml−60 Panel/Menus/Overview/OverviewGrid.qml−135 Panel/Menus/Overview/VolumeWidget.qml−88 Panel/Menus/Overview/graphics/plane_icon@18.png Panel/Menus/Overview/graphics/sound_off_icon@18.png Panel/Menus/Overview/graphics/sound_on_icon@18.png Panel/Panel.qml+28−9 Panel/SearchIndicator.qml+3−5 Shell.qml+27−9 cmake/modules/Plugins.cmake+25−15 cmake/modules/autopilot.cmake+2−1 debian/changelog+92 debian/control+3−1 main.cpp+4−6 plugins/HudClient/CMakeLists.txt+2−1 plugins/LightDM/CMakeLists.txt+2−1 plugins/ListViewWithPageHeader/CMakeLists.txt+2−1 plugins/ListViewWithPageHeader/listviewwithpageheader.cpp+8−4 plugins/Ubuntu/Gestures/CMakeLists.txt+2−1 plugins/Unity/CMakeLists.txt+3−2 plugins/Unity/Indicators/CMakeLists.txt+7−8 plugins/Unity/Indicators/Messaging/qml/CMakeLists.txt+4−14 plugins/Unity/Indicators/Network/CMakeLists.txt+6−3 plugins/Unity/Indicators/Network/qml/CMakeLists.txt+4−14 plugins/Unity/Indicators/Network/qml/qmldir+1 plugins/Unity/Indicators/flatmenuproxymodel.h+3−1 plugins/Unity/Indicators/indicator.h+3−1 plugins/Unity/Indicators/indicators.h+7−5 plugins/Unity/Indicators/indicatorsmanager.h+4−3 plugins/Unity/Indicators/indicatorsmodel.h+2−1 plugins/Unity/Indicators/menucontentactivator.cpp+345 plugins/Unity/Indicators/menucontentactivator.h+117 plugins/Unity/Indicators/plugin.cpp+3 plugins/Unity/Indicators/qml/CMakeLists.txt+4−13 plugins/Unity/Indicators/qml/IndicatorPage.qml−6 plugins/Unity/Indicators/qml/qmldir+1 plugins/Unity/Indicators/unityindicatorsglobal.h+23 plugins/Unity/Launcher/CMakeLists.txt+1 plugins/Unity/Launcher/backend/launcherbackend.cpp+3−3 plugins/Unity/Launcher/qmldir+1 plugins/Unity/categories.cpp+6−3 plugins/Unity/categories.h+3−2 plugins/Unity/previewinfohint.cpp+2−2 plugins/Unity/variantutils.cpp+2−118 plugins/Unity/variantutils.h−2 plugins/Utils/CMakeLists.txt+2−1 po/ar.po+3−27 po/az.po+4−10 po/bg.po+3−15 po/ca.po+4−16 po/de.po+5−29 po/el.po+4−10 po/en_AU.po+3−27 po/en_GB.po+4−28 po/eo.po+3−3 po/es.po+1−1 po/fi.po+4−25 po/fo.po+4−10 po/fr.po+40−61 po/gl.po+4−16 po/he.po+4−10 po/hr.po+4−10 po/hu.po+4−10 po/hy.po+3−15 po/id.po+3−3 po/it.po+4−10 po/ja.po+3−3 po/kn.po+4−10 po/ko.po+4−4 po/ky.po+4−10 po/lv.po+4−10 po/my.po+4−7 po/nb.po+3−9 po/nl.po+4−10 po/pl.po+8−32 po/pt_BR.po+4−10 po/ro.po+4−10 po/ru.po+22−37 po/si.po+3−3 po/sl.po+1−1 po/sr.po+4−10 po/sv.po+3−3 po/te.po+4−4 po/tr.po+3−24 po/ug.po+6−27 po/uk.po+4−28 po/zh_CN.po+3−27 po/zh_TW.po+4−16 run_on_device+3−19 tests/autopilot/setup.py+17−6 tests/autopilot/unity8/__init__.py+90−5 tests/autopilot/unity8/indicators_client/__init__.py+1−1 tests/autopilot/unity8/indicators_client/tests/__init__.py+13−9 tests/autopilot/unity8/indicators_client/tests/test_battery.py+14−3 tests/autopilot/unity8/shell/__init__.py+35−5 tests/autopilot/unity8/shell/emulators/__init__.py+23−4 tests/autopilot/unity8/shell/emulators/dash.py+52 tests/autopilot/unity8/shell/emulators/greeter.py+40 tests/autopilot/unity8/shell/emulators/hud.py+83 tests/autopilot/unity8/shell/emulators/launcher.py+38 tests/autopilot/unity8/shell/emulators/main_window.py+43−12 tests/autopilot/unity8/shell/tests/__init__.py+216−58 tests/autopilot/unity8/shell/tests/helpers.py−161 tests/autopilot/unity8/shell/tests/test_hud.py+151 tests/autopilot/unity8/shell/tests/test_lock_screen.py+131 tests/autopilot/unity8/shell/tests/testbigscreen.py−25 tests/autopilot/unity8/shell/tests/testhud.py−145 tests/autopilot/unity8/shell/tests/testlockscreen.py−118 tests/mocks/CMakeLists.txt+1 tests/mocks/QMenuModel/CMakeLists.txt+11 tests/mocks/QMenuModel/QDBusActionGroup.qml+27 tests/mocks/QMenuModel/qmldir+2 tests/mocks/Ubuntu/Application/ApplicationManager.cpp+8−4 tests/mocks/Unity/CMakeLists.txt+1 tests/mocks/Unity/Indicators/CMakeLists.txt+2−5 tests/mocks/Unity/Indicators/fakeplugin.cpp+4 tests/mocks/Unity/Launcher/MockLauncherModel.cpp+1−1 tests/mocks/Unity/Notifications/CMakeLists.txt+11 tests/mocks/Unity/fake_categories.cpp+5−2 tests/mocks/Unity/fake_categories.h+2−1 tests/mocks/Unity/fake_scopes.cpp+2−2 tests/plugins/ListViewWithPageHeader/listviewwithpageheadertest.cpp+19 tests/plugins/ListViewWithPageHeader/listviewwithpageheadertestsection.cpp+19 tests/plugins/Unity/Indicators/CMakeLists.txt+1 tests/plugins/Unity/Indicators/menucontentactivatortest.cpp+303 tests/plugins/Unity/previewbindingstest.cpp−2 tests/qmltests/CMakeLists.txt+3−2 tests/qmltests/Components/tst_Stage.qml+1 tests/qmltests/Dash/tst_Dash.qml+4−4 tests/qmltests/Dash/tst_DashContent.qml+12−12 tests/qmltests/Notifications/tst_Notifications.qml+20−9 tests/qmltests/Panel/Menus/tst_IndicatorMenuWindow.qml−68 tests/qmltests/Panel/tst_IndicatorItem.qml+10−16 tests/qmltests/Panel/tst_IndicatorRow.qml+21−35 tests/qmltests/Panel/tst_Indicators.qml+1−39 tests/qmltests/Panel/tst_MenuContent.qml+14−84 tests/qmltests/Panel/tst_Overview.qml−142 tests/qmltests/Panel/tst_Panel.qml+101−61 tests/qmltests/Panel/tst_SearchIndicator.qml+8−10 tests/qmltests/tst_Shell.qml+281 tests/utils/modules/Unity/Test/UnityTestCase.qml+22−18
ApplicationArguments.hnew file+49
@@ -0,0 +1,49 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authored by: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+
20+#ifndef APPLICATION_ARGUMENTS_H
21+#define APPLICATION_ARGUMENTS_H
22+
23+#include <QObject>
24+#include <QSize>
25+#include <QStringList>
26+
27+class ApplicationArguments : public QObject
28+{
29+ Q_OBJECT
30+public:
31+ ApplicationArguments(const QStringList& args) {
32+ if (args.contains(QLatin1String("-geometry")) && args.size() > args.indexOf(QLatin1String("-geometry")) + 1) {
33+ QStringList geometryArg = args.at(args.indexOf(QLatin1String("-geometry")) + 1).split('x');
34+ if (geometryArg.size() == 2) {
35+ m_size.rwidth() = geometryArg.at(0).toInt();
36+ m_size.rheight() = geometryArg.at(1).toInt();
37+ }
38+ }
39+ }
40+
41+ Q_INVOKABLE bool hasGeometry() const { return m_size.isValid(); }
42+ Q_INVOKABLE int width() const { return m_size.width(); }
43+ Q_INVOKABLE int height() const { return m_size.height(); }
44+
45+private:
46+ QSize m_size;
47+};
48+
49+#endif // APPLICATION_ARGUMENTS_H
Applications/applications.js+2−2
@@ -70,10 +70,10 @@ var __availableApplications = {
7070 'exec': '/usr/bin/qmlscene /usr/share/notes-app/NotesApp.qml',
7171 'stage': SideStage
7272 },
73− '/usr/share/applications/ubuntu-calendar-app.desktop': {
73+ '/usr/share/applications/calendar-app.desktop': {
7474 'name': i18n.tr("Calendar"),
7575 'icon': 'calendar',
76− 'exec': '/usr/bin/qmlscene /usr/share/ubuntu-calendar-app/calendar.qml',
76+ 'exec': '/usr/bin/qmlscene /usr/share/calendar-app/calendar.qml',
7777 'stage': SideStage
7878 },
7979 '/usr/share/applications/mediaplayer-app.desktop': {
CMakeLists.txt+1
@@ -101,6 +101,7 @@ set(shell_app_HDRS
101101
102102add_executable(${SHELL_APP}
103103 ${shellapplication_MOC_SRCS}
104+ ApplicationArguments.h
104105 main.cpp
105106 MouseTouchAdaptor.cpp
106107 )
Components/ApplicationManagerWrapper.qml+8−1
@@ -121,9 +121,16 @@ Item {
121121 desktopFile = "/usr/share/applications/share-app.desktop"
122122 } else if (favoriteApplication == ApplicationManager.BrowserApplication) {
123123 desktopFile = "/usr/share/applications/webbrowser-app.desktop"
124− } else if (favoriteApplication == 4) { // ApplicationManager.PhoneApplication
124+ } else if (favoriteApplication == ApplicationManager.PhoneApplication) {
125125 desktopFile = "/usr/share/applications/phone-app.desktop"
126+ } else if (favoriteApplication == ApplicationManager.DialerApplication) {
127+ desktopFile = "/usr/share/applications/dialer-app.desktop"
128+ } else if (favoriteApplication == ApplicationManager.MessagingApplication) {
129+ desktopFile = "/usr/share/applications/messaging-app.desktop"
130+ } else if (favoriteApplication == ApplicationManager.AddressbookApplication) {
131+ desktopFile = "/usr/share/applications/address-book-app.desktop"
126132 }
133+
127134 applicationManager.focusRequested(desktopFile);
128135 }
129136 }
Components/ApplicationScreenshot.qml+1−1
@@ -46,7 +46,7 @@ Item {
4646 Rectangle {
4747 id: background
4848 anchors.fill: parent
49− color: "white"
49+ color: "white" // FIXME should use normal background color of Suru theme
5050 visible: applicationScreenshot.withBackground
5151 }
5252
Components/Carousel.qml+38−1
@@ -52,6 +52,12 @@ Item {
5252 /// @param itemY is y of the clicked delegate
5353 signal clicked(int index, var delegateItem, real itemY)
5454
55+ /// Emitted when the user pressed and held on an item
56+ /// @param index is the index of the held item
57+ /// @param delegateItem is the held component/delegate itself
58+ /// @param itemY is y of the held delegate
59+ signal pressAndHold(int index, var delegateItem, real itemY)
60+
5561 implicitHeight: listView.tileHeight * selectedItemScaleFactor
5662
5763 /* Basic idea behind the carousel effect is to move the items of the delegates (compacting /stuffing them).
@@ -159,6 +165,30 @@ Item {
159165 newContentXAnimation.start()
160166 }
161167
168+ function itemPressAndHold(index, delegateItem) {
169+ var x = CarouselJS.getXFromContinuousIndex(index,
170+ realWidth,
171+ realContentWidth,
172+ tileWidth,
173+ gapToMiddlePhase,
174+ gapToEndPhase,
175+ carousel.drawBuffer);
176+
177+ if (Math.abs(x - contentX) < 1) {
178+ /* We're pressAndHold the selected item and
179+ we're in the neighbourhood of radius 1 pixel from it.
180+ Let's emit the pressAndHold signal. */
181+ carousel.pressAndHold(index, delegateItem, delegateItem.y);
182+ return;
183+ }
184+
185+ stepAnimation.stop();
186+ newContentXAnimation.stop();
187+
188+ newContentX = x;
189+ newContentXAnimation.start();
190+ }
191+
162192 onMovementStarted: {
163193 stepAnimation.stop()
164194 newContentXAnimation.stop()
@@ -279,7 +309,14 @@ Item {
279309
280310 anchors.fill: parent
281311
282− onClicked: listView.itemClicked(index, item)
312+ onClicked: {
313+ listView.itemClicked(index, item)
314+ }
315+
316+ onPressAndHold: {
317+ listView.itemPressAndHold(index, item)
318+ }
319+
283320 }
284321 }
285322 }
Components/FilterGrid.qml+2−2
@@ -99,7 +99,7 @@ Item {
9999
100100 UbuntuShape {
101101 anchors.fill: parent
102− color: "#33ffffff"
102+ color: "#33ffffff" // FIXME no palette
103103 radius: "small"
104104 }
105105
@@ -123,7 +123,7 @@ Item {
123123 }
124124 text: (filter) ? "+ View all (" + model.count + ")" : "- Show fewer"
125125 fontSize: "small"
126− color: "#f3f3e7"
126+ color: Theme.palette.selected.backgroundText
127127 opacity: 0.6
128128 style: Text.Raised
129129 styleColor: "black"
Components/ListItems/Header.qml+1−1
@@ -45,7 +45,7 @@ Item {
4545 verticalCenter: parent.verticalCenter
4646 }
4747
48− color: "#f3f3e7" //at 60%
48+ color: Theme.palette.selected.backgroundText //at 60%
4949 opacity: 0.6
5050 font.family: "Ubuntu"
5151 fontSize: "medium"
Components/PageHeader.qml+1−1
@@ -90,7 +90,7 @@ Item {
9090 verticalCenter: parent.verticalCenter
9191 }
9292
93− color: "#f3f3e7"
93+ color: Theme.palette.selected.backgroundText
9494 opacity: 0.8
9595 font.family: "Ubuntu"
9696 font.weight: Font.Light
Components/Stage.qml+14
@@ -39,6 +39,7 @@ Showable {
3939 type == ApplicationInfo.MainStage ? applicationManager.mainStageApplications
4040 : applicationManager.sideStageApplications
4141 property bool fullyShown: false
42+ property bool fullyHidden: true
4243 readonly property var focusedApplication:
4344 type == ApplicationInfo.MainStage ? applicationManager.mainStageFocusedApplication
4445 : applicationManager.sideStageFocusedApplication
@@ -249,6 +250,19 @@ Showable {
249250 }
250251 }
251252
253+ Connections {
254+ target: stage.applications
255+ onCountChanged: { __discardObsoleteScreenshots(); }
256+ ignoreUnknownSignals: true
257+ }
258+ onFullyHiddenChanged: { __discardObsoleteScreenshots(); }
259+ function __discardObsoleteScreenshots() {
260+ if (stage.fullyHidden && stage.applications && stage.applications.count == 0) {
261+ stage.focusedApplicationWhenUsingScreenshots = undefined;
262+ stage.__hideScreenshots();
263+ }
264+ }
265+
252266
253267 SequentialAnimation {
254268 id: showStartingApplicationAnimation
Components/Tile.qml+1−1
@@ -75,7 +75,7 @@ AbstractButton {
7575 rightMargin: units.gu(1)
7676 }
7777
78− color: "#f3f3e7"
78+ color: Theme.palette.selected.backgroundText
7979 opacity: 0.9
8080 style: Text.Raised
8181 styleColor: "black"
Dash/Apps/RunningApplicationTile.qml+1−1
@@ -113,7 +113,7 @@ AbstractButton {
113113 }
114114 text: (application) ? application.name : ""
115115
116− color: "#f3f3e7"
116+ color: Theme.palette.selected.backgroundText
117117 opacity: 0.9
118118 style: Text.Raised
119119 styleColor: "black"
Dash/Dash.qml−3
@@ -27,9 +27,6 @@ Showable {
2727 property string showScopeOnLoaded: "home.scope"
2828 property real contentScale: 1.0
2929
30− width: units.gu(40)
31− height: units.gu(71)
32−
3330 function setCurrentScope(scopeId, animate, reset) {
3431 var scopeIndex = filteredScopes.findFirst(Scopes.RoleId, scopeId)
3532
Dash/DashApps.qml+1−1
@@ -87,7 +87,7 @@ ScopeView {
8787 model: scopeView.scope.categories
8888
8989 delegate: Item {
90− Component.onCompleted: if (index == 3) scopeView.installedModel = Qt.binding(function() { return model.results; });
90+ Component.onCompleted: if (categoryId == "installed") scopeView.installedModel = Qt.binding(function() { return model.results; });
9191 }
9292 }
9393
Dash/DashBar.qml+1−1
@@ -117,7 +117,7 @@ Item {
117117
118118 Rectangle {
119119 id: highlightLine
120− color: "#dd4814"
120+ color: Theme.palette.selected.foreground
121121 height: parent.height
122122 anchors.bottom: parent.bottom
123123 z: 1
Dash/DashContent.qml+5−2
@@ -20,8 +20,6 @@ import Unity 0.1
2020
2121Item {
2222 id: dashContent
23− width: units.gu(40)
24− height: units.gu(71)
2523
2624 property var model: null
2725 property var scopes: null
@@ -117,6 +115,11 @@ Item {
117115 asynchronous: true
118116 source: scopeMapper.map(scope.id)
119117
118+ // these are needed for autopilot tests
119+ readonly property string scopeId: scope.id
120+ readonly property bool isCurrent: ListView.isCurrentItem
121+ readonly property bool isLoaded: status == Loader.Ready
122+
120123 onLoaded: {
121124 item.scope = Qt.binding(function() { return scope })
122125 item.isCurrent = Qt.binding(function() { return ListView.isCurrentItem })
Dash/DashHome.qml+1
@@ -26,6 +26,7 @@ import "Music"
2626
2727ScopeView {
2828 id: root
29+ objectName: "DashHome"
2930
3031 onMovementStarted: listView.showHeader()
3132
Dash/DashMusic.qml+2−2
@@ -52,7 +52,7 @@ ScopeView {
5252
5353 function getRenderer(categoryId) {
5454 switch (categoryId) {
55− case 1: return "Music/MusicCarousel.qml"
55+ case "featured": return "Music/MusicCarousel.qml"
5656 default: return "Music/MusicFilterGrid.qml"
5757 }
5858 }
@@ -69,7 +69,7 @@ ScopeView {
6969 id: base
7070 highlightWhenPressed: false
7171
72− property int categoryId: id
72+ property string categoryId: model.categoryId
7373
7474 Loader {
7575 anchors { top: parent.top; left: parent.left; right: parent.right }
Dash/DashPreview.qml+1−1
@@ -92,7 +92,7 @@ Rectangle {
9292 elide: Text.ElideRight
9393 fontSize: "x-large"
9494 font.weight: Font.Light
95− color: "#f3f3e7"
95+ color: Theme.palette.selected.backgroundText
9696 opacity: 0.9
9797 text: root.title
9898 style: Text.Raised
Dash/DashVideos.qml+2−3
@@ -53,7 +53,7 @@ ScopeView {
5353
5454 function getRenderer(categoryId) {
5555 switch (categoryId) {
56− case 1: return "Video/VideosCarousel.qml"
56+ case "featured": return "Video/VideosCarousel.qml"
5757 default: return "Video/VideosFilterGrid.qml"
5858 }
5959 }
@@ -102,8 +102,7 @@ ScopeView {
102102 delegate: ListItems.Base {
103103 id: base
104104 highlightWhenPressed: false
105− property int categoryIndex: index
106− property int categoryId: id
105+ property string categoryId: model.categoryId
107106
108107 Loader {
109108 id: loader
Dash/Generic/GenericCarousel.qmlnew file+58
@@ -0,0 +1,58 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+import "../../Components"
20+import "../../Components/IconUtil.js" as IconUtil
21+
22+Carousel {
23+ id: genericCarousel
24+
25+ tileAspectRatio: 198 / 288
26+ minimumTileWidth: units.gu(13)
27+ itemComponent: carouselDelegateGeneric
28+ selectedItemScaleFactor: 1.14
29+ cacheBuffer: 1404 // 18px * 13gu * 6
30+ height: implicitHeight + units.gu(6)
31+
32+ Component {
33+ id: carouselDelegateGeneric
34+
35+ BaseCarouselDelegate {
36+ id: item
37+
38+ UbuntuShape {
39+ anchors.fill: parent
40+ radius: "medium"
41+ borderSource: ""
42+ image: Image {
43+ asynchronous: true
44+ sourceSize { width: item.width; height: item.height }
45+ source: model ? IconUtil.from_gicon(model.icon) : ""
46+ }
47+ }
48+
49+ BorderImage {
50+ anchors.centerIn: parent
51+ opacity: 0.6
52+ source: "../../Components/graphics/non-selected.sci"
53+ width: parent.width + units.gu(1.5)
54+ height: parent.height + units.gu(1.5)
55+ }
56+ }
57+ }
58+}
Dash/Generic/GenericFilterGrid.qmlnew file+59
@@ -0,0 +1,59 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import "../../Components"
19+import "../../Components/IconUtil.js" as IconUtil
20+
21+FilterGrid {
22+ id: filtergrid
23+
24+ filter: false
25+ minimumHorizontalSpacing: units.gu(0.5)
26+ delegateWidth: units.gu(11)
27+ delegateHeight: units.gu(9.5)
28+ verticalSpacing: units.gu(2)
29+
30+ property int iconWidth: units.gu(8)
31+ property int iconHeight: units.gu(7.5)
32+
33+ signal clicked(int index, var delegateItem, real itemY)
34+ signal pressAndHold(int index, var delegateItem, real itemY)
35+
36+ delegate: Tile {
37+ id: tile
38+ objectName: "delegate" + index
39+ width: filtergrid.cellWidth
40+ height: filtergrid.cellHeight
41+ text: model.title
42+ imageWidth: filtergrid.iconWidth
43+ imageHeight: filtergrid.iconHeight
44+
45+ source: IconUtil.from_gicon(model.icon)
46+
47+ fillMode: Image.PreserveAspectCrop
48+
49+ onClicked: {
50+ var data = { model: model }
51+ filtergrid.clicked(index, data, tile.y)
52+ }
53+
54+ onPressAndHold: {
55+ var data = { model: model }
56+ filtergrid.pressAndHold(index, data, tile.y)
57+ }
58+ }
59+}
Panel/Menus/IndicatorMenuWindow.qml → Dash/Generic/GenericFilterGridPotrait.qmlrenamed+9−14
@@ -15,21 +15,16 @@
1515 */
1616
1717import QtQuick 2.0
18+import "../../Components"
1819
19−Item {
20− id: menuWindow
20+GenericFilterGrid {
21+ id: filtergrid
2122
22− property bool shown: false
23− property int contentHeight: height
24− property string headingText: name
25− property string name
23+ minimumHorizontalSpacing: units.gu(0.5)
24+ delegateWidth: units.gu(11)
25+ delegateHeight: units.gu(18)
26+ verticalSpacing: units.gu(2)
2627
27− enabled: shown
28− opacity: shown ? 1.0 : 0.0
29− Behavior on opacity { NumberAnimation {duration: shown ? 300 : 150 } }
30−
31− // don't pass any events under this screen
32− MouseArea {
33− anchors.fill: parent
34− }
28+ iconWidth: (width / columns) * 0.8
29+ iconHeight: iconWidth * 16 / 11
3530}
Dash/GenericScopeView.qml+55−41
@@ -56,53 +56,45 @@ ScopeView {
5656 delegate: ListItems.Base {
5757 highlightWhenPressed: false
5858
59− FilterGrid {
60− id: filtergrid
61− model: results
62−
59+ Loader {
60+ id: rendererLoader
6361 anchors {
6462 top: parent.top
6563 left: parent.left
6664 right: parent.right
6765 }
6866
69− filter: false
70− minimumHorizontalSpacing: units.gu(0.5)
71− delegateWidth: units.gu(11)
72− delegateHeight: units.gu(18)
73− verticalSpacing: units.gu(2)
74−
75− delegate: Tile {
76− width: filtergrid.cellWidth
77− height: filtergrid.cellHeight
78− text: title
79− imageWidth: units.gu(11)
80− imageHeight: units.gu(16)
81−
82− source: IconUtil.from_gicon(icon)
83−
84− MouseArea {
85− anchors {
86− fill: parent
87− }
88− onClicked: {
89− mouse.accepted = true
90− effect.positionPx = mapToItem(categoryView, 0, 0).y
91− scopeView.scope.activate(uri, icon,
92− category, 0,
93− mimetype, title,
94− comment, dndUri,
95− metadata)
96− }
97− onPressAndHold: {
98− mouse.accepted = true
99− effect.positionPx = mapToItem(categoryView, 0, 0).y
100− scopeView.scope.preview(uri, icon,
101− category, 0,
102− mimetype, title,
103− comment, dndUri,
104− metadata)
105− }
67+ source: getRenderer(model.renderer, model.contentType)
68+
69+ onLoaded: {
70+ item.model = results
71+ }
72+
73+ Connections {
74+ target: rendererLoader.item
75+ onClicked: {
76+ effect.positionPx = mapToItem(categoryView, 0, itemY).y
77+ scopeView.scope.activate(delegateItem.model.uri,
78+ delegateItem.model.icon,
79+ delegateItem.model.category,
80+ 0,
81+ delegateItem.model.mimetype,
82+ delegateItem.model.title,
83+ delegateItem.model.comment,
84+ delegateItem.model.dndUri,
85+ delegateItem.model.metadata)
86+ }
87+ onPressAndHold: {
88+ effect.positionPx = mapToItem(categoryView, 0, itemY).y
89+ scopeView.scope.preview( delegateItem.model.uri,
90+ delegateItem.model.icon,
91+ delegateItem.model.category,
92+ 0,
93+ delegateItem.model.mimetype,
94+ delegateItem.model.title,
95+ delegateItem.model.comment,
96+ delegateItem.model.dndUri,
97+ delegateItem.model.metadata)
10698 }
10799 }
108100 }
@@ -122,6 +114,28 @@ ScopeView {
122114 }
123115 }
124116
117+ function getDefaultRendererId(contentType) {
118+ switch (contentType) {
119+ default: return "grid";
120+ }
121+ }
122+
123+ function getRenderer(rendererId, contentType) {
124+ if (rendererId == "default") {
125+ rendererId = getDefaultRendererId(contentType);
126+ }
127+ switch (rendererId) {
128+ case "grid": {
129+ switch (contentType) {
130+ case "video": return "Generic/GenericFilterGridPotrait.qml";
131+ default: return "Generic/GenericFilterGrid.qml";
132+ }
133+ }
134+ case "carousel": return "Generic/GenericCarousel.qml";
135+ default: return "Generic/GenericFilterGrid.qml";
136+ }
137+ }
138+
125139 OpenEffect {
126140 id: effect
127141 anchors {
Dash/Music/AlbumTile.qml+2−2
@@ -69,7 +69,7 @@ AbstractButton {
6969 rightMargin: units.gu(1)
7070 }
7171
72− color: "#f3f3e7"
72+ color: Theme.palette.selected.backgroundText
7373 opacity: 0.9
7474 style: Text.Raised
7575 styleColor: "black"
@@ -97,7 +97,7 @@ AbstractButton {
9797 rightMargin: units.gu(1)
9898 }
9999
100− color: "#f3f3e7"
100+ color: Theme.palette.selected.backgroundText
101101 opacity: 0.9
102102 style: Text.Raised
103103 styleColor: "black"
Dash/Video/VideoPreview.qml+3−3
@@ -48,7 +48,7 @@ DashPreview {
4848 property int buttonWidth: Math.min(units.gu(22), (width - spacing) / 2)
4949 Button {
5050 width: parent.buttonWidth
51− color: "#dd4814"
51+ color: Theme.palette.selected.foreground
5252 text: nfo.ready && nfo.video.buyPrice != "" ? "Buy for " + nfo.video.buyPrice : ""
5353 visible: text != ""
5454 iconSource: "graphics/icon_button_amazon.png"
@@ -56,7 +56,7 @@ DashPreview {
5656 }
5757 Button {
5858 width: parent.buttonWidth
59− color: "#dd4814"
59+ color: Theme.palette.selected.foreground
6060 text: nfo.ready && nfo.video.buyPrice != "" ? "Rent for " + nfo.video.rentPrice : ""
6161 visible: text != ""
6262 iconSource: "graphics/icon_button_u1.png"
@@ -73,7 +73,7 @@ DashPreview {
7373
7474 Label {
7575 text: nfo.ready ? nfo.video.plot : ""
76− color: "#f3f3e7"
76+ color: Theme.palette.selected.backgroundText
7777 opacity: .6
7878 fontSize: "medium"
7979 width: parent.width
Notifications/Notification.qml+6−5
@@ -28,6 +28,7 @@ UbuntuShape {
2828 property var actions
2929 property var notificationId
3030 property var type
31+ property var hints
3132 property var notification
3233
3334 objectName: "background"
@@ -117,7 +118,7 @@ UbuntuShape {
117118 }
118119 fontSize: "medium"
119120 font.bold: true
120− color: "#f3f3e7"
121+ color: Theme.palette.selected.backgroundText
121122 elide: Text.ElideRight
122123 }
123124
@@ -131,7 +132,7 @@ UbuntuShape {
131132 }
132133 visible: body != ""
133134 fontSize: "small"
134− color: "#f3f3e7"
135+ color: Theme.palette.selected.backgroundText
135136 opacity: 0.6
136137 wrapMode: Text.WordWrap
137138 maximumLineCount: 10
@@ -164,7 +165,7 @@ UbuntuShape {
164165 bottom: parent.bottom
165166 }
166167 text: notification.type == Notification.SnapDecision && actionRepeater.count >= 2 ? actionRepeater.itemAt(1).actionLabel : ""
167− color: "#cdcdcb"
168+ gradient: UbuntuColors.greyGradient
168169 onClicked: {
169170 if (actionRepeater.count > 2) {
170171 buttonRow.expanded = !buttonRow.expanded
@@ -194,7 +195,7 @@ UbuntuShape {
194195 top: parent.top
195196 bottom: parent.bottom
196197 }
197− color: "#d85317"
198+ gradient: notification.hints == Notification.ButtonTint ? UbuntuColors.orangeGradient : UbuntuColors.greyGradient
198199 visible: width > 0
199200 onClicked: notification.notification.invokeAction(actionRepeater.itemAt(0).actionId)
200201 }
@@ -241,7 +242,7 @@ UbuntuShape {
241242
242243 text: loader.actionLabel
243244 height: units.gu(4)
244− color: "#cdcdcb"
245+ gradient: UbuntuColors.greyGradient
245246 onClicked: notification.notification.invokeAction(loader.actionId)
246247 }
247248 }
Notifications/Notifications.qml+1
@@ -32,6 +32,7 @@ ListView {
3232 right: parent.right
3333 }
3434 type: model.type
35+ hints: model.hints
3536 iconSource: model.icon
3637 secondaryIconSource: model.secondaryIcon
3738 summary: model.summary
Panel/IndicatorItem.qml+6−1
@@ -17,6 +17,7 @@
1717import QtQuick 2.0
1818import Ubuntu.Components 0.1
1919import Unity.Indicators 0.1 as Indicators
20+import "../Components"
2021
2122Item {
2223 id: indicatorItem
@@ -27,6 +28,7 @@ Item {
2728 property var indicatorProperties: undefined
2829
2930 opacity: dimmed ? 0.4 : 1
31+ Behavior on opacity { StandardAnimation {} }
3032
3133 // only visible when non-empty
3234 visible: loader.item != undefined && loader.status == Loader.Ready ? loader.item.enabled : false
@@ -47,11 +49,14 @@ Item {
4749 }
4850
4951 Rectangle {
50− color: "#dd4814"
52+ color: Theme.palette.selected.foreground
5153 objectName: "highlight"
5254 height: units.dp(2)
5355 width: parent.width
5456 anchors.top: parent.bottom
5557 visible: highlighted
58+
59+ opacity: highlighted ? 1 : 0
60+ Behavior on opacity { StandardAnimation {} }
5661 }
5762}
Panel/IndicatorRow.qml+34−49
@@ -16,6 +16,7 @@
1616
1717import QtQuick 2.0
1818import Ubuntu.Components 0.1
19+import "../Components"
1920
2021Item {
2122 id: indicatorRow
@@ -24,23 +25,19 @@ Item {
2425 readonly property int currentItemIndex: currentItem ? currentItem.ownIndex : -1
2526 property alias row: row
2627 property QtObject indicatorsModel: null
27− property bool overviewActive: false // "state of the menu"
28−
29− Behavior on y {NumberAnimation {duration: 300; easing.type: Easing.OutCubic} }
3028
3129 width: units.gu(40)
3230 height: units.gu(3)
3331
34− Component.onCompleted: setDefaultItem()
35−
3632 function setDefaultItem() {
3733 // The leftmost indicator
38− var defaultItemIndex = 0
39− setItem(defaultItemIndex)
34+ setCurrentItem(0);
4035 }
4136
42− function setItem(index) {
43− currentItem = rowRepeater.itemAt(index)
37+ function setCurrentItem(index) {
38+ if (currentItemIndex !== index) {
39+ currentItem = rowRepeater.itemAt(index);
40+ }
4441 }
4542
4643 Row {
@@ -55,51 +52,39 @@ Item {
5552 objectName: "rowRepeater"
5653 model: indicatorsModel ? indicatorsModel : undefined
5754
58− IndicatorItem {
59− id: indicatorItem
55+ Item {
56+ id: itemWrapper
57+ height: indicatorRow.height
58+ width: childrenRect.width
6059
61− property int ownIndex: index
60+ property int ownIndex: index
61+ property alias highlighted: indicatorItem.highlighted
62+ property alias dimmed: indicatorItem.dimmed
6263
63− widgetSource: model.widgetSource
64+ IndicatorItem {
65+ id: indicatorItem
66+ height: parent.height
67+
68+ highlighted: indicatorRow.state != "initial" ? itemWrapper.ownIndex == indicatorRow.currentItemIndex : false
69+ dimmed: indicatorRow.state != "initial" ? itemWrapper.ownIndex != indicatorRow.currentItemIndex : false
70+
71+ widgetSource: model.widgetSource
72+ indicatorProperties : model.indicatorProperties
73+ }
6474
65− indicatorProperties : model.indicatorProperties
66− highlighted: indicatorRow.state == "reveal" || indicatorRow.state == "locked" || indicatorRow.state == "commit" ? ownIndex == indicatorRow.currentItemIndex : false
67− dimmed: { //See FIXME in Indicators regarding the "states" change
68− if (indicatorRow.state == "initial" || indicatorRow.state == "") {
69− return false;
70− } else if (indicatorRow.state == "hint") {
71− return true
72− } else {
73− return ownIndex != indicatorRow.currentItemIndex
74− }
75− }
76− height: indicatorRow.height
77− y: {
78− //FIXME: all indicators will be initial for now.
79− if (!highlighted && !overviewActive && (indicatorRow.state == "locked" || indicatorRow.state == "commit")) {
80− return -indicatorRow.height
81− } else {
82− return 0
83− }
84− }
85− Behavior on y {
86− NumberAnimation{
87− duration: {
88− if (index == 0) {
89− return 250
90− } else if (index == 1) {
91− return 150
92− } else if (index == 2) {
93− return 550
94− } else if (index == 3) {
95− return 400
96− } else {
97− return 100 + Math.random() * 200
98− }
99− }
100− easing.type: Easing.OutCubic
75+ opacity: {
76+ if (!indicatorItem.highlighted && (indicatorRow.state == "locked" || indicatorRow.state == "commit")) {
77+ return 0.0;
78+ } else {
79+ return 1.0;
10180 }
10281 }
82+ Behavior on opacity {
83+ StandardAnimation {
84+ // flow away from current index
85+ duration: (rowRepeater.count - Math.abs(indicatorRow.currentItemIndex - index)) * (500/rowRepeater.count)
86+ }
87+ }
10388 }
10489 }
10590 }
Panel/Indicators.qml+102−137
@@ -25,16 +25,15 @@ import "../Components/Math.js" as MathLocal
2525Showable {
2626 id: indicators
2727
28− property int referenceOpenedHeight: units.gu(71)
29− property real openedHeight: pinnedMode ? referenceOpenedHeight - panelHeight
30− : referenceOpenedHeight
28+ property real openedHeight: units.gu(71)
3129 property int panelHeight: units.gu(3)
3230 property bool pinnedMode: true //should be set true if indicators menu can cover whole screen
3331
3432 property int hintValue
35− readonly property int lockThreshold: referenceOpenedHeight / 2
33+ readonly property int lockThreshold: openedHeight / 2
3634 property bool fullyOpened: height == openedHeight
3735 property bool partiallyOpened: height > panelHeight && !fullyOpened
36+ property real visualBottom: Math.max(y+height, y+indicatorRow.y+indicatorRow.height)
3837
3938 // TODO: Perhaps we need a animation standard for showing/hiding? Each showable seems to
4039 // use its own values. Need to ask design about this.
@@ -43,6 +42,13 @@ Showable {
4342 duration: 350
4443 to: openedHeight
4544 easing.type: Easing.OutCubic
45+
46+ // Re-size if we've changed the openHeight while shown.
47+ onToChanged: {
48+ if (indicators.shown) {
49+ indicators.show();
50+ }
51+ }
4652 }
4753
4854 hideAnimation: StandardAnimation {
@@ -56,44 +62,32 @@ Showable {
5662
5763 onHeightChanged: {
5864 // need to use handle.get_height(). As the handle height depends on indicators.height changes (but this is called first!)
59− var contentProgress = indicators.height - handle.get_height()
65+ var revealProgress = indicators.height - panelHeight
6066 if (!showAnimation.running && !hideAnimation.running) {
61− if (contentProgress <= hintValue && indicators.state == "reveal") {
62− indicators.state = "hint"
63− menuContent.hideAll()
64− } else if (contentProgress > hintValue && contentProgress < lockThreshold) {
65− menuContent.showMenu()
66− indicators.state = "reveal"
67− } else if (contentProgress >= lockThreshold && lockThreshold > 0) {
68− // If we've shown the overview and are closing the view with progress changes (revealer handle),
69− // we dont want to switch to a indicator menu until we hit reveal state.
70− if (menuContent.overviewActive) {
71− menuContent.showOverview()
72− } else {
73− menuContent.showMenu()
74− }
75− indicators.state = "locked"
67+ if (revealProgress == 0) {
68+ indicators.state = "initial";
69+ } else if (revealProgress <= hintValue) {
70+ indicators.state = "hint";
71+ } else if (revealProgress > hintValue && revealProgress < lockThreshold) {
72+ indicators.state = "reveal";
73+ } else if (revealProgress >= lockThreshold && lockThreshold > 0) {
74+ indicators.state = "locked";
7675 }
7776 }
7877
79− if (contentProgress == 0) {
80− menuContent.releaseContent()
78+ if (revealProgress == 0) {
79+ menuContent.releaseContent();
8180 }
8281 }
8382
84− function openOverview() {
85− indicatorRow.currentItem = null
86− menuContent.showOverview()
87− }
88−
8983 function calculateCurrentItem(xValue, useBuffer) {
90− var rowCoordinates
91− var itemCoordinates
92− var currentItem
93− var distanceFromRightEdge
94− var bufferExceeded = false
84+ var rowCoordinates;
85+ var itemCoordinates;
86+ var currentItem;
87+ var distanceFromRightEdge;
88+ var bufferExceeded = false;
9589
96− if (indicators.state == "commit" || indicators.state == "locked" || showAnimation.running || hideAnimation.running) return
90+ if (indicators.state == "commit" || indicators.state == "locked" || showAnimation.running || hideAnimation.running) return;
9791
9892 /*
9993 If user drags the indicator handle bar down a distance hintValue or less, this is 0.
@@ -102,14 +96,22 @@ Showable {
10296 */
10397 var verticalProgress =
10498 MathLocal.clamp((indicators.height - handle.height - hintValue) /
105− (lockThreshold - hintValue), 0, 1)
99+ (lockThreshold - hintValue), 0, 1);
100+
101+ /*
102+ Vertical velocity check. Don't change the indicator if we're moving too quickly.
103+ */
104+ var verticalSpeed = Math.abs(yVelocityCalculator.calculate());
105+ if (verticalSpeed >= 0.05 && indicatorRow.currentItem!=null) {
106+ return;
107+ }
106108
107109 /*
108110 Percentage of an indicator icon's width the user's press can stray horizontally from the
109111 focused icon before we change focus to another icon. E.g. a value of 0.5 means you must
110112 go right a distance of half an icon's width before focus moves to the icon on the right
111113 */
112− var maxBufferThreshold = 0.5
114+ var maxBufferThreshold = 0.5;
113115
114116 /*
115117 To help users find the indicator of their choice while opening the indicators, we add logic to add a
@@ -124,19 +126,21 @@ Showable {
124126 currentItem = indicatorRow.row.childAt(rowCoordinates.x, 0);
125127 if (currentItem && currentItem != indicatorRow.currentItem ) {
126128 itemCoordinates = indicatorRow.row.mapToItem(currentItem, rowCoordinates.x, 0);
127− distanceFromRightEdge = (currentItem.width - itemCoordinates.x) / (currentItem.width)
129+ distanceFromRightEdge = (currentItem.width - itemCoordinates.x) / (currentItem.width);
128130 if (Math.abs(currentItem.ownIndex - indicatorRow.currentItemIndex) > 1) {
129− bufferExceeded = true
131+ bufferExceeded = true;
130132 } else {
131133 if (indicatorRow.currentItemIndex < currentItem.ownIndex && distanceFromRightEdge < (1 - effectiveBufferThreshold)) {
132− bufferExceeded = true
134+ bufferExceeded = true;
133135 } else if (indicatorRow.currentItemIndex > currentItem.ownIndex && distanceFromRightEdge > effectiveBufferThreshold) {
134− bufferExceeded = true
136+ bufferExceeded = true;
135137 }
136138 }
137139 if ((!useBuffer || (useBuffer && bufferExceeded)) || indicatorRow.currentItem < 0 || indicatorRow.currentItem == null) {
138140 indicatorRow.currentItem = currentItem;
139141 }
142+ } else if (!currentItem) {
143+ indicatorRow.setDefaultItem();
140144 }
141145 }
142146
@@ -169,14 +173,20 @@ Showable {
169173 left: parent.left
170174 right: parent.right
171175 top: indicatorRow.bottom
172− bottom: parent.bottom
176+ bottom: handle.top
173177 }
174178 indicatorsModel: indicatorsModel
175− animate: false
176− clip: indicators.partiallyOpened
179+ clip: !indicators.fullyOpened
177180
178− onMenuSelected: {
179− indicatorRow.setItem(index)
181+ Connections {
182+ target: indicatorRow
183+ onCurrentItemIndexChanged: menuContent.updateCurrentMenu();
184+ }
185+
186+ function updateCurrentMenu() {
187+ if (currentMenuIndex != indicatorRow.currentItemIndex) {
188+ currentMenuIndex = indicatorRow.currentItemIndex;
189+ }
180190 }
181191
182192 //small shadow gradient at bottom of menu
@@ -203,13 +213,13 @@ Showable {
203213 anchors {
204214 left: parent.left
205215 right: parent.right
206− top: menuContent.bottom
216+ bottom: parent.bottom
207217 }
208218 height: get_height()
209219 clip: height < handleImage.height
210220
211221 function get_height() {
212− return Math.max(Math.min(handleImage.height, indicators.height - handleImage.height), 0)
222+ return Math.max(Math.min(handleImage.height, indicators.height - handleImage.height), 0);
213223 }
214224
215225 BorderImage {
@@ -227,33 +237,14 @@ Showable {
227237 }
228238 }
229239
230− PanelSeparatorLine {
231− id: indicatorsSeparatorLine
232− visible: true
233− anchors {
234− top: handle.bottom
235− left: indicators.left
236− right: indicators.right
237− }
238− }
239−
240− BorderImage {
241− id: dropShadow
242− anchors {
243− top: indicators.top
244− bottom: indicatorsSeparatorLine.bottom
245− left: indicators.left
246− right: indicators.right
247− margins: -units.gu(1)
248− }
249− visible: indicators.height > panelHeight
250− source: "graphics/rectangular_dropshadow.sci"
251− }
252−
253240 PanelBackground {
254241 anchors.fill: indicatorRow
255242 }
256243
244+ IndicatorsDataModel {
245+ id: indicatorsModel
246+ }
247+
257248 IndicatorRow {
258249 id: indicatorRow
259250 objectName: "indicatorRow"
@@ -264,63 +255,46 @@ Showable {
264255 height: indicators.panelHeight
265256 indicatorsModel: indicatorsModel
266257 state: indicators.state
267− overviewActive: menuContent.overviewActive
268−
269− onCurrentItemIndexChanged: menuContent.currentIndex = currentItemIndex
270258
271− }
272−
273− IndicatorsDataModel {
274− id: indicatorsModel
259+ Connections {
260+ target: menuContent
261+ onCurrentMenuIndexChanged: {
262+ indicatorRow.setCurrentItem(menuContent.currentMenuIndex);
263+ }
264+ }
275265 }
276266
277267 Connections {
278− target: hideAnimation
268+ target: showAnimation
279269 onRunningChanged: {
280− if (hideAnimation.running) {
281− indicators.state = "initial"
282− menuContent.hideAll()
283− } else {
284− if (state == "initial") indicatorRow.setDefaultItem()
270+ if (showAnimation.running) {
271+ indicators.state = "commit";
285272 }
286273 }
287274 }
275+
288276 Connections {
289− target: showAnimation
277+ target: hideAnimation
290278 onRunningChanged: {
291− if (showAnimation.running) {
292− if (indicators.state == "initial") {
293− openOverview()
294− } else {
295− indicators.calculateCurrentItem(dragHandle.touchX, false)
296− menuContent.showMenu()
297− }
298− indicators.state = "commit"
279+ if (hideAnimation.running) {
280+ indicators.state = "initial";
281+ indicatorRow.currentItem = null;
299282 }
300283 }
301284 }
302285
286+ property var activeDragHandle: showDragHandle.dragging ? showDragHandle : hideDragHandle.dragging ? hideDragHandle : null
287+ // connections to the active drag handle
303288 Connections {
304− target: dragHandle
289+ target: activeDragHandle
305290 onTouchXChanged: {
306− var buffer = dragHandle.dragging ? true : false
307− indicators.calculateCurrentItem(dragHandle.touchX, buffer)
291+ indicators.calculateCurrentItem(activeDragHandle.touchX, true);
292+ }
293+ onTouchSceneYChanged: {
294+ yVelocityCalculator.trackedPosition = activeDragHandle.touchSceneY;
308295 }
309296 }
310297
311− // We start with pinned states. (default pinnedMode: true)
312− states: pinnedModeStates
313− // because of dynamic assignment of states, we need to assign state after completion.
314− Component.onCompleted: state = "initial"
315−
316− // changing states will reset state to "".
317− onPinnedModeChanged: {
318− var last_state = state;
319− states = (pinnedMode) ? pinnedModeStates : offScreenModeStates;
320− state = last_state;
321− }
322−
323− property var dragHandle: showDragHandle.dragging ? showDragHandle : hideDragHandle
324298 DragHandle {
325299 id: showDragHandle
326300 anchors.bottom: parent.bottom
@@ -334,42 +308,52 @@ Showable {
334308 hintDisplacement: pinnedMode ? indicators.hintValue : 0
335309 autoCompleteDragThreshold: maxTotalDragDistance / 2
336310 stretch: true
337− maxTotalDragDistance: openedHeight - handle.height
311+ maxTotalDragDistance: openedHeight - panelHeight
338312 distanceThreshold: pinnedMode ? 0 : units.gu(3)
339313
340314 onStatusChanged: {
341315 if (status === DirectionalDragArea.Recognized) {
342− menuContent.hideAll()
343− menuContent.activateContent()
316+ menuContent.activateContent();
344317 }
345318 }
346319 }
347320 DragHandle {
348321 id: hideDragHandle
349322 anchors.fill: handle
350− height: panelHeight
351323 direction: Direction.Upwards
352324 enabled: indicators.shown
353− hintDisplacement: units.gu(2)
325+ hintDisplacement: indicators.hintValue
354326 autoCompleteDragThreshold: maxTotalDragDistance / 6
355327 stretch: true
356− maxTotalDragDistance: referenceOpenedHeight - 2*panelHeight
328+ maxTotalDragDistance: openedHeight - panelHeight
357329 distanceThreshold: 0
358330 }
359331
360− property list<State> offScreenModeStates: [
332+ AxisVelocityCalculator {
333+ id: yVelocityCalculator
334+ }
335+
336+ states: [
361337 State {
362338 name: "initial"
363339 },
364340 State {
365341 name: "hint"
366− PropertyChanges { target: indicatorRow; y: panelHeight }
342+ PropertyChanges {
343+ target: indicatorRow;
344+ y: pinnedMode ? 0 : panelHeight
345+ }
346+ StateChangeScript {
347+ script: {
348+ if (activeDragHandle) {
349+ calculateCurrentItem(activeDragHandle.touchX, false);
350+ }
351+ }
352+ }
367353 },
368354 State {
369355 name: "reveal"
370356 extend: "hint"
371− PropertyChanges { target: menuContent; animate: true }
372− StateChangeScript { script: calculateCurrentItem(dragHandle.touchX, false); }
373357 },
374358 State {
375359 name: "locked"
@@ -380,26 +364,7 @@ Showable {
380364 extend: "hint"
381365 }
382366 ]
383−
384− property list<State> pinnedModeStates: [
385− State {
386− name: "initial"
387− },
388− State {
389− name: "hint"
390− },
391− State {
392− name: "reveal"
393− PropertyChanges { target: menuContent; animate: true }
394− StateChangeScript { script: calculateCurrentItem(dragHandle.touchX, false); }
395− },
396− State {
397− name: "locked"
398− },
399− State {
400− name: "commit"
401− }
402− ]
367+ state: "initial"
403368
404369 transitions: [
405370 Transition {
Panel/Indicators/DatetimeIndicatorPage.qml+2−2
@@ -54,7 +54,7 @@ Flickable {
5454 }
5555
5656 text: ""
57− color: "#f3f3e7"
57+ color: Theme.palette.selected.backgroundText
5858 font.weight: Font.Light
5959 font.pixelSize: units.gu(6)
6060 }
@@ -69,7 +69,7 @@ Flickable {
6969 bottomMargin: units.gu(1.5)
7070 }
7171 text: ""
72− color: "#f3f3e7"
72+ color: Theme.palette.selected.backgroundText
7373 fontSize: "small"
7474 }
7575 }
Panel/Indicators/DatetimeIndicatorWidget.qml+1−1
@@ -31,7 +31,7 @@ Indicators.IndicatorWidget {
3131 Label {
3232 id: timeLabel
3333 objectName: "timeLabel"
34− color: "#f3f3e7"
34+ color: Theme.palette.selected.backgroundText
3535 opacity: 0.8
3636 font.family: "Ubuntu"
3737 fontSize: "medium"
Panel/Indicators/DefaultIndicatorWidget.qml+1−1
@@ -52,7 +52,7 @@ Indicators.IndicatorWidget {
5252 Label {
5353 id: itemLabel
5454 objectName: "itemLabel"
55− color: "#f3f3e7"
55+ color: Theme.palette.selected.backgroundText
5656 opacity: 0.8
5757 font.family: "Ubuntu"
5858 fontSize: "medium"
Panel/MenuContent.qml+22−71
@@ -17,50 +17,31 @@
1717import QtQuick 2.0
1818import Ubuntu.Components 0.1
1919import Unity.Indicators 0.1 as Indicators
20−import "Menus"
2120import "../Components"
2221
2322Rectangle {
2423 id: content
2524
2625 property QtObject indicatorsModel: null
27− property bool animate: true // FIXME: Remove. This doesnt seem to be being used and it's referenced in Indicators.
28− property bool overviewActive: true // "state of the menu"
29− property bool __shown: false
3026 property bool __contentActive: false
31− property alias currentIndex : menus.currentIndex
32− property color backgroundColor: "#221e1c"
27+ property alias currentMenuIndex : menus.currentIndex
28+ property color backgroundColor: "#221e1c" // FIXME not in palette yet
3329 property int contentReleaseInterval: 20000
3430
3531 width: units.gu(40)
3632 height: units.gu(42)
3733 color: backgroundColor
38− enabled: __shown
3934
4035 signal menuSelected(int index)
4136
42− function showMenu() {
43− __shown = true
44− overviewActive = false
45− }
46−
47− function showOverview() {
48− __shown = true
49− overviewActive = true
50− }
51−
52− function hideAll() {
53− __shown = false
54− }
55−
5637 function activateContent() {
57− contentReleaseTimer.stop()
58− __contentActive = true
38+ contentReleaseTimer.stop();
39+ __contentActive = true;
5940 }
6041
6142 function releaseContent() {
6243 if (__contentActive)
63− contentReleaseTimer.restart()
44+ contentReleaseTimer.restart();
6445 }
6546
6647 ListView {
@@ -72,7 +53,6 @@ Rectangle {
7253 width: parent.width
7354
7455 spacing: units.gu(0.5)
75− opacity: !overviewActive && __shown ? 1 : 0
7656 enabled: opacity != 0
7757 orientation: ListView.Horizontal
7858 model: indicatorsModel
@@ -83,24 +63,23 @@ Rectangle {
8363
8464 delegate: Loader {
8565 clip: true
86− property bool contentActive: content.__contentActive
66+ property bool contentActive: content.__contentActive && menuActivator.content[index].active
8767
8868 onContentActiveChanged: {
8969 if (contentActive && item) {
90− item.start()
70+ item.start();
9171 } else if (!contentActive && item) {
92− item.stop()
72+ item.stop();
9373 }
9474 }
9575
9676 width: menus.width
9777 height: menus.height
9878 source: pageSource
99− visible: content.__shown
10079 onVisibleChanged: {
10180 // Reset the indicator states
10281 if (!visible && item && item["reset"]) {
103− item.reset()
82+ item.reset();
10483 }
10584 }
10685 asynchronous: true
@@ -108,11 +87,11 @@ Rectangle {
10887 onLoaded: {
10988 for(var pName in indicatorProperties) {
11089 if (item.hasOwnProperty(pName)) {
111− item[pName] = indicatorProperties[pName]
90+ item[pName] = indicatorProperties[pName];
11291 }
11392 }
11493 if (contentActive && menus.visible) {
115− item.start()
94+ item.start();
11695 }
11796 }
11897
@@ -124,40 +103,6 @@ Rectangle {
124103 value: handle.height
125104 }
126105 }
127−
128− Behavior on opacity { NumberAnimation {duration: 200} }
129− }
130−
131− Overview {
132− id: overview
133− objectName: "overview"
134−
135− anchors.top: header.bottom
136− anchors.bottom: parent.bottom
137− // FIXME: Dont know why we're using handle height (introduces dep).. Check with design about bottom margin
138− anchors.bottomMargin: handle.height
139−
140− width: content.width
141− indicatorsModel: content.indicatorsModel
142− enabled: content.overviewActive && content.__shown
143− opacity: content.overviewActive && content.__shown ? 1 : 0
144− Behavior on opacity {NumberAnimation{duration: 200}}
145− visible: opacity != 0
146−
147− onMenuSelected: {
148− var storedDuration = menus.highlightMoveDuration
149− var storedVelocity = menus.highlightMoveVelocity
150− menus.highlightMoveDuration = 0
151− menus.highlightMoveVelocity = 100000
152−
153− menus.currentIndex = modelIndex
154− content.overviewActive = false
155−
156− menus.highlightMoveDuration = storedDuration
157− menus.highlightMoveVelocity = storedVelocity
158−
159− content.menuSelected(modelIndex)
160− }
161106 }
162107
163108 Rectangle {
@@ -181,14 +126,10 @@ Rectangle {
181126 right: parent.right
182127 }
183128 text: {
184− if (content.overviewActive)
185− return i18n.tr("Device");
186129 if (indicatorsModel && menus.currentIndex >= 0 && menus.currentIndex < indicatorsModel.count)
187130 return indicatorsModel.data(menus.currentIndex, Indicators.IndicatorsModelRole.Title);
188131 return "";
189132 }
190− opacity: __shown ? 1 : 0
191− Behavior on opacity {NumberAnimation{duration: 100}}
192133 }
193134 }
194135
@@ -196,6 +137,16 @@ Rectangle {
196137 id: contentReleaseTimer
197138
198139 interval: contentReleaseInterval
199− onTriggered: __contentActive = false
140+ onTriggered: {
141+ __contentActive = false;
142+ menuActivator.clear();
143+ }
144+ }
145+
146+ Indicators.MenuContentActivator {
147+ id: menuActivator
148+ running: content.__contentActive
149+ baseIndex: content.currentMenuIndex
150+ count: indicatorsModel.count
200151 }
201152}
Panel/Menus/Overview.qmldeleted−70
@@ -1,70 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−import "../../Components/ListItems" as ListItems
20−import "Overview"
21−
22−IndicatorMenuWindow {
23− id: overview
24−
25− property QtObject indicatorsModel: null
26−
27− signal menuSelected(int modelIndex)
28−
29− VisualItemModel {
30− id: itemModel
31−
32− FlightModeWidget {
33− anchors {
34− left: parent ? parent.left : undefined
35− right: parent ? parent.right : undefined
36− }
37− height: units.gu(8)
38−
39− ListItems.ThinDivider {
40− anchors {
41− bottom: parent.bottom
42− bottomMargin: -units.dp(1)
43− }
44− }
45− }
46−
47− VolumeWidget {
48− anchors {
49− left: parent ? parent.left : undefined
50− right: parent ? parent.right : undefined
51− }
52− height: units.gu(10)
53− }
54−
55− OverviewGrid {
56− model: indicatorsModel
57− anchors {
58− left: parent ? parent.left : undefined
59− right: parent ? parent.right : undefined
60− }
61− }
62− }
63−
64− ListView {
65− id: listView
66− anchors.fill: parent
67− model: itemModel
68− interactive: false
69− }
70−}
Panel/Menus/Overview/FlightModeWidget.qmldeleted−60
@@ -1,60 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−
20−Item {
21− id: flightModeWidget
22−
23− Image {
24− id: flightIcon
25− source: "graphics/plane_icon.png"
26− anchors {
27− left: parent.left
28− leftMargin: units.gu(2)
29− verticalCenter: parent.verticalCenter
30− }
31− width: units.gu(4)
32− height: units.gu(4)
33− }
34−
35− Label {
36− anchors {
37− left: flightIcon.right
38− right: flightModeSwitch.left
39− margins: units.gu(2)
40− verticalCenter: parent.verticalCenter
41− }
42− text: i18n.tr("Flight mode")
43− color: "#f3f3e7"
44− style: Text.Raised
45− styleColor: "black"
46− opacity: 0.6;
47− fontSize: "medium"
48− elide: Text.ElideRight
49− horizontalAlignment: Text.AlignLeft
50− }
51−
52− Switch {
53− id: flightModeSwitch
54− anchors {
55− right: parent.right
56− rightMargin: units.gu(2)
57− verticalCenter: parent.verticalCenter
58− }
59− }
60−}
Panel/Menus/Overview/OverviewGrid.qmldeleted−135
@@ -1,135 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−
20−import "../../../Components/ListItems" as ListItems
21−
22−Item {
23− id: overviewGrid
24−
25− height: childrenRect.height
26−
27− property var model
28−
29− UbuntuShape {
30− id: gridBackground
31− anchors.fill: grid
32− color: "#282421"
33− radius: "medium"
34− }
35−
36− GridView {
37− id: grid
38− objectName: "overviewGrid"
39−
40− property int columnCount: 3
41− property int rowCount: Math.ceil(model.count / columnCount)
42−
43− anchors {
44− top: parent.top
45− left: parent.left
46− right: parent.right
47− topMargin: units.gu(3)
48− leftMargin: units.gu(2)
49− rightMargin: units.gu(2)
50− }
51− height: cellWidth * rowCount
52− cellWidth: Math.floor((width) / columnCount)
53− cellHeight: cellWidth
54− model: overviewGrid.model
55− visible: opacity != 0
56− interactive: false
57− delegate:
58− AbstractButton {
59− objectName: "overviewGridButton" + index
60− width: grid.cellWidth
61− height: grid.cellHeight
62−
63− Loader {
64− id: loader
65− source: widgetSource
66−
67− width: units.gu(5)
68− height: units.gu(5)
69− anchors.centerIn: parent
70− anchors.verticalCenterOffset: -units.gu(1)
71−
72− onLoaded: {
73− for(var pName in indicatorProperties) {
74− if (item.hasOwnProperty(pName)) {
75− item[pName] = indicatorProperties[pName]
76− }
77− }
78− }
79− }
80−
81− Label {
82− text: title
83− color: "#f3f3e7"
84− fontSize: "small"
85− anchors {
86− left: parent.left
87− right: parent.right
88− bottom: parent.bottom
89− leftMargin: units.gu(0.5)
90− rightMargin: units.gu(0.5)
91− bottomMargin: units.gu(1.5)
92− }
93− elide: Text.ElideRight
94− horizontalAlignment: Text.AlignHCenter
95−
96− }
97− onClicked: overview.menuSelected(index)
98− }
99− }
100−
101− Item {
102− id: gridOverlay
103− anchors.fill: grid
104−
105− Column {
106− anchors {
107− left: parent.left
108− right: parent.right
109− }
110− spacing: grid.cellHeight
111− y: grid.cellHeight - units.dp(1)
112− Repeater {
113− model: grid.rowCount - 1
114− ListItems.ThinDivider {
115− anchors.margins: units.dp(1)
116− }
117− }
118− }
119−
120− Row {
121− anchors {
122− top: parent.top
123− bottom: parent.bottom
124− }
125− spacing: grid.cellWidth - units.dp(1)
126− x: grid.cellWidth - units.dp(1)
127− Repeater {
128− model: grid.columnCount - 1
129− ListItems.VerticalThinDivider {
130− anchors.margins: units.dp(1)
131− }
132− }
133− }
134− }
135−}
Panel/Menus/Overview/VolumeWidget.qmldeleted−88
@@ -1,88 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−import "../../../Components/"
20−
21−Item {
22− id: volumeWidget
23−
24− Image {
25− id: minVolumeIcon
26− objectName: "minVolumeIcon"
27− source: "graphics/sound_off_icon.png"
28− anchors {
29− left: parent.left
30− leftMargin: units.gu(2)
31− verticalCenter: parent.verticalCenter
32− }
33− width: units.gu(4)
34− height: units.gu(4)
35−
36− MouseArea {
37− anchors.fill: parent
38− onClicked: volumeControl.volumeDown();
39− }
40− }
41−
42− Slider {
43− id: slider
44− objectName: "volumeSlider"
45− anchors {
46− left: minVolumeIcon.right
47− right: maxVolumeIcon.left
48− margins: units.gu(1)
49− verticalCenter: parent.verticalCenter
50− }
51− live: false
52− minimumValue: 0
53− maximumValue: 100
54− value: 50
55−
56− onValueChanged: {
57− volumeControl.volume = value;
58− }
59−
60− Binding {
61− target: slider
62− property: "value"
63− value: volumeControl.volume
64− }
65−
66− function formatValue(v) {
67− return ""
68− }
69− }
70−
71− Image {
72− id: maxVolumeIcon
73− objectName: "maxVolumeIcon"
74− source: "graphics/sound_on_icon.png"
75− anchors {
76− right: parent.right
77− rightMargin: units.gu(2)
78− verticalCenter: parent.verticalCenter
79− }
80− width: units.gu(4)
81− height: units.gu(4)
82−
83− MouseArea {
84− anchors.fill: parent
85− onClicked: volumeControl.volumeUp();
86− }
87− }
88−}
Panel/Menus/Overview/graphics/plane_icon@18.pngdeleted

Binary file not shown.

Panel/Menus/Overview/graphics/sound_off_icon@18.pngdeleted

Binary file not shown.

Panel/Menus/Overview/graphics/sound_on_icon@18.pngdeleted

Binary file not shown.

Panel/Panel.qml+28−9
@@ -39,11 +39,8 @@ Item {
3939 }
4040 height: __panelMinusSeparatorLineHeight
4141 y: 0
42− onYChanged: indicatorsMenu.y = y;
4342
44− Behavior on y {
45− NumberAnimation { duration: 200 }
46− }
43+ Behavior on y { StandardAnimation {} }
4744 }
4845
4946 PanelSeparatorLine {
@@ -78,18 +75,40 @@ Item {
7875 id: indicatorsMenu
7976
8077 anchors.right: parent.right
81− y: 0
78+ y: panelBackground.y
8279 width: root.indicatorsMenuWidth
8380 shown: false
8481 hintValue: __panelMinusSeparatorLineHeight * 3
8582 panelHeight: __panelMinusSeparatorLineHeight
86− referenceOpenedHeight: parent.height
87−
83+ openedHeight: parent.height + (pinnedMode ? 0 : root.panelHeight)
8884 pinnedMode: !fullscreenMode
8985
9086 property real unitProgress: (height - panelHeight) / (openedHeight - panelHeight)
9187 }
9288
89+ PanelSeparatorLine {
90+ id: indicatorsSeparatorLine
91+ visible: true
92+ anchors {
93+ left: indicatorsMenu.left
94+ right: indicatorsMenu.right
95+ }
96+ y: indicatorsMenu.visualBottom
97+ }
98+
99+ BorderImage {
100+ id: dropShadow
101+ anchors {
102+ top: indicators.top
103+ bottom: indicatorsSeparatorLine.bottom
104+ left: indicators.left
105+ right: indicators.right
106+ margins: -units.gu(1)
107+ }
108+ visible: indicatorsMenu.height > indicatorsMenu.panelHeight
109+ source: "graphics/rectangular_dropshadow.sci"
110+ }
111+
93112 SearchIndicator {
94113 id: search
95114 objectName: "search"
@@ -98,14 +117,14 @@ Item {
98117 state: {
99118 if (parent.width < indicatorsMenu.width + width) {
100119 if (indicatorsMenu.state != "initial") {
101− return "hiddenUp";
120+ return "hidden";
102121 }
103122 }
104123 if (root.searchVisible) {
105124 return "visible";
106125 }
107126
108− return "hiddenUp";
127+ return "hidden";
109128 }
110129
111130 width: units.gu(13)
Panel/SearchIndicator.qml+3−5
@@ -16,6 +16,7 @@
1616
1717import QtQuick 2.0
1818import Ubuntu.Components 0.1
19+import "../Components"
1920
2021Item {
2122 id: search
@@ -40,8 +41,7 @@ Item {
4041 width: parent.width
4142 height: parent.height
4243
43− Behavior on opacity {NumberAnimation {}}
44− Behavior on y {NumberAnimation {duration: 300; easing.type: Easing.OutCubic}}
44+ Behavior on opacity { StandardAnimation {} }
4545
4646 Image {
4747 id: icon
@@ -67,15 +67,13 @@ Item {
6767 name: "visible"
6868 PropertyChanges {
6969 target: container
70− y: 0
7170 opacity: 1
7271 }
7372 },
7473 State {
75− name: "hiddenUp"
74+ name: "hidden"
7675 PropertyChanges {
7776 target: container
78− y: -height
7977 opacity: 0
8078 }
8179 }
Shell.qml+27−9
@@ -36,8 +36,8 @@ FocusScope {
3636
3737 // this is only here to select the width / height of the window if not running fullscreen
3838 property bool tablet: false
39− width: tablet ? units.gu(160) : units.gu(40)
40− height: tablet ? units.gu(100) : units.gu(71)
39+ width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
40+ height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
4141
4242 property real edgeSize: units.gu(2)
4343 property url default_background: shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg"
@@ -62,6 +62,8 @@ FocusScope {
6262 property var applicationManager: ApplicationManagerWrapper {}
6363
6464 Component.onCompleted: {
65+ Theme.name = "Ubuntu.Components.Themes.SuruGradient"
66+
6567 applicationManager.sideStageEnabled = Qt.binding(function() { return sideStage.enabled })
6668
6769 // FIXME: if application focused before shell starts, shell draws on top of it only.
@@ -127,11 +129,20 @@ FocusScope {
127129
128130 Item {
129131 id: underlay
132+ objectName: "underlay"
130133 anchors.fill: parent
131− visible: !(panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth)
132− && (stages.fullyHidden
133− || (stages.fullyShown && mainStage.usingScreenshots)
134− || !stages.fullyShown && (mainStage.usingScreenshots || (sideStage.shown && sideStage.usingScreenshots)))
134+
135+ // Whether the underlay is fully covered by opaque UI elements.
136+ property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
137+
138+ // Whether the user should see the topmost application surface (if there's one at all).
139+ property bool applicationSurfaceShouldBeSeen:
140+ (mainStage.applications && mainStage.applications.count > 0)
141+ && (!stages.fullyHidden && !mainStage.usingScreenshots)
142+
143+ // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user
144+ // through the translucent parts of the shell surface.
145+ visible: !fullyCovered && !applicationSurfaceShouldBeSeen
135146
136147 Image {
137148 id: backgroundImage
@@ -149,6 +160,7 @@ FocusScope {
149160
150161 Dash {
151162 id: dash
163+ objectName: "dash"
152164
153165 available: !greeter.shown && !lockscreen.shown
154166 hides: [stages, launcher, panel.indicators]
@@ -176,7 +188,7 @@ FocusScope {
176188 }
177189 }
178190
179− // FIXME: only necessary because stagesRevealer.animatedProgress and
191+ // FIXME: only necessary because stagesOuterContainer.showProgress and
180192 // greeterRevealer.animatedProgress are not animated
181193 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
182194 }
@@ -196,6 +208,7 @@ FocusScope {
196208
197209 Showable {
198210 id: stages
211+ objectName: "stages"
199212
200213 x: width
201214
@@ -252,6 +265,7 @@ FocusScope {
252265
253266 anchors.fill: parent
254267 fullyShown: stages.fullyShown
268+ fullyHidden: stages.fullyHidden
255269 shouldUseScreenshots: !fullyShown
256270 rightEdgeEnabled: !sideStage.enabled
257271
@@ -336,7 +350,7 @@ FocusScope {
336350 }
337351
338352 DragHandle {
339− id: stagesRevealer
353+ id: stagesDragHandle
340354
341355 anchors.top: parent.top
342356 anchors.bottom: parent.bottom
@@ -344,7 +358,11 @@ FocusScope {
344358
345359 width: shell.edgeSize
346360 direction: Direction.Leftwards
347− enabled: mainStage.applications.count > 0 || sideStage.applications.count > 0
361+ property bool haveApps: mainStage.applications.count > 0 || sideStage.applications.count > 0
362+
363+ maxTotalDragDistance: haveApps ? parent.width : parent.width * 0.7
364+ // Make autocompletion impossible when !haveApps
365+ edgeDragEvaluator.minDragDistance: haveApps ? maxTotalDragDistance * 0.1 : Number.MAX_VALUE
348366 }
349367 }
350368 }
cmake/modules/Plugins.cmake+25−15
@@ -1,17 +1,12 @@
1−# Creates targets for copying and installing qmlfiles and generating the qmltypes file for a plugin
1+# Creates target for copying and installing qmlfiles
22#
3−# export_qmlplugin(plugin version sub_path [TARGETS target1 [target2 ...]])
3+# export_qmlfiles(plugin sub_path)
44#
5−# TARGETS additional install targets (eg the plugin shared object)
65#
7−# Two targets will be created:
6+# Target to be created:
87# - plugin-qmlfiles - Copies the qml files (*.qml, *.js, qmldir) into the shadow build folder.
9−# - plugin-qmltypes - Generates the qmltypes file in the shadow build folder.
10−
118
12−macro(export_qmlplugin PLUGIN VERSION PLUGIN_SUBPATH)
13− set(multi_value_keywords TARGETS)
14− cmake_parse_arguments(qmlplugin "" "" "${multi_value_keywords}" ${ARGN})
9+macro(export_qmlfiles PLUGIN PLUGIN_SUBPATH)
1510
1611 file(GLOB QMLFILES
1712 *.qml
@@ -25,17 +20,32 @@ macro(export_qmlplugin PLUGIN VERSION PLUGIN_SUBPATH)
2520 DEPENDS ${QMLFILES}
2621 )
2722
23+ # install the qmlfiles file.
24+ install(FILES ${QMLFILES}
25+ DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/${PLUGIN_SUBPATH}
26+ )
27+endmacro(export_qmlfiles)
28+
29+
30+# Creates target for generating the qmltypes file for a plugin and installs plugin files
31+#
32+# export_qmlplugin(plugin version sub_path [TARGETS target1 [target2 ...]])
33+#
34+# TARGETS additional install targets (eg the plugin shared object)
35+#
36+# Target to be created:
37+# - plugin-qmltypes - Generates the qmltypes file in the shadow build folder.
38+
39+macro(export_qmlplugin PLUGIN VERSION PLUGIN_SUBPATH)
40+ set(multi_value_keywords TARGETS)
41+ cmake_parse_arguments(qmlplugin "" "" "${multi_value_keywords}" ${ARGN})
42+
2843 # create the plugin.qmltypes file
2944 add_custom_target(${PLUGIN}-qmltypes ALL
3045 COMMAND qmlplugindump -notrelocatable ${PLUGIN} ${VERSION} ${CMAKE_BINARY_DIR}/plugins > ${CMAKE_BINARY_DIR}/plugins/${PLUGIN_SUBPATH}/plugin.qmltypes
3146 )
3247 add_dependencies(${PLUGIN}-qmltypes ${PLUGIN}-qmlfiles ${qmlplugin_TARGETS})
3348
34− # install the qmldir file.
35− install(FILES ${QMLFILES}
36− DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/${PLUGIN_SUBPATH}
37− )
38−
3949 # install the qmltypes file.
4050 install(FILES ${CMAKE_BINARY_DIR}/plugins/${PLUGIN_SUBPATH}/plugin.qmltypes
4151 DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/${PLUGIN_SUBPATH}
@@ -45,4 +55,4 @@ macro(export_qmlplugin PLUGIN VERSION PLUGIN_SUBPATH)
4555 install(TARGETS ${qmlplugin_TARGETS}
4656 DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/${PLUGIN_SUBPATH}
4757 )
48−endmacro(export_qmlplugin)
58+endmacro(export_qmlplugin)
cmake/modules/autopilot.cmake+2−1
@@ -2,8 +2,9 @@ add_custom_target(autopilot)
22
33function(declare_autopilot_test TEST_NAME TEST_SUITE WORKING_DIR)
44 add_custom_target(autopilot-${TEST_NAME}
5− COMMAND autopilot run ${TEST_SUITE}
5+ COMMAND LANG=C UBUNTU_ICON_THEME=ubuntu-mobile QML2_IMPORT_PATH=${SHELL_PRIVATE_LIBDIR}/qml/mocks autopilot run ${TEST_SUITE}
66 WORKING_DIRECTORY ${WORKING_DIR}
7+ DEPENDS install
78 )
89
910 add_dependencies(autopilot autopilot-${TEST_NAME})
debian/changelog+92
@@ -1,3 +1,95 @@
1+unity8 (7.81.3+13.10.20130726ubuntu.unity.next-0ubuntu1) saucy; urgency=low
2+
3+ [ Michal Hruby ]
4+ * Expose real category ids and not just indices.
5+ * Provide fallbacks for default renderer.
6+
7+ [ Daniel d'Andrada ]
8+ * Give a visual feedback on right-edge drag with no running apps (LP:
9+ #1116207). (LP: #1116207)
10+
11+ [ Albert Astals ]
12+ * Update m_firstVisibleIndex if there's no visible items anymore Also
13+ remove the check for m_visibleItems.isEmpty() on insert, the
14+ m_firstVisibleIndex == 0 already protects us against that.
15+
16+ [ Christopher Lee ]
17+ * Tests now use a default lightdm mock if not decorated. (LP:
18+ #1204772)
19+
20+ [ Ubuntu daily release ]
21+ * Automatic snapshot from revision 135 (ubuntu-unity/next)
22+
23+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 26 Jul 2013 07:06:08 +0000
24+
25+unity8 (7.81.3+13.10.20130725ubuntu.unity.next-0ubuntu1) saucy; urgency=low
26+
27+ [ Michal Hruby ]
28+ * Fix adding items into an empty LVWPH.
29+
30+ [ Michał Sawicz ]
31+ * Move to using upstart in run_on_device.
32+ * Added heeding and qmltest for button-tint hint.
33+ * Refactoring and cleanup of the Unity8 Autopilot tests.
34+ * Hide the Unity launcher during autopiloting and skip battery tests
35+ if unavailable.
36+
37+ [ Ying-Chun Liu ]
38+ * Let GenericScope support loading different renderers.
39+
40+ [ Nick Dedekind ]
41+ * Removed overview from indicators.
42+ * Menus for indicators is now created prioritised by distance from
43+ current item to speed up user experience.
44+ * Add ApplicationArguments to know the geometry from start.
45+
46+ [ Mirco Müller ]
47+ * Added heeding and qmltest for button-tint hint.
48+
49+ [ Christopher Lee ]
50+ * Refactoring and cleanup of the Unity8 Autopilot tests.
51+
52+ [ Thomi Richards ]
53+ * Refactoring and cleanup of the Unity8 Autopilot tests.
54+
55+ [ Ubuntu daily release ]
56+ * Automatic snapshot from revision 129 (ubuntu-unity/next)
57+
58+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 25 Jul 2013 03:02:12 +0000
59+
60+unity8 (7.81.3+13.10.20130718ubuntu.unity.next-0ubuntu1) saucy; urgency=low
61+
62+ [ Andrea Cimitan ]
63+ * Add support for the colour palette.
64+
65+ [ Omer Akram ]
66+ * fix the calendar icon in the launcher. (LP: #1201905)
67+
68+ [ Nick Dedekind ]
69+ * Export indicator plugin symbols using Q_DECL_EXPORT.
70+
71+ [ Ying-Chun Liu ]
72+ * Don't include .moc in previewbindingstest.cpp.
73+
74+ [ Ubuntu daily release ]
75+ * Automatic snapshot from revision 118 (ubuntu-unity/next)
76+
77+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 18 Jul 2013 06:07:00 +0000
78+
79+unity8 (7.81.3+13.10.20130717ubuntu.unity.next-0ubuntu1) saucy; urgency=low
80+
81+ [ Daniel d'Andrada ]
82+ * Remove dead code: Showable::showWithoutAnimation.
83+
84+ [ Nick Dedekind ]
85+ * Split Plugins export macro into export_qmlfiles and
86+ export_qmlplugins. Added qmltypes to indicators plugin.
87+
88+ [ Ubuntu daily release ]
89+ * Automatic snapshot from revision 112 (ubuntu-unity/next)
90+
91+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 17 Jul 2013 03:14:49 +0000
92+
193unity8 (7.81.3+13.10.20130716ubuntu.unity.next-0ubuntu1) saucy; urgency=low
294
395 [ Pete Woods ]
debian/control+3−1
@@ -4,7 +4,7 @@ Priority: optional
44Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
55Build-Depends: cmake,
66 debhelper (>= 9),
7− libdee-qt5-dev (>= 3.0),
7+ libdee-qt5-dev (>= 3.2),
88 libgl1-mesa-dev[!armhf] | libgl-dev[!armhf],
99 libgl1-mesa-dri,
1010 libgles2-mesa-dev[armhf],
@@ -87,6 +87,8 @@ Depends: indicators-client (>= ${source:Version}),
8787 ${misc:Depends},
8888 ${python:Depends},
8989 ${shlibs:Depends},
90+Recommends: gir1.2-glib-2.0,
91+ python-gi,
9092Description: Test package for Unity 8 shell
9193 Autopilot tests for the unity8 package
9294
main.cpp+4−6
@@ -18,6 +18,7 @@
1818 */
1919
2020// Qt
21+#include <QtQml>
2122#include <QtQuick/QQuickView>
2223#include <QtGui/QIcon>
2324#include <QtGui/QGuiApplication>
@@ -30,6 +31,7 @@
3031// local
3132#include "paths.h"
3233#include "MouseTouchAdaptor.h"
34+#include "ApplicationArguments.h"
3335
3436namespace {
3537
@@ -86,6 +88,7 @@ int main(int argc, char** argv)
8688 resolveIconTheme();
8789
8890 QStringList args = application.arguments();
91+ ApplicationArguments qmlArgs(args);
8992
9093 // The testability driver is only loaded by QApplication but not by QGuiApplication.
9194 // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
@@ -110,6 +113,7 @@ int main(int argc, char** argv)
110113 view->setResizeMode(QQuickView::SizeRootObjectToView);
111114 view->setTitle("Qml Phone Shell");
112115 view->engine()->setBaseUrl(QUrl::fromLocalFile(::shellAppDirectory()));
116+ view->rootContext()->setContextProperty("applicationArguments", &qmlArgs);
113117 if (args.contains(QLatin1String("-frameless"))) {
114118 view->setFlags(Qt::FramelessWindowHint);
115119 }
@@ -141,12 +145,6 @@ int main(int argc, char** argv)
141145 if (qgetenv("QT_QPA_PLATFORM") == "ubuntu" || args.contains(QLatin1String("-fullscreen"))) {
142146 view->showFullScreen();
143147 } else {
144− if (args.contains(QLatin1String("-geometry")) && args.size() > args.indexOf(QLatin1String("-geometry")) + 1) {
145− QStringList geometryArg = args.at(args.indexOf(QLatin1String("-geometry")) + 1).split('x');
146− if (geometryArg.size() == 2) {
147− view->resize(geometryArg.at(0).toInt(), geometryArg.at(1).toInt());
148− }
149− }
150148 view->show();
151149 }
152150
plugins/HudClient/CMakeLists.txt+2−1
@@ -38,5 +38,6 @@ target_link_libraries(HudClientQml
3838
3939qt5_use_modules(HudClientQml Qml)
4040
41−# export the qmldir and qmltypes files
41+# export the qmldir qmltypes and plugin files
42+export_qmlfiles(HudClient HudClient)
4243export_qmlplugin(HudClient 0.1 HudClient TARGETS HudClientQml)
plugins/LightDM/CMakeLists.txt+2−1
@@ -35,5 +35,6 @@ target_link_libraries(LightDM-qml
3535
3636qt5_use_modules(LightDM-qml Gui Qml)
3737
38−# export the qmldir and qmltypes files
38+# export the qmldir qmltypes and plugin files
39+export_qmlfiles(LightDM LightDM)
3940export_qmlplugin(LightDM 0.1 LightDM TARGETS LightDM-qml)
plugins/ListViewWithPageHeader/CMakeLists.txt+2−1
@@ -33,5 +33,6 @@ target_link_libraries(ListViewWithPageHeader-qml
3333
3434qt5_use_modules(ListViewWithPageHeader-qml Qml Quick)
3535
36−# export the qmldir and qmltypes files
36+# export the qmldir qmltypes and plugin files
37+export_qmlfiles(ListViewWithPageHeader ListViewWithPageHeader)
3738export_qmlplugin(ListViewWithPageHeader 0.1 ListViewWithPageHeader TARGETS ListViewWithPageHeader-qml)
plugins/ListViewWithPageHeader/listviewwithpageheader.cpp+8−4
@@ -785,10 +785,14 @@ void ListViewWithPageHeader::onModelUpdated(const QQuickChangeSet &changeSet, bo
785785 }
786786 if (growDown) {
787787 adjustMinYExtent();
788− } else if (remove.index <= m_firstVisibleIndex && !m_visibleItems.isEmpty()) {
789− // We removed the first item that is the one that positions the rest
790− // position the new first item correctly
791− m_visibleItems.first()->setY(oldFirstValidIndexPos);
788+ } else if (remove.index <= m_firstVisibleIndex) {
789+ if (!m_visibleItems.isEmpty()) {
790+ // We removed the first item that is the one that positions the rest
791+ // position the new first item correctly
792+ m_visibleItems.first()->setY(oldFirstValidIndexPos);
793+ } else {
794+ m_firstVisibleIndex = -1;
795+ }
792796 }
793797 } else if (remove.index + remove.count <= m_firstVisibleIndex) {
794798 m_firstVisibleIndex -= remove.count;
plugins/Ubuntu/Gestures/CMakeLists.txt+2−1
@@ -23,5 +23,6 @@ qt5_use_modules(UbuntuGestureQml Core Quick)
2323# So that Foo.cpp can #include "Foo.moc"
2424include_directories(${CMAKE_CURRENT_BINARY_DIR})
2525
26−# export the qmldir and qmltypes files
26+# export the qmldir qmltypes and plugin files
27+export_qmlfiles(Ubuntu.Gestures Ubuntu/Gestures)
2728export_qmlplugin(Ubuntu.Gestures 0.1 Ubuntu/Gestures TARGETS UbuntuGestureQml)
plugins/Unity/CMakeLists.txt+3−2
@@ -5,7 +5,7 @@ add_subdirectory(Indicators)
55
66# Dependencies
77include(FindPkgConfig)
8−pkg_check_modules(QTDEE REQUIRED libdee-qt5)
8+pkg_check_modules(QTDEE REQUIRED libdee-qt5>=3.2)
99pkg_check_modules(UNITYCORE REQUIRED unity-core-6.0)
1010
1111include_directories(
@@ -48,7 +48,8 @@ target_link_libraries(Unity-qml
4848
4949qt5_use_modules(Unity-qml Qml)
5050
51−# export the qmldir and qmltypes files
51+# export the qmldir qmltypes and plugin files
52+export_qmlfiles(Unity Unity)
5253export_qmlplugin(Unity 0.1 Unity TARGETS Unity-qml)
5354
5455add_subdirectory(Launcher)
plugins/Unity/Indicators/CMakeLists.txt+7−8
@@ -1,3 +1,6 @@
1+# export_qmlplugin macro
2+include(Plugins)
3+
14project(IndicatorsQml)
25
36pkg_check_modules(QMENUMODEL REQUIRED qmenumodel)
@@ -20,9 +23,11 @@ set(IndicatorsQML_SOURCES
2023 indicators.h
2124 indicatorsmanager.cpp
2225 indicatorsmodel.cpp
26+ menucontentactivator.cpp
2327 modelprinter.cpp
2428 plugin.cpp
2529)
30+add_definitions(-DUNITYINDICATORS_LIBRARY)
2631
2732set(IndicatorsQML_RESOURCES
2833 resources/indicators.qrc
@@ -36,11 +41,6 @@ add_library(IndicatorsQml SHARED
3641 ${IndicatorsQML_RESOURCES_RCC}
3742 ${IndicatorsQML_SOURCES}
3843)
39−# Because this is an internal support library, we want
40−# to expose all symbols in it. Consider changing this
41−# either to a static library or just using the
42−# files directly in targets.
43−set_target_properties(IndicatorsQml PROPERTIES COMPILE_FLAGS -fvisibility=default)
4444
4545target_link_libraries(IndicatorsQml
4646 ${QMENUMODEL_LDFLAGS}
@@ -48,6 +48,5 @@ target_link_libraries(IndicatorsQml
4848
4949qt5_use_modules(IndicatorsQml Core Qml Quick)
5050
51−install(TARGETS IndicatorsQml
52− DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/Unity/Indicators
53− )
51+# export the qmltypes and plugin files
52+export_qmlplugin(Unity.Indicators 0.1 Unity/Indicators TARGETS IndicatorsQml)
plugins/Unity/Indicators/Messaging/qml/CMakeLists.txt+4−14
@@ -1,15 +1,5 @@
1−file(GLOB IndicatorsMessaging_QMLFILES
2− *.qml
3− *.js
4− qmldir
5−)
1+# export_qmlfiles macro
2+include(Plugins)
63
7−# copy qml & qmldir files into build directory for shadow builds
8−add_custom_target(UnityIndicatorsMessagingQmlFiles ALL
9− COMMAND cp ${IndicatorsMessaging_QMLFILES} ${CMAKE_BINARY_DIR}/plugins/Unity/Indicators/Messaging
10− DEPENDS ${IndicatorsMessaging_QMLFILES}
11−)
12−
13−install(FILES ${IndicatorsMessaging_QMLFILES}
14− DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/Unity/Indicators/Messaging
15−)
4+# export the qmldir, *.qml files.
5+export_qmlfiles(Unity.Indicators.Messaging Unity/Indicators/Messaging)
plugins/Unity/Indicators/Network/CMakeLists.txt+6−3
@@ -1,3 +1,6 @@
1+# export_qmlplugin macro
2+include(Plugins)
3+
14project(IndicatorsNetworkQml)
25
36pkg_check_modules(GLIB glib-2.0>=2.32)
@@ -32,9 +35,9 @@ if(LIBNM_GLIB_FOUND AND LIBNM_UTIL_FOUND AND GLIB_FOUND)
3235 ${LIBNM_UTIL_LIBRARIES}
3336 )
3437
35− install(TARGETS IndicatorsNetworkQml
36− DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/Unity/Indicators/Network
37− )
38+ # export the qmltypes and plugin files
39+ export_qmlplugin(Unity.Indicators.Network 0.1 Unity/Indicators/Network TARGETS IndicatorsNetworkQml)
40+
3841else()
3942 message(WARNING "Missing glib or libnm dependency for the network plugin, not building")
4043endif()
plugins/Unity/Indicators/Network/qml/CMakeLists.txt+4−14
@@ -1,15 +1,5 @@
1−file(GLOB IndicatorsNetwork_QMLFILES
2− *.qml
3− *.js
4− qmldir
5−)
1+# export_qmlplugin macro
2+include(Plugins)
63
7−# copy qml & qmldir files into build directory for shadow builds
8−add_custom_target(UnityIndicatorsNetworkQmlFiles ALL
9− COMMAND cp ${IndicatorsNetwork_QMLFILES} ${CMAKE_BINARY_DIR}/plugins/Unity/Indicators/Network
10− DEPENDS ${IndicatorsNetwork_QMLFILES}
11−)
12−
13−install(FILES ${IndicatorsNetwork_QMLFILES}
14− DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/Unity/Indicators/Network
15−)
4+# export the qmldir, *.qml files.
5+export_qmlfiles(Unity.Indicators.Network Unity/Indicators/Network)
plugins/Unity/Indicators/Network/qml/qmldir+1
@@ -1,5 +1,6 @@
11module Unity.Indicators.Network
22plugin IndicatorsNetworkQml
3+typeinfo plugin.qmltypes
34
45NetworkPage 0.1 NetworkPage.qml
56AccessPoint 0.1 AccessPoint.qml
plugins/Unity/Indicators/flatmenuproxymodel.h+3−1
@@ -20,12 +20,14 @@
2020#ifndef FLATMENUPROXYMODEL_H
2121#define FLATMENUPROXYMODEL_H
2222
23+#include "unityindicatorsglobal.h"
24+
2325#include <QAbstractProxyModel>
2426#include <qdbusmenumodel.h>
2527
2628class SectionInfo;
2729
28−class FlatMenuProxyModel : public QAbstractProxyModel
30+class UNITYINDICATORS_EXPORT FlatMenuProxyModel : public QAbstractProxyModel
2931{
3032 Q_OBJECT
3133 Q_PROPERTY(int count READ count NOTIFY countChanged)
plugins/Unity/Indicators/indicator.h+3−1
@@ -21,10 +21,12 @@
2121#ifndef INDICATOR_H
2222#define INDICATOR_H
2323
24+#include "unityindicatorsglobal.h"
25+
2426#include <QObject>
2527#include <QSettings>
2628
27−class Indicator : public QObject
29+class UNITYINDICATORS_EXPORT Indicator : public QObject
2830{
2931 Q_OBJECT
3032 Q_PROPERTY(QString identifier READ identifier NOTIFY identifierChanged)
plugins/Unity/Indicators/indicators.h+7−5
@@ -19,9 +19,11 @@
1919#ifndef INDICATORS_H
2020#define INDICATORS_H
2121
22+#include "unityindicatorsglobal.h"
23+
2224#include <QObject>
2325
24−class ActionState : public QObject
26+class UNITYINDICATORS_EXPORT ActionState : public QObject
2527{
2628 Q_OBJECT
2729public:
@@ -36,7 +38,7 @@ public:
3638 ActionState(QObject*parent=0):QObject(parent) {}
3739};
3840
39−class NetworkActionState : public QObject
41+class UNITYINDICATORS_EXPORT NetworkActionState : public QObject
4042{
4143 Q_OBJECT
4244public:
@@ -49,7 +51,7 @@ public:
4951 NetworkActionState(QObject*parent=0):QObject(parent) {}
5052};
5153
52−class NetworkConnection : public QObject
54+class UNITYINDICATORS_EXPORT NetworkConnection : public QObject
5355{
5456 Q_OBJECT
5557public:
@@ -64,7 +66,7 @@ public:
6466 NetworkConnection(QObject*parent=0):QObject(parent) {}
6567};
6668
67−class IndicatorsModelRole : public QObject
69+class UNITYINDICATORS_EXPORT IndicatorsModelRole : public QObject
6870{
6971 Q_OBJECT
7072public:
@@ -83,7 +85,7 @@ public:
8385 IndicatorsModelRole(QObject*parent=0):QObject(parent) {}
8486};
8587
86−class FlatMenuProxyModelRole : public QObject
88+class UNITYINDICATORS_EXPORT FlatMenuProxyModelRole : public QObject
8789{
8890 Q_OBJECT
8991public:
plugins/Unity/Indicators/indicatorsmanager.h+4−3
@@ -19,15 +19,16 @@
1919#ifndef INDICATORS_MANAGER_H
2020#define INDICATORS_MANAGER_H
2121
22+#include "indicator.h"
23+#include "unityindicatorsglobal.h"
24+
2225#include <QObject>
2326#include <QFileSystemWatcher>
2427#include <QDir>
2528#include <QHash>
2629#include <QSharedPointer>
2730
28−#include "indicator.h"
29−
30−class IndicatorsManager : public QObject
31+class UNITYINDICATORS_EXPORT IndicatorsManager : public QObject
3132{
3233 Q_OBJECT
3334 Q_PROPERTY(bool loaded READ isLoaded NOTIFY loadedChanged)
plugins/Unity/Indicators/indicatorsmodel.h+2−1
@@ -22,13 +22,14 @@
2222#define INDICATORSMODEL_H
2323
2424#include "indicator.h"
25+#include "unityindicatorsglobal.h"
2526
2627#include <QAbstractListModel>
2728#include <QQmlEngine>
2829
2930class IndicatorsManager;
3031
31−class IndicatorsModel : public QAbstractListModel
32+class UNITYINDICATORS_EXPORT IndicatorsModel : public QAbstractListModel
3233{
3334 Q_OBJECT
3435 Q_ENUMS(Roles)
plugins/Unity/Indicators/menucontentactivator.cppnew file+345
@@ -0,0 +1,345 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+ #include "menucontentactivator.h"
20+
21+// Essentially a QTimer wrapper
22+class ContentTimer : public UnityIndicators::AbstractTimer
23+{
24+ Q_OBJECT
25+public:
26+ ContentTimer(QObject *parent) : UnityIndicators::AbstractTimer(parent) {
27+ m_timer.setSingleShot(false);
28+ connect(&m_timer, &QTimer::timeout,
29+ this, &UnityIndicators::AbstractTimer::timeout);
30+ }
31+ virtual int interval() const { return m_timer.interval(); }
32+ virtual void setInterval(int msecs) { m_timer.setInterval(msecs); }
33+ virtual void start() { m_timer.start(); UnityIndicators::AbstractTimer::start(); }
34+ virtual void stop() { m_timer.stop(); UnityIndicators::AbstractTimer::stop(); }
35+private:
36+ QTimer m_timer;
37+};
38+
39+class MenuContentActivatorPrivate : public QObject
40+{
41+ Q_OBJECT
42+public:
43+ MenuContentActivatorPrivate(MenuContentActivator* parent)
44+ : m_running(false),
45+ m_baseIndex(0),
46+ m_delta(0),
47+ m_count(0),
48+ m_timer(NULL),
49+ q(parent)
50+ {}
51+
52+ ~MenuContentActivatorPrivate()
53+ {
54+ qDeleteAll(m_content);
55+ m_content.clear();
56+ }
57+
58+ int findNextInactiveDelta(bool* finished = NULL);
59+
60+ static int content_count(QQmlListProperty<MenuContentState> *prop);
61+ static MenuContentState* content_at(QQmlListProperty<MenuContentState> *prop, int index);
62+
63+ bool m_running;
64+ int m_baseIndex;
65+ int m_delta;
66+ int m_count;
67+ UnityIndicators::AbstractTimer* m_timer;
68+ QMap<int, MenuContentState*> m_content;
69+ MenuContentActivator* q;
70+};
71+
72+MenuContentActivator::MenuContentActivator(QObject* parent)
73+ : QObject(parent),
74+ d(new MenuContentActivatorPrivate(this))
75+{
76+ qRegisterMetaType<QQmlListProperty<MenuContentState> > ("QQmlListProperty<MenuContentState>");
77+
78+ setContentTimer(new ContentTimer(this));
79+ d->m_timer->setInterval(75);
80+}
81+
82+MenuContentActivator::~MenuContentActivator()
83+{
84+ delete d;
85+}
86+
87+void MenuContentActivator::restart()
88+{
89+ // when we start, make sure we have the base index in the list.
90+ setMenuContentState(d->m_baseIndex, true);
91+ setDelta(0);
92+
93+ // check if we've finished before starting the timer.
94+ bool finished = false;
95+ d->findNextInactiveDelta(&finished);
96+ if (!finished) {
97+ d->m_timer->start();
98+ } else {
99+ d->m_timer->stop();
100+ }
101+
102+ if (!d->m_running) {
103+ d->m_running = true;
104+ Q_EMIT runningChanged(true);
105+ }
106+}
107+
108+void MenuContentActivator::stop()
109+{
110+ d->m_timer->stop();
111+ if (!d->m_running) {
112+ d->m_running = false;
113+ Q_EMIT runningChanged(false);
114+ }
115+}
116+
117+void MenuContentActivator::clear()
118+{
119+ qDeleteAll(d->m_content);
120+ d->m_content.clear();
121+
122+ setDelta(0);
123+ d->m_timer->stop();
124+
125+ Q_EMIT contentChanged();
126+}
127+
128+bool MenuContentActivator::isMenuContentActive(int index) const
129+{
130+ if (d->m_content.contains(index))
131+ return d->m_content[index]->isActive();
132+ return false;
133+}
134+
135+void MenuContentActivator::setRunning(bool running)
136+{
137+ if (running) {
138+ restart();
139+ } else {
140+ stop();
141+ }
142+}
143+
144+bool MenuContentActivator::isRunning() const
145+{
146+ return d->m_running;
147+}
148+
149+void MenuContentActivator::setBaseIndex(int index)
150+{
151+ if (d->m_baseIndex != index) {
152+ d->m_baseIndex = index;
153+
154+ if (isRunning()) {
155+ restart();
156+ }
157+
158+ Q_EMIT baseIndexChanged(index);
159+ }
160+}
161+
162+int MenuContentActivator::baseIndex() const
163+{
164+ return d->m_baseIndex;
165+}
166+
167+void MenuContentActivator::setCount(int count)
168+{
169+ if (d->m_count != count) {
170+ d->m_count = count;
171+ Q_EMIT countChanged(count);
172+
173+ if (isRunning()) {
174+ restart();
175+ }
176+ }
177+}
178+
179+int MenuContentActivator::count() const
180+{
181+ return d->m_count;
182+}
183+
184+void MenuContentActivator::setDelta(int delta)
185+{
186+ if (d->m_delta != delta) {
187+ d->m_delta = delta;
188+ Q_EMIT deltaChanged(d->m_delta);
189+ }
190+}
191+
192+int MenuContentActivator::delta() const
193+{
194+ return d->m_delta;
195+}
196+
197+QQmlListProperty<MenuContentState> MenuContentActivator::content()
198+{
199+ return QQmlListProperty<MenuContentState>(this,
200+ 0,
201+ MenuContentActivatorPrivate::content_count,
202+ MenuContentActivatorPrivate::content_at);
203+}
204+
205+void MenuContentActivator::onTimeout()
206+{
207+ bool finished = false;
208+ int tempDelta = d->findNextInactiveDelta(&finished);
209+ if (!finished) {
210+ setMenuContentState(d->m_baseIndex + tempDelta, true);
211+ setDelta(tempDelta);
212+ }
213+
214+ if (finished) {
215+ d->m_timer->stop();
216+ }
217+}
218+
219+void MenuContentActivator::setContentTimer(UnityIndicators::AbstractTimer *timer)
220+{
221+ int interval = 0;
222+ bool timerWasRunning = false;
223+
224+ // can be null when called from the constructor
225+ if (d->m_timer) {
226+ interval = d->m_timer->interval();
227+ timerWasRunning = d->m_timer->isRunning();
228+ if (d->m_timer->parent() == this) {
229+ delete d->m_timer;
230+ }
231+ }
232+
233+ d->m_timer = timer;
234+ timer->setInterval(interval);
235+ connect(timer, &UnityIndicators::AbstractTimer::timeout,
236+ this, &MenuContentActivator::onTimeout);
237+ if (timerWasRunning) {
238+ d->m_timer->start();
239+ }
240+}
241+
242+void MenuContentActivator::setMenuContentState(int index, bool active)
243+{
244+ if (d->m_content.contains(index)) {
245+ d->m_content[index]->setActive(active);
246+ } else {
247+ d->m_content[index] = new MenuContentState(active);
248+ Q_EMIT contentChanged();
249+ }
250+}
251+
252+int MenuContentActivatorPrivate::findNextInactiveDelta(bool* finished)
253+{
254+ if (m_count == 0 || m_baseIndex >= m_count) {
255+ if (finished) *finished = true;
256+ return 0;
257+ }
258+
259+ int tmpDelta = m_delta;
260+ bool topReached = false, bottomReached = false;
261+ while(true) {
262+
263+ // prechecks for bottom and top limits.
264+ if (tmpDelta > 0 && bottomReached) tmpDelta = -tmpDelta;
265+ if (tmpDelta < 0 && topReached) tmpDelta = (-tmpDelta) + 1;
266+
267+ if (tmpDelta > 0) {
268+ // negative of baseIndex
269+ tmpDelta = -tmpDelta;
270+ // reached the bottom?
271+ if (m_baseIndex + tmpDelta < 0) {
272+ bottomReached = true;
273+ // if we've reached the top as well, then we know we're done.
274+ if (topReached) {
275+ if (finished) *finished = true;
276+ return 0;
277+ }
278+ continue;
279+ }
280+ } else {
281+ // positive of baseIndex
282+ tmpDelta = (-tmpDelta) + 1;
283+ // reached the top?
284+ if (m_baseIndex + tmpDelta >= m_count) {
285+ topReached = true;
286+ // if we've reached the bottom as well, then we know we're done.
287+ if (bottomReached) {
288+ if (finished) *finished = true;
289+ return 0;
290+ }
291+ continue;
292+ }
293+ }
294+
295+ if (q->isMenuContentActive(m_baseIndex + tmpDelta)) {
296+ continue;
297+ }
298+ break;
299+ }
300+ if (finished) *finished = false;
301+ return tmpDelta;
302+}
303+
304+int MenuContentActivatorPrivate::content_count(QQmlListProperty<MenuContentState> *prop)
305+{
306+ MenuContentActivator *p = qobject_cast<MenuContentActivator*>(prop->object);
307+ // we'll create MenuContentState on demand.
308+ return p->count();
309+}
310+
311+MenuContentState* MenuContentActivatorPrivate::content_at(QQmlListProperty<MenuContentState> *prop, int index)
312+{
313+ MenuContentActivator *p = qobject_cast<MenuContentActivator*>(prop->object);
314+ MenuContentActivatorPrivate *d = p->d;
315+
316+ if (!d->m_content.contains(index)) {
317+ MenuContentState* content = new MenuContentState(false);
318+ d->m_content[index] = content;
319+ Q_EMIT p->contentChanged();
320+ return content;
321+ }
322+
323+ return d->m_content[index];
324+}
325+
326+MenuContentState::MenuContentState(bool active)
327+ : m_active(active)
328+{
329+}
330+
331+bool MenuContentState::isActive() const
332+{
333+ return m_active;
334+}
335+
336+void MenuContentState::setActive(bool active)
337+{
338+ if (m_active != active) {
339+ m_active = active;
340+ Q_EMIT activeChanged();
341+ }
342+}
343+
344+// Because we are defining a new QObject-based class (ContentTimer) here.
345+#include "menucontentactivator.moc"
plugins/Unity/Indicators/menucontentactivator.hnew file+117
@@ -0,0 +1,117 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+#ifndef MENUCONTENTACTIVATOR_H
20+#define MENUCONTENTACTIVATOR_H
21+
22+#include "unityindicatorsglobal.h"
23+
24+#include <QObject>
25+#include <QTimer>
26+#include <QQmlListProperty>
27+
28+
29+namespace UnityIndicators {
30+/* Defines an interface for a Timer. */
31+class UNITYINDICATORS_EXPORT AbstractTimer : public QObject {
32+ Q_OBJECT
33+public:
34+ AbstractTimer(QObject *parent) : QObject(parent), m_isRunning(false) {}
35+ virtual int interval() const = 0;
36+ virtual void setInterval(int msecs) = 0;
37+ virtual void start() { m_isRunning = true; };
38+ virtual void stop() { m_isRunning = false; }
39+ bool isRunning() const { return m_isRunning; }
40+Q_SIGNALS:
41+ void timeout();
42+private:
43+ bool m_isRunning;
44+};
45+}
46+
47+/* Defines a object to express the active state of a menu. */
48+class UNITYINDICATORS_EXPORT MenuContentState : public QObject
49+{
50+ Q_OBJECT
51+ Q_PROPERTY(bool active READ isActive NOTIFY activeChanged)
52+public:
53+ MenuContentState(bool active);
54+
55+ bool isActive() const;
56+ void setActive(bool active);
57+
58+Q_SIGNALS:
59+ void activeChanged();
60+private:
61+ bool m_active;
62+};
63+
64+class MenuContentActivatorPrivate;
65+
66+class UNITYINDICATORS_EXPORT MenuContentActivator : public QObject
67+{
68+ Q_OBJECT
69+ Q_PROPERTY(int baseIndex READ baseIndex WRITE setBaseIndex NOTIFY baseIndexChanged)
70+ Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged)
71+ Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged)
72+ Q_PROPERTY(QQmlListProperty<MenuContentState> content READ content NOTIFY contentChanged DESIGNABLE false)
73+public:
74+ MenuContentActivator(QObject* parent = NULL);
75+ ~MenuContentActivator();
76+
77+ Q_INVOKABLE void restart();
78+ Q_INVOKABLE void stop();
79+ Q_INVOKABLE void clear();
80+ Q_INVOKABLE bool isMenuContentActive(int index) const;
81+
82+ void setRunning(bool running);
83+ bool isRunning() const;
84+
85+ void setBaseIndex(int index);
86+ int baseIndex() const;
87+
88+ void setCount(int index);
89+ int count() const;
90+
91+ void setDelta(int index);
92+ int delta() const;
93+
94+ QQmlListProperty<MenuContentState> content();
95+
96+ // Replaces the existing Timer with the given one.
97+ //
98+ // Useful for providing a fake timer when testing.
99+ void setContentTimer(UnityIndicators::AbstractTimer *timer);
100+ void setMenuContentState(int index, bool active);
101+
102+Q_SIGNALS:
103+ void baseIndexChanged(int baseIndex);
104+ void deltaChanged(int delta);
105+ void runningChanged(bool running);
106+ void countChanged(int count);
107+ void contentChanged();
108+
109+private Q_SLOTS:
110+ void onTimeout();
111+
112+private:
113+ MenuContentActivatorPrivate* d;
114+ friend class MenuContentActivatorPrivate;
115+};
116+
117+#endif // MENUCONTENTACTIVATOR_H
plugins/Unity/Indicators/plugin.cpp+3
@@ -27,6 +27,7 @@
2727#include "indicatorsmanager.h"
2828#include "indicatorsmodel.h"
2929#include "indicators.h"
30+#include "menucontentactivator.h"
3031#include "modelprinter.h"
3132
3233void IndicatorsPlugin::registerTypes(const char *uri)
@@ -37,7 +38,9 @@ void IndicatorsPlugin::registerTypes(const char *uri)
3738 qmlRegisterType<FlatMenuProxyModel>(uri, 0, 1, "FlatMenuProxyModel");
3839 qmlRegisterType<IndicatorsModel>(uri, 0, 1, "IndicatorsModel");
3940 qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
41+ qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
4042
43+ qmlRegisterUncreatableType<MenuContentState>(uri, 0, 1, "MenuContentState", "Can't create MenuContentState class");
4144 qmlRegisterUncreatableType<ActionState>(uri, 0, 1, "ActionState", "Can't create ActionState class");
4245 qmlRegisterUncreatableType<NetworkActionState>(uri, 0, 1, "NetworkActionState", "Can't create NetworkActionState class");
4346 qmlRegisterUncreatableType<NetworkConnection>(uri, 0, 1, "NetworkConnection", "Can't create NetworkConnection class");
plugins/Unity/Indicators/qml/CMakeLists.txt+4−13
@@ -1,14 +1,5 @@
1−file(GLOB Indicators_QMLFILES
2− *.qml
3− qmldir
4−)
1+# export_qmlplugin macro
2+include(Plugins)
53
6−# copy qml & qmldir files into build directory for shadow builds
7−add_custom_target(UnityIndicatorsQmlFiles ALL
8− COMMAND cp ${Indicators_QMLFILES} ${CMAKE_BINARY_DIR}/plugins/Unity/Indicators
9− DEPENDS ${Indicators_QMLFILES}
10−)
11−
12−install(FILES ${Indicators_QMLFILES}
13− DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/Unity/Indicators
14−)
4+# export the qmldir, *.qml files.
5+export_qmlfiles(Unity.Indicators Unity/Indicators)
plugins/Unity/Indicators/qml/IndicatorPage.qml−6
@@ -188,10 +188,4 @@ IndicatorBase {
188188 mainMenu.currentIndex = -1;
189189 mainMenu.positionViewAtBeginning();
190190 }
191−
192− Component.onCompleted: {
193− if ((busType != 0) && (busName != "") && (deviceMenuObjectPath != "")) {
194− start();
195− }
196− }
197191}
plugins/Unity/Indicators/qml/qmldir+1
@@ -1,5 +1,6 @@
11module Unity.Indicators
22plugin IndicatorsQml
3+typeinfo plugin.qmltypes
34
45BasicMenuItem 0.1 BasicMenuItem.qml
56ButtonMenuItem 0.1 ButtonMenuItem.qml
plugins/Unity/Indicators/unityindicatorsglobal.hnew file+23
@@ -0,0 +1,23 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <QtCore/QtGlobal>
18+
19+#if defined(UNITYINDICATORS_LIBRARY)
20+# define UNITYINDICATORS_EXPORT Q_DECL_EXPORT
21+#else
22+# define UNITYINDICATORS_EXPORT Q_DECL_IMPORT
23+#endif
plugins/Unity/Launcher/CMakeLists.txt+1
@@ -24,4 +24,5 @@ add_library(UnityLauncher-qml MODULE
2424qt5_use_modules(UnityLauncher-qml Qml)
2525
2626# export the qmldir and qmltypes files
27+export_qmlfiles(Unity.Launcher Unity/Launcher)
2728export_qmlplugin(Unity.Launcher 0.1 Unity/Launcher TARGETS UnityLauncher-qml)
plugins/Unity/Launcher/backend/launcherbackend.cpp+3−3
@@ -37,7 +37,7 @@ LauncherBackend::LauncherBackend(QObject *parent):
3737 QLatin1String("gmail-webapp.desktop") <<
3838 QLatin1String("ubuntu-weather-app.desktop") <<
3939 QLatin1String("notes-app.desktop") <<
40− QLatin1String("ubuntu-calendar-app.desktop");
40+ QLatin1String("calendar-app.desktop");
4141
4242 // TODO: get stuff from desktop files instead this hardcoded map
4343 m_displayNameMap.insert("phone-app.desktop", "Phone");
@@ -49,7 +49,7 @@ LauncherBackend::LauncherBackend(QObject *parent):
4949 m_displayNameMap.insert("gmail-webapp.desktop", "GMail");
5050 m_displayNameMap.insert("ubuntu-weather-app.desktop", "Weather");
5151 m_displayNameMap.insert("notes-app.desktop", "Notes");
52− m_displayNameMap.insert("ubuntu-calendar-app.desktop", "Calendar");
52+ m_displayNameMap.insert("calendar-app.desktop", "Calendar");
5353
5454 // TODO: get stuff from desktop files instead this hardcoded map
5555 m_iconMap.insert("phone-app.desktop", "phone-app");
@@ -61,7 +61,7 @@ LauncherBackend::LauncherBackend(QObject *parent):
6161 m_iconMap.insert("gmail-webapp.desktop", "gmail");
6262 m_iconMap.insert("ubuntu-weather-app.desktop", "weather");
6363 m_iconMap.insert("notes-app.desktop", "notepad");
64− m_iconMap.insert("ubuntu-calendar-app.desktop", "calendar");
64+ m_iconMap.insert("calendar-app.desktop", "calendar");
6565
6666}
6767
plugins/Unity/Launcher/qmldir+1
@@ -1,2 +1,3 @@
11module Unity.Launcher
22plugin UnityLauncher-qml
3+typeinfo plugin.qmltypes
plugins/Unity/categories.cpp+6−3
@@ -34,7 +34,7 @@ enum CategoryColumn {
3434Categories::Categories(QObject* parent)
3535 : DeeListModel(parent)
3636{
37− m_roles[Categories::RoleId] = "id";
37+ m_roles[Categories::RoleCategoryId] = "categoryId";
3838 m_roles[Categories::RoleName] = "name";
3939 m_roles[Categories::RoleIcon] = "icon";
4040 m_roles[Categories::RoleRenderer] = "renderer";
@@ -42,6 +42,7 @@ Categories::Categories(QObject* parent)
4242 m_roles[Categories::RoleHints] = "hints";
4343 m_roles[Categories::RoleResults] = "results";
4444 m_roles[Categories::RoleCount] = "count";
45+ m_roles[Categories::RoleCategoryIndex] = "categoryIndex";
4546
4647 // TODO This should not be needed but accumulatting the count changes
4748 // makes the visualization more stable and also makes crashes on fast
@@ -130,8 +131,8 @@ Categories::data(const QModelIndex& index, int role) const
130131 }
131132
132133 switch (role) {
133− case RoleId:
134− return QVariant::fromValue(index.row());
134+ case RoleCategoryId:
135+ return DeeListModel::data(index, CategoryColumn::ID);
135136 case RoleName:
136137 return DeeListModel::data(index, CategoryColumn::DISPLAY_NAME);
137138 case RoleIcon:
@@ -149,6 +150,8 @@ Categories::data(const QModelIndex& index, int role) const
149150 return QVariant::fromValue(getResults(index.row()));
150151 case RoleCount:
151152 return QVariant::fromValue(getResults(index.row())->rowCount());
153+ case RoleCategoryIndex:
154+ return QVariant::fromValue(index.row());
152155 default:
153156 return QVariant();
154157 }
plugins/Unity/categories.h+3−2
@@ -42,14 +42,15 @@ public:
4242 explicit Categories(QObject* parent = 0);
4343
4444 enum Roles {
45− RoleId,
45+ RoleCategoryId,
4646 RoleName,
4747 RoleIcon,
4848 RoleRenderer,
4949 RoleContentType,
5050 RoleHints,
5151 RoleResults,
52− RoleCount
52+ RoleCount,
53+ RoleCategoryIndex
5354 };
5455
5556 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
plugins/Unity/previewinfohint.cpp+2−2
@@ -17,9 +17,9 @@
1717 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1818 */
1919
20+#include <deelistmodel.h>
2021// local
2122#include "previewinfohint.h"
22−#include "variantutils.h"
2323
2424PreviewInfoHint::PreviewInfoHint(QObject *parent):
2525 QObject(parent),
@@ -61,7 +61,7 @@ QString PreviewInfoHint::iconHint() const
6161QVariant PreviewInfoHint::value() const
6262{
6363 if (m_unityInfoHint) {
64− return QVariantFromGVariant(m_unityInfoHint->value);
64+ return DeeListModel::VariantForData(m_unityInfoHint->value);
6565 }
6666 return QVariant();
6767}
plugins/Unity/variantutils.cpp+2−118
@@ -17,131 +17,15 @@
1717 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1818 */
1919
20+#include <deelistmodel.h>
2021#include "variantutils.h"
2122
22−/* Returns floating ref */
23−GVariant* GVariantFromQVariant(const QVariant &var)
24−{
25− GVariant *gv = nullptr;
26−
27− switch (var.type()) {
28− case QVariant::String:
29− gv = g_variant_new_string(var.toString().toUtf8());
30− break;
31− case QVariant::UInt:
32− gv = g_variant_new_uint32(var.toUInt());
33− break;
34− case QVariant::Int:
35− gv = g_variant_new_int32(var.toInt());
36− break;
37− case QVariant::LongLong:
38− gv = g_variant_new_int64(var.toLongLong());
39− break;
40− case QVariant::ULongLong:
41− gv = g_variant_new_uint64(var.toULongLong());
42− break;
43− case QVariant::Double:
44− gv = g_variant_new_double(var.toDouble());
45− break;
46− case QVariant::Bool:
47− gv = g_variant_new_boolean(var.toBool());
48− break;
49− case QVariant::Hash: {
50− const auto hash = var.toHash();
51− GVariant **children = new GVariant*[hash.size()];
52− int cnt = 0;
53− QHash<QString, QVariant>::const_iterator it = hash.constBegin();
54− while (it != hash.constEnd()) {
55− GVariant *key = g_variant_new_string(it.key().toUtf8());
56− children[cnt++] = g_variant_new_dict_entry(key, g_variant_new_variant(GVariantFromQVariant(it.value())));
57− ++it;
58− }
59− gv = g_variant_new_array(G_VARIANT_TYPE("{sv}"), children, hash.size());
60− delete [] children;
61− break;
62− }
63− default:
64− break;
65− }
66− return gv;
67−}
68−
69−// implementation of this conversion taken from libdee-qt private utility function.
70−QVariant QVariantFromGVariant(GVariant *value)
71−{
72− /* We need to special-case a{sv} here as G_VARIANT_CLASS_ARRAY handles simple arrays;
73− we need to create QVariantHash, not a list */
74− if (g_variant_is_of_type(value, G_VARIANT_TYPE_VARDICT)) {
75− const gsize nChildren = g_variant_n_children(value);
76− QVariantHash hash;
77− for (gsize i = 0; i < nChildren; ++i)
78− {
79− GVariant* dict_entry = g_variant_get_child_value(value, i);
80−
81− gchar* dict_key;
82− GVariant *dict_var;
83− g_variant_get(dict_entry, "{&sv}", &dict_key, &dict_var);
84− hash.insert(QString::fromUtf8(dict_key), QVariantFromGVariant(dict_var));
85− g_variant_unref(dict_var);
86− g_variant_unref(dict_entry);
87− }
88− return hash;
89− }
90−
91− switch (g_variant_classify(value)) {
92− case G_VARIANT_CLASS_BOOLEAN:
93− return QVariant((bool) g_variant_get_boolean(value));
94− case G_VARIANT_CLASS_BYTE:
95− return QVariant((uchar) g_variant_get_byte(value));
96− case G_VARIANT_CLASS_INT16:
97− return QVariant((qint16) g_variant_get_int16(value));
98− case G_VARIANT_CLASS_UINT16:
99− return QVariant((quint16) g_variant_get_uint16(value));
100− case G_VARIANT_CLASS_INT32:
101− return QVariant((qint32) g_variant_get_int32(value));
102− case G_VARIANT_CLASS_UINT32:
103− return QVariant((quint32) g_variant_get_uint32(value));
104− case G_VARIANT_CLASS_INT64:
105− return QVariant((qint64) g_variant_get_int64(value));
106− case G_VARIANT_CLASS_UINT64:
107− return QVariant((quint64) g_variant_get_uint64(value));
108− case G_VARIANT_CLASS_DOUBLE:
109− return QVariant(g_variant_get_double(value));
110− case G_VARIANT_CLASS_STRING:
111− return QVariant(QString::fromUtf8(g_variant_get_string(value, NULL)));
112− case G_VARIANT_CLASS_ARRAY:
113− case G_VARIANT_CLASS_TUPLE:
114− {
115− const gsize nChildren = g_variant_n_children(value);
116− QList<QVariant> array;
117− for (gsize i = 0; i < nChildren; ++i)
118− {
119− GVariant* gvariant = g_variant_get_child_value(value, i);
120− array << QVariantFromGVariant(gvariant);
121− g_variant_unref(gvariant);
122− }
123− return array;
124− }
125− default:
126− /* Fallback on an empty QVariant.
127− FIXME: Missing conversion of following GVariant types:
128− - G_VARIANT_CLASS_HANDLE
129− - G_VARIANT_CLASS_OBJECT_PATH
130− - G_VARIANT_CLASS_SIGNATURE
131− - G_VARIANT_CLASS_VARIANT
132− - G_VARIANT_CLASS_MAYBE
133− - G_VARIANT_CLASS_DICT_ENTRY
134− */
135− return QVariant();
136− }
137−}
138−
13923unity::glib::HintsMap convertToHintsMap(const QHash<QString, QVariant> &val)
14024{
14125 unity::glib::HintsMap hintsMap;
14226 QHash<QString, QVariant>::const_iterator it = val.constBegin();
14327 while (it != val.constEnd()) {
144− hintsMap[it.key().toStdString()] = GVariantFromQVariant(it.value());
28+ hintsMap[it.key().toStdString()] = DeeListModel::DataFromVariant(it.value());
14529 ++it;
14630 }
14731 return hintsMap;
plugins/Unity/variantutils.h−2
@@ -26,8 +26,6 @@
2626// libunity-core
2727#include <UnityCore/Variant.h>
2828
29−GVariant* GVariantFromQVariant(const QVariant &value);
30−QVariant QVariantFromGVariant(GVariant *value);
3129unity::glib::HintsMap convertToHintsMap(const QHash<QString, QVariant> &val);
3230unity::glib::HintsMap convertToHintsMap(const QVariant &var);
3331
plugins/Utils/CMakeLists.txt+2−1
@@ -23,5 +23,6 @@ set_target_properties(Utils-qml PROPERTIES COMPILE_FLAGS -fvisibility=default)
2323
2424qt5_use_modules(Utils-qml Qml Quick)
2525
26−# export the qmldir and qmltypes files
26+# export the qmldir qmltypes and plugin files
27+export_qmlfiles(Utils Utils)
2728export_qmlplugin(Utils 0.1 Utils TARGETS Utils-qml)
po/ar.po+3−27
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-22 16:15+0000\n"
11+"PO-Revision-Date: 2013-06-26 23:26+0000\n"
1212"Last-Translator: Ibrahim Saed <ibraheem5000@gmail.com>\n"
1313"Language-Team: Arabic <ar@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:15+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,27 +218,3 @@ msgstr "عذراً، لا يوجد ما يطابق بحثك."
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "أشخاص"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "الشاشة الرئيسية"
227−
228−#~ msgid "Favourite People"
229−#~ msgstr "الأشخاص المفضلون"
230−
231−#~ msgid "People Recently in Touch"
232−#~ msgstr "أشخاص كانوا على اتصال مؤخرا"
233−
234−#~ msgid "A-Z"
235−#~ msgstr "A-Z"
236−
237−#~ msgid "Recently in touch"
238−#~ msgstr "على اتصال موخرا"
239−
240−#~ msgid "New Contacts"
241−#~ msgstr "أشخاص جدد"
242−
243−#~ msgid "Favourites"
244−#~ msgstr "المفضلات"
po/az.po+4−10
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-26 23:48+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Azerbaijani <az@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:15+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,9 +218,3 @@ msgstr "Bağışlayın, amma axtarışınıza heç bir nəticə uyğun gəlmir."
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "İnsanlar"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Əsas Ekran"
po/bg.po+3−15
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-19 15:47+0000\n"
11+"PO-Revision-Date: 2013-06-27 00:06+0000\n"
1212"Last-Translator: Miroslav E. Hadzhiev (Хаджиев) "
1313"<miroslav.hadzhiev@gmail.com>\n"
1414"Language-Team: Bulgarian <bg@li.org>\n"
1515"MIME-Version: 1.0\n"
1616"Content-Type: text/plain; charset=UTF-8\n"
1717"Content-Transfer-Encoding: 8bit\n"
18−"X-Launchpad-Export-Date: 2013-06-27 16:15+0000\n"
19−"X-Generator: Launchpad (build 16681)\n"
18+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
19+"X-Generator: Launchpad (build 16696)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -219,15 +219,3 @@ msgstr "Съжаляваме, няма нищо, което да отговар
219219#, qt-format
220220msgid "Please enter %1"
221221msgstr ""
222−
223−#~ msgid "Favourites"
224−#~ msgstr "Любими"
225−
226−#~ msgid "People"
227−#~ msgstr "Хора"
228−
229−#~ msgid "A-Z"
230−#~ msgstr "А-Я"
231−
232−#~ msgid "Home screen"
233−#~ msgstr "Начален екран"
po/ca.po+4−16
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 00:24+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Catalan <ca@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:15+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,15 +218,3 @@ msgstr "No hi ha res que coincideixi amb la cerca"
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "Gent"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Pantalla d'inici"
227−
228−#~ msgid "Favourite People"
229−#~ msgstr "Gent preferida"
230−
231−#~ msgid "People Recently in Touch"
232−#~ msgstr "Gent amb qui heu parlat"
po/de.po+5−29
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-29 12:53+0000\n"
12−"Last-Translator: Phillip Sz <Unknown>\n"
11+"PO-Revision-Date: 2013-06-27 00:37+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: German <de@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-30 07:12+0000\n"
18−"X-Generator: Launchpad (build 16692)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -217,28 +217,4 @@ msgstr "Entschuldigung, es wurden keine Treffer erzielt."
217217#: Shell.qml:383
218218#, qt-format
219219msgid "Please enter %1"
220−msgstr "Bitte geben Sie %1 ein"
221−
222−#~ msgid "People"
223−#~ msgstr "Personen"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Startseite"
227−
228−#~ msgid "Favourite People"
229−#~ msgstr "Lieblingspersonen"
230−
231−#~ msgid "People Recently in Touch"
232−#~ msgstr "Zuletzt kontaktierte Personen"
233−
234−#~ msgid "A-Z"
235−#~ msgstr "A-Z"
236−
237−#~ msgid "Recently in touch"
238−#~ msgstr "Kürzlich im Gespräch"
239−
240−#~ msgid "New Contacts"
241−#~ msgstr "Neue Kontakte"
242−
243−#~ msgid "Favourites"
244−#~ msgstr "Favoriten"
220+msgstr ""
po/el.po+4−10
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 00:53+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Greek <el@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,9 +218,3 @@ msgstr "Λυπούμαστε, δεν υπάρχει τίποτα που να τ
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "Πρόσωπα"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Αρχική οθόνη"
po/en_AU.po+3−27
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-18 20:36+0000\n"
11+"PO-Revision-Date: 2013-06-27 01:13+0000\n"
1212"Last-Translator: Jackson Doak <Doak.jackson@gmail.com>\n"
1313"Language-Team: English (Australia) <en_AU@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,27 +218,3 @@ msgstr "Sorry, there is nothing that matches your search."
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "People"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Home screen"
227−
228−#~ msgid "Favourite People"
229−#~ msgstr "Favourite People"
230−
231−#~ msgid "People Recently in Touch"
232−#~ msgstr "People Recently in Touch"
233−
234−#~ msgid "A-Z"
235−#~ msgstr "A-Z"
236−
237−#~ msgid "Recently in touch"
238−#~ msgstr "Recently in touch"
239−
240−#~ msgid "New Contacts"
241−#~ msgstr "New Contacts"
242−
243−#~ msgid "Favourites"
244−#~ msgstr "Favourites"
po/en_GB.po+4−28
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-27 00:22+0000\n"
12−"Last-Translator: Andi Chandler <Unknown>\n"
11+"PO-Revision-Date: 2013-06-27 08:53+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: English (United Kingdom) <en_GB@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,27 +218,3 @@ msgstr "Sorry, there is nothing that matches your search."
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr "Please enter %1"
221−
222−#~ msgid "Favourite People"
223−#~ msgstr "Favourite People"
224−
225−#~ msgid "People Recently in Touch"
226−#~ msgstr "People Recently in Touch"
227−
228−#~ msgid "People"
229−#~ msgstr "People"
230−
231−#~ msgid "A-Z"
232−#~ msgstr "A-Z"
233−
234−#~ msgid "Recently in touch"
235−#~ msgstr "Recently in touch"
236−
237−#~ msgid "New Contacts"
238−#~ msgstr "New Contacts"
239−
240−#~ msgid "Favourites"
241−#~ msgstr "Favourites"
242−
243−#~ msgid "Home screen"
244−#~ msgstr "Home screen"
po/eo.po+3−3
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
11+"PO-Revision-Date: 2013-06-26 22:35+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Esperanto <eo@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:15+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
po/es.po+1−1
@@ -14,7 +14,7 @@ msgstr ""
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-07-09 06:13+0000\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
1818"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
po/fi.po+4−25
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-10 16:19+0000\n"
12−"Last-Translator: Jiri Grönroos <Unknown>\n"
11+"PO-Revision-Date: 2013-06-27 02:43+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Finnish <fi@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:15+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,24 +218,3 @@ msgstr "Hakuun täsmääviä tuloksia ei löydy."
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "Ihmiset"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Kotinäyttö"
227−
228−#~ msgid "Favourite People"
229−#~ msgstr "Suosikkihenkilöt"
230−
231−#~ msgid "People Recently in Touch"
232−#~ msgstr "Viimeksi yhteydessä olleet henkilöt"
233−
234−#~ msgid "Favourites"
235−#~ msgstr "Suosikit"
236−
237−#~ msgid "Recently in touch"
238−#~ msgstr "Viimeksi yhteydessä"
239−
240−#~ msgid "New Contacts"
241−#~ msgstr "Uudet yhteystiedot"
po/fo.po+4−10
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 02:57+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Faroese <fo@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:15+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,9 +218,3 @@ msgstr "Tíanverri er tað einki sum hóskar til tína leiting."
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "Fólk"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Heimaskíggi"
po/fr.po+40−61
@@ -8,62 +8,62 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-26 15:11+0000\n"
12−"Last-Translator: Anne <anneonyme017@gmail.com>\n"
11+"PO-Revision-Date: 2013-06-27 03:21+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: French <fr@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
22−msgstr "Téléphone"
22+msgstr ""
2323
2424#: Applications/applications.js:29
2525msgid "Camera"
26−msgstr "Appareil photo"
26+msgstr ""
2727
2828#: Applications/applications.js:35
2929msgid "Gallery"
30−msgstr "Galerie"
30+msgstr ""
3131
3232#: Applications/applications.js:40
3333msgid "Facebook"
34−msgstr "Facebook"
34+msgstr ""
3535
3636#: Applications/applications.js:46
3737msgid "Browser"
38−msgstr "Navigateur"
38+msgstr ""
3939
4040#: Applications/applications.js:51
4141msgid "Twitter"
42−msgstr "Twitter"
42+msgstr ""
4343
4444#: Applications/applications.js:57
4545msgid "GMail"
46−msgstr "GMail"
46+msgstr ""
4747
4848#: Applications/applications.js:62
4949msgid "Weather"
50−msgstr "Météo"
50+msgstr ""
5151
5252#: Applications/applications.js:68
5353msgid "Notepad"
54−msgstr "Bloc-notes"
54+msgstr ""
5555
5656#: Applications/applications.js:74
5757msgid "Calendar"
58−msgstr "Agenda"
58+msgstr ""
5959
6060#: Applications/applications.js:80
6161msgid "Media Player"
62−msgstr "Lecteur multimédia"
62+msgstr ""
6363
6464#: Applications/applications.js:86
6565msgid "Evernote"
66−msgstr "Evernote"
66+msgstr ""
6767
6868#: Applications/applications.js:91
6969msgid "Map"
@@ -71,27 +71,27 @@ msgstr ""
7171
7272#: Applications/applications.js:96
7373msgid "Pinterest"
74−msgstr "Pinterest"
74+msgstr ""
7575
7676#: Applications/applications.js:101
7777msgid "SoundCloud"
78−msgstr "SoundCloud"
78+msgstr ""
7979
8080#: Applications/applications.js:106
8181msgid "Wikipedia"
82−msgstr "Wikipédia"
82+msgstr ""
8383
8484#: Applications/applications.js:111
8585msgid "YouTube"
86−msgstr "YouTube"
86+msgstr ""
8787
8888#: Components/PageHeader.qml:300
8989msgid "Recent searches"
90−msgstr "Recherches récentes"
90+msgstr ""
9191
9292#: Dash/DashApps.qml:65
9393msgid "Running apps"
94−msgstr "Applications en cours d’exécution"
94+msgstr ""
9595
9696#: Dash/DashApps.qml:68
9797msgid "Frequently used"
@@ -99,7 +99,7 @@ msgstr ""
9999
100100#: Dash/DashApps.qml:71
101101msgid "Installed"
102−msgstr "Installées"
102+msgstr ""
103103
104104#: Dash/DashApps.qml:74
105105msgid "Available for download"
@@ -111,19 +111,19 @@ msgstr "Applications"
111111
112112#: Dash/DashHome.qml:40
113113msgid "Frequent Apps"
114−msgstr "Applications fréquemment utilisées"
114+msgstr ""
115115
116116#: Dash/DashHome.qml:43
117117msgid "Recent Music"
118−msgstr "Musique récente"
118+msgstr ""
119119
120120#: Dash/DashHome.qml:46
121121msgid "Videos Popular Online"
122−msgstr "Vidéos populaires en ligne"
122+msgstr ""
123123
124124#: Dash/DashHome.qml:175
125125msgid "Home"
126−msgstr "Accueil"
126+msgstr ""
127127
128128#: Dash/DashMusic.qml:27 Dash/DashVideos.qml:28
129129msgid "Featured"
@@ -135,7 +135,7 @@ msgstr ""
135135
136136#: Dash/DashMusic.qml:29 Dash/DashVideos.qml:30
137137msgid "New Releases"
138−msgstr "Nouveautés"
138+msgstr ""
139139
140140#: Dash/DashMusic.qml:30
141141msgid "Top Charting"
@@ -155,15 +155,15 @@ msgstr "Vidéos"
155155
156156#: Dash/Video/VideoPreview.qml:98
157157msgid "Directed by:"
158−msgstr "Réalisé par :"
158+msgstr ""
159159
160160#: Dash/Video/VideoPreview.qml:121
161161msgid "Starring:"
162−msgstr "Acteurs :"
162+msgstr ""
163163
164164#: Dash/Video/VideoPreview.qml:144
165165msgid "Author:"
166−msgstr "Auteur :"
166+msgstr ""
167167
168168#: Greeter/Lockscreen.qml:133
169169msgid "Emergency call"
@@ -172,27 +172,27 @@ msgstr ""
172172#: Greeter/PassphraseLockscreen.qml:59
173173#, qt-format
174174msgid "Hello %1"
175−msgstr "Bonjour %1"
175+msgstr ""
176176
177177#: Greeter/PassphraseLockscreen.qml:59
178178msgid "Hello"
179−msgstr "Bonjour"
179+msgstr ""
180180
181181#: Hud/HudParametrizedActionsPage.qml:132
182182msgid "Confirm"
183−msgstr "Confirmer"
183+msgstr ""
184184
185185#: Hud/Hud.qml:61 Hud/Hud.qml:99 Hud/Hud.qml:372
186186msgid "Type or say a command"
187−msgstr "Entrez ou dictez une commande"
187+msgstr ""
188188
189189#: Hud/Hud.qml:84
190190msgid "Loading. Please Wait..."
191−msgstr "Chargement. Veuillez patienter..."
191+msgstr ""
192192
193193#: Hud/Hud.qml:89
194194msgid "Speak Now..."
195−msgstr "Parlez maintenant..."
195+msgstr ""
196196
197197#: Hud/Hud.qml:94
198198msgid "Speaking..."
@@ -204,38 +204,17 @@ msgstr ""
204204
205205#: Panel/Menus/Overview/FlightModeWidget.qml:42
206206msgid "Flight mode"
207−msgstr "Mode avion"
207+msgstr ""
208208
209209#: Panel/SearchIndicator.qml:26
210210msgid "Search"
211−msgstr "Rechercher"
211+msgstr ""
212212
213213#: plugins/Unity/scope.cpp:268
214214msgid "Sorry, there is nothing that matches your search."
215−msgstr "Désolé, aucun résultat ne correspond à votre recherche."
215+msgstr ""
216216
217217#: Shell.qml:383
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "Home screen"
223−#~ msgstr "Écran d’accueil"
224−
225−#~ msgid "People"
226−#~ msgstr "Contacts"
227−
228−#~ msgid "Favourite People"
229−#~ msgstr "Contacts favoris"
230−
231−#~ msgid "People Recently in Touch"
232−#~ msgstr "Contacts récents"
233−
234−#~ msgid "A-Z"
235−#~ msgstr "A-Z"
236−
237−#~ msgid "New Contacts"
238−#~ msgstr "Nouveaux contacts"
239−
240−#~ msgid "Favourites"
241−#~ msgstr "Favoris"
po/gl.po+4−16
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 03:38+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Galician <gl@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,15 +218,3 @@ msgstr "Sentímolo, non hai nada que coincida con esta busca."
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "Persoas"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Pantalla de inicio"
227−
228−#~ msgid "Favourite People"
229−#~ msgstr "Xente favorita"
230−
231−#~ msgid "People Recently in Touch"
232−#~ msgstr "Xente contactada recentemente"
po/he.po+4−10
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 03:54+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Hebrew <he@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,9 +218,3 @@ msgstr "שום דבר אינו תואם לחיפוש שלך, עמך הסליחה
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "אנשים"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "מסך הבית"
po/hr.po+4−10
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 04:14+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Croatian <hr@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,9 +218,3 @@ msgstr "Nažalost, ne postoji ništa što odgovara vašoj pretrazi."
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "Osobe"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Početni zaslon"
po/hu.po+4−10
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 04:28+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Hungarian <hu@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,9 +218,3 @@ msgstr "Elnézést, nincs találat a keresésre."
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "Emberek"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Kezdőképernyő"
po/hy.po+3−15
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-07 12:42+0000\n"
11+"PO-Revision-Date: 2013-06-27 04:43+0000\n"
1212"Last-Translator: iAbaS <sosabazyan@yandex.ru>\n"
1313"Language-Team: Armenian <hy@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:15+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,15 +218,3 @@ msgstr "Կներերք, բայց Ձեր հարցումով ոչինչ չկար"
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People Recently in Touch"
223−#~ msgstr "Վերջին մարդիկ"
224−
225−#~ msgid "Favourite People"
226−#~ msgstr "Ֆավորիտներ"
227−
228−#~ msgid "People"
229−#~ msgstr "Մարդիկ"
230−
231−#~ msgid "Home screen"
232−#~ msgstr "Տուն"
po/id.po+3−3
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
11+"PO-Revision-Date: 2013-06-26 22:35+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Indonesian <id@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
po/it.po+4−10
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 05:14+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Italian <it@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,9 +218,3 @@ msgstr "Nessun risultato corrispondente alla ricerca."
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "Contatti"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Home"
po/ja.po+3−3
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-15 14:12+0000\n"
11+"PO-Revision-Date: 2013-06-26 22:36+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Japanese <ja@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
po/kn.po+4−10
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 05:43+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Kannada <kn@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,9 +218,3 @@ msgstr "ಕ್ಷಮಿಸಿ ನಿಮ್ಮ ಶೋಧನೆಗೆ ಸರಿಯ
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "ಜನರು"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "ಹೋಮ್ ಸ್ಕ್ರೀನ್"
po/ko.po+4−4
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 05:59+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Korean <ko@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
po/ky.po+4−10
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 06:17+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Kirghiz <ky@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,9 +218,3 @@ msgstr "Тилекке каршы, сиздин издөөңүзгө эч нер
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "Адамдар"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Башкы экран"
po/lv.po+4−10
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 06:31+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Latvian <lv@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,9 +218,3 @@ msgstr "Diemžēl nekas neatbilst jūsu meklētajam."
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "Cilvēki"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Mājas ekrāns"
po/my.po+4−7
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 06:47+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Burmese <my@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:15+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,6 +218,3 @@ msgstr "ဆောရီး၊ သင်၏ရှာဖွေမူနှင့
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "လူ"
po/nb.po+3−9
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-17 09:10+0000\n"
11+"PO-Revision-Date: 2013-06-27 07:02+0000\n"
1212"Last-Translator: Joakim Langvand <jlangvand@gmail.com>\n"
1313"Language-Team: Norwegian Bokmal <nb@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,9 +218,3 @@ msgstr "Søket ga ingen treff."
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "Personer"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Hjemmeskjerm"
po/nl.po+4−10
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 07:19+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Dutch <nl@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:15+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,9 +218,3 @@ msgstr "Er is helaas niets dat met uw zoekopdracht overeenkomt."
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "Mensen"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Startscherm"
po/pl.po+8−32
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:43+0000\n"
12−"Last-Translator: Piotr Merski <Unknown>\n"
11+"PO-Revision-Date: 2013-06-27 08:52+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Polish <pl@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -167,16 +167,16 @@ msgstr "Autor:"
167167
168168#: Greeter/Lockscreen.qml:133
169169msgid "Emergency call"
170−msgstr ""
170+msgstr "Telefon alarmowy"
171171
172172#: Greeter/PassphraseLockscreen.qml:59
173173#, qt-format
174174msgid "Hello %1"
175−msgstr ""
175+msgstr "Witaj %1"
176176
177177#: Greeter/PassphraseLockscreen.qml:59
178178msgid "Hello"
179−msgstr ""
179+msgstr "Witaj"
180180
181181#: Hud/HudParametrizedActionsPage.qml:132
182182msgid "Confirm"
@@ -217,28 +217,4 @@ msgstr "Nie znaleziono pasujących wyników."
217217#: Shell.qml:383
218218#, qt-format
219219msgid "Please enter %1"
220−msgstr ""
221−
222−#~ msgid "Home screen"
223−#~ msgstr "Ekran domowy"
224−
225−#~ msgid "People"
226−#~ msgstr "Ludzie"
227−
228−#~ msgid "People Recently in Touch"
229−#~ msgstr "Osoby ostatnio w kontakcie"
230−
231−#~ msgid "Favourite People"
232−#~ msgstr "Ulubione kontakty"
233−
234−#~ msgid "A-Z"
235−#~ msgstr "A-Z"
236−
237−#~ msgid "Recently in touch"
238−#~ msgstr "Ostatnio w kontakcie"
239−
240−#~ msgid "New Contacts"
241−#~ msgstr "Nowe kontakty"
242−
243−#~ msgid "Favourites"
244−#~ msgstr "Ulubione"
220+msgstr "Wprowadź %1"
po/pt_BR.po+4−10
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 07:39+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,9 +218,3 @@ msgstr "Desculpe, não há nada que corresponda à sua pesquisa."
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "Pessoas"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Tela inicial"
po/ro.po+4−10
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 07:53+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Romanian <ro@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -219,9 +219,3 @@ msgstr ""
219219#, qt-format
220220msgid "Please enter %1"
221221msgstr ""
222−
223−#~ msgid "People"
224−#~ msgstr "Persoane"
225−
226−#~ msgid "Home screen"
227−#~ msgstr "Ecran principal"
po/ru.po+22−37
@@ -8,58 +8,58 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-21 10:08+0000\n"
12−"Last-Translator: Andrey Dedikov <iamawesome.ndr@gmail.com>\n"
11+"PO-Revision-Date: 2013-06-28 06:40+0000\n"
12+"Last-Translator: Eugene Marshal <Unknown>\n"
1313"Language-Team: Russian <ru@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
22−msgstr ""
22+msgstr "Телефон"
2323
2424#: Applications/applications.js:29
2525msgid "Camera"
26−msgstr ""
26+msgstr "Камера"
2727
2828#: Applications/applications.js:35
2929msgid "Gallery"
30−msgstr ""
30+msgstr "Галерея"
3131
3232#: Applications/applications.js:40
3333msgid "Facebook"
34−msgstr ""
34+msgstr "Facebook"
3535
3636#: Applications/applications.js:46
3737msgid "Browser"
38−msgstr ""
38+msgstr "Обозреватель"
3939
4040#: Applications/applications.js:51
4141msgid "Twitter"
42−msgstr ""
42+msgstr "Twitter"
4343
4444#: Applications/applications.js:57
4545msgid "GMail"
46−msgstr ""
46+msgstr "GMail"
4747
4848#: Applications/applications.js:62
4949msgid "Weather"
50−msgstr ""
50+msgstr "Погода"
5151
5252#: Applications/applications.js:68
5353msgid "Notepad"
54−msgstr ""
54+msgstr "Блокнот"
5555
5656#: Applications/applications.js:74
5757msgid "Calendar"
58−msgstr ""
58+msgstr "Календарь"
5959
6060#: Applications/applications.js:80
6161msgid "Media Player"
62−msgstr ""
62+msgstr "Медиапроигрыватель"
6363
6464#: Applications/applications.js:86
6565msgid "Evernote"
@@ -67,7 +67,7 @@ msgstr "Evernote"
6767
6868#: Applications/applications.js:91
6969msgid "Map"
70−msgstr ""
70+msgstr "Карта"
7171
7272#: Applications/applications.js:96
7373msgid "Pinterest"
@@ -79,11 +79,11 @@ msgstr "SoundCloud"
7979
8080#: Applications/applications.js:106
8181msgid "Wikipedia"
82−msgstr ""
82+msgstr "Википедия"
8383
8484#: Applications/applications.js:111
8585msgid "YouTube"
86−msgstr ""
86+msgstr "YouTube"
8787
8888#: Components/PageHeader.qml:300
8989msgid "Recent searches"
@@ -131,7 +131,7 @@ msgstr ""
131131
132132#: Dash/DashMusic.qml:28 Dash/DashVideos.qml:29
133133msgid "Recent"
134−msgstr ""
134+msgstr "Недавние"
135135
136136#: Dash/DashMusic.qml:29 Dash/DashVideos.qml:30
137137msgid "New Releases"
@@ -163,11 +163,11 @@ msgstr "В главных ролях"
163163
164164#: Dash/Video/VideoPreview.qml:144
165165msgid "Author:"
166−msgstr ""
166+msgstr "Автор:"
167167
168168#: Greeter/Lockscreen.qml:133
169169msgid "Emergency call"
170−msgstr ""
170+msgstr "Экстренный вызов"
171171
172172#: Greeter/PassphraseLockscreen.qml:59
173173#, qt-format
@@ -217,19 +217,4 @@ msgstr "Извините, поиск не дал результатов."
217217#: Shell.qml:383
218218#, qt-format
219219msgid "Please enter %1"
220−msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "Люди"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Главный экран"
227−
228−#~ msgid "Favourite People"
229−#~ msgstr "Избранные контакты"
230−
231−#~ msgid "People Recently in Touch"
232−#~ msgstr "Недавние контакты"
233−
234−#~ msgid "New Contacts"
235−#~ msgstr "Новые контакты"
220+msgstr "Пожалуйста, введите %1"
po/si.po+3−3
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
11+"PO-Revision-Date: 2013-06-26 22:36+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Sinhalese <si@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
po/sl.po+1−1
@@ -14,7 +14,7 @@ msgstr ""
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-07-06 08:44+0000\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
1818"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
po/sr.po+4−10
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 09:19+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Serbian <sr@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,9 +218,3 @@ msgstr "Нажалост, претрага није наишла на икакв
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "Људи"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Почетни екран"
po/sv.po+3−3
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
11+"PO-Revision-Date: 2013-06-26 22:36+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Swedish <sv@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
po/te.po+4−4
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 09:57+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Telugu <te@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
po/tr.po+3−24
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-24 17:24+0000\n"
11+"PO-Revision-Date: 2013-06-27 10:08+0000\n"
1212"Last-Translator: Adar Cem EREN <adarcemeren34@gmail.com>\n"
1313"Language-Team: Turkish <tr@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,24 +218,3 @@ msgstr "Üzgünüz, aramanızla eşleşen hiçbir şey yok."
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "Kişiler"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Başlangıç ekranı"
227−
228−#~ msgid "Favourite People"
229−#~ msgstr "Favori Kişiler"
230−
231−#~ msgid "People Recently in Touch"
232−#~ msgstr "En Son Konuşulan Kişiler"
233−
234−#~ msgid "A-Z"
235−#~ msgstr "A-Z"
236−
237−#~ msgid "New Contacts"
238−#~ msgstr "Yeni Kişiler"
239−
240−#~ msgid "Favourites"
241−#~ msgstr "Favoriler"
po/ug.po+6−27
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-28 04:36+0000\n"
12−"Last-Translator: Gheyret T.Kenji <Unknown>\n"
11+"PO-Revision-Date: 2013-06-27 11:02+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Uyghur <ug@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-30 07:12+0000\n"
18−"X-Generator: Launchpad (build 16692)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -172,7 +172,7 @@ msgstr "جىددىي تېلېفون"
172172#: Greeter/PassphraseLockscreen.qml:59
173173#, qt-format
174174msgid "Hello %1"
175−msgstr "سالام %1"
175+msgstr ""
176176
177177#: Greeter/PassphraseLockscreen.qml:59
178178msgid "Hello"
@@ -217,25 +217,4 @@ msgstr "كەچۈرۈڭ، ئىزدىگىنىڭىزگە ماس نەتىجە تېپ
217217#: Shell.qml:383
218218#, qt-format
219219msgid "Please enter %1"
220−msgstr "%1 نى كىرگۈزۈڭ"
221−
222−#~ msgid "Favourite People"
223−#~ msgstr "ھەمىشە ئالاقە قىلىدىغان كىشىلەر"
224−
225−#~ msgid "People Recently in Touch"
226−#~ msgstr "يېقىندا ئالاقە قىلغان كىشىلەر"
227−
228−#~ msgid "People"
229−#~ msgstr "كىشىلەر"
230−
231−#~ msgid "Home screen"
232−#~ msgstr "ماكان ئېكران"
233−
234−#~ msgid "A-Z"
235−#~ msgstr "A-Z"
236−
237−#~ msgid "Recently in touch"
238−#~ msgstr "يېقىندا ئالاقە قىلغانلار"
239−
240−#~ msgid "New Contacts"
241−#~ msgstr "يېڭى ئالاقەداشلار"
220+msgstr ""
po/uk.po+4−28
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-29 14:27+0000\n"
12−"Last-Translator: Roman Koshkin <mar2008m@rambler.ru>\n"
11+"PO-Revision-Date: 2013-07-21 05:47+0000\n"
12+"Last-Translator: Ярослав Смук <Smuk.Slavko@yandex.ua>\n"
1313"Language-Team: Ukrainian <uk@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-30 07:12+0000\n"
18−"X-Generator: Launchpad (build 16692)\n"
17+"X-Launchpad-Export-Date: 2013-07-22 06:19+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,27 +218,3 @@ msgstr "Вибачте, для вказаних вами ключових слі
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr "Будь ласка, введіть %1"
221−
222−#~ msgid "People"
223−#~ msgstr "Люди"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "Екран домівки"
227−
228−#~ msgid "Favourite People"
229−#~ msgstr "Вибрані контакти"
230−
231−#~ msgid "People Recently in Touch"
232−#~ msgstr "Недавні контакти"
233−
234−#~ msgid "A-Z"
235−#~ msgstr "А-Я"
236−
237−#~ msgid "New Contacts"
238−#~ msgstr "Нові контакти"
239−
240−#~ msgid "Favourites"
241−#~ msgstr "Улюблене"
242−
243−#~ msgid "Recently in touch"
244−#~ msgstr "Нещодавні контакти"
po/zh_CN.po+3−27
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-22 21:20+0000\n"
11+"PO-Revision-Date: 2013-06-27 10:33+0000\n"
1212"Last-Translator: Anthony Wong <anthony.wong@canonical.com>\n"
1313"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,27 +218,3 @@ msgstr "对不起,没有找到您想要的结果。"
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "Home screen"
223−#~ msgstr "主页"
224−
225−#~ msgid "Favourite People"
226−#~ msgstr "常用联系人"
227−
228−#~ msgid "People Recently in Touch"
229−#~ msgstr "近期联系人"
230−
231−#~ msgid "People"
232−#~ msgstr "联系人"
233−
234−#~ msgid "Recently in touch"
235−#~ msgstr "最近联系"
236−
237−#~ msgid "New Contacts"
238−#~ msgstr "新建联系人"
239−
240−#~ msgid "Favourites"
241−#~ msgstr "最爱"
242−
243−#~ msgid "A-Z"
244−#~ msgstr "A-Z"
po/zh_TW.po+4−16
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-06-26 22:58+0200\n"
11−"PO-Revision-Date: 2013-06-05 22:20+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2013-06-27 10:24+0000\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Chinese (Traditional) <zh_TW@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-06-27 16:16+0000\n"
18−"X-Generator: Launchpad (build 16681)\n"
17+"X-Launchpad-Export-Date: 2013-07-17 07:49+0000\n"
18+"X-Generator: Launchpad (build 16696)\n"
1919
2020#: Applications/applications.js:23
2121msgid "Phone"
@@ -218,15 +218,3 @@ msgstr "很抱歉,找不到符合您想搜尋的結果。"
218218#, qt-format
219219msgid "Please enter %1"
220220msgstr ""
221−
222−#~ msgid "People"
223−#~ msgstr "朋友"
224−
225−#~ msgid "Home screen"
226−#~ msgstr "家屋畫面"
227−
228−#~ msgid "Favourite People"
229−#~ msgstr "喜歡的朋友"
230−
231−#~ msgid "People Recently in Touch"
232−#~ msgstr "最近接觸的朋友"
run_on_device+3−19
@@ -33,7 +33,7 @@ usage() {
3333}
3434
3535exec_with_ssh() {
36− ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -t $USER@$TARGET_IP -p $TARGET_SSH_PORT "bash -ic \"$@\""
36+ ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -t $USER@$TARGET_IP -p $TARGET_SSH_PORT sudo -u $USER -i bash -ic \"$@\"
3737}
3838
3939exec_with_adb() {
@@ -83,17 +83,6 @@ build() {
8383}
8484
8585run() {
86− SERVICEFILE="${CHROOT_PREFIX}/etc/device-services"
87− adb shell "egrep '$BINARY' $SERVICEFILE"
88− CONTAINS_SHELL=$?
89− if [ $CONTAINS_SHELL -eq 0 ]; then
90− echo "Modifying $SERVICEFILE on device. Old contents will be saved in $SERVICEFILE.backup"
91− adb shell cp $SERVICEFILE $SERVICEFILE.backup
92− adb shell sed -i "/$BINARY/d" $SERVICEFILE
93− if $FLIPPED; then exec_with_adb /usr/bin/service ubuntu-touch-session restart
94− else exec_with_adb /usr/bin/service ubuntu-session restart; fi
95− fi
96− adb shell pkill $BINARY
9786 ARGS="--nomousetouch"
9887 if $GDB; then
9988 ARGS="$ARGS --gdb"
@@ -105,14 +94,9 @@ run() {
10594 ARGS="$ARGS -k"
10695 fi
10796
97+ exec_with_ssh "stop unity8"
10898 exec_with_ssh "cd $CODE_DIR/ && ./run $ARGS -- $RUN_OPTIONS"
109−
110− if [ $CONTAINS_SHELL -eq 0 ]; then
111− echo "Restoring $SERVICEFILE on device from $SERVICEFILE.backup"
112− adb shell cp $SERVICEFILE.backup $SERVICEFILE
113− if $FLIPPED; then exec_with_adb /usr/bin/service ubuntu-touch-session restart
114− else exec_with_adb /usr/bin/service ubuntu-session restart; fi
115− fi
99+ exec_with_ssh "start unity8"
116100}
117101
118102set -- `getopt -n$0 -u -a --longoptions="setup,gdb,pinlock,keylock,help" "sgpkh" "$@"`
tests/autopilot/setup.py+17−6
@@ -1,11 +1,22 @@
11#!/usr/bin/python
2−
32# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
4−# Copyright 2013 Canonical
53#
6−# This program is free software: you can redistribute it and/or modify it
7−# under the terms of the GNU General Public License version 3, as published
8−# by the Free Software Foundation.
4+# Unity Autopilot Test Suite
5+# Copyright (C) 2012-2013 Canonical
6+#
7+# This program is free software: you can redistribute it and/or modify
8+# it under the terms of the GNU General Public License as published by
9+# the Free Software Foundation, either version 3 of the License, or
10+# (at your option) any later version.
11+#
12+# This program is distributed in the hope that it will be useful,
13+# but WITHOUT ANY WARRANTY; without even the implied warranty of
14+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+# GNU General Public License for more details.
16+#
17+# You should have received a copy of the GNU General Public License
18+# along with this program. If not, see <http://www.gnu.org/licenses/>.
19+#
920
1021
1122from distutils.core import setup
@@ -14,7 +25,7 @@ from setuptools import find_packages
1425setup(
1526 name='unity',
1627 version='8.0',
17− description='Unity 8 autopilot tests.',
28+ description='Unity autopilot tests.',
1829 url='https://launchpad.net/unity',
1930 license='GPLv3',
2031 packages=find_packages(),
tests/autopilot/unity8/__init__.py+90−5
@@ -1,8 +1,93 @@
11# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
32#
4−# This program is free software: you can redistribute it and/or modify it
5−# under the terms of the GNU General Public License version 3, as published
6−# by the Free Software Foundation.
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+"""unity autopilot tests and emulators - top level package."""
21+import os
22+import os.path
23+import subprocess
24+import sysconfig
25+
26+
27+def running_installed_tests():
28+ binary_path = get_binary_path()
29+ return binary_path.startswith('/usr')
30+
31+
32+def get_lib_path():
33+ """Return the library path to use in this test run."""
34+ if running_installed_tests():
35+ lib_path = os.path.join(
36+ "/usr/lib/",
37+ sysconfig.get_config_var('MULTIARCH'),
38+ "unity8"
39+ )
40+ else:
41+ binary_path = get_binary_path()
42+ lib_path = os.path.dirname(binary_path)
43+ return lib_path
44+
45+
46+def get_default_extra_mock_libraries():
47+ mocks_path = get_mocks_library_path()
48+ return os.path.join(mocks_path, 'libusermetrics')
49+
50+
51+def get_mocks_library_path():
52+ if running_installed_tests():
53+ mock_path = "qml/mocks/"
54+ else:
55+ mock_path = "../lib/x86_64-linux-gnu/unity8/qml/mocks/"
56+ lib_path = get_lib_path()
57+ ld_library_path = os.path.abspath(
58+ os.path.join(
59+ lib_path,
60+ mock_path,
61+ )
62+ )
63+
64+ if not os.path.exists(ld_library_path):
65+ raise RuntimeError(
66+ "Expected library path does not exists: %s." % (ld_library_path)
67+ )
68+ return ld_library_path
69+
70+
71+def get_binary_path(binary="unity8"):
72+ """Return the path to the specified binary."""
73+ binary_path = os.path.abspath(
74+ os.path.join(
75+ os.path.dirname(__file__),
76+ "../../../builddir/install/bin/%s" % binary
77+ )
78+ )
79+ if not os.path.exists(binary_path):
80+ try:
81+ binary_path = subprocess.check_output(['which', binary]).strip()
82+ except subprocess.CalledProcessError as e:
83+ raise RuntimeError("Unable to locate %s binary: %r" % (binary, e))
84+ return binary_path
85+
786
8−"""unity8 autopilot tests and emulators - top level package."""
87+def get_grid_size():
88+ grid_size = os.getenv('GRID_UNIT_PX')
89+ if grid_size is None:
90+ raise RuntimeError(
91+ "Environment variable GRID_UNIT_PX has not been set."
92+ )
93+ return int(grid_size)
tests/autopilot/unity8/indicators_client/__init__.py+1−1
@@ -5,4 +5,4 @@
55# under the terms of the GNU General Public License version 3, as published
66# by the Free Software Foundation.
77
8−"""unity8 indicators-client autopilot tests and emulators - sub level package."""
8+"""unity indicators-client autopilot tests and emulators - sub level package."""
tests/autopilot/unity8/indicators_client/tests/__init__.py+13−9
@@ -5,7 +5,7 @@
55# under the terms of the GNU General Public License version 3, as published
66# by the Free Software Foundation.
77
8−"""qml-phone-shell autopilot tests."""
8+"""indicators-client autopilot tests."""
99
1010import os.path
1111
@@ -15,6 +15,7 @@ from autopilot.matchers import Eventually
1515from autopilot.platform import model
1616from testtools.matchers import Equals
1717
18+from unity8 import get_binary_path
1819from unity8.indicators_client.emulators.main_window import MainWindow
1920from logging import getLogger
2021import sys
@@ -29,9 +30,12 @@ class IndicatorsTestCase(AutopilotTestCase):
2930
3031 """A common test case class that provides several useful methods for indicator tests."""
3132
33+ geometry = None
34+ grid_unit = None
35+
3236 if model() == 'Desktop':
3337 scenarios = [
34− ('with mouse', dict(input_device_class=Mouse)),
38+ ('with mouse', dict(input_device_class=Mouse, geometry="400x800", grid_unit="12")),
3539 ]
3640 else:
3741 scenarios = [
@@ -41,11 +45,11 @@ class IndicatorsTestCase(AutopilotTestCase):
4145 def setUp(self, geometry, grid_size):
4246 self.pointing_device = Pointer(self.input_device_class.create())
4347 super(IndicatorsTestCase, self).setUp()
44− if grid_size != "0":
45− os.environ['GRID_UNIT_PX'] = grid_size
48+ if grid_size is not None:
49+ self.patch_environment("GRID_UNIT_PX", str(grid_size))
4650 self.grid_size = int(grid_size)
4751 else:
48− self.grid_size = int(os.environ['GRID_UNIT_PX'])
52+ self.grid_size = int(os.getenv('GRID_UNIT_PX'))
4953
5054 if os.path.realpath(__file__).startswith("/usr/"):
5155 self.launch_test_installed(geometry)
@@ -53,15 +57,15 @@ class IndicatorsTestCase(AutopilotTestCase):
5357 self.launch_test_local(geometry)
5458
5559 def launch_test_local(self, geometry):
56− if geometry != "0x0":
57− self.app = self.launch_test_application("../../builddir/src/Panel/Indicators/client/indicators-client",
60+ if geometry is None:
61+ self.app = self.launch_test_application(get_binary_path("indicators-client"),
5862 "-geometry", geometry, app_type='qt')
5963 else:
60− self.app = self.launch_test_application("../../builddir/src/Panel/Indicators/client/indicators-client",
64+ self.app = self.launch_test_application(get_binary_path("indicators-client"),
6165 app_type='qt')
6266
6367 def launch_test_installed(self, geometry):
64− if geometry != "0x0":
68+ if geometry is not None:
6569 self.app = self.launch_test_application("indicators-client", "-geometry", geometry, app_type='qt')
6670 else:
6771 self.app = self.launch_test_application("indicators-client", app_type='qt')
tests/autopilot/unity8/indicators_client/tests/test_battery.py+14−3
@@ -8,6 +8,7 @@
88"""Add tests here if you want to ensure the behaviour of the power indicator menus are correct"""
99
1010from testtools.matchers import Equals, NotEquals
11+from autopilot.input import Touch
1112from autopilot.matchers import Eventually
1213
1314from unity8.indicators_client.tests import IndicatorsTestCase
@@ -17,7 +18,7 @@ import math
1718
1819class TestDisplayMenus(IndicatorsTestCase):
1920 def setUp(self):
20− super(TestDisplayMenus, self).setUp("400x800", "12")
21+ super(TestDisplayMenus, self).setUp(self.geometry, self.grid_unit)
2122
2223 # This opens the messaging menu so you don't have to do that in
2324 # every test case
@@ -69,7 +70,11 @@ class TestDisplayMenus(IndicatorsTestCase):
6970
7071 # wait for the switch menu item
7172 fn_auto_brightness = lambda: self.app.select_single("SwitchMenuItem", objectName="auto-brightness");
72− self.assertThat(fn_auto_brightness, Eventually(NotEquals(None)));
73+ # FIXME: this should go away when we switch to indicator-power
74+ try:
75+ self.assertThat(fn_auto_brightness, Eventually(NotEquals(None)));
76+ except AssertionError:
77+ self.skipTest("WARNING: test skipped due to missing indicator-battery")
7378 auto_brightness = fn_auto_brightness();
7479
7580 old_ab_value = auto_brightness.checked
@@ -82,8 +87,14 @@ class TestDisplayMenus(IndicatorsTestCase):
8287 def test_brightness_slider(self):
8388 """Test the auto-bright switch"""
8489
90+ if self.input_device_class is Touch:
91+ self.skipTest("Dragging is broken with Touch input (LP: #1203808).")
8592 fn_brightness_menu = lambda: self.app.select_single("SliderMenuItem", objectName="brightness");
86− self.assertThat(fn_brightness_menu, Eventually(NotEquals(None)));
93+ # FIXME: this should go away when we switch to indicator-power
94+ try:
95+ self.assertThat(fn_brightness_menu, Eventually(NotEquals(None)));
96+ except AssertionError:
97+ self.skipTest("WARNING: test skipped due to missing indicator-battery")
8798 brightness_menu = fn_brightness_menu();
8899
89100 old_ab_value = brightness_menu.value
tests/autopilot/unity8/shell/__init__.py+35−5
@@ -1,8 +1,38 @@
11# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
32#
4−# This program is free software: you can redistribute it and/or modify it
5−# under the terms of the GNU General Public License version 3, as published
6−# by the Free Software Foundation.
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+"""unity shell autopilot tests and emulators - sub level package."""
21+
22+from functools import wraps
23+import logging
24+
25+
26+logger = logging.getLogger(__name__)
27+
728
8−"""unity8 shell autopilot tests and emulators - sub level package."""
29+def with_lightdm_mock(mock_type):
30+ """A simple decorator that sets up the LightDM mock for a single test."""
31+ def with_lightdm_mock_internal(fn):
32+ @wraps(fn)
33+ def wrapper(*args, **kwargs):
34+ tests_self = args[0]
35+ tests_self.patch_lightdm_mock(mock_type)
36+ return fn(*args, **kwargs)
37+ return wrapper
38+ return with_lightdm_mock_internal
tests/autopilot/unity8/shell/emulators/__init__.py+23−4
@@ -1,6 +1,25 @@
11# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
32#
4−# This program is free software: you can redistribute it and/or modify it
5−# under the terms of the GNU General Public License version 3, as published
6−# by the Free Software Foundation.
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+
21+from autopilot.introspection import CustomEmulatorBase
22+
23+
24+class UnityEmulatorBase(CustomEmulatorBase):
25+ """A base class for all unity emulators."""
tests/autopilot/unity8/shell/emulators/dash.pynew file+52
@@ -0,0 +1,52 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+from unity8.shell.emulators import UnityEmulatorBase
21+
22+
23+class Dash(UnityEmulatorBase):
24+
25+ """An emulator that understands the Dash."""
26+
27+ def get_home_applications_grid(self):
28+ return self.select_single(
29+ "ApplicationsFilterGrid",
30+ objectName="dashHomeApplicationsGrid"
31+ )
32+
33+ def get_application_icon(self, text):
34+ """Returns a 'Tile' icon that has the text 'text' from the application
35+ grid.
36+
37+ Will return None if the icon isn't found.
38+
39+ :param text: String containing the text of the icon to search for.
40+
41+ """
42+ app_grid = self.get_home_applications_grid()
43+ resp_grid = app_grid.select_single('ResponsiveGridView')
44+ return resp_grid.select_single('Tile', text=text)
45+
46+ def get_scope(self, scope_name='home'):
47+ dash_content = self.select_single(
48+ 'QQuickListView',
49+ objectName='dashContentList'
50+ )
51+ scope_id = "%s.scope" % scope_name
52+ return dash_content.select_single('QQuickLoader', scopeId=scope_id)
tests/autopilot/unity8/shell/emulators/greeter.pynew file+40
@@ -0,0 +1,40 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+from unity8.shell.emulators import UnityEmulatorBase
21+from autopilot.input import Touch
22+
23+
24+class Greeter(UnityEmulatorBase):
25+
26+ """An emulator that understands the greeter screen."""
27+
28+ def unlock(self):
29+ """Swipe the greeter screen away."""
30+ self.created.wait_for(True)
31+ touch = Touch.create()
32+
33+ rect = self.globalRect
34+ start_x = rect[0] + rect[2] - 3
35+ start_y = int(rect[1] + rect[3] / 2)
36+ stop_x = int(rect[0] + rect[2] * 0.2)
37+ stop_y = start_y
38+ touch.drag(start_x, start_y, stop_x, stop_y)
39+
40+ self.created.wait_for(False)
tests/autopilot/unity8/shell/emulators/hud.pynew file+83
@@ -0,0 +1,83 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+from collections import namedtuple
21+
22+from unity8 import get_grid_size
23+from unity8.shell.emulators import UnityEmulatorBase
24+from autopilot.input import Touch
25+
26+
27+SwipeCoords = namedtuple('SwipeCoords', 'start_x end_x start_y end_y')
28+
29+
30+class Hud(UnityEmulatorBase):
31+
32+ """An emulator that understands the Hud."""
33+
34+ def show(self):
35+ """Swipes open the Hud."""
36+ touch = Touch.create()
37+
38+ window = self.get_root_instance().select_single('QQuickView')
39+ hud_show_button = window.select_single("HudButton")
40+
41+ start_x = int(window.x + (window.width / 2))
42+ end_x = start_x
43+ start_y = window.y + window.height
44+ end_y = int(hud_show_button.y + (hud_show_button.height/2))
45+
46+ touch.press(start_x, start_y)
47+ touch._finger_move(end_x, end_y)
48+ try:
49+ hud_show_button.opacity.wait_for(1.0)
50+ touch.release()
51+ self.shown.wait_for(True)
52+ except AssertionError:
53+ raise
54+ finally:
55+ if touch._touch_finger is not None:
56+ touch.release()
57+
58+ def dismiss(self):
59+ """Closes the open Hud."""
60+ # Ensure that the Hud is actually open
61+ self.shown.wait_for(True)
62+ touch = Touch.create()
63+ x, y = self.get_close_button_coords()
64+ touch.tap(x, y)
65+ self.y.wait_for(self.height)
66+
67+ def get_close_button_coords(self):
68+ """Returns the coordinates of the Huds close button bar."""
69+ rect = self.globalRect
70+ x = int(rect[0] + rect[2] / 2)
71+ y = rect[1] + get_grid_size()
72+ return x, y
73+
74+ def get_button_swipe_coords(self, main_view, hud_show_button):
75+ """Returns the coords both start and end x,y for swiping to make the
76+ 'hud show' button appear.
77+ """
78+ start_x = int(main_view.x + (main_view.width / 2))
79+ end_x = start_x
80+ start_y = main_view.y + (main_view.height - 3)
81+ end_y = int(hud_show_button.y + (hud_show_button.height/2))
82+
83+ return SwipeCoords(start_x, end_x, start_y, end_y)
tests/autopilot/unity8/shell/emulators/launcher.pynew file+38
@@ -0,0 +1,38 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+from unity8.shell.emulators import UnityEmulatorBase
21+from autopilot.input import Touch
22+
23+
24+class Launcher(UnityEmulatorBase):
25+
26+ """An emulator that understands the Launcher."""
27+
28+ def show(self):
29+ """Swipes open the launcher."""
30+ touch = Touch.create()
31+
32+ view = self.get_root_instance().select_single('QQuickView')
33+ start_x = view.x + 1
34+ start_y = view.y + view.height / 2
35+ stop_x = start_x + self.panelWidth + 1
36+ stop_y = start_y
37+ touch.drag(start_x, start_y, stop_x, stop_y)
38+ self.shown.wait_for(True)
tests/autopilot/unity8/shell/emulators/main_window.py+43−12
@@ -1,9 +1,28 @@
11# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
32#
4−# This program is free software: you can redistribute it and/or modify it
5−# under the terms of the GNU General Public License version 3, as published
6−# by the Free Software Foundation.
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+
21+from unity8.shell.emulators.greeter import Greeter
22+from unity8.shell.emulators.hud import Hud
23+from unity8.shell.emulators.dash import Dash
24+from unity8.shell.emulators.launcher import Launcher
25+
726
827class MainWindow(object):
928 """An emulator class that makes it easy to interact with the shell"""
@@ -16,10 +35,13 @@ class MainWindow(object):
1635 return self.app.select_single("QQuickView")
1736
1837 def get_greeter(self):
19− return self.app.select_single("Greeter")
38+ return self.app.select_single(Greeter)
2039
2140 def get_greeter_content_loader(self):
22− return self.app.select_single("QQuickLoader", objectName="greeterContentLoader")
41+ return self.app.select_single(
42+ "QQuickLoader",
43+ objectName="greeterContentLoader"
44+ )
2345
2446 def get_login_loader(self):
2547 return self.app.select_single("QQuickLoader", objectName="loginLoader")
@@ -28,7 +50,7 @@ class MainWindow(object):
2850 return self.app.select_single("LoginList")
2951
3052 def get_hud(self):
31− return self.app.select_single("Hud")
53+ return self.app.select_single(Hud)
3254
3355 def get_hud_showable(self):
3456 return self.app.select_single("Showable", objectName="hudShowable")
@@ -37,22 +59,31 @@ class MainWindow(object):
3759 return self.app.select_single("HudButton")
3860
3961 def get_dash(self):
40− return self.app.select_single("Dash")
62+ return self.app.select_single(Dash)
4163
4264 def get_dash_home_applications_grid(self):
43− return self.app.select_single("ApplicationsFilterGrid", objectName="dashHomeApplicationsGrid")
65+ return self.app.select_single(
66+ "ApplicationsFilterGrid",
67+ objectName="dashHomeApplicationsGrid"
68+ )
4469
4570 def get_bottombar(self):
4671 return self.app.select_single("Bottombar")
4772
4873 def get_launcher(self):
49− return self.app.select_single("Launcher")
74+ return self.app.select_single(Launcher)
5075
5176 def get_pinPadLoader(self):
52− return self.app.select_single("QQuickLoader", objectName="pinPadLoader")
77+ return self.app.select_single(
78+ "QQuickLoader",
79+ objectName="pinPadLoader"
80+ )
5381
5482 def get_pinPadButton(self, buttonId):
55− return self.app.select_single("PinPadButton", objectName="pinPadButton%i" % buttonId)
83+ return self.app.select_single(
84+ "PinPadButton",
85+ objectName="pinPadButton%i" % buttonId
86+ )
5687
5788 def get_lockscreen(self):
5889 return self.app.select_single("Lockscreen")
tests/autopilot/unity8/shell/tests/__init__.py+216−58
@@ -1,81 +1,239 @@
11# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
32#
4−# This program is free software: you can redistribute it and/or modify it
5−# under the terms of the GNU General Public License version 3, as published
6−# by the Free Software Foundation.
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
719
8−"""unity8 autopilot tests."""
20+"""unity autopilot tests."""
921
10−import os.path
11−import sysconfig
22+try:
23+ from gi.repository import Gio
24+except ImportError:
25+ Gio = None
1226
27+from autopilot.platform import model
1328from autopilot.testcase import AutopilotTestCase
1429from autopilot.matchers import Eventually
15−from autopilot.platform import model
16−from testtools.matchers import Equals
30+from autopilot.input import Touch
31+from autopilot.display import Display
32+import logging
33+import os.path
34+from testtools.matchers import Equals, NotEquals
1735
36+from unity8 import (
37+ get_lib_path,
38+ get_binary_path,
39+ get_mocks_library_path,
40+ get_default_extra_mock_libraries,
41+)
42+from unity8.shell.emulators import UnityEmulatorBase
43+from unity8.shell.emulators.dash import Dash
1844from unity8.shell.emulators.main_window import MainWindow
1945
20−class FormFactors(object):
21− Phone, Tablet, Desktop = range(3)
2246
23−class ShellTestCase(AutopilotTestCase):
47+logger = logging.getLogger(__name__)
2448
25− """A common test case class that provides several useful methods for shell tests."""
49+UNITYSHELL_GSETTINGS_SCHEMA = "org.compiz.unityshell"
50+UNITYSHELL_GSETTINGS_PATH = "/org/compiz/profiles/unity/plugins/unityshell/"
51+UNITYSHELL_LAUNCHER_KEY = "launcher-hide-mode"
52+UNITYSHELL_LAUNCHER_MODE = 1 # launcher hidden
2653
27− libdir = "/usr/lib/{0}/unity8".format(sysconfig.get_config_var('MULTIARCH'))
28− lightdm_mock = "full"
54+def _get_device_emulation_scenarios():
55+ if model() == 'Desktop':
56+ return [
57+ (
58+ 'Desktop Nexus 4',
59+ dict(app_width=768, app_height=1280, grid_unit_px=18)
60+ ),
61+ (
62+ 'Desktop Nexus 10',
63+ dict(app_width=2560, app_height=1600, grid_unit_px=20)
64+ ),
65+ ]
66+ else:
67+ return [
68+ (
69+ 'Native Device',
70+ dict(app_width=0, app_height=0, grid_unit_px=0)
71+ )
72+ ]
2973
30− def setUp(self, geometry, grid_size):
31− super(ShellTestCase, self).setUp()
32− # Lets assume we are installed system wide if this file is somewhere in /usr
33− if grid_size != "0":
34− os.environ['GRID_UNIT_PX'] = grid_size
35− self.grid_size = int(grid_size)
36− else:
37− self.grid_size = int(os.environ['GRID_UNIT_PX'])
38− if os.path.realpath(__file__).startswith("/usr/"):
39− self.launch_test_installed(geometry)
74+
75+class UnityTestCase(AutopilotTestCase):
76+
77+ """A test case base class for the Unity shell tests."""
78+
79+ def setUp(self):
80+ super(UnityTestCase, self).setUp()
81+ if Gio is not None and UNITYSHELL_GSETTINGS_SCHEMA in Gio.Settings.list_relocatable_schemas():
82+ # Hide Unity launcher
83+ self._unityshell_schema = Gio.Settings.new_with_path(UNITYSHELL_GSETTINGS_SCHEMA, UNITYSHELL_GSETTINGS_PATH)
84+ self._launcher_hide_mode = self._unityshell_schema.get_int(UNITYSHELL_LAUNCHER_KEY)
85+ self._unityshell_schema.set_int(UNITYSHELL_LAUNCHER_KEY, UNITYSHELL_LAUNCHER_MODE)
86+ self.addCleanup(self._reset_launcher)
87+
88+ self._proxy = None
89+ self._lightdm_mock_type = None
90+ self.touch = Touch.create()
91+ self._setup_display_details()
92+
93+ def _reset_launcher(self):
94+ """Reset Unity launcher hide mode"""
95+ self._unityshell_schema.set_int(UNITYSHELL_LAUNCHER_KEY, self._launcher_hide_mode)
96+
97+ def _setup_display_details(self):
98+ scale_divisor = self._determine_geometry()
99+ self._setup_grid_size(scale_divisor)
100+
101+ def _determine_geometry(self):
102+ """Use the geometry that may be supplied or use the default."""
103+ width = getattr(self, 'app_width', 0)
104+ height = getattr(self, 'app_height', 0)
105+ scale_divisor = 1
106+ if width == 0 and width == 0:
107+ self.unity_geometry_args = ['-fullscreen']
40108 else:
41− self.launch_test_local(geometry)
42−
43− def launch_test_local(self, geometry):
44− os.environ['LD_LIBRARY_PATH'] = "../../builddir/tests/mocks/libusermetrics:../../builddir/tests/mocks/LightDM" + self.lightdm_mock
45− os.environ['QML2_IMPORT_PATH'] = "../../builddir/tests/mocks"
46− if geometry != "0x0":
47− self.app = self.launch_test_application(
48− "../../builddir/unity8", "-geometry", geometry, "-frameless", app_type='qt')
109+ if self._geo_larger_than_display(width, height):
110+ scale_divisor = self._get_scaled_down_geo(width, height)
111+ width = width / scale_divisor
112+ height = height / scale_divisor
113+ logger.info(
114+ "Geometry larger than display, scaled down to: %dx%d",
115+ width,
116+ height
117+ )
118+ geo_string = "%dx%d" % (width, height)
119+ self.unity_geometry_args = [
120+ '-geometry',
121+ geo_string,
122+ '-frameless',
123+ '-mousetouch'
124+ ]
125+ return scale_divisor
126+
127+ def _setup_grid_size(self, scale_divisor):
128+ """Use the grid size that may be supplied or use the default."""
129+ if getattr(self, 'grid_unit_px', 0) == 0:
130+ self.grid_size = int(os.getenv('GRID_UNIT_PX'))
49131 else:
50− self.app = self.launch_test_application(
51− "../../builddir/unity8", "-fullscreen", app_type='qt')
52−
53− def launch_test_installed(self, geometry):
54− os.environ['LD_LIBRARY_PATH'] = "{0}/qml/mocks/libusermetrics:{0}/qml/mocks/LightDM/{1}".format(self.libdir, self.lightdm_mock)
55− os.environ['QML2_IMPORT_PATH'] = "{0}/qml/mocks".format(self.libdir)
56− if model() == 'Desktop' and geometry != "0x0":
57− self.app = self.launch_test_application(
58− "unity8", "-geometry", geometry, "-frameless", app_type='qt')
132+ self.grid_size = int(self.grid_unit_px / scale_divisor)
133+ self.patch_environment("GRID_UNIT_PX", str(self.grid_size))
134+
135+ def _geo_larger_than_display(self, width, height):
136+ should_scale = getattr(self, 'scale_geo', True)
137+ if should_scale:
138+ screen = Display.create()
139+ screen_width = screen.get_screen_width()
140+ screen_height = screen.get_screen_height()
141+ return (width > screen_width) or (height > screen_height)
59142 else:
60− self.app = self.launch_test_application(
61− "unity8", "-fullscreen", app_type='qt')
143+ return False
144+
145+ def _get_scaled_down_geo(self, width, height):
146+ divisor = 1
147+ while self._geo_larger_than_display(width / divisor, height / divisor):
148+ divisor = divisor * 2
149+ return divisor
150+
151+ def launch_unity(self):
152+ """Launch the unity shell, return a proxy object for it."""
153+ binary_path = get_binary_path()
154+ lib_path = get_lib_path()
155+
156+ logger.info(
157+ "Lib path is '%s', binary path is '%s'",
158+ lib_path,
159+ binary_path
160+ )
161+
162+ if self._lightdm_mock_type is None:
163+ self.patch_lightdm_mock()
164+ self._setup_extra_mock_environment_patch()
165+
166+ app_proxy = self.launch_test_application(
167+ binary_path,
168+ *self.unity_geometry_args,
169+ app_type='qt',
170+ emulator_base=UnityEmulatorBase
171+ )
172+ self._set_proxy(app_proxy)
173+
174+ # Ensure that the dash is visible before we return:
175+ logger.debug("Unity started, waiting for it to be ready.")
176+ self.assertUnityReady()
177+ logger.debug("Unity loaded and ready.")
178+
179+ return app_proxy
180+
181+ def patch_lightdm_mock(self, mock_type='single'):
182+ self._lightdm_mock_type = mock_type
183+ logger.info("Setting up LightDM mock type '%s'", mock_type)
184+ new_ld_library_path = "%s:%s" % (
185+ get_default_extra_mock_libraries(),
186+ self._get_lightdm_mock_path(mock_type)
187+ )
188+ logger.info("New library path: %s", new_ld_library_path)
189+
190+ self.patch_environment('LD_LIBRARY_PATH', new_ld_library_path)
191+
192+ def _get_lightdm_mock_path(self, mock_type):
193+ lib_path = get_mocks_library_path()
194+ lightdm_mock_path = os.path.abspath(
195+ os.path.join(lib_path, "LightDM", mock_type)
196+ )
197+
198+ if not os.path.exists(lightdm_mock_path):
199+ raise RuntimeError(
200+ "LightDM mock '%s' does not exist at path '%s'."
201+ % (mock_type, lightdm_mock_path)
202+ )
203+ return lightdm_mock_path
204+
205+ def _setup_extra_mock_environment_patch(self):
206+ mocks_library_path = get_mocks_library_path()
207+ self.patch_environment('QML2_IMPORT_PATH', mocks_library_path)
208+
209+ def _set_proxy(self, proxy):
210+ """Keep a copy of the proxy object, so we can use it to get common
211+ parts of the shell later on.
212+
213+ """
214+ self._proxy = proxy
215+ self.addCleanup(self._clear_proxy)
216+
217+ def _clear_proxy(self):
218+ self._proxy = None
62219
63− def skipWrapper(*args, **kwargs):
64− pass
220+ def assertUnityReady(self):
221+ dash = self.get_dash()
222+ home_scope = dash.get_scope('home')
65223
66− def form_factor(self):
67− return FormFactors.Desktop
224+ # FIXME! There is a huge timeout here for when we're doing CI on
225+ # VMs. See lp:1203715
226+ self.assertThat(
227+ home_scope.isLoaded,
228+ Eventually(Equals(True), timeout=60)
229+ )
230+ self.assertThat(home_scope.isCurrent, Eventually(Equals(True)))
68231
69− def __getattribute__(self, attr_name):
70− attr = object.__getattribute__(self, attr_name);
71− if attr_name.startswith("test_"):
72− try:
73− if self.form_factor() in attr.blacklist:
74− return self.skipWrapper
75− except:
76− pass
77− return attr
232+ def get_dash(self):
233+ dash = self._proxy.select_single(Dash)
234+ self.assertThat(dash, NotEquals(None))
235+ return dash
78236
79237 @property
80238 def main_window(self):
81− return MainWindow(self.app)
239+ return MainWindow(self._proxy)
tests/autopilot/unity8/shell/tests/helpers.pydeleted−161
@@ -1,161 +0,0 @@
1−# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
3−#
4−# This program is free software: you can redistribute it and/or modify it
5−# under the terms of the GNU General Public License version 3, as published
6−# by the Free Software Foundation.
7−
8−from testtools.matchers import Equals, NotEquals
9−from autopilot.matchers import Eventually
10−
11−import logging
12−
13−logger = logging.getLogger(__name__)
14−
15−class TestShellHelpers(object):
16− """Helpers for testing the Shell"""
17−
18− def select_greeter_user(self, username):
19− greeter = self.main_window.get_greeter()
20− self.assertThat(greeter.created, Eventually(Equals(True)))
21− self.assertThat(greeter.multiUser, Eventually(Equals(True)))
22−
23− login_loader = self.main_window.get_login_loader()
24− self.assertThat(login_loader.progress, Eventually(Equals(1)))
25−
26− login_list = self.main_window.get_login_list()
27− list_view = login_list.get_children_by_type("QQuickListView")[0]
28−
29− try_count = 0
30− max_tries = 50 # just in case we go off rails
31− while try_count < max_tries:
32− users = list_view.get_children_by_type("QQuickItem")[0].get_children_by_type("QQuickItem")
33− target_user = None
34− for user in users:
35− try:
36− user_label = user.get_children_by_type("Label")[0]
37− if user.opacity < 0.1:
38− continue # off-screen item
39− if user_label.text == username:
40− target_user = user
41− break
42− elif target_user is None or user.y > target_user.y:
43− target_user = user
44− except Exception:
45− pass
46− if target_user is None:
47− break
48− user_label = target_user.get_children_by_type("Label")[0]
49− self.touch.tap_object(user_label)
50− self.assertThat(list_view.movingInternally, Eventually(Equals(False)))
51− if user_label.text == username:
52− return login_list.get_children_by_type("TextField")[0]
53− try_count = try_count + 1
54− self.fail() # We didn't find it
55−
56− def unlock_greeter(self, retries=2):
57− greeter = self.main_window.get_greeter()
58− self.assertThat(greeter.created, Eventually(Equals(True)))
59−
60− if greeter.multiUser:
61− password_field = self.select_greeter_user("No Password")
62− self.assertThat(password_field.opacity, Eventually(Equals(1)))
63− self.touch.tap_object(password_field)
64−
65− else:
66− rect = greeter.globalRect
67− start_x = rect[0] + rect[2] - 3
68− start_y = int(rect[1] + rect[3] / 2)
69− stop_x = int(rect[0] + rect[2] * 0.2)
70− stop_y = start_y
71− self.touch.drag(start_x, start_y, stop_x, stop_y)
72−
73− # Because the shell loads up lots of stuff, unlocking the greeter can
74− # be a bit stuttery while scopes are still consuming all resources.
75− # Give it another (max retries) chance
76− try:
77− self.assertThat(greeter.created, Eventually(Equals(False)))
78− except:
79− if retries > 0:
80− logger.warning("Failed to unlock greeter. Retrying...")
81− self.unlock_greeter(retries-1)
82− else:
83− logger.warning("Failed to unlock greeter. Giving up. Tests may fail...")
84−
85−
86− def open_first_dash_home_app(self, retries=2):
87− self.assertThat(lambda: self.main_window.get_dash_home_applications_grid(), Eventually(NotEquals(None)))
88− app_grid = self.main_window.get_dash_home_applications_grid()
89− # Wait for the grids to be expanded
90− self.assertThat(app_grid.get_children()[0].totalContentHeight, Eventually(NotEquals(0)))
91− self.assertThat(app_grid.get_children()[0].height, Eventually(Equals(app_grid.get_children()[0].totalContentHeight)))
92− first_app = app_grid.get_children()[0].get_children()[0].get_children()[0].get_children()[0]
93− self.touch.tap_object(first_app)
94− bottombar = self.main_window.get_bottombar()
95−
96− # if we have huge amounts of pixels (e.g. Nexus10), but slow video (e.g. VM) it might take a little
97− # until the dash is fully rendered after sliding away the greeter/lockscreen and the click might
98− # go to the void for the first time because there's no real way to know what the display has
99− # painted already. Give it another (max retries) chance.
100− try:
101− self.assertThat(lambda: getattr(bottombar, "applicationIsOnForeground"),
102− Eventually(Equals(True)))
103− except:
104− if retries > 0:
105− logger.warning("Failed to launch app. Retrying...")
106− self.open_first_dash_home_app(retries-1)
107− else:
108− logger.warning("Failed to launch app. Giving up. Tests may fail...")
109−
110−
111− def show_hud(self, retries=2):
112− self.open_first_dash_home_app()
113−
114− hud_show_button = self.main_window.get_hud_show_button()
115− hud = self.main_window.get_hud()
116− window = self.main_window.get_qml_view()
117− start_x = int(window.x + window.width / 2)
118− start_y = window.y + window.height - 3
119− self.assertThat(hud_show_button.opacity, Eventually(Equals(0)))
120− self.touch.press(start_x, start_y)
121− self.touch._finger_move(start_x, start_y - self.grid_size)
122− self.assertThat(hud_show_button.opacity, Eventually(Equals(0)))
123− self.touch._finger_move(start_x, start_y - self.grid_size * 2)
124− self.touch._finger_move(start_x, start_y - self.grid_size * 3)
125− self.touch._finger_move(start_x, start_y - self.grid_size * 4)
126− try:
127− self.assertThat(hud_show_button.opacity, Eventually(Equals(1.0)))
128− except:
129− if retries > 0:
130− logger.warning("Failed to get hud button to show. Retrying...")
131− self.touch.release()
132− self.show_hud(retries-1)
133− return
134− else:
135− logger.warning("Failed to get hud button to show. Giving up. Tests may fail...")
136− self.assertThat(hud_show_button.mouseOver, Eventually(Equals(False)))
137− self.touch._finger_move(start_x, start_y - self.grid_size * 34)
138− self.assertThat(hud_show_button.opacity, Eventually(Equals(1.0)))
139− self.assertThat(hud_show_button.mouseOver, Eventually(Equals(True)))
140− self.touch.release()
141− self.assertThat(hud_show_button.opacity, Eventually(Equals(0)))
142− self.assertThat(hud.shown, Eventually(Equals(True)))
143−
144− def close_hud_click(self):
145− hud = self.main_window.get_hud()
146− self.assertThat(hud.shown, Eventually(Equals(True)))
147− rect = hud.globalRect
148− x = int(rect[0] + rect[2] / 2)
149− y = rect[1] + self.grid_size
150− self.touch.tap(x, y)
151− self.assertThat(hud.shown, Eventually(Equals(False)))
152−
153− def show_launcher(self):
154− launcher = self.main_window.get_launcher()
155− view = self.main_window.get_qml_view()
156− start_x = view.x + 1
157− start_y = view.y + view.height / 2
158− stop_x = start_x + launcher.panelWidth + 1
159− stop_y = start_y
160− self.touch.drag(start_x, start_y, stop_x, stop_y)
161− self.assertThat(launcher.shown, Eventually(Equals(True)))
tests/autopilot/unity8/shell/tests/test_hud.pynew file+151
@@ -0,0 +1,151 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+from __future__ import absolute_import
21+
22+from unity8.shell import with_lightdm_mock
23+from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
24+
25+from testtools.matchers import Equals
26+from autopilot.matchers import Eventually
27+
28+
29+class TestHud(UnityTestCase):
30+
31+ """Tests the Shell HUD."""
32+
33+ scenarios = _get_device_emulation_scenarios()
34+
35+ def test_show_hud_button_appears(self):
36+ """Swiping up while an app is active must show the 'show hud' button.
37+
38+ """
39+ self.launch_unity()
40+ self.main_window.get_greeter().unlock()
41+ window = self.main_window.get_qml_view()
42+ hud_show_button = self.main_window.get_hud_show_button()
43+ hud = self.main_window.get_hud()
44+
45+ self._launch_test_app_from_app_screen()
46+
47+ swipe_coords = hud.get_button_swipe_coords(
48+ window,
49+ hud_show_button
50+ )
51+ self.touch.press(swipe_coords.start_x, swipe_coords.start_y)
52+ self.addCleanup(self.touch.release)
53+ self.touch._finger_move(swipe_coords.end_x, swipe_coords.end_y)
54+ self.assertThat(hud_show_button.opacity, Eventually(Equals(1.0)))
55+
56+ def test_show_hud_appears(self):
57+ """Releasing the touch on the 'show hud' button must display the hud.
58+
59+ """
60+ self.launch_unity()
61+ self.main_window.get_greeter().unlock()
62+ window = self.main_window.get_qml_view()
63+ hud_show_button = self.main_window.get_hud_show_button()
64+ hud = self.main_window.get_hud()
65+
66+ self._launch_test_app_from_app_screen()
67+
68+ swipe_coords = hud.get_button_swipe_coords(
69+ window,
70+ hud_show_button
71+ )
72+ self.touch.press(swipe_coords.start_x, swipe_coords.start_y)
73+ self.addCleanup(self._maybe_release_finger)
74+ self.touch._finger_move(swipe_coords.end_x, swipe_coords.end_y)
75+
76+ self.assertThat(hud.shown, Eventually(Equals(False)))
77+ self.assertThat(hud_show_button.opacity, Eventually(Equals(1.0)))
78+ self.touch.release()
79+ self.assertThat(hud.shown, Eventually(Equals(True)))
80+
81+ def test_hide_hud_click(self):
82+ """Tapping the close button of the Hud must dismiss it."""
83+ self.launch_unity()
84+ self.main_window.get_greeter().unlock()
85+ hud = self.main_window.get_hud()
86+
87+ self._launch_test_app_from_app_screen()
88+ hud.show()
89+
90+ x, y = hud.get_close_button_coords()
91+ self.touch.tap(x, y)
92+ self.assertThat(hud.shown, Eventually(Equals(False)))
93+
94+ def test_hide_hud_dragging(self):
95+ """Once open the Hud must close if the upper bar is dragged and
96+ released downward.
97+
98+ """
99+ self.launch_unity()
100+ self.main_window.get_greeter().unlock()
101+ hud = self.main_window.get_hud()
102+ window = self.main_window.get_qml_view()
103+
104+ self._launch_test_app_from_app_screen()
105+ hud.show()
106+
107+ start_x, start_y = hud.get_close_button_coords()
108+ end_x = start_x
109+ end_y = int(window.height / 2)
110+
111+ self.touch.drag(start_x, start_y, end_x, end_y)
112+ self.assertThat(hud.shown, Eventually(Equals(False)))
113+
114+ def test_launcher_hides_hud(self):
115+ """Opening the Launcher while the Hud is active must close the Hud."""
116+ self.launch_unity()
117+ self.main_window.get_greeter().unlock()
118+ hud = self.main_window.get_hud()
119+ launcher = self.main_window.get_launcher()
120+
121+ self._launch_test_app_from_app_screen()
122+
123+ hud.show()
124+ launcher.show()
125+
126+ self.assertThat(hud.shown, Eventually(Equals(False)))
127+
128+ def _launch_test_app_from_app_screen(self):
129+ """Launches the camera app using the Dash UI.
130+
131+ Because when testing on the desktop running
132+ self.launch_application() will launch the application on the desktop
133+ itself and not within the Unity UI.
134+
135+ """
136+ dash = self.main_window.get_dash()
137+ icon = dash.get_application_icon('Camera')
138+ self.touch.tap_object(icon)
139+
140+ # Ensure application is open
141+ bottombar = self.main_window.get_bottombar()
142+ self.assertThat(bottombar.applicationIsOnForeground,
143+ Eventually(Equals(True)))
144+
145+ # Because some tests are manually manipulating the finger, we want to
146+ # cleanup if the test fails, but we don't want to fail with an exception if
147+ # we don't.
148+ def _maybe_release_finger(self):
149+ """Only release the finger if it is in fact down."""
150+ if self.touch._touch_finger is not None:
151+ self.touch.release()
tests/autopilot/unity8/shell/tests/test_lock_screen.pynew file+131
@@ -0,0 +1,131 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+
21+from __future__ import absolute_import
22+
23+from unity8.shell import with_lightdm_mock
24+from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
25+
26+from autopilot.matchers import Eventually
27+from autopilot.platform import model
28+from testtools import skipUnless
29+from testtools.matchers import Equals
30+
31+
32+class TestLockscreen(UnityTestCase):
33+
34+ """Tests for the lock screen."""
35+
36+ scenarios = _get_device_emulation_scenarios()
37+
38+ @with_lightdm_mock("single-pin")
39+ def test_can_unlock_pin_screen(self):
40+ """Must be able to unlock the PIN entry lock screen."""
41+ self.launch_unity()
42+ greeter = self.main_window.get_greeter()
43+ greeter.unlock()
44+
45+ lockscreen = self._wait_for_lockscreen()
46+ self._enter_pincode("1234")
47+
48+ self.assertThat(lockscreen.shown, Eventually(Equals(False)))
49+
50+ @with_lightdm_mock("single-passphrase")
51+ def test_can_unlock_passphrase_screen(self):
52+ """Must be able to unlock the passphrase entry screen."""
53+ self.launch_unity()
54+ greeter = self.main_window.get_greeter()
55+ greeter.unlock()
56+
57+ lockscreen = self._wait_for_lockscreen()
58+ self._enter_passphrase("password")
59+
60+ self.assertThat(lockscreen.shown, Eventually(Equals(False)))
61+
62+ @with_lightdm_mock("single-pin")
63+ def test_pin_screen_wrong_code(self):
64+ """Entering the wrong pin code must not dismiss the lock screen."""
65+ self.launch_unity()
66+ greeter = self.main_window.get_greeter()
67+ greeter.unlock()
68+
69+ lockscreen = self._wait_for_lockscreen()
70+ self._enter_pincode("4321")
71+
72+ pinentryField = self.main_window.get_pinentryField()
73+ self.assertThat(pinentryField.text, Eventually(Equals("")))
74+ self.assertThat(lockscreen.shown, Eventually(Equals(True)))
75+
76+ @with_lightdm_mock("single-passphrase")
77+ def test_passphrase_screen_wrong_password(self):
78+ """Entering the wrong password must not dismiss the lock screen."""
79+ self.launch_unity()
80+ greeter = self.main_window.get_greeter()
81+ greeter.unlock()
82+
83+ lockscreen = self._wait_for_lockscreen()
84+ self._enter_passphrase("foobar")
85+
86+ pinentryField = self.main_window.get_pinentryField()
87+ self.assertThat(pinentryField.text, Eventually(Equals("")))
88+ self.assertThat(lockscreen.shown, Eventually(Equals(True)))
89+
90+ def _wait_for_lockscreen(self):
91+ """Wait for the lock screen to load, and return it."""
92+ pinPadLoader = self.main_window.get_pinPadLoader()
93+ self.assertThat(pinPadLoader.progress, Eventually(Equals(1)))
94+ lockscreen = self.main_window.get_lockscreen()
95+ self.assertThat(lockscreen.shown, Eventually(Equals(True)))
96+ return lockscreen
97+
98+ def _enter_pincode(self, code):
99+ """Enter code 'code' into the single-pin lightdm pincode entry
100+ screen.
101+
102+ :param code: must be a string of numeric characters.
103+ :raises: TypeError if code is not a string.
104+ :raises: ValueError if code contains non-numeric characters.
105+
106+ """
107+
108+ if not isinstance(code, basestring):
109+ raise TypeError("'code' parameter must be a string.")
110+ for num in code:
111+ if not num.isdigit():
112+ raise ValueError(
113+ "'code' parameter contains non-numeric characters."
114+ )
115+ self.touch.tap_object(self.main_window.get_pinPadButton(int(num)))
116+
117+ def _enter_passphrase(self, passphrase):
118+ """Enter the password specified in 'passphrase' into the password entry
119+ field.
120+
121+ :param passphrase: The string you want to enter.
122+ :raises: TypeError if passphrase is not a string.
123+
124+ """
125+ if not isinstance(passphrase, basestring):
126+ raise TypeError("'passphrase' parameter must be a string.")
127+
128+ pinentryField = self.main_window.get_pinentryField()
129+ self.touch.tap_object(pinentryField)
130+ self.keyboard.type(passphrase)
131+ self.keyboard.type("\n")
tests/autopilot/unity8/shell/tests/testbigscreen.pydeleted−25
@@ -1,25 +0,0 @@
1−# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
3−#
4−# This program is free software: you can redistribute it and/or modify it
5−# under the terms of the GNU General Public License version 3, as published
6−# by the Free Software Foundation.
7−
8−"""Add tests here if you want to ensure everything is aligned correctly on huge screens. Because this test does not fit on most screens, you should not use input devices here or your tests are likely to fail"""
9−
10−from testtools.matchers import Equals
11−from autopilot.matchers import Eventually
12−
13−from unity8.shell.tests import ShellTestCase
14−
15−class TestBig(ShellTestCase):
16− def setUp(self):
17− super(TestBig, self).setUp("2560x1600", "20")
18− self.assertThat(self.main_window.get_qml_view().visible, Eventually(Equals(True)))
19−
20− def tearDown(self):
21− super(TestBig, self).tearDown()
22−
23− def test_hud_not_shown_greeter(self):
24− hud_showable = self.main_window.get_hud_showable()
25− self.assertThat(hud_showable.y, Eventually(Equals(1600)))
tests/autopilot/unity8/shell/tests/testhud.pydeleted−145
@@ -1,145 +0,0 @@
1−# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
3−#
4−# This program is free software: you can redistribute it and/or modify it
5−# under the terms of the GNU General Public License version 3, as published
6−# by the Free Software Foundation.
7−
8−
9−# This file contains general purpose test cases for Unity.
10−# Each test written in this file will be executed for a variety of
11−# configurations, such as Phone, Tablet or Desktop form factors.
12−#
13−# Sometimes there is the need to disable a certain test for a particular
14−# configuration. To do so, add this in a new line directly below your test:
15−#
16−# test_testname.blacklist = (FormFactors.Tablet, FormFactors.Desktop,)
17−#
18−# Available form factors are:
19−# FormFactors.Phone
20−# FormFactors.Tablet
21−# FormFactors.Desktop
22−
23−
24−"""Tests for the Shell"""
25−
26−from __future__ import absolute_import
27−
28−from unity8.shell.tests import ShellTestCase, FormFactors
29−from unity8.shell.tests.helpers import TestShellHelpers
30−
31−from autopilot.input import Mouse, Touch, Pointer
32−from testtools.matchers import Equals, NotEquals, GreaterThan, MismatchError
33−from autopilot.matchers import Eventually
34−from autopilot.display import Display
35−from autopilot.platform import model
36−
37−import unittest
38−import time
39−import os
40−from os import path
41−
42−
43−class TestHud(ShellTestCase, TestShellHelpers):
44−
45− """Tests the Shell"""
46−
47− # Scenarios:
48− # Fill in the scenarios to run the whole test suite with multiple configurations.
49− # Use app_width, app_height and grid_unit_px to set the apps geometry.
50− # Set app_width and app_height to 0 to use fullscreen.
51− # Set grid_unit_px to 0 to use the current system environment.
52−
53− if model() == 'Desktop':
54− scenarios = [
55− ('Nexus 4', dict(app_width=768, app_height=1280, grid_unit_px=18, lightdm_mock="single")),
56− ('Nexus 10', dict(app_width=2560, app_height=1600, grid_unit_px=20, lightdm_mock="full")),
57−# TODO: don't run fullscreen tests just yet as the VM is performing too badly for that. Enable this once
58−# Autopilot tests are running on bear metal.
59−# ('Fullscreen', dict(app_width=0, app_height=0, grid_unit_px=10, lightdm_mock="full")),
60− ]
61− else:
62− scenarios = [
63− ('Fullscreen', dict(app_width=0, app_height=0, grid_unit_px=0, lightdm_mock="single")),
64− ]
65−
66− def setUp(self):
67− self.touch = Touch.create()
68−
69− sg = Display().create()
70− divisor = 1
71− while (sg.get_screen_width() < self.app_width / divisor or sg.get_screen_height() < self.app_height / divisor):
72− divisor = divisor * 2
73− super(TestHud, self).setUp("%sx%s" % (self.app_width / divisor, self.app_height / divisor), "%s" % (self.grid_unit_px / divisor))
74−
75− dash = self.main_window.get_dash()
76− self.assertThat(dash.showScopeOnLoaded, Eventually(Equals(""), timeout=30))
77−
78− def test_show_hud(self):
79− hud = self.main_window.get_hud()
80− self.unlock_greeter()
81− self.show_hud()
82− self.assertThat(hud.shown, Eventually(Equals(True)))
83−
84− def test_show_hud_button_dont_open(self):
85− self.unlock_greeter()
86− self.open_first_dash_home_app()
87− hud_show_button = self.main_window.get_hud_show_button()
88− hud = self.main_window.get_hud()
89− window = self.main_window.get_qml_view()
90− start_x = int(window.x + window.width / 2)
91− start_y = window.y + window.height - 2
92− self.assertThat(hud_show_button.opacity, Eventually(Equals(0)))
93− self.touch.press(start_x, start_y)
94− self.touch._finger_move(start_x, start_y - self.grid_size)
95− self.assertThat(hud_show_button.opacity, Eventually(Equals(0)))
96− self.touch._finger_move(start_x, start_y - self.grid_size * 2)
97− self.touch._finger_move(start_x, start_y - self.grid_size * 3)
98− self.touch._finger_move(start_x, start_y - self.grid_size * 4)
99− self.assertThat(hud_show_button.opacity, Eventually(Equals(1.0)))
100− self.assertThat(hud_show_button.mouseOver, Eventually(Equals(False)))
101− self.touch._finger_move(start_x, start_y - self.grid_size * 34)
102− self.assertThat(hud_show_button.opacity, Eventually(Equals(1.0)))
103− self.assertThat(hud_show_button.mouseOver, Eventually(Equals(True)))
104− self.touch._finger_move(start_x, start_y - self.grid_size)
105− self.assertThat(hud_show_button.opacity, Eventually(Equals(1.0)))
106− self.assertThat(hud_show_button.mouseOver, Eventually(Equals(False)))
107− self.touch.release()
108− self.assertThat(hud_show_button.opacity, Eventually(Equals(0)))
109− self.assertThat(hud.shown, Eventually(Equals(False)))
110−
111− def test_hide_hud_click(self):
112− hud = self.main_window.get_hud()
113− self.unlock_greeter()
114− self.show_hud()
115− self.close_hud_click()
116− self.assertThat(hud.shown, Eventually(Equals(False)))
117−
118− def test_hide_hud_click_outside_handle(self):
119− hud = self.main_window.get_hud()
120− self.unlock_greeter()
121− self.show_hud()
122− rect = hud.globalRect
123− x = int(rect[0] + rect[2] / 2)
124− y = rect[1] + hud.handleHeight + 1
125− self.touch.tap(x, y)
126− self.assertRaises(MismatchError, lambda: self.assertThat(hud.shown, Eventually(Equals(False), timeout=3)))
127−
128− def test_hide_hud_dragging(self):
129− hud = self.main_window.get_hud()
130− self.unlock_greeter()
131− self.show_hud()
132− rect = hud.globalRect
133− start_x = rect[0] + (rect[2] - rect[0]) / 2
134− start_y = rect[1] + 1
135− stop_x = start_x
136− stop_y = start_y + (rect[3] - rect[1]) / 2
137− self.touch.drag(start_x, start_y, stop_x, stop_y)
138− self.assertThat(hud.shown, Eventually(Equals(False)))
139−
140− def test_hide_hud_launcher(self):
141− hud = self.main_window.get_hud()
142− self.unlock_greeter()
143− self.show_hud()
144− self.show_launcher()
145− self.assertThat(hud.shown, Eventually(Equals(False)))
tests/autopilot/unity8/shell/tests/testlockscreen.pydeleted−118
@@ -1,118 +0,0 @@
1−# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
3−#
4−# This program is free software: you can redistribute it and/or modify it
5−# under the terms of the GNU General Public License version 3, as published
6−# by the Free Software Foundation.
7−
8−
9−# This file contains general purpose test cases for Unity.
10−# Each test written in this file will be executed for a variety of
11−# configurations, such as Phone, Tablet or Desktop form factors.
12−#
13−# Sometimes there is the need to disable a certain test for a particular
14−# configuration. To do so, add this in a new line directly below your test:
15−#
16−# test_testname.blacklist = (FormFactors.Tablet, FormFactors.Desktop,)
17−#
18−# Available form factors are:
19−# FormFactors.Phone
20−# FormFactors.Tablet
21−# FormFactors.Desktop
22−
23−
24−"""Tests for the Shell"""
25−
26−from __future__ import absolute_import
27−
28−from unity8.shell.tests import ShellTestCase, FormFactors
29−from unity8.shell.tests.helpers import TestShellHelpers
30−
31−from autopilot.input import Mouse, Touch, Pointer
32−from testtools.matchers import Equals, NotEquals, GreaterThan, MismatchError
33−from autopilot.matchers import Eventually
34−from autopilot.display import Display
35−from autopilot.platform import model
36−
37−import unittest
38−import time
39−import os
40−from os import path
41−
42−
43−class TestLockscreens(ShellTestCase, TestShellHelpers):
44−
45− """Tests the Lockscreens"""
46−
47− # Scenarios:
48− # Fill in the scenarios to run the whole test suite with multiple configurations.
49− # Use app_width, app_height and grid_unit_px to set the apps geometry.
50− # Set app_width and app_height to 0 to use fullscreen.
51− # Set grid_unit_px to 0 to use the current system environment.
52−
53− if model() == 'Desktop':
54− scenarios = [
55− ('Pinlock', dict(app_width=768, app_height=1280, grid_unit_px=18, lightdm_mock="single-pin")),
56− ('Keylock', dict(app_width=768, app_height=1280, grid_unit_px=18, lightdm_mock="single-passphrase")),
57− ]
58− else:
59− scenarios = [
60− ('Pinlock', dict(app_width=0, app_height=0, grid_unit_px=0, lightdm_mock="single-pin")),
61− ('Keylock', dict(app_width=0, app_height=0, grid_unit_px=0, lightdm_mock="single-key")),
62− ]
63−
64− def setUp(self):
65− self.touch = Touch.create()
66−
67− sg = Display().create()
68− divisor = 1
69− while (sg.get_screen_width() < self.app_width / divisor or sg.get_screen_height() < self.app_height / divisor):
70− divisor = divisor * 2
71− super(TestLockscreens, self).setUp("%sx%s" % (self.app_width / divisor, self.app_height / divisor), "%s" % (self.grid_unit_px / divisor))
72−
73− dash = self.main_window.get_dash()
74− self.assertThat(dash.showScopeOnLoaded, Eventually(Equals(""), timeout=30))
75−
76− def test_unlock(self):
77− self.unlock_greeter()
78−
79− pinPadLoader = self.main_window.get_pinPadLoader();
80− self.assertThat(pinPadLoader.progress, Eventually(Equals(1)))
81− lockscreen = self.main_window.get_lockscreen();
82− self.assertThat(lockscreen.shown, Eventually(Equals(True)))
83−
84− if self.lightdm_mock == "single-pin":
85− self.touch.tap_object(self.main_window.get_pinPadButton(1))
86− self.touch.tap_object(self.main_window.get_pinPadButton(2))
87− self.touch.tap_object(self.main_window.get_pinPadButton(3))
88− self.touch.tap_object(self.main_window.get_pinPadButton(4))
89− self.assertThat(lockscreen.shown, Eventually(Equals(False)))
90− else:
91− pinentryField = self.main_window.get_pinentryField()
92− self.touch.tap_object(pinentryField)
93− self.keyboard.type("password\n")
94− self.assertThat(lockscreen.shown, Eventually(Equals(False)))
95−
96− def test_unlock_wrong(self):
97− self.unlock_greeter()
98−
99− pinPadLoader = self.main_window.get_pinPadLoader();
100− self.assertThat(pinPadLoader.progress, Eventually(Equals(1)))
101− lockscreen = self.main_window.get_lockscreen();
102− self.assertThat(lockscreen.shown, Eventually(Equals(True)))
103− pinentryField = self.main_window.get_pinentryField()
104−
105− if self.lightdm_mock == "single-pin":
106− self.touch.tap_object(self.main_window.get_pinPadButton(4))
107− self.touch.tap_object(self.main_window.get_pinPadButton(3))
108− self.touch.tap_object(self.main_window.get_pinPadButton(2))
109− self.assertThat(pinentryField.text, Eventually(Equals("432")))
110− self.touch.tap_object(self.main_window.get_pinPadButton(1))
111−
112− self.assertThat(pinentryField.text, Eventually(Equals("")))
113− self.assertThat(lockscreen.shown, Eventually(Equals(True)))
114− else:
115− self.touch.tap_object(pinentryField)
116− self.keyboard.type("foobar\n")
117− self.assertThat(pinentryField.text, Eventually(Equals("")))
118− self.assertThat(lockscreen.shown, Eventually(Equals(True)))
tests/mocks/CMakeLists.txt+1
@@ -1,5 +1,6 @@
11add_subdirectory(libusermetrics)
22add_subdirectory(LightDM)
3+add_subdirectory(QMenuModel)
34add_subdirectory(Ubuntu)
45add_subdirectory(Unity)
56add_subdirectory(HudClient)
tests/mocks/QMenuModel/CMakeLists.txtnew file+11
@@ -0,0 +1,11 @@
1+file(GLOB QMLFILES
2+ *.qml
3+ *.js
4+ qmldir
5+)
6+
7+# copy files into build directory for shadow builds
8+add_custom_target(QMenuModelQmlFiles ALL
9+ COMMAND cp ${QMLFILES} ${CMAKE_CURRENT_BINARY_DIR}
10+ DEPENDS ${QMLFILES}
11+)
tests/mocks/QMenuModel/QDBusActionGroup.qmlnew file+27
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Daniel d'Andrada <daniel.dandrada@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+import QtQuick 2.0
21+
22+QtObject {
23+ property int busType
24+ property string busName
25+ property string objectPath
26+ function start() {}
27+}
tests/mocks/QMenuModel/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module QMenuModel
2+QDBusActionGroup 0.1 QDBusActionGroup.qml
tests/mocks/Ubuntu/Application/ApplicationManager.cpp+8−4
@@ -37,8 +37,6 @@ ApplicationManager::ApplicationManager(QObject *parent)
3737 , m_sideStage(0)
3838{
3939 buildListOfAvailableApplications();
40− createMainStageComponent();
41− createSideStageComponent();
4240}
4341
4442ApplicationManager::~ApplicationManager()
@@ -321,10 +319,10 @@ void ApplicationManager::buildListOfAvailableApplications()
321319 m_availableApplications.append(application);
322320
323321 application = new ApplicationInfo(this);
324− application->setDesktopFile("/usr/share/applications/ubuntu-calendar-app.desktop");
322+ application->setDesktopFile("/usr/share/applications/calendar-app.desktop");
325323 application->setName("Calendar");
326324 application->setIcon("calendar");
327− application->setExec("/usr/bin/qmlscene /usr/share/ubuntu-calendar-app/calendar.qml");
325+ application->setExec("/usr/bin/qmlscene /usr/share/calendar-app/calendar.qml");
328326 application->setStage(ApplicationInfo::SideStage);
329327 application->setHandle(nextHandle++);
330328 m_availableApplications.append(application);
@@ -403,6 +401,9 @@ void ApplicationManager::createMainStageComponent()
403401
404402void ApplicationManager::createMainStage()
405403{
404+ if (!m_mainStageComponent)
405+ createMainStageComponent();
406+
406407 // The assumptions I make here really should hold.
407408 QQuickView *quickView =
408409 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
@@ -437,6 +438,9 @@ void ApplicationManager::createSideStageComponent()
437438
438439void ApplicationManager::createSideStage()
439440{
441+ if (!m_sideStageComponent)
442+ createSideStageComponent();
443+
440444 // The assumptions I make here really should hold.
441445 QQuickView *quickView =
442446 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
tests/mocks/Unity/CMakeLists.txt+1
@@ -44,3 +44,4 @@ file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/qmldir"
4444 )
4545
4646add_subdirectory(Launcher)
47+add_subdirectory(Notifications)
tests/mocks/Unity/Indicators/CMakeLists.txt+2−5
@@ -12,6 +12,7 @@ include_directories(
1212set(IndicatorsFakeQml_SOURCES
1313 fakeplugin.cpp
1414 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/indicators.h
15+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/menucontentactivator.cpp
1516)
1617
1718set(IndicatorsFakeQml_RESOURCES
@@ -26,11 +27,7 @@ add_library(IndicatorsFakeQml SHARED
2627 ${IndicatorsFakeQml_RESOURCES_RCC}
2728 ${IndicatorsFakeQml_SOURCES}
2829)
29−# Because this is an internal support library, we want
30−# to expose all symbols in it. Consider changing this
31−# either to a static library or just using the
32−# files directly in targets.
33−set_target_properties(IndicatorsFakeQml PROPERTIES COMPILE_FLAGS -fvisibility=default)
30+add_definitions(-DUNITYINDICATORS_LIBRARY)
3431
3532target_link_libraries(IndicatorsFakeQml)
3633
tests/mocks/Unity/Indicators/fakeplugin.cpp+4
@@ -24,11 +24,15 @@
2424
2525// local
2626#include "indicators.h"
27+#include "menucontentactivator.h"
2728
2829void IndicatorsFakePlugin::registerTypes(const char * uri)
2930{
3031 Q_INIT_RESOURCE(indicators_fake);
3132
33+ qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
34+
35+ qmlRegisterUncreatableType<MenuContentState>(uri, 0, 1, "MenuContentState", "Can't create MenuContentState class");
3236 qmlRegisterUncreatableType<ActionState>(uri, 0, 1, "ActionState", "Can't create ActionState class");
3337 qmlRegisterUncreatableType<NetworkActionState>(uri, 0, 1, "NetworkActionState", "Can't create NetworkActionState class");
3438 qmlRegisterUncreatableType<NetworkConnection>(uri, 0, 1, "NetworkConnection", "Can't create NetworkConnection class");
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+1−1
@@ -40,7 +40,7 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
4040 m_list.append(item);
4141 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad");
4242 m_list.append(item);
43− item = new MockLauncherItem("ubuntu-calendar-app", "/usr/share/applications/ubuntu-calendar-app.desktop","Calendar", "calendar");
43+ item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar");
4444 m_list.append(item);
4545}
4646
tests/mocks/Unity/Notifications/CMakeLists.txtnew file+11
@@ -0,0 +1,11 @@
1+file(GLOB QMLFILES
2+ *.qml
3+ *.js
4+ qmldir
5+)
6+
7+# copy files into build directory for shadow builds
8+add_custom_target(NotificationsQmlFiles ALL
9+ COMMAND cp ${QMLFILES} ${CMAKE_CURRENT_BINARY_DIR}
10+ DEPENDS ${QMLFILES}
11+)
tests/mocks/Unity/fake_categories.cpp+5−2
@@ -32,10 +32,11 @@ Categories::Categories(QObject* parent)
3232 m_roles[Categories::RoleName] = "name";
3333 m_roles[Categories::RoleIcon] = "icon";
3434 m_roles[Categories::RoleRenderer] = "renderer";
35− m_roles[Categories::RoleContentType] = "content_type";
35+ m_roles[Categories::RoleContentType] = "contentType";
3636 m_roles[Categories::RoleHints] = "hints";
3737 m_roles[Categories::RoleResults] = "results";
3838 m_roles[Categories::RoleCount] = "count";
39+ m_roles[Categories::RoleCategoryIndex] = "categoryIndex";
3940
4041 // TODO This should not be needed but accumulatting the count changes
4142 // makes the visualization more stable and also makes crashes on fast
@@ -154,7 +155,7 @@ Categories::data(const QModelIndex& index, int role) const
154155
155156 switch (role) {
156157 case RoleId:
157− return QVariant::fromValue(index.row());
158+ return DeeListModel::data(index, 0); //ID
158159 case RoleName:
159160 return DeeListModel::data(index, 1); //DISPLAY_NAME
160161 case RoleIcon:
@@ -172,6 +173,8 @@ Categories::data(const QModelIndex& index, int role) const
172173 return QVariant::fromValue(getFilter(index.row()));
173174 case RoleCount:
174175 return QVariant::fromValue(getFilter(index.row())->rowCount());
176+ case RoleCategoryIndex:
177+ return QVariant::fromValue(index.row());
175178 default:
176179 return QVariant();
177180 }
tests/mocks/Unity/fake_categories.h+2−1
@@ -41,7 +41,8 @@ public:
4141 RoleContentType,
4242 RoleHints,
4343 RoleResults,
44− RoleCount
44+ RoleCount,
45+ RoleCategoryIndex
4546 };
4647
4748 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
tests/mocks/Unity/fake_scopes.cpp+2−2
@@ -52,8 +52,8 @@ void Scopes::updateScopes()
5252 clear();
5353 addScope(new Scope("MockScope1", "People", true, this));
5454 addScope(new Scope("MockScope2", "Music", false, this));
55− addScope(new Scope("MockScope3", "Home", true, this));
56− addScope(new Scope("MockScope4", "Applications", true, this));
55+ addScope(new Scope("home.scope", "Home", true, this));
56+ addScope(new Scope("applications.scope", "Applications", true, this));
5757 addScope(new Scope("MockScope5", "Videos", true, this));
5858
5959 if (!m_loaded) {
tests/plugins/ListViewWithPageHeader/listviewwithpageheadertest.cpp+19
@@ -991,6 +991,25 @@ private Q_SLOTS:
991991 QCOMPARE(lvwph->m_headerItemShownHeight, 50.);
992992 }
993993
994+ void testInsertToEmpty()
995+ {
996+ QMetaObject::invokeMethod(model, "removeItems", Q_ARG(QVariant, 0), Q_ARG(QVariant, 6));
997+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 0);
998+ QCOMPARE(lvwph->m_minYExtent, 0.);
999+ QCOMPARE(lvwph->m_clipItem->y(), 0.);
1000+ QCOMPARE(lvwph->m_clipItem->clip(), false);
1001+ QCOMPARE(lvwph->m_headerItem->y(), 0.);
1002+ QCOMPARE(lvwph->m_headerItem->height(), 50.);
1003+ QCOMPARE(lvwph->contentY(), 0.);
1004+
1005+ QMetaObject::invokeMethod(model, "insertItem", Q_ARG(QVariant, 0), Q_ARG(QVariant, 100));
1006+ QMetaObject::invokeMethod(model, "insertItem", Q_ARG(QVariant, 0), Q_ARG(QVariant, 125));
1007+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 2);
1008+ QCOMPARE(lvwph->m_firstVisibleIndex, 0);
1009+ verifyItem(0, 50., 125., false);
1010+ verifyItem(1, 175., 100., false);
1011+ }
1012+
9941013 void testRemoveItemsAtTop()
9951014 {
9961015 QMetaObject::invokeMethod(model, "removeItems", Q_ARG(QVariant, 0), Q_ARG(QVariant, 2));
tests/plugins/ListViewWithPageHeader/listviewwithpageheadertestsection.cpp+19
@@ -1121,6 +1121,25 @@ private Q_SLOTS:
11211121 QCOMPARE(lvwph->m_topSectionItem->y(), -23.);
11221122 }
11231123
1124+ void testInsertToEmpty()
1125+ {
1126+ QMetaObject::invokeMethod(model, "removeItems", Q_ARG(QVariant, 0), Q_ARG(QVariant, 6));
1127+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 0);
1128+ QCOMPARE(lvwph->m_minYExtent, 0.);
1129+ QCOMPARE(lvwph->m_clipItem->y(), 0.);
1130+ QCOMPARE(lvwph->m_clipItem->clip(), false);
1131+ QCOMPARE(lvwph->m_headerItem->y(), 0.);
1132+ QCOMPARE(lvwph->m_headerItem->height(), 50.);
1133+ QCOMPARE(lvwph->contentY(), 0.);
1134+
1135+ QMetaObject::invokeMethod(model, "insertItem", Q_ARG(QVariant, 0), Q_ARG(QVariant, 100), Q_ARG(QVariant, "Agressive"));
1136+ QMetaObject::invokeMethod(model, "insertItem", Q_ARG(QVariant, 1), Q_ARG(QVariant, 125), Q_ARG(QVariant, "Regular"));
1137+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 2);
1138+ QCOMPARE(lvwph->m_firstVisibleIndex, 0);
1139+ verifyItem(0, 50., 140., false, "Agressive", false);
1140+ verifyItem(1, 190., 165., false, "Regular", false);
1141+ }
1142+
11241143 void testRemoveItemsAtTop()
11251144 {
11261145 QMetaObject::invokeMethod(model, "removeItems", Q_ARG(QVariant, 0), Q_ARG(QVariant, 2));
tests/plugins/Unity/Indicators/CMakeLists.txt+1
@@ -24,4 +24,5 @@ endmacro(run_tests)
2424run_tests(
2525 indicatorsmanagertest
2626 indicatorsmodeltest
27+ menucontentactivatortest
2728 )
tests/plugins/Unity/Indicators/menucontentactivatortest.cppnew file+303
@@ -0,0 +1,303 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#include "menucontentactivator.h"
21+#include "paths.h"
22+
23+#include <QtTest>
24+#include <QDebug>
25+
26+class FakeTimer : public UnityIndicators::AbstractTimer
27+{
28+ Q_OBJECT
29+public:
30+ FakeTimer(QObject *parent = 0)
31+ : UnityIndicators::AbstractTimer(parent)
32+ {}
33+
34+ virtual int interval() const { return m_duration; }
35+ virtual void setInterval(int msecs) { m_duration = msecs; }
36+
37+ void emitTimeout() {
38+ if (isRunning()) {
39+ Q_EMIT timeout();
40+ }
41+ }
42+private:
43+ int m_duration;
44+};
45+
46+
47+class MenuConentActivatorTest : public QObject
48+{
49+ Q_OBJECT
50+private Q_SLOTS:
51+ void init() // called right before each and every test function is executed
52+ {
53+ m_fakeTimeSource = new FakeTimer(this);
54+ m_deltas.clear();
55+ }
56+ void cleanup() // called right after each and every test function is executed
57+ {
58+ delete m_fakeTimeSource;
59+ m_fakeTimeSource = 0;
60+ }
61+
62+ /*
63+ * Tests the ordering of activation
64+ */
65+ void testContentActiveChange()
66+ {
67+ MenuContentActivator activator;
68+ activator.setContentTimer(m_fakeTimeSource);
69+ activator.setCount(10);
70+ activator.setBaseIndex(5);
71+ activator.restart();
72+
73+ QCOMPARE(activator.isMenuContentActive(4), false);
74+ QCOMPARE(activator.isMenuContentActive(5), true); // always active at base index.
75+ QCOMPARE(activator.isMenuContentActive(6), false);
76+
77+ m_fakeTimeSource->emitTimeout();
78+ QCOMPARE(activator.isMenuContentActive(4), false);
79+ QCOMPARE(activator.isMenuContentActive(5), true);
80+ QCOMPARE(activator.isMenuContentActive(6), true);
81+ QCOMPARE(activator.isMenuContentActive(7), false);
82+
83+ m_fakeTimeSource->emitTimeout();
84+ QCOMPARE(activator.isMenuContentActive(3), false);
85+ QCOMPARE(activator.isMenuContentActive(4), true);
86+ QCOMPARE(activator.isMenuContentActive(5), true);
87+ QCOMPARE(activator.isMenuContentActive(6), true);
88+ QCOMPARE(activator.isMenuContentActive(7), false);
89+
90+ m_fakeTimeSource->emitTimeout();
91+ QCOMPARE(activator.isMenuContentActive(3), false);
92+ QCOMPARE(activator.isMenuContentActive(4), true);
93+ QCOMPARE(activator.isMenuContentActive(5), true);
94+ QCOMPARE(activator.isMenuContentActive(6), true);
95+ QCOMPARE(activator.isMenuContentActive(7), true);
96+ QCOMPARE(activator.isMenuContentActive(8), false);
97+
98+ m_fakeTimeSource->emitTimeout();
99+ QCOMPARE(activator.isMenuContentActive(2), false);
100+ QCOMPARE(activator.isMenuContentActive(3), true);
101+ QCOMPARE(activator.isMenuContentActive(4), true);
102+ QCOMPARE(activator.isMenuContentActive(5), true);
103+ QCOMPARE(activator.isMenuContentActive(6), true);
104+ QCOMPARE(activator.isMenuContentActive(7), true);
105+ QCOMPARE(activator.isMenuContentActive(8), false);
106+
107+ m_fakeTimeSource->emitTimeout();
108+ QCOMPARE(activator.isMenuContentActive(2), false);
109+ QCOMPARE(activator.isMenuContentActive(3), true);
110+ QCOMPARE(activator.isMenuContentActive(4), true);
111+ QCOMPARE(activator.isMenuContentActive(5), true);
112+ QCOMPARE(activator.isMenuContentActive(6), true);
113+ QCOMPARE(activator.isMenuContentActive(7), true);
114+ QCOMPARE(activator.isMenuContentActive(8), true);
115+ QCOMPARE(activator.isMenuContentActive(9), false);
116+
117+ m_fakeTimeSource->emitTimeout();
118+ QCOMPARE(activator.isMenuContentActive(1), false);
119+ QCOMPARE(activator.isMenuContentActive(2), true);
120+ QCOMPARE(activator.isMenuContentActive(3), true);
121+ QCOMPARE(activator.isMenuContentActive(4), true);
122+ QCOMPARE(activator.isMenuContentActive(5), true);
123+ QCOMPARE(activator.isMenuContentActive(6), true);
124+ QCOMPARE(activator.isMenuContentActive(7), true);
125+ QCOMPARE(activator.isMenuContentActive(8), true);
126+ QCOMPARE(activator.isMenuContentActive(9), false);
127+
128+ m_fakeTimeSource->emitTimeout();
129+ QCOMPARE(activator.isMenuContentActive(1), false);
130+ QCOMPARE(activator.isMenuContentActive(2), true);
131+ QCOMPARE(activator.isMenuContentActive(3), true);
132+ QCOMPARE(activator.isMenuContentActive(4), true);
133+ QCOMPARE(activator.isMenuContentActive(5), true);
134+ QCOMPARE(activator.isMenuContentActive(6), true);
135+ QCOMPARE(activator.isMenuContentActive(7), true);
136+ QCOMPARE(activator.isMenuContentActive(8), true);
137+ QCOMPARE(activator.isMenuContentActive(9), true);
138+ QCOMPARE(activator.isMenuContentActive(10), false);
139+
140+ m_fakeTimeSource->emitTimeout();
141+ QCOMPARE(activator.isMenuContentActive(0), false);
142+ QCOMPARE(activator.isMenuContentActive(1), true);
143+ QCOMPARE(activator.isMenuContentActive(2), true);
144+ QCOMPARE(activator.isMenuContentActive(3), true);
145+ QCOMPARE(activator.isMenuContentActive(4), true);
146+ QCOMPARE(activator.isMenuContentActive(5), true);
147+ QCOMPARE(activator.isMenuContentActive(6), true);
148+ QCOMPARE(activator.isMenuContentActive(7), true);
149+ QCOMPARE(activator.isMenuContentActive(8), true);
150+ QCOMPARE(activator.isMenuContentActive(9), true);
151+ QCOMPARE(activator.isMenuContentActive(10), false);
152+
153+ m_fakeTimeSource->emitTimeout();
154+ QCOMPARE(activator.isMenuContentActive(-1), false);
155+ QCOMPARE(activator.isMenuContentActive(0), true);
156+ QCOMPARE(activator.isMenuContentActive(1), true);
157+ QCOMPARE(activator.isMenuContentActive(2), true);
158+ QCOMPARE(activator.isMenuContentActive(3), true);
159+ QCOMPARE(activator.isMenuContentActive(4), true);
160+ QCOMPARE(activator.isMenuContentActive(5), true);
161+ QCOMPARE(activator.isMenuContentActive(6), true);
162+ QCOMPARE(activator.isMenuContentActive(7), true);
163+ QCOMPARE(activator.isMenuContentActive(8), true);
164+ QCOMPARE(activator.isMenuContentActive(9), true);
165+ QCOMPARE(activator.isMenuContentActive(10), false);
166+ }
167+
168+ /*
169+ * Tests the delta calculation for each timeout.
170+ */
171+ void testDelta()
172+ {
173+ MenuContentActivator activator;
174+ activator.setContentTimer(m_fakeTimeSource);
175+ activator.setCount(9);
176+ activator.setBaseIndex(3);
177+
178+ connect(&activator, &MenuContentActivator::deltaChanged,
179+ this, &MenuConentActivatorTest::onDeltaChange);
180+ activator.restart();
181+
182+ QCOMPARE(m_deltas, QList<int>()); // empty
183+ QCOMPARE(getIndexList(&activator), QList<int>()); // empty
184+
185+ m_fakeTimeSource->emitTimeout();
186+ QCOMPARE(m_deltas, QList<int>() << 1);
187+ QCOMPARE(getIndexList(&activator), QList<int>() << 4);
188+
189+ m_fakeTimeSource->emitTimeout();
190+ QCOMPARE(m_deltas, QList<int>() << 1 << -1);
191+ QCOMPARE(getIndexList(&activator), QList<int>() << 4 << 2);
192+
193+ m_fakeTimeSource->emitTimeout();
194+ QCOMPARE(m_deltas, QList<int>() << 1 << -1 << 2);
195+ QCOMPARE(getIndexList(&activator), QList<int>() << 4 << 2 << 5);
196+
197+ m_fakeTimeSource->emitTimeout();
198+ QCOMPARE(m_deltas, QList<int>() << 1 << -1 << 2 << -2);
199+ QCOMPARE(getIndexList(&activator), QList<int>() << 4 << 2 << 5 << 1);
200+
201+ m_fakeTimeSource->emitTimeout();
202+ QCOMPARE(m_deltas, QList<int>() << 1 << -1 << 2 << -2 << 3);
203+ QCOMPARE(getIndexList(&activator), QList<int>() << 4 << 2 << 5 << 1 << 6);
204+
205+ m_fakeTimeSource->emitTimeout();
206+ QCOMPARE(m_deltas, QList<int>() << 1 << -1 << 2 << -2 << 3 << -3);
207+ QCOMPARE(getIndexList(&activator), QList<int>() << 4 << 2 << 5 << 1 << 6 << 0);
208+
209+ m_fakeTimeSource->emitTimeout();
210+ QCOMPARE(m_deltas, QList<int>() << 1 << -1 << 2 << -2 << 3 << -3 << 4);
211+ QCOMPARE(getIndexList(&activator), QList<int>() << 4 << 2 << 5 << 1 << 6 << 0 << 7);
212+
213+ m_fakeTimeSource->emitTimeout();
214+ QCOMPARE(m_deltas, QList<int>() << 1 << -1 << 2 << -2 << 3 << -3 << 4 << 5);
215+ QCOMPARE(getIndexList(&activator), QList<int>() << 4 << 2 << 5 << 1 << 6 << 0 << 7 << 8);
216+
217+ m_fakeTimeSource->emitTimeout();
218+ QCOMPARE(m_deltas, QList<int>() << 1 << -1 << 2 << -2 << 3 << -3 << 4 << 5);
219+ QCOMPARE(getIndexList(&activator), QList<int>() << 4 << 2 << 5 << 1 << 6 << 0 << 7 << 8);
220+ }
221+
222+ /*
223+ * Tests that changing the base index re-prioritizes the activation
224+ * around the base index.
225+ */
226+ void testBaseIndexChange()
227+ {
228+ MenuContentActivator activator;
229+ activator.setContentTimer(m_fakeTimeSource);
230+ activator.setCount(12);
231+ activator.setBaseIndex(5);
232+ activator.restart();
233+
234+ m_fakeTimeSource->emitTimeout();
235+ m_fakeTimeSource->emitTimeout();
236+ QCOMPARE(activator.isMenuContentActive(3), false);
237+ QCOMPARE(activator.isMenuContentActive(4), true);
238+ QCOMPARE(activator.isMenuContentActive(5), true); // always active at base index.
239+ QCOMPARE(activator.isMenuContentActive(6), true);
240+ QCOMPARE(activator.isMenuContentActive(7), false);
241+
242+ activator.setBaseIndex(8);
243+ QCOMPARE(activator.isMenuContentActive(3), false);
244+ QCOMPARE(activator.isMenuContentActive(4), true);
245+ QCOMPARE(activator.isMenuContentActive(5), true);
246+ QCOMPARE(activator.isMenuContentActive(6), true);
247+ QCOMPARE(activator.isMenuContentActive(7), false);
248+ QCOMPARE(activator.isMenuContentActive(8), true);
249+ QCOMPARE(activator.isMenuContentActive(9), false);
250+
251+ m_fakeTimeSource->emitTimeout();
252+ QCOMPARE(activator.isMenuContentActive(3), false);
253+ QCOMPARE(activator.isMenuContentActive(4), true);
254+ QCOMPARE(activator.isMenuContentActive(5), true);
255+ QCOMPARE(activator.isMenuContentActive(6), true);
256+ QCOMPARE(activator.isMenuContentActive(7), false);
257+ QCOMPARE(activator.isMenuContentActive(8), true);
258+ QCOMPARE(activator.isMenuContentActive(9), true);
259+ QCOMPARE(activator.isMenuContentActive(10), false);
260+
261+ m_fakeTimeSource->emitTimeout();
262+ QCOMPARE(activator.isMenuContentActive(3), false);
263+ QCOMPARE(activator.isMenuContentActive(4), true);
264+ QCOMPARE(activator.isMenuContentActive(5), true);
265+ QCOMPARE(activator.isMenuContentActive(6), true);
266+ QCOMPARE(activator.isMenuContentActive(7), true);
267+ QCOMPARE(activator.isMenuContentActive(8), true);
268+ QCOMPARE(activator.isMenuContentActive(9), true);
269+ QCOMPARE(activator.isMenuContentActive(10), false);
270+
271+ m_fakeTimeSource->emitTimeout();
272+ QCOMPARE(activator.isMenuContentActive(3), false);
273+ QCOMPARE(activator.isMenuContentActive(4), true);
274+ QCOMPARE(activator.isMenuContentActive(5), true);
275+ QCOMPARE(activator.isMenuContentActive(6), true);
276+ QCOMPARE(activator.isMenuContentActive(7), true);
277+ QCOMPARE(activator.isMenuContentActive(8), true);
278+ QCOMPARE(activator.isMenuContentActive(9), true);
279+ QCOMPARE(activator.isMenuContentActive(10), true);
280+ QCOMPARE(activator.isMenuContentActive(11), false);
281+ }
282+
283+ void onDeltaChange(int delta)
284+ {
285+ m_deltas << delta;
286+ }
287+
288+private:
289+ QList<int> getIndexList(MenuContentActivator* activator)
290+ {
291+ QList<int> list;
292+ Q_FOREACH(int delta, m_deltas) {
293+ list << (activator->baseIndex() + delta);
294+ }
295+ return list;
296+ }
297+
298+ FakeTimer* m_fakeTimeSource;
299+ QList<int> m_deltas;
300+};
301+
302+QTEST_MAIN(MenuConentActivatorTest)
303+#include "menucontentactivatortest.moc"
tests/plugins/Unity/previewbindingstest.cpp−2
@@ -165,5 +165,3 @@ void PreviewBindingsTest::testSocialPreview()
165165}
166166
167167QTEST_MAIN(PreviewBindingsTest)
168−
169−#include "previewbindingstest.moc"
tests/qmltests/CMakeLists.txt+3−2
@@ -25,6 +25,9 @@ add_qml_test(utils/Unity/Test UnityTest)
2525set(qmltest_DEFAULT_TARGETS qmluitests)
2626set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
2727set(qmltest_DEFAULT_PROPERTIES "")
28+
29+add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
30+ ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
2831add_manual_qml_test(Components DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
2932add_qml_test(Components DraggingArea)
3033add_qml_test(Components FilterGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
@@ -61,10 +64,8 @@ add_qml_test(Notifications Notifications IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_P
6164add_qml_test(Panel IndicatorRow)
6265add_qml_test(Panel Indicators IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
6366add_qml_test(Panel MenuContent)
64−add_qml_test(Panel Overview)
6567add_qml_test(Panel Panel IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
6668add_qml_test(Panel SearchIndicator)
67−add_qml_test(Panel/Menus IndicatorMenuWindow IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS})
6869add_qml_test(SideStage SideStage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}
6970 ${CMAKE_BINARY_DIR}/tests/mocks
7071 ${CMAKE_BINARY_DIR}/plugins)
tests/qmltests/Components/tst_Stage.qml+1
@@ -161,6 +161,7 @@ Rectangle {
161161 applicationManager: fakeAppManager
162162 rightEdgeDraggingAreaWidth: units.gu(2)
163163 fullyShown: true
164+ fullyHidden: false
164165 newApplicationScreenshot: FakeApplicationScreenshot {
165166 id: newAppScreenshot
166167 parent: stage
tests/qmltests/Dash/tst_Dash.qml+4−4
@@ -38,8 +38,8 @@ Item {
3838 scopeDelegateMapping: {
3939 "MockScope1": "../tests/qmltests/Dash/qml/fake_scopeView1.qml",
4040 "MockScope2": "../tests/qmltests/Dash/qml/fake_scopeView2.qml",
41− "MockScope3": "../tests/qmltests/Dash/qml/fake_scopeView3.qml",
42− "MockScope4": "../tests/qmltests/Dash/qml/fake_scopeView4.qml"
41+ "home.scope": "../tests/qmltests/Dash/qml/fake_scopeView3.qml",
42+ "applications.scope": "../tests/qmltests/Dash/qml/fake_scopeView4.qml"
4343 }
4444 genericScope: "../tests/qmltests/Dash/qml/fake_generic_scopeView.qml"
4545 }
@@ -69,8 +69,8 @@ Item {
6969 return [
7070 { tag: "MockScope1", visualIndex: 0, shouldBeVisible: true },
7171 { tag: "MockScope2", visualIndex: -1, shouldBeVisible: false },
72− { tag: "MockScope3", visualIndex: 1, shouldBeVisible: true },
73− { tag: "MockScope4", visualIndex: 2, shouldBeVisible: true },
72+ { tag: "home.scope", visualIndex: 1, shouldBeVisible: true },
73+ { tag: "applications.scope", visualIndex: 2, shouldBeVisible: true },
7474 { tag: "MockScope5", visualIndex: 3, shouldBeVisible: true },
7575 ]
7676 }
tests/qmltests/Dash/tst_DashContent.qml+12−12
@@ -31,8 +31,8 @@ Item {
3131 property var scopeStatus: {
3232 'MockScope1': { 'movementStarted': 0, 'positionedAtBeginning': 0 },
3333 'MockScope2': { 'movementStarted': 0, 'positionedAtBeginning': 0 },
34− 'MockScope3': { 'movementStarted': 0, 'positionedAtBeginning': 0 },
35− 'MockScope4': { 'movementStarted': 0, 'positionedAtBeginning': 0 },
34+ 'home.scope': { 'movementStarted': 0, 'positionedAtBeginning': 0 },
35+ 'applications.scope': { 'movementStarted': 0, 'positionedAtBeginning': 0 },
3636 'MockScope5': { 'movementStarted': 0, 'positionedAtBeginning': 0 }
3737 }
3838
@@ -55,8 +55,8 @@ Item {
5555 scopeDelegateMapping: {
5656 "MockScope1": "../tests/qmltests/Dash/qml/fake_scopeView1.qml",
5757 "MockScope2": "../tests/qmltests/Dash/qml/fake_scopeView2.qml",
58− "MockScope3": "../tests/qmltests/Dash/qml/fake_scopeView3.qml",
59− "MockScope4": "../tests/qmltests/Dash/qml/fake_scopeView4.qml"
58+ "home.scope": "../tests/qmltests/Dash/qml/fake_scopeView3.qml",
59+ "applications.scope": "../tests/qmltests/Dash/qml/fake_scopeView4.qml"
6060 }
6161 genericScope: "../tests/qmltests/Dash/qml/fake_generic_scopeView.qml"
6262 }
@@ -68,11 +68,11 @@ Item {
6868 scopeStatus["MockScope2"].movementStarted = 0;
6969 scopeStatus["MockScope2"].positionedAtBeginning = 0;
7070
71− scopeStatus["MockScope3"].movementStarted = 0;
72− scopeStatus["MockScope3"].positionedAtBeginning = 0;
71+ scopeStatus["home.scope"].movementStarted = 0;
72+ scopeStatus["home.scope"].positionedAtBeginning = 0;
7373
74− scopeStatus["MockScope4"].movementStarted = 0;
75− scopeStatus["MockScope4"].positionedAtBeginning = 0;
74+ scopeStatus["applications.scope"].movementStarted = 0;
75+ scopeStatus["applications.scope"].positionedAtBeginning = 0;
7676
7777 scopeStatus["MockScope5"].movementStarted = 0;
7878 scopeStatus["MockScope5"].positionedAtBeginning = 0;
@@ -127,8 +127,8 @@ Item {
127127 compare(movementStartedSpy.count, 1, "DashContent should have emitted movementStarted signal when content list did.");
128128 compare(scopeStatus["MockScope1"].movementStarted, 1, "MockScope1 should have emitted movementStarted signal when content list did.");
129129 compare(scopeStatus["MockScope2"].movementStarted, 1, "MockScope2 should have emitted movementStarted signal when content list did.");
130− compare(scopeStatus["MockScope3"].movementStarted, 1, "MockScope3 should have emitted movementStarted signal when content list did.");
131− compare(scopeStatus["MockScope4"].movementStarted, 1, "MockScope4 should have emitted movementStarted signal when content list did.");
130+ compare(scopeStatus["home.scope"].movementStarted, 1, "home.scope should have emitted movementStarted signal when content list did.");
131+ compare(scopeStatus["applications.scope"].movementStarted, 1, "applications.scope should have emitted movementStarted signal when content list did.");
132132 compare(scopeStatus["MockScope5"].movementStarted, 1, "MockScope5 should have emitted movementStarted signal when content list did.");
133133 }
134134
@@ -140,8 +140,8 @@ Item {
140140 dashContent.positionedAtBeginning();
141141 compare(scopeStatus["MockScope1"].positionedAtBeginning, 1, "MockScope1 should have emitted positionedAtBeginning signal when DashContent did.");
142142 compare(scopeStatus["MockScope2"].positionedAtBeginning, 1, "MockScope2 should have emitted positionedAtBeginning signal when DashContent did.");
143− compare(scopeStatus["MockScope3"].positionedAtBeginning, 1, "MockScope3 should have emitted positionedAtBeginning signal when DashContent did.");
144− compare(scopeStatus["MockScope4"].positionedAtBeginning, 1, "MockScope4 should have emitted positionedAtBeginning signal when DashContent did.");
143+ compare(scopeStatus["home.scope"].positionedAtBeginning, 1, "home.scope should have emitted positionedAtBeginning signal when DashContent did.");
144+ compare(scopeStatus["applications.scope"].positionedAtBeginning, 1, "applications.scope should have emitted positionedAtBeginning signal when DashContent did.");
145145 compare(scopeStatus["MockScope5"].positionedAtBeginning, 1, "MockScope5 should have emitted positionedAtBeginning signal when DashContent did.");
146146 }
147147
tests/qmltests/Notifications/tst_Notifications.qml+20−9
@@ -48,6 +48,7 @@ Row {
4848 function addSnapDecisionNotification() {
4949 var n = {
5050 type: Notification.SnapDecision,
51+ hints: Notification.ButtonTint,
5152 summary: "Tom Ato",
5253 body: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.",
5354 icon: "../graphics/avatars/funky.png",
@@ -189,8 +190,9 @@ Row {
189190 function test_NotificationRenderer_data() {
190191 return [
191192 {
192− tag: "Snap Decision with secondary icon",
193+ tag: "Snap Decision with secondary icon and button-tint",
193194 type: Notification.SnapDecision,
195+ hints: Notification.ButtonTint,
194196 summary: "Tom Ato",
195197 body: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.",
196198 icon: "../graphics/avatars/funky.png",
@@ -205,7 +207,8 @@ Row {
205207 interactiveAreaEnabled: false,
206208 iconVisible: true,
207209 secondaryIconVisible: true,
208− buttonRowVisible: true
210+ buttonRowVisible: true,
211+ buttonTinted: true
209212 },
210213 {
211214 tag: "Ephemeral notification - icon-summary layout",
@@ -220,7 +223,8 @@ Row {
220223 interactiveAreaEnabled: false,
221224 iconVisible: false,
222225 secondaryIconVisible: true,
223− buttonRowVisible: false
226+ buttonRowVisible: false,
227+ buttonTinted: false
224228 },
225229 {
226230 tag: "Ephemeral notification - check suppression of secondary icon for icon-summary layout",
@@ -235,7 +239,8 @@ Row {
235239 interactiveAreaEnabled: false,
236240 iconVisible: false,
237241 secondaryIconVisible: true,
238− buttonRowVisible: false
242+ buttonRowVisible: false,
243+ buttonTinted: false
239244 },
240245 {
241246 tag: "Interactive notification",
@@ -250,10 +255,11 @@ Row {
250255 interactiveAreaEnabled: true,
251256 iconVisible: true,
252257 secondaryIconVisible: false,
253− buttonRowVisible: false
258+ buttonRowVisible: false,
259+ buttonTinted: false
254260 },
255261 {
256− tag: "Snap Decision without secondary icon",
262+ tag: "Snap Decision without secondary icon and no button-tint",
257263 type: Notification.SnapDecision,
258264 summary: "Bro Coly",
259265 body: "At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
@@ -266,7 +272,8 @@ Row {
266272 interactiveAreaEnabled: false,
267273 iconVisible: true,
268274 secondaryIconVisible: false,
269− buttonRowVisible: true
275+ buttonRowVisible: true,
276+ buttonTinted: false
270277 },
271278 {
272279 tag: "Ephemeral notification",
@@ -281,7 +288,8 @@ Row {
281288 interactiveAreaEnabled: false,
282289 iconVisible: true,
283290 secondaryIconVisible: true,
284− buttonRowVisible: false
291+ buttonRowVisible: false,
292+ buttonTinted: false
285293 }
286294 ]
287295 }
@@ -356,6 +364,9 @@ Row {
356364 actionSpy.clear()
357365 }
358366
367+ // check the tinting of the positive/right button
368+ verify(buttonAccept.gradient === data.buttonTinted ? UbuntuColors.orangeGradient : UbuntuColors.greyGradient, "button has the wrong color-tint")
369+
359370 // click the positive/right button
360371 mouseClick(buttonAccept, buttonAccept.width / 2, buttonAccept.height / 2)
361372 actionSpy.wait()
@@ -370,7 +381,7 @@ Row {
370381 mouseClick(buttonCancel, buttonCancel.width / 2, buttonCancel.height / 2)
371382 waitForRendering(notification)
372383 actionSpy.clear()
373− 
384+
374385 // test the additional buttons
375386 for (var i = 2; i < data.actions.length; i++) {
376387 waitForRendering(notification)
tests/qmltests/Panel/Menus/tst_IndicatorMenuWindow.qmldeleted−68
@@ -1,68 +0,0 @@
1−/*
2− * Copyright 2013 Canonical Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import QtTest 1.0
19−import "../.."
20−import "../../../../Panel/Menus"
21−import Ubuntu.Components 0.1
22−import Unity.Test 0.1 as UT
23−
24−Item {
25− id: testIndicatorMenuWindowItem
26− width: units.gu(9)
27− height: units.gu(3)
28−
29− MouseArea {
30− id: mouseArea
31− anchors.fill: parent
32− }
33−
34− IndicatorMenuWindow {
35− id: indicatorMenuWindow
36− name: "TestIndicatorMenuWindow"
37− anchors.fill: parent
38− }
39−
40− UT.UnityTestCase {
41− name: "IndicatorMenuWindow"
42− when: windowShown
43−
44− function test_mouseEvent_data() {
45− return [
46− {tag: "visible", shown: true, opacity: 1.0, mouseClicks: 0},
47− {tag: "invisible", shown: false, opacity: 0.0, mouseClicks: 1},
48− ]
49− }
50−
51− function test_mouseEvent(data) {
52− indicatorMenuWindow.shown = data.shown
53− tryCompare(indicatorMenuWindow, "opacity", data.opacity)
54−
55− clickedSpy.clear()
56− mouseClick(indicatorMenuWindow, indicatorMenuWindow.width / 2,
57− indicatorMenuWindow.height / 2)
58− compare(clickedSpy.count, data.mouseClicks,
59− "Check for Mouse event eating by indicatorMenuWindow failed")
60− }
61− }
62−
63− SignalSpy {
64− id: clickedSpy
65− target: mouseArea
66− signalName: "clicked"
67− }
68−}
tests/qmltests/Panel/tst_IndicatorItem.qml+10−16
@@ -34,34 +34,28 @@ Rectangle {
3434 UT.UnityTestCase {
3535 name: "IndicatorItem"
3636
37− function init_test() {
38− }
39−
4037 function test_dimmed() {
41− init_test()
42−
43− indicatorItem.dimmed = true
44− compare(indicatorItem.opacity > 0, true, "IndicatorItem opacity should not be 0")
45− compare(indicatorItem.opacity < 1, true, "IndicatorItem opacity should not be 1")
38+ indicatorItem.dimmed = false;
39+ tryCompareFunction(function(){return indicatorItem.opacity}, 1.0);
40+ indicatorItem.dimmed = true;
41+ tryCompareFunction(function(){return indicatorItem.opacity < 1.0}, true);
4642 }
4743
4844 function test_highlight() {
49− var itemHighlight = findChild(indicatorItem, "highlight")
50− verify(itemHighlight != undefined)
45+ var itemHighlight = findChild(indicatorItem, "highlight");
46+ verify(itemHighlight != undefined);
5147
5248 indicatorItem.highlighted = true;
53− compare(itemHighlight.visible, true, "Indicator should be highlighted")
49+ compare(itemHighlight.visible, true, "Indicator should be highlighted");
5450
5551 indicatorItem.highlighted = false;
56− compare(itemHighlight.visible, false, "Indicator should not be highlighted")
52+ compare(itemHighlight.visible, false, "Indicator should not be highlighted");
5753 }
5854
5955 function test_empty() {
60− init_test()
61−
62− compare(indicatorItem.visible, false, "IndicatorItem should not be visible.")
56+ compare(indicatorItem.visible, false, "IndicatorItem should not be visible.");
6357 indicatorItem.widgetSource = "qrc:/tests/indciators/qml/fake_menu_widget1.qml";
64− tryCompare(indicatorItem, "visible", true)
58+ tryCompare(indicatorItem, "visible", true);
6559 }
6660 }
6761}
tests/qmltests/Panel/tst_IndicatorRow.qml+21−35
@@ -32,7 +32,6 @@ Item {
3232
3333 function init_test()
3434 {
35− indicatorRow.overviewActive = false;
3635 indicatorRow.state = "initial";
3736 indicatorRow.currentItem = null;
3837 }
@@ -62,13 +61,13 @@ Item {
6261
6362 function test_set_current_item() {
6463 init_test();
65− indicatorRow.setItem(0);
64+ indicatorRow.setCurrentItem(0);
6665 compare(indicatorRow.indicatorsModel.get(indicatorRow.currentItemIndex).title, "Menu1", "Incorrect item at position 0");
6766
68− indicatorRow.setItem(1);
67+ indicatorRow.setCurrentItem(1);
6968 compare(indicatorRow.indicatorsModel.get(indicatorRow.currentItemIndex).title, "Menu2", "Incorrect item at position 1");
7069
71− indicatorRow.setItem(2);
70+ indicatorRow.setCurrentItem(2);
7271 compare(indicatorRow.indicatorsModel.get(indicatorRow.currentItemIndex).title, "Menu3", "Incorrect item at position 2");
7372 }
7473 }
@@ -84,13 +83,13 @@ Item {
8483 function test_current_item_commit() {
8584 init_test();
8685
87− indicatorRow.setItem(1);
86+ indicatorRow.setCurrentItem(1);
8887 indicatorRow.state = "commit";
89− tryCompare(get_indicator_item_at(0), "y", -indicatorRow.height);
90− tryCompare(get_indicator_item_at(1), "y", 0);
91− tryCompare(get_indicator_item_at(2), "y", -indicatorRow.height);
92− tryCompare(get_indicator_item_at(3), "y", -indicatorRow.height);
93− tryCompare(get_indicator_item_at(4), "y", -indicatorRow.height);
88+ tryCompare(get_indicator_item_at(0), "opacity", 0.0);
89+ tryCompare(get_indicator_item_at(1), "opacity", 1.0);
90+ tryCompare(get_indicator_item_at(2), "opacity", 0.0);
91+ tryCompare(get_indicator_item_at(3), "opacity", 0.0);
92+ tryCompare(get_indicator_item_at(4), "opacity", 0.0);
9493
9594 }
9695 }
@@ -107,7 +106,7 @@ Item {
107106 init_test();
108107
109108 indicatorRow.state = "initial";
110− indicatorRow.setItem(0);
109+ indicatorRow.setCurrentItem(0);
111110
112111 compare(indicatorRow.currentItem.highlighted, false, "Indicator should not highlight when in initial state");
113112 compare(get_indicator_item_at(1).highlighted, false, "Other indicators should not highlight when in initial state");
@@ -120,9 +119,9 @@ Item {
120119 init_test();
121120
122121 indicatorRow.state = "hint";
123− indicatorRow.setItem(0);
122+ indicatorRow.setCurrentItem(0);
124123
125− compare(indicatorRow.currentItem.highlighted, false, "Indicator should not highlight when in hint state");
124+ compare(indicatorRow.currentItem.highlighted, true, "Indicator should highlight when in hint state");
126125 compare(get_indicator_item_at(1).highlighted, false, "Other indicators should not highlight when in hint state");
127126 compare(get_indicator_item_at(2).highlighted, false, "Other indicators should not highlight when in hint state");
128127 compare(get_indicator_item_at(3).highlighted, false, "Other indicators should not highlight when in hint state");
@@ -133,7 +132,7 @@ Item {
133132 init_test();
134133
135134 indicatorRow.state = "reveal";
136− indicatorRow.setItem(0);
135+ indicatorRow.setCurrentItem(0);
137136
138137 compare(indicatorRow.currentItem.highlighted, true, "Indicator should highlight when in reveal state");
139138 compare(get_indicator_item_at(1).highlighted, false, "Other indicators should not highlight when in commit state");
@@ -146,7 +145,7 @@ Item {
146145 init_test();
147146
148147 indicatorRow.state = "commit";
149− indicatorRow.setItem(0);
148+ indicatorRow.setCurrentItem(0);
150149
151150 compare(indicatorRow.currentItem.highlighted, true, "Indicator should highlight when in commit state");
152151 compare(get_indicator_item_at(1).highlighted, false, "Other indicators should not highlight when in commit state");
@@ -159,7 +158,7 @@ Item {
159158 init_test();
160159
161160 indicatorRow.state = "locked";
162− indicatorRow.setItem(0);
161+ indicatorRow.setCurrentItem(0);
163162
164163 compare(indicatorRow.currentItem.highlighted, true, "Indicator should highlight when in locked state");
165164 compare(get_indicator_item_at(1).highlighted, false, "Other indicators should not highlight when in locked state");
@@ -181,7 +180,7 @@ Item {
181180 init_test();
182181
183182 indicatorRow.state = "initial";
184− indicatorRow.setItem(0);
183+ indicatorRow.setCurrentItem(0);
185184
186185 compare(get_indicator_item_at(0).dimmed, false, "Current indicator should not dim when in intiial state");
187186 compare(get_indicator_item_at(1).dimmed, false, "Other indicators should not dim when in initial state");
@@ -194,9 +193,9 @@ Item {
194193 init_test();
195194
196195 indicatorRow.state = "hint";
197− indicatorRow.setItem(0);
196+ indicatorRow.setCurrentItem(0);
198197
199− compare(get_indicator_item_at(0).dimmed, true, "Current indicator should dim when in hint state");
198+ compare(get_indicator_item_at(0).dimmed, false, "Current indicator should not dim when in hint state");
200199 compare(get_indicator_item_at(1).dimmed, true, "Other indicators should dim when in hint state");
201200 compare(get_indicator_item_at(2).dimmed, true, "Other indicators should dim when in hint state");
202201 compare(get_indicator_item_at(3).dimmed, true, "Other indicators should dim when in hint state");
@@ -207,7 +206,7 @@ Item {
207206 init_test();
208207
209208 indicatorRow.state = "reveal";
210− indicatorRow.setItem(0);
209+ indicatorRow.setCurrentItem(0);
211210
212211 compare(get_indicator_item_at(0).dimmed, false, "Current indicator should not dim when in reveal state");
213212 compare(get_indicator_item_at(1).dimmed, true, "Other indicators should dim when in reveal state");
@@ -220,7 +219,7 @@ Item {
220219 init_test();
221220
222221 indicatorRow.state = "commit";
223− indicatorRow.setItem(0);
222+ indicatorRow.setCurrentItem(0);
224223
225224 compare(get_indicator_item_at(0).dimmed, false, "Current indicator should not dim when in commit state");
226225 compare(get_indicator_item_at(1).dimmed, true, "Other indicators should dim when in commit state");
@@ -233,7 +232,7 @@ Item {
233232 init_test();
234233
235234 indicatorRow.state = "locked";
236− indicatorRow.setItem(0);
235+ indicatorRow.setCurrentItem(0);
237236
238237 compare(get_indicator_item_at(0).dimmed, false, "Current indicator should not dim when in locked state");
239238 compare(get_indicator_item_at(1).dimmed, true, "Other indicators should dim when in locked state");
@@ -241,18 +240,5 @@ Item {
241240 compare(get_indicator_item_at(3).dimmed, true, "Other indicators should dim when in locked state");
242241 compare(get_indicator_item_at(4).dimmed, true, "Other indicators should dim when in locked state");
243242 }
244−
245− function test_active_overview() {
246− init_test();
247−
248− indicatorRow.overviewActive = true;
249− indicatorRow.state = "commit";
250−
251− compare(get_indicator_item_at(0).dimmed, true, "Indicator should dim when in overview");
252− compare(get_indicator_item_at(1).dimmed, true, "Indicator should dim when in overview");
253− compare(get_indicator_item_at(2).dimmed, true, "Indicator should dim when in overview");
254− compare(get_indicator_item_at(3).dimmed, true, "Indicator should dim when in overview");
255− compare(get_indicator_item_at(4).dimmed, true, "Indicator should dim when in overview");
256− }
257243 }
258244}
tests/qmltests/Panel/tst_Indicators.qml+1−39
@@ -69,7 +69,6 @@ Item {
6969
7070 onClicked: {
7171 if (!indicators.shown) {
72− indicators.openOverview();
7372 indicators.show();
7473 } else {
7574 indicators.hide();
@@ -89,47 +88,10 @@ Item {
8988 tryCompare(indicators, "state", "initial");
9089 }
9190
92− // Showing the indicators should fully open the indicator panel with the overview menu visible.
91+ // Showing the indicators should fully open the indicator panel.
9392 function test_show() {
9493 indicators.show()
9594 tryCompare(indicators, "fullyOpened", true);
96−
97− // A show must open the indicators.
98− compare(findChild(indicators, "indicatorRow").overviewActive, true, "Overview indicator should be active when opened overview.")
99− }
100−
101− // Opening the overview menu will activate the overview panel.
102− function test_open_overview() {
103− indicators.openOverview();
104− compare(findChild(indicators, "indicatorRow").overviewActive, true, "Overview should be active when opened overview.")
105− }
106−
107− // Showing the indicators, then changing the progress (simulating drag) should keep the overview panel open until
108− // the reveal state is reached.
109− function test_slow_close_open_overview() {
110−
111− var indicator_row = findChild(indicators, "indicatorRow");
112− verify(indicator_row != undefined);
113−
114− indicators.show()
115− // wait for animation to end. (progress needs to be updated)
116− tryCompare(indicators.showAnimation, "running", false);
117− compare(indicator_row.overviewActive, true, "Overview should be active when opened overview.")
118−
119− // iteratively decrease the progress and ensure that it keeps the correct behaviour
120− var current_progress = indicators.progress - shell.height/20;
121− while (current_progress > 0) {
122− indicators.progress = current_progress;
123−
124− if (indicators.state == "commit" || indicators.state == "locked") {
125− compare(indicator_row.overviewActive, true, "Overview should be active when in locked or commit state after show.")
126− }
127− else if (indicators.state == "reveal" || indicators.state == "hint") {
128− compare(indicator_row.overviewActive, false, "Overview should be not active when not in commit or locked state.")
129− }
130−
131− current_progress = current_progress - shell.height/20;
132− }
13395 }
13496
13597 // Test the change in the revealer lateral position changes the current panel menu to fit the position
tests/qmltests/Panel/tst_MenuContent.qml+14−84
@@ -52,8 +52,8 @@ Item {
5252 height: parent.height - 50
5353 }
5454
55− Item {
56− id: click_me
55+ Rectangle {
56+ color: "#bbbbbb"
5757
5858 height: 50
5959 anchors {
@@ -62,65 +62,30 @@ Item {
6262 right: parent.right
6363 }
6464
65− Rectangle {
66− color: "#999999"
67−
68− anchors {
69− top: parent.top
70− left: parent.left
71− right: parent.horizontalCenter
72− bottom: parent.bottom
73− }
74−
75− Text {
76− text: "Devices"
77− anchors.fill: parent
78− horizontalAlignment: Text.AlignHCenter
79− verticalAlignment: Text.AlignVCenter
80− }
81−
82− MouseArea {
83− anchors.fill: parent
84− onClicked: menuContent.showOverview()
85− }
65+ Text {
66+ text: "Next Indicator"
67+ anchors.fill: parent
68+ horizontalAlignment: Text.AlignHCenter
69+ verticalAlignment: Text.AlignVCenter
8670 }
8771
88− Rectangle {
89− color: "#bbbbbb"
90−
91− anchors {
92− top: parent.top
93− left: parent.horizontalCenter
94− right: parent.right
95− bottom: parent.bottom
96− }
97−
98− Text {
99− text: "Next Indicator"
100− anchors.fill: parent
101− horizontalAlignment: Text.AlignHCenter
102− verticalAlignment: Text.AlignVCenter
103− }
104−
105− MouseArea {
106− anchors.fill: parent
107− onClicked: activate_next_content()
108− }
72+ MouseArea {
73+ anchors.fill: parent
74+ onClicked: activate_next_content()
10975 }
11076 }
11177
11278 function activate_next_content()
11379 {
114− if (menuContent.currentIndex == -1)
80+ if (menuContent.currentMenuIndex == -1)
11581 activate_content(0);
11682 else
117− activate_content((menuContent.currentIndex + 1) % indicatorsModel.count)
83+ activate_content((menuContent.currentMenuIndex + 1) % indicatorsModel.count)
11884 }
11985
12086 function activate_content(index)
12187 {
122− menuContent.currentIndex = index
123− menuContent.showMenu();
88+ menuContent.currentMenuIndex = index
12489 }
12590
12691 function get_test_menu_objecName(index) {
@@ -151,7 +116,6 @@ Item {
151116 when: windowShown
152117
153118 function init() {
154− menuContent.hideAll();
155119 if (menuContent.__contentActive)
156120 menuContent.releaseContent();
157121 tryCompare(menuContent, "__contentActive", false);
@@ -174,40 +138,6 @@ Item {
174138 compare(menus.currentIndex, menu_index, "Current menu index does not match selected menu index");
175139 tryCompareFunction(current_menu_equals_test_menu, true);
176140 }
177− // overview should not be visible.
178− tryCompare(findChild(menuContent, "overview"), "visible", false);
179− }
180−
181− function test_show_overview() {
182− menuContent.showOverview();
183−
184− var overview = findChild(menuContent, "overview");
185− tryCompare(overview, "visible", true);
186− tryCompare(overview, "enabled", true);
187− tryCompare(overview, "opacity", 1.0);
188−
189− // menus should not be enabled.
190− tryCompare(findChild(menuContent, "menus"), "opacity", 0);
191− tryCompare(findChild(menuContent, "menus"), "enabled", false);
192− }
193−
194− function test_overview_menuSelected_signal() {
195− menuContent.showOverview();
196−
197− var overview = findChild(menuContent, "overview");
198− var menus = menu_content_test.findChild(menuContent, "menus")
199−
200− var menu_count = indicatorsModel.count;
201− verify(menu_count > 0, "Menu count should be greater than zero");
202− for (var i = 0; i < menu_count; i++) {
203− // manually emit signal from overview.
204− // we dont want to go with mouse events here, otherwise we're testing the overview and not the signal.
205− overview.menuSelected(i);
206−
207− test_menu_objectName = get_test_menu_objecName(i);
208− compare(menus.currentIndex, i, "Current menu index does not match selected menu index");
209− tryCompareFunction(current_menu_equals_test_menu, true);
210− }
211141 }
212142
213143 // Calling activateContent should call start on all menus
@@ -267,7 +197,7 @@ Item {
267197
268198 for (var i = 0; i < 100; i++) {
269199 activate_content(i % menu_count);
270− compare(menuContent.currentIndex, i%menu_count);
200+ compare(menuContent.currentMenuIndex, i%menu_count);
271201 }
272202 wait(100);
273203 }
tests/qmltests/Panel/tst_Overview.qmldeleted−142
@@ -1,142 +0,0 @@
1−/*
2− * Copyright 2013 Canonical Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import QtTest 1.0
19−import ".."
20−import "../../../Panel/Menus"
21−import "../../../Components"
22−import Unity.Test 0.1 as UT
23−import Unity.Indicators 0.1 as Indicators
24−
25−Rectangle {
26− id: shell
27− width: units.gu(40)
28− height: units.gu(80)
29− color: "black"
30−
31− Overview {
32− id: overview
33− anchors.fill: parent
34− indicatorsModel: mockModel
35− shown: true
36− }
37−
38− Item {
39− id: volumeControl
40− property int volume
41− function volumeUp() {
42− volume = Math.min(100, volume + 10);
43− }
44− function volumeDown() {
45− volume = Math.max(0, volume - 10);
46− }
47− }
48−
49− ListModel {
50− id: mockModel
51− ListElement {title: "fake1"; widgetSource: "qrc:/tests/indciators/qml/fake_menu_widget1.qml"; indicatorProperties: ""}
52− ListElement {title: "fake2"; widgetSource: "qrc:/tests/indciators/qml/fake_menu_widget2.qml"; indicatorProperties: ""}
53− ListElement {title: "fake3"; widgetSource: "qrc:/tests/indciators/qml/fake_menu_widget3.qml"; indicatorProperties: ""}
54− }
55−
56− ListModel {
57− id: mockModel2
58− }
59−
60− SignalSpy {
61− id: clickSpy
62− target: overview
63− signalName: "menuSelected"
64− }
65−
66− UT.UnityTestCase {
67− name: "Overview"
68− when: windowShown
69−
70− function initTestCase() {
71− // Spin the event loop once because the UI builds up delayed
72− wait(0);
73− }
74−
75− function test_menuSelected() {
76− overview.indicatorsModel = mockModel;
77−
78− for (var i = 0; i < mockModel.count; ++i) {
79− var button = findChild(overview, "overviewGridButton" + i);
80− clickSpy.clear();
81− mouseClick(button, button.width / 2, button.height / 2);
82− compare(clickSpy.count, 1, "Clicking on grid didn't work");
83− compare(clickSpy.signalArguments[0][0], i, "Clicking the grid returned a wrong index");
84− }
85− }
86−
87− function test_dynamic_addition() {
88− var overviewGrid = findChild(overview, "overviewGrid");
89−
90− for(var i = 0; i < mockModel.count; ++i) {
91− mockModel2.append(mockModel.get(i));
92− }
93− overview.indicatorsModel = mockModel2;
94− overviewGrid.forceLayout();
95− waitForRendering(overviewGrid);
96−
97− var button = findChild(overview, "overviewGridButton2");
98− verify(button !== undefined, "button2 wasn't found");
99− button = findChild(overview, "overviewGridButton3");
100− compare(button, undefined, "There should only be 3 buttons... found at least 4...");
101−
102− mockModel2.append({title: "humppa", widgetSource: "qrc:/tests/indciators/qml/fake_menu_widget4.qml", indicatorProperties: ""})
103− overviewGrid.forceLayout();
104− waitForRendering(overviewGrid);
105−
106− button = findChild(overview, "overviewGridButton3");
107− verify(button !== undefined, "button3 wasn't found");
108− }
109−
110− function test_volume_slider_data() {
111− return [
112− {tag: "0%", sliderPos:0},
113− {tag: "100%", sliderPos: 100},
114− {tag: "50%", sliderPos: 50}
115− ];
116− }
117−
118− function test_volume_slider(data) {
119− var volumeSlider = findChild(overview, "volumeSlider");
120− var startX = volumeSlider.width * volumeSlider.value / 100;
121− var startY = volumeSlider.y + volumeSlider.height / 2;
122− var stopX = (volumeSlider.width - units.gu(5)) * data.sliderPos / 100;
123− var stopY = volumeSlider.y + volumeSlider.height / 2;
124− mouseFlick(volumeSlider, startX, startY, stopX, stopY, true, true, 0.1);
125− }
126−
127− function test_volume_buttons(data) {
128− var volumeDownButton = findChild(overview, "minVolumeIcon");
129− var volumeUpButton = findChild(overview, "maxVolumeIcon");
130−
131− for (var i = 0; i < 10; ++i) {
132− mouseClick(volumeDownButton, volumeDownButton.width / 2, volumeDownButton.height / 2);
133− wait(0)
134− }
135− tryCompare(volumeControl, "volume", 0);
136− for (var i = 0; i < 10; ++i) {
137− mouseClick(volumeUpButton, 1, 1);
138− wait(0)
139− }
140− }
141− }
142−}
tests/qmltests/Panel/tst_Panel.qml+101−61
@@ -30,11 +30,11 @@ Item {
3030 width: units.gu(40)
3131 height: units.gu(80)
3232
33− property bool search_clicked: false
33+ property bool searchClicked: false
3434
3535 Connections {
3636 target: panel
37− onSearchClicked: search_clicked = true
37+ onSearchClicked: searchClicked = true
3838 }
3939
4040 Panel {
@@ -54,27 +54,26 @@ Item {
5454
5555
5656 function init() {
57− search_clicked = false;
57+ searchClicked = false;
5858 panel.indicators.hide();
5959 tryCompare(panel.indicators.hideAnimation, "running", false);
6060 tryCompare(panel.indicators, "state", "initial");
6161 }
6262
6363 function get_indicator_item(index) {
64− var row_repeater = findChild(panel.indicators, "rowRepeater");
65− verify(row_repeater != undefined)
66− return row_repeater.itemAt(index);
64+ var rowRepeater = findChild(panel.indicators, "rowRepeater");
65+ verify(rowRepeater != undefined);
66+ return rowRepeater.itemAt(index);
6767 }
6868
6969 function get_indicator_item_position(index) {
70+ var indicatorRow = findChild(panel.indicators, "indicatorRow");
71+ verify(indicatorRow != undefined);
7072
71− var indicator_row = findChild(panel.indicators, "indicatorRow");
72− verify(indicator_row != undefined)
73−
74− var indicator_item = get_indicator_item(index);
75− verify(indicator_item != undefined);
73+ var indicatorItem = get_indicator_item(index);
74+ verify(indicatorItem != undefined);
7675
77− return panel.mapFromItem(indicator_item, indicator_item.width/2, indicator_item.height/2);
76+ return panel.mapFromItem(indicatorItem, indicatorItem.width/2, indicatorItem.height/2);
7877 }
7978
8079 // Pressing on the indicator panel should activate the indicator hints
@@ -83,11 +82,11 @@ Item {
8382 panel.fullscreenMode = false;
8483 // Wait for the indicators to get into position.
8584 // (switches between normal and fullscreen modes are animated)
86− tryCompare(panel.indicators, "y", 0)
85+ tryCompare(panel.indicators, "y", 0);
8786
88− var indicator_item_coord = get_indicator_item_position(0);
87+ var indicatorItemCoord = get_indicator_item_position(0);
8988
90− touchPress(panel, indicator_item_coord.x, panel.panelHeight / 2)
89+ touchPress(panel, indicatorItemCoord.x, panel.panelHeight / 2);
9190
9291 // hint animation should be run, meaning that indicators will move downwards
9392 // by hintValue pixels without any drag taking place
@@ -96,7 +95,7 @@ Item {
9695 tryCompare(panel.indicators, "partiallyOpened", true);
9796 tryCompare(panel.indicators, "fullyOpened", false);
9897
99− touchRelease(panel, indicator_item_coord.x, panel.panelHeight/2)
98+ touchRelease(panel, indicatorItemCoord.x, panel.panelHeight/2);
10099 }
101100
102101 // Pressing on the top edge of the screen should have no effect if the panel
@@ -105,31 +104,31 @@ Item {
105104 panel.fullscreenMode = true;
106105 // Wait for the indicators to get into position.
107106 // (switches between normal and fullscreen modes are animated)
108− tryCompare(panel.indicators, "y", -panel.panelHeight)
107+ tryCompare(panel.indicators, "y", -panel.panelHeight);
109108
110− var indicator_item_coord = get_indicator_item_position(0);
109+ var indicatorItemCoord = get_indicator_item_position(0);
111110
112− touchPress(panel, indicator_item_coord.x, panel.panelHeight / 2)
111+ touchPress(panel, indicatorItemCoord.x, panel.panelHeight / 2);
113112
114113 // Give some time for a hint animation to change things, if any
115− wait(500)
114+ wait(500);
116115
117116 // no hint animation when fullscreen
118− compare(panel.indicators.y, -panel.panelHeight)
117+ compare(panel.indicators.y, -panel.panelHeight);
119118 var indicatorRow = findChild(panel.indicators, "indicatorRow");
120− verify(indicatorRow != undefined)
121− compare(indicatorRow.y, 0)
122− compare(panel.indicators.height, panel.indicators.panelHeight)
119+ verify(indicatorRow != undefined);
120+ compare(indicatorRow.y, 0);
121+ compare(panel.indicators.height, panel.indicators.panelHeight);
123122 compare(panel.indicators.partiallyOpened, false,
124123 "Indicator should not be partially opened when panel is pressed in" +
125− " fullscreenmode")
124+ " fullscreenmode");
126125 compare(panel.indicators.fullyOpened, false, "Indicator should not be partially" +
127− " opened when panel is pressed in fullscreenmode")
126+ " opened when panel is pressed in fullscreenmode");
128127
129− touchRelease(panel, indicator_item_coord.x, panel.panelHeight/2)
128+ touchRelease(panel, indicatorItemCoord.x, panel.panelHeight/2);
130129 }
131130
132− function test_drag_show_data() { return get_window_data() }
131+ function test_drag_show_data() { return get_window_data(); }
133132
134133 // Dragging from a indicator item in the panel will gradually expose the
135134 // indicators, first by running the hint animation, then after dragging down will
@@ -137,53 +136,53 @@ Item {
137136 function test_drag_show(data) {
138137 panel.fullscreenMode = data.fullscreenFlag;
139138
140− var indicator_row = findChild(panel.indicators, "indicatorRow");
141− verify(indicator_row != undefined)
139+ var indicatorRow = findChild(panel.indicators, "indicatorRow");
140+ verify(indicatorRow != undefined);
142141
143− var row_repeater = findChild(panel.indicators, "rowRepeater");
144− verify(indicator_row != undefined)
142+ var rowRepeater = findChild(panel.indicators, "rowRepeater");
143+ verify(indicatorRow != undefined);
145144
146− var menu_content = findChild(panel.indicators, "menuContent");
147− verify(indicator_row != undefined)
145+ var menuContent = findChild(panel.indicators, "menuContent");
146+ verify(indicatorRow != undefined);
148147
149148 // Wait for the indicators to get into position.
150149 // (switches between normal and fullscreen modes are animated)
151150 if (data.fullscreenFlag) {
152− tryCompare(panel.indicators, "y", -panel.panelHeight)
151+ tryCompare(panel.indicators, "y", -panel.panelHeight);
153152 } else {
154− tryCompare(panel.indicators, "y", 0)
153+ tryCompare(panel.indicators, "y", 0);
155154 }
156155
157156 // do this for each indicator item
158− for (var i = 0; i < row_repeater.count; i++) {
157+ for (var i = 0; i < rowRepeater.count; i++) {
159158
160− var indicator_item = get_indicator_item(i);
161− verify(indicator_item != undefined)
159+ var indicatorItem = get_indicator_item(i);
160+ verify(indicatorItem != undefined);
162161
163− var indicator_item_coord = get_indicator_item_position(i);
162+ var indicatorItemCoord = get_indicator_item_position(i);
164163
165164 touchPress(panel,
166− indicator_item_coord.x, panel.panelHeight / 2)
165+ indicatorItemCoord.x, panel.panelHeight / 2);
167166
168167 // 1) Drag the mouse down
169168 touchFlick(panel,
170− indicator_item_coord.x, panel.panelHeight / 2,
171− indicator_item_coord.x, panel.height * 0.8,
172− false /* beginTouch */, false /* endTouch */)
169+ indicatorItemCoord.x, panel.panelHeight / 2,
170+ indicatorItemCoord.x, panel.height * 0.8,
171+ false /* beginTouch */, false /* endTouch */);
173172
174173 // Indicators height should follow the drag, and therefore increase accordingly.
175174 // They should be at least half-way through the screen
176175 tryCompareFunction(
177176 function() {return panel.indicators.height >= panel.height * 0.5},
178− true)
177+ true);
179178
180− touchRelease(panel, indicator_item_coord.x, panel.height * 0.8)
179+ touchRelease(panel, indicatorItemCoord.x, panel.height * 0.8);
181180
182− compare(indicator_row.currentItem, indicator_item,
183− "Incorrect item activated at position " + i)
184− compare(menu_content.__shown, true, "Menu conetent should be enabled for item at position " + i);
181+ compare(indicatorRow.currentItem, indicatorItem,
182+ "Incorrect item activated at position " + i);
183+ compare(menuContent.currentMenuIndex, i, "Menu conetent should be enabled for item at position " + i);
185184
186− // init for next indicator_item
185+ // init for next indicatorItem
187186 init();
188187 }
189188 }
@@ -192,26 +191,67 @@ Item {
192191 panel.fullscreenMode = false;
193192 panel.searchVisible = true;
194193
195− var search_indicator = findChild(panel, "search");
196− verify(search_indicator != undefined);
194+ var searchIndicator = findChild(panel, "search");
195+ verify(searchIndicator != undefined);
197196
198− tap(search_indicator, 1, 1)
197+ tap(searchIndicator, 1, 1);
199198
200− compare(search_clicked, true,
201− "Tapping search indicator while it was enabled did not emit searchClicked signal")
199+ compare(searchClicked, true,
200+ "Tapping search indicator while it was enabled did not emit searchClicked signal");
202201 }
203202
204203 function test_search_click_when_not_visible() {
205204 panel.fullscreenMode = false;
206− panel.searchVisible = false
205+ panel.searchVisible = false;
206+
207+ var searchIndicator = findChild(panel, "search");
208+ verify(searchIndicator != undefined);
209+
210+ tap(searchIndicator, 1, 1);
211+
212+ compare(searchClicked, false,
213+ "Tapping search indicator while it was not visible emitted searchClicked signal");
214+ }
215+
216+ // Test the vertical velocity check when flicking the indicators open at an angle.
217+ // If the vertical velocity is above a specific point, we shouldnt change active indicators
218+ // if the x position changes
219+ function test_vertical_velocity_detector() {
220+ panel.fullscreenMode = false;
221+ panel.searchVisible = false;
222+
223+ var indicatorRow = findChild(panel.indicators, "indicatorRow");
224+ verify(indicatorRow != undefined);
225+
226+ var rowRepeater = findChild(panel.indicators, "rowRepeater");
227+ verify(indicatorRow != undefined);
228+
229+ // Get the first indicator
230+ var indicatorItemFirst = get_indicator_item(0);
231+ verify(indicatorItemFirst != undefined);
232+
233+ var indicatorItemCoordFirst = get_indicator_item_position(0);
234+ var indicatorItemCoordNext = get_indicator_item_position(indicatorRow.count - 1);
235+
236+ touchPress(panel,
237+ indicatorItemCoordFirst.x, panel.panelHeight / 2);
238+
239+ // 1) Drag the mouse down to hint a bit
240+ touchFlick(panel,
241+ indicatorItemCoordFirst.x, panel.panelHeight / 2,
242+ indicatorItemCoordFirst.x, panel.panelHeight * 2,
243+ false /* beginTouch */, false /* endTouch */);
207244
208− var search_indicator = findChild(panel, "search");
209− verify(search_indicator != undefined);
245+ tryCompare(indicatorRow, "currentItem", indicatorItemFirst)
210246
211− tap(search_indicator, 1, 1)
247+ // 1) Flick mouse down to bottom
248+ touchFlick(panel,
249+ indicatorItemCoordFirst.x, panel.panelHeight * 2,
250+ indicatorItemCoordNext.x, panel.height,
251+ false /* beginTouch */, true /* endTouch */,
252+ units.gu(10) /* speed */, 30 /* iterations */); // more samples needed for accurate velocity
212253
213− compare(search_clicked, false,
214− "Tapping search indicator while it was not visible emitted searchClicked signal")
254+ compare(indicatorRow.currentItem, indicatorItemFirst, "First indicator should still be the current item");
215255 }
216256 }
217257}
tests/qmltests/Panel/tst_SearchIndicator.qml+8−10
@@ -41,24 +41,22 @@ Item {
4141 when: windowShown
4242
4343 function test_clickedSignal() {
44− clickedSpy.clear()
44+ clickedSpy.clear();
4545 mouseClick(searchIndicator,
4646 searchIndicator.width / 2, searchIndicator.height / 2);
47− compare(clickedSpy.count, 1)
47+ compare(clickedSpy.count, 1);
4848 }
4949
5050 function test_hideUp() {
51− var container = findChild(searchIndicator, "container")
52− searchIndicator.state = "hiddenUp"
53− tryCompare(container, "opacity", 0)
54− tryCompare(container, "y", -container.height)
51+ var container = findChild(searchIndicator, "container");
52+ searchIndicator.state = "hidden";
53+ tryCompare(container, "opacity", 0);
5554 }
5655
5756 function test_show() {
58− var container = findChild(searchIndicator, "container")
59− searchIndicator.state = "visible"
60− tryCompare(container, "opacity", 1)
61− tryCompare(container, "y", 0)
57+ var container = findChild(searchIndicator, "container");
58+ searchIndicator.state = "visible";
59+ tryCompare(container, "opacity", 1);
6260 }
6361 }
6462}
tests/qmltests/tst_Shell.qmlnew file+281
@@ -0,0 +1,281 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Daniel d'Andrada <daniel.dandrada@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+import QtQuick 2.0
21+import QtTest 1.0
22+import Ubuntu.Application 0.1
23+import Unity.Test 0.1 as UT
24+
25+import "../.."
26+
27+Item {
28+ width: shell.width
29+ height: shell.height
30+
31+ QtObject {
32+ id: applicationArguments
33+
34+ function hasGeometry() {
35+ return false;
36+ }
37+
38+ function width() {
39+ return 0;
40+ }
41+
42+ function height() {
43+ return 0;
44+ }
45+ }
46+
47+ Shell {
48+ id: shell
49+ }
50+
51+ UT.UnityTestCase {
52+ name: "Shell"
53+ when: windowShown
54+
55+ function initTestCase() {
56+ // swipe away the greeter/lockscreen
57+ var touchX = shell.width - (shell.edgeSize / 2);
58+ var touchY = shell.height / 2;
59+ touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
60+
61+ var dash = findChild(shell, "dash");
62+ // wait until the animation has finished
63+ tryCompare(dash, "contentScale", 1.0);
64+ tryCompare(dash, "opacity", 1.0);
65+ }
66+
67+ function cleanup() {
68+ // kill all (fake) running apps
69+ ApplicationManager.mainStageApplications.clear();
70+ ApplicationManager.sideStageApplications.clear();
71+
72+ var dashHome = findChild(shell, "DashHome");
73+ swipeUntilScopeViewIsReached(dashHome);
74+ }
75+
76+ /*
77+ Test the effect of a right-edge drag on the dash in 3 situations:
78+ 1 - when no application has been launched yet
79+ 2 - when there's a minimized application
80+ 3 - after the last running application has been closed/stopped
81+
82+ The behavior of Dash on 3 should be the same as on 1.
83+ */
84+ function test_rightEdgeDrag() {
85+ checkRightEdgeDragWithNoRunningApps();
86+
87+ dragLauncherIntoView();
88+
89+ // Launch an app from the launcher
90+ tapOnAppIconInLauncher();
91+ waitUntilApplicationWindowIsFullyVisible();
92+
93+ // Minimize the application we just launched
94+ swipeFromLeftEdge();
95+
96+ waitForUIToSettle();
97+
98+ checkRightEdgeDragWithMinimizedApp();
99+
100+ // Minimize that application once again
101+ swipeFromLeftEdge();
102+
103+ // kill it
104+ ApplicationManager.mainStageApplications.clear();
105+
106+ waitForUIToSettle();
107+
108+ // Right edge behavior should now be the same as before that app was launched
109+ checkRightEdgeDragWithNoRunningApps();
110+ }
111+
112+ /*
113+ Perform a right-edge drag when the Dash is being show and there are
114+ no running/minimized apps to be restored.
115+
116+ The expected behavior is that an animation should be played to hint the
117+ user that his right-edge drag gesture has been successfully recognized
118+ but there is no application to be brought to foreground.
119+ */
120+ function checkRightEdgeDragWithNoRunningApps() {
121+ var touchX = shell.width - (shell.edgeSize / 2);
122+ var touchY = shell.height / 2;
123+
124+ var dash = findChild(shell, "dash");
125+ // check that dash has normal scale and opacity
126+ compare(dash.contentScale, 1.0);
127+ compare(dash.opacity, 1.0);
128+
129+ touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY,
130+ true /* beginTouch */, false /* endTouch */);
131+
132+ // check that Dash has been scaled down and had its opacity reduced
133+ tryCompareFunction(function() { return dash.contentScale <= 0.9; }, true);
134+ tryCompareFunction(function() { return dash.opacity <= 0.5; }, true);
135+
136+ touchRelease(shell, shell.width * 0.1, touchY);
137+
138+ // and now everything should have gone back to normal
139+ tryCompare(dash, "contentScale", 1.0);
140+ tryCompare(dash, "opacity", 1.0);
141+ }
142+
143+ /*
144+ Perform a right-edge drag when the Dash is being show and there is
145+ a running/minimized app to be restored.
146+
147+ The expected behavior is that the dash should fade away and ultimately be
148+ made invisible once the gesture is finished as the restored app will now
149+ be on foreground.
150+ */
151+ function checkRightEdgeDragWithMinimizedApp() {
152+ var touchX = shell.width - (shell.edgeSize / 2);
153+ var touchY = shell.height / 2;
154+
155+ var dash = findChild(shell, "dash");
156+ // check that dash has normal scale and opacity
157+ tryCompare(dash, "contentScale", 1.0);
158+ tryCompare(dash, "opacity", 1.0);
159+
160+ touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY,
161+ true /* beginTouch */, false /* endTouch */);
162+
163+ // check that Dash has been scaled down and had its opacity reduced
164+ tryCompareFunction(function() { return dash.contentScale <= 0.9; }, true);
165+ tryCompareFunction(function() { return dash.opacity <= 0.5; }, true);
166+
167+ touchRelease(shell, shell.width * 0.1, touchY);
168+
169+ // dash should have gone away, now that the app is on foreground
170+ tryCompare(dash, "visible", false);
171+ }
172+
173+ // Wait for the whole UI to settle down
174+ function waitForUIToSettle() {
175+ waitUntilApplicationWindowIsFullyHidden();
176+ var dashContentList = findChild(shell, "dashContentList");
177+ tryCompare(dashContentList, "moving", false);
178+ }
179+
180+ function dragLauncherIntoView() {
181+ var launcherPanel = findChild(shell, "launcherPanel");
182+ verify(launcherPanel.x = - launcherPanel.width);
183+ swipeFromLeftEdge();
184+ tryCompare(launcherPanel, "x", 0);
185+ }
186+
187+ function tapOnAppIconInLauncher() {
188+ var launcherPanel = findChild(shell, "launcherPanel");
189+
190+ // pick the first icon, the one at the bottom.
191+ var appIcon = findChild(launcherPanel, "launcherDelegate0")
192+
193+ // Swipe upwards over the launcher to ensure that this icon
194+ // at the bottom is not folded and faded away.
195+ var touchStartX = launcherPanel.width / 2;
196+ var touchStartY = launcherPanel.height / 2;
197+ touchFlick(launcherPanel, touchStartX, touchStartY, touchStartX, 0);
198+ tryCompare(launcherPanel, "moving", false);
199+
200+ // NB tapping (i.e., using touch events) doesn't activate the icon... go figure...
201+ mouseClick(appIcon, appIcon.width / 2, appIcon.height / 2);
202+ }
203+
204+ function waitUntilApplicationWindowIsFullyVisible() {
205+ var underlay = findChild(shell, "underlay");
206+ tryCompare(underlay, "visible", false);
207+ }
208+
209+ function waitUntilApplicationWindowIsFullyHidden() {
210+ var stages = findChild(shell, "stages");
211+ tryCompare(stages, "fullyHidden", true);
212+ }
213+
214+ function swipeUntilScopeViewIsReached(scopeView) {
215+ while (!itemIsOnScreen(scopeView)) {
216+ if (itemIsToLeftOfScreen(scopeView)) {
217+ swipeRightFromCenter();
218+ } else {
219+ swipeLeftFromCenter();
220+ }
221+ waitUntilItemStopsMoving(scopeView);
222+ }
223+ }
224+
225+ function swipeFromLeftEdge() {
226+ var touchStartX = 2;
227+ var touchStartY = shell.height / 2;
228+ touchFlick(shell, touchStartX, touchStartY, shell.width * 0.75, touchStartY);
229+ }
230+
231+ function swipeLeftFromCenter() {
232+ var touchStartX = shell.width / 2;
233+ var touchStartY = shell.height / 2;
234+ touchFlick(shell, touchStartX, touchStartY, 0, touchStartY);
235+ }
236+
237+ function swipeRightFromCenter() {
238+ var touchStartX = shell.width / 2;
239+ var touchStartY = shell.height / 2;
240+ touchFlick(shell, touchStartX, touchStartY, shell.width, touchStartY);
241+ }
242+
243+ function swipeUpFromCenter() {
244+ var touchStartX = shell.width / 2;
245+ var touchStartY = shell.height / 2;
246+ touchFlick(shell, touchStartX, touchStartY, touchStartX, 0);
247+ }
248+
249+ function itemIsOnScreen(item) {
250+ var itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);
251+
252+ return itemRectInShell.x >= 0
253+ && itemRectInShell.y >= 0
254+ && itemRectInShell.x + itemRectInShell.width <= shell.width
255+ && itemRectInShell.y + itemRectInShell.height <= shell.height;
256+ }
257+
258+ function itemIsToLeftOfScreen(item) {
259+ var itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);
260+ return itemRectInShell.x < 0;
261+ }
262+
263+ function waitUntilItemStopsMoving(item) {
264+ var itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);
265+ var previousX = itemRectInShell.x;
266+ var previousY = itemRectInShell.y;
267+ var isStill = false;
268+
269+ do {
270+ wait(100);
271+ itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);
272+ if (itemRectInShell.x == previousX && itemRectInShell.y == previousY) {
273+ isStill = true;
274+ } else {
275+ previousX = itemRectInShell.x;
276+ previousY = itemRectInShell.y;
277+ }
278+ } while (!isStill);
279+ }
280+ }
281+}
tests/utils/modules/Unity/Test/UnityTestCase.qml+22−18
@@ -32,32 +32,34 @@ TestCase {
3232 // Qt uses in QQuickViewTestUtil::flick
3333 // speed is in pixels/second
3434 function mouseFlick(item, x, y, toX, toY, pressMouse, releaseMouse,
35− speed) {
35+ speed, iterations) {
3636 pressMouse = ((pressMouse != null) ? pressMouse : true); // Default to true for pressMouse if not present
3737 releaseMouse = ((releaseMouse != null) ? releaseMouse : true); // Default to true for releaseMouse if not present
3838
3939 // set a default speed if not specified
4040 speed = (speed != null) ? speed : units.gu(10);
4141
42+ // set a default iterations if not specified
43+ iterations = (iterations !== undefined) ? iterations : 5
44+
4245 var distance = Math.sqrt(Math.pow(toX - x, 2) + Math.pow(toY - y, 2))
4346 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
4447
45− var nIterations = 5
46− var timeStep = totalTime / nIterations
47− var diffX = (toX - x) / nIterations
48− var diffY = (toY - y) / nIterations
48+ var timeStep = totalTime / iterations
49+ var diffX = (toX - x) / iterations
50+ var diffY = (toY - y) / iterations
4951 if (pressMouse) {
5052 fakeDateTime.currentTimeMs += timeStep
5153 mousePress(item, x, y)
5254 }
53− for (var i = 0; i < nIterations; ++i) {
55+ for (var i = 0; i < iterations; ++i) {
5456 fakeDateTime.currentTimeMs += timeStep
55− if (i === nIterations - 1) {
57+ if (i === iterations - 1) {
5658 // Avoid any rounding errors by making the last move be at precisely
5759 // the point specified
58− mouseMove(item, toX, toY, nIterations / speed)
60+ mouseMove(item, toX, toY, iterations / speed)
5961 } else {
60− mouseMove(item, x + (i + 1) * diffX, y + (i + 1) * diffY, nIterations / speed)
62+ mouseMove(item, x + (i + 1) * diffX, y + (i + 1) * diffY, iterations / speed)
6163 }
6264 }
6365 if (releaseMouse) {
@@ -186,7 +188,7 @@ TestCase {
186188 }
187189
188190 // speed is in pixels/second
189− function touchFlick(item, x, y, toX, toY, beginTouch, endTouch, speed) {
191+ function touchFlick(item, x, y, toX, toY, beginTouch, endTouch, speed, iterations) {
190192
191193 // Default to true for beginTouch if not present
192194 beginTouch = (beginTouch !== undefined) ? beginTouch : true
@@ -197,13 +199,15 @@ TestCase {
197199 // Set a default speed if not specified
198200 speed = (speed !== undefined) ? speed : units.gu(10)
199201
202+ // Set a default iterations if not specified
203+ var iterations = (iterations !== undefined) ? iterations : 5
204+
200205 var distance = Math.sqrt(Math.pow(toX - x, 2) + Math.pow(toY - y, 2))
201206 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
202207
203− var nIterations = 5
204− var timeStep = totalTime / nIterations
205− var diffX = (toX - x) / nIterations
206− var diffY = (toY - y) / nIterations
208+ var timeStep = totalTime / iterations
209+ var diffX = (toX - x) / iterations
210+ var diffY = (toY - y) / iterations
207211 if (beginTouch) {
208212 fakeDateTime.currentTimeMs += timeStep
209213
@@ -211,17 +215,17 @@ TestCase {
211215 event.press(0 /* touchId */, x, y)
212216 event.commit()
213217 }
214− for (var i = 0; i < nIterations; ++i) {
218+ for (var i = 0; i < iterations; ++i) {
215219 fakeDateTime.currentTimeMs += timeStep
216− if (i === nIterations - 1) {
220+ if (i === iterations - 1) {
217221 // Avoid any rounding errors by making the last move be at precisely
218222 // the point specified
219− wait(nIterations / speed)
223+ wait(iterations / speed)
220224 var event = touchEvent()
221225 event.move(0 /* touchId */, toX, toY)
222226 event.commit()
223227 } else {
224− wait(nIterations / speed)
228+ wait(iterations / speed)
225229 var event = touchEvent()
226230 event.move(0 /* touchId */, x + (i + 1) * diffX, y + (i + 1) * diffY)
227231 event.commit()