on xenial help sign in
Add the ApplicationDrawer
fb0d4b4Michael Zanetti
data/com.canonical.Unity8.gschema.xml+2−2
@@ -12,7 +12,7 @@
12 <description>The usage mode chosen will affect the Window Management behaviour.</description>
12 <description>The usage mode chosen will affect the Window Management behaviour.</description>
13 </key>
13 </key>
14 <key type="y" name="edge-barrier-sensitivity">
14 <key type="y" name="edge-barrier-sensitivity">
15− <default>35</default>
15+ <default>50</default>
16 <range min="1" max="100"/>
16 <range min="1" max="100"/>
17 <summary>Sensitivity of screen edge barriers for the mouse pointer.</summary>
17 <summary>Sensitivity of screen edge barriers for the mouse pointer.</summary>
18 <description>Some UI actions like revealing the launcher or the applications spread are triggered by pushing the mouse pointer against a screen edge. This key defines how much you have to push in order to trigger the associated action.</description>
18 <description>Some UI actions like revealing the launcher or the applications spread are triggered by pushing the mouse pointer against a screen edge. This key defines how much you have to push in order to trigger the associated action.</description>
@@ -23,7 +23,7 @@
23 <description>How much you have to push (in grid units)the mouse against an edge barrier when sensibility is 100.</description>
23 <description>How much you have to push (in grid units)the mouse against an edge barrier when sensibility is 100.</description>
24 </key>
24 </key>
25 <key type="u" name="edge-barrier-max-push">
25 <key type="u" name="edge-barrier-max-push">
26− <default>60</default>
26+ <default>120</default>
27 <summary>Maximum push needed to overcome edge barrier</summary>
27 <summary>Maximum push needed to overcome edge barrier</summary>
28 <description>How much you have to push (in grid units) the mouse against an edge barrier when sensibility is 1.</description>
28 <description>How much you have to push (in grid units) the mouse against an edge barrier when sensibility is 1.</description>
29 </key>
29 </key>
debian/control+3−1
@@ -38,7 +38,7 @@ Build-Depends: cmake,
38 libubuntugestures5-private-dev (>= 1.3.2030),
38 libubuntugestures5-private-dev (>= 1.3.2030),
39 libudev-dev,
39 libudev-dev,
40 libudm-common-dev,
40 libudm-common-dev,
41− libunity-api-dev (>= 7.120),
41+ libunity-api-dev (>= 7.121),
42 libusermetricsoutput1-dev,
42 libusermetricsoutput1-dev,
43# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
43# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
44 libx11-dev[!arm64 !armhf],
44 libx11-dev[!arm64 !armhf],
@@ -49,6 +49,7 @@ Build-Depends: cmake,
49 python3-all:any,
49 python3-all:any,
50 python3-setuptools,
50 python3-setuptools,
51 qml-module-qt-labs-folderlistmodel,
51 qml-module-qt-labs-folderlistmodel,
52+ qml-module-qt-labs-settings,
52 qml-module-qtqml-statemachine,
53 qml-module-qtqml-statemachine,
53 qml-module-qtmultimedia (>= 5.4.1-1ubuntu19~overlay2),
54 qml-module-qtmultimedia (>= 5.4.1-1ubuntu19~overlay2),
54 qml-module-qtquick-layouts,
55 qml-module-qtquick-layouts,
@@ -122,6 +123,7 @@ Depends: dmz-cursor-theme,
122 qmenumodel-qml (>= 0.2.10),
123 qmenumodel-qml (>= 0.2.10),
123 qml-module-biometryd,
124 qml-module-biometryd,
124 qml-module-qt-labs-folderlistmodel,
125 qml-module-qt-labs-folderlistmodel,
126+ qml-module-qt-labs-settings,
125 qml-module-qtqml-statemachine,
127 qml-module-qtqml-statemachine,
126 qml-module-qtquick-xmllistmodel,
128 qml-module-qtquick-xmllistmodel,
127 qml-module-qtsysteminfo,
129 qml-module-qtsysteminfo,
plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=10)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=11)
2pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
3
3
4add_definitions(-DSM_BUSNAME=systemBus)
4add_definitions(-DSM_BUSNAME=systemBus)
plugins/Greeter/Unity/Launcher/launcheritem.cpp+13
@@ -76,6 +76,19 @@ void LauncherItem::setIcon(const QString &icon)
76 }
76 }
77}
77}
78
78
79+QStringList LauncherItem::keywords() const
80+{
81+ return m_keywords;
82+}
83+
84+void LauncherItem::setKeywords(const QStringList &keywords)
85+{
86+ if (m_keywords != keywords) {
87+ m_keywords = keywords;
88+ Q_EMIT keywordsChanged(keywords);
89+ }
90+}
91+
79bool LauncherItem::pinned() const
92bool LauncherItem::pinned() const
80{
93{
81 return m_pinned;
94 return m_pinned;
plugins/Greeter/Unity/Launcher/launcheritem.h+3
@@ -34,6 +34,7 @@ public:
34 QString appId() const override;
34 QString appId() const override;
35 QString name() const override;
35 QString name() const override;
36 QString icon() const override;
36 QString icon() const override;
37+ QStringList keywords() const override;
37 bool pinned() const override;
38 bool pinned() const override;
38 bool running() const override;
39 bool running() const override;
39 bool recent() const override;
40 bool recent() const override;
@@ -49,6 +50,7 @@ public:
49private:
50private:
50 void setName(const QString &name);
51 void setName(const QString &name);
51 void setIcon(const QString &icon);
52 void setIcon(const QString &icon);
53+ void setKeywords(const QStringList &keywords);
52 void setPinned(bool pinned);
54 void setPinned(bool pinned);
53 void setRunning(bool running);
55 void setRunning(bool running);
54 void setRecent(bool recent);
56 void setRecent(bool recent);
@@ -63,6 +65,7 @@ private:
63 QString m_appId;
65 QString m_appId;
64 QString m_name;
66 QString m_name;
65 QString m_icon;
67 QString m_icon;
68+ QStringList m_keywords;
66 bool m_pinned;
69 bool m_pinned;
67 bool m_running;
70 bool m_running;
68 bool m_recent;
71 bool m_recent;
plugins/Unity/Launcher/CMakeLists.txt+4−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=10)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=11)
2pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
3
3
4add_definitions(-DSM_BUSNAME=systemBus)
4add_definitions(-DSM_BUSNAME=systemBus)
@@ -25,6 +25,8 @@ set(QMLLAUNCHERPLUGIN_SRC
25 dbusinterface.cpp
25 dbusinterface.cpp
26 gsettings.cpp
26 gsettings.cpp
27 asadapter.cpp
27 asadapter.cpp
28+ appdrawermodel.cpp
29+ ualwrapper.cpp
28 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
30 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
29 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
31 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
30 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
32 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
@@ -32,6 +34,7 @@ set(QMLLAUNCHERPLUGIN_SRC
32 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
34 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
33 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
35 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
34 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
36 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
37+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/AppDrawerModelInterface.h
35 )
38 )
36
39
37add_library(UnityLauncher-qml MODULE
40add_library(UnityLauncher-qml MODULE
plugins/Unity/Launcher/appdrawermodel.cppnew file+62
@@ -0,0 +1,62 @@
1+/*
2+ * Copyright (C) 2016 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 "appdrawermodel.h"
18+#include "ualwrapper.h"
19+
20+#include <QDebug>
21+#include <QDateTime>
22+
23+AppDrawerModel::AppDrawerModel(QObject *parent):
24+ AppDrawerModelInterface(parent)
25+{
26+ Q_FOREACH (const QString &appId, UalWrapper::installedApps()) {
27+ UalWrapper::AppInfo info = UalWrapper::getApplicationInfo(appId);
28+ if (!info.valid) {
29+ qWarning() << "Failed to get app info for app" << appId;
30+ continue;
31+ }
32+ m_list.append(new LauncherItem(appId, info.name, info.icon, this));
33+ m_list.last()->setKeywords(info.keywords);
34+ }
35+ qsrand(QDateTime::currentMSecsSinceEpoch() / 100);
36+}
37+
38+int AppDrawerModel::rowCount(const QModelIndex &parent) const
39+{
40+ Q_UNUSED(parent)
41+ return m_list.count();
42+}
43+
44+QVariant AppDrawerModel::data(const QModelIndex &index, int role) const
45+{
46+ switch (role) {
47+ case RoleAppId:
48+ return m_list.at(index.row())->appId();
49+ case RoleName:
50+ return m_list.at(index.row())->name();
51+ case RoleIcon:
52+ return m_list.at(index.row())->icon();
53+ case RoleKeywords:
54+ return m_list.at(index.row())->keywords();
55+ case RoleUsage:
56+ // FIXME: u-a-l needs to provide API for usage stats.
57+ // don't forget to drop the qsrand() call in the ctor when dropping this.
58+ return qrand();
59+ }
60+
61+ return QVariant();
62+}
plugins/Unity/Launcher/appdrawermodel.hnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2016 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+
18+#include <unity/shell/launcher/AppDrawerModelInterface.h>
19+
20+#include "launcheritem.h"
21+
22+class AppDrawerModel: public AppDrawerModelInterface
23+{
24+ Q_OBJECT
25+public:
26+ AppDrawerModel(QObject* parent = nullptr);
27+
28+ int rowCount(const QModelIndex &parent) const override;
29+ QVariant data(const QModelIndex &index, int role) const override;
30+
31+private:
32+ QList<LauncherItem*> m_list;
33+};
plugins/Unity/Launcher/launcheritem.cpp+13
@@ -90,6 +90,19 @@ void LauncherItem::setIcon(const QString &icon)
90 }
90 }
91}
91}
92
92
93+QStringList LauncherItem::keywords() const
94+{
95+ return m_keywords;
96+}
97+
98+void LauncherItem::setKeywords(const QStringList &keywords)
99+{
100+ if (m_keywords != keywords) {
101+ m_keywords = keywords;
102+ Q_EMIT keywordsChanged(keywords);
103+ }
104+}
105+
93bool LauncherItem::pinned() const
106bool LauncherItem::pinned() const
94{
107{
95 return m_pinned;
108 return m_pinned;
plugins/Unity/Launcher/launcheritem.h+4
@@ -37,6 +37,7 @@ public:
37 QString appId() const override;
37 QString appId() const override;
38 QString name() const override;
38 QString name() const override;
39 QString icon() const override;
39 QString icon() const override;
40+ QStringList keywords() const override;
40 bool pinned() const override;
41 bool pinned() const override;
41 bool running() const override;
42 bool running() const override;
42 bool recent() const override;
43 bool recent() const override;
@@ -52,6 +53,7 @@ public:
52private:
53private:
53 void setName(const QString &name);
54 void setName(const QString &name);
54 void setIcon(const QString &icon);
55 void setIcon(const QString &icon);
56+ void setKeywords(const QStringList &keywords);
55 void setPinned(bool pinned);
57 void setPinned(bool pinned);
56 void setRunning(bool running);
58 void setRunning(bool running);
57 void setRecent(bool recent);
59 void setRecent(bool recent);
@@ -66,6 +68,7 @@ private:
66 QString m_appId;
68 QString m_appId;
67 QString m_name;
69 QString m_name;
68 QString m_icon;
70 QString m_icon;
71+ QStringList m_keywords;
69 bool m_pinned;
72 bool m_pinned;
70 bool m_running;
73 bool m_running;
71 bool m_recent;
74 bool m_recent;
@@ -79,6 +82,7 @@ private:
79 QuickListEntry m_quitAction;
82 QuickListEntry m_quitAction;
80
83
81 friend class LauncherModel;
84 friend class LauncherModel;
85+ friend class AppDrawerModel;
82};
86};
83
87
84#endif // LAUNCHERITEM_H
88#endif // LAUNCHERITEM_H
plugins/Unity/Launcher/launchermodel.cpp+6−37
@@ -19,18 +19,14 @@
19#include "gsettings.h"
19#include "gsettings.h"
20#include "dbusinterface.h"
20#include "dbusinterface.h"
21#include "asadapter.h"
21#include "asadapter.h"
22+#include "ualwrapper.h"
22
23
23−#include <ubuntu-app-launch/appid.h>
24−#include <ubuntu-app-launch/application.h>
25−#include <ubuntu-app-launch/registry.h>
26#include <unity/shell/application/ApplicationInfoInterface.h>
24#include <unity/shell/application/ApplicationInfoInterface.h>
27#include <unity/shell/application/MirSurfaceListInterface.h>
25#include <unity/shell/application/MirSurfaceListInterface.h>
28
26
29#include <QDesktopServices>
27#include <QDesktopServices>
30#include <QDebug>
28#include <QDebug>
31
29
32−namespace ual = ubuntu::app_launch;
33−
34using namespace unity::shell::application;
30using namespace unity::shell::application;
35
31
36LauncherModel::LauncherModel(QObject *parent):
32LauncherModel::LauncherModel(QObject *parent):
@@ -38,8 +34,7 @@ LauncherModel::LauncherModel(QObject *parent):
38 m_settings(new GSettings(this)),
34 m_settings(new GSettings(this)),
39 m_dbusIface(new DBusInterface(this)),
35 m_dbusIface(new DBusInterface(this)),
40 m_asAdapter(new ASAdapter()),
36 m_asAdapter(new ASAdapter()),
41− m_appManager(nullptr),
37+ m_appManager(nullptr)
42− m_ualRegistry(std::make_shared<ual::Registry>())
43{
38{
44 connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged);
39 connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged);
45 connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged);
40 connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged);
@@ -159,7 +154,7 @@ void LauncherModel::pin(const QString &appId, int index)
159 index = m_list.count();
154 index = m_list.count();
160 }
155 }
161
156
162− auto appInfo = getApplicationInfo(appId);
157+ UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(appId);
163 if (!appInfo.valid) {
158 if (!appInfo.valid) {
164 qWarning() << "Can't pin application, appId not found:" << appId;
159 qWarning() << "Can't pin application, appId not found:" << appId;
165 return;
160 return;
@@ -335,32 +330,6 @@ int LauncherModel::findApplication(const QString &appId)
335 return -1;
330 return -1;
336}
331}
337
332
338−LauncherModel::AppInfo LauncherModel::getApplicationInfo(const QString &appId)
339−{
340− AppInfo info;
341−
342− ual::AppID ualAppId = ual::AppID::find(m_ualRegistry, appId.toStdString());
343− if (ualAppId.empty()) {
344− return info;
345− }
346−
347− std::shared_ptr<ual::Application> ualApp;
348− try
349− {
350− ualApp = ual::Application::create(ualAppId, m_ualRegistry);
351− }
352− catch (std::runtime_error &e)
353− {
354− qWarning() << "Couldn't find application info for" << appId << "-" << e.what();
355− return info;
356− }
357−
358− info.valid = true;
359− info.name = QString::fromStdString(ualApp->info()->name());
360− info.icon = QString::fromStdString(ualApp->info()->iconPath());
361− return info;
362−}
363−
364void LauncherModel::progressChanged(const QString &appId, int progress)
333void LauncherModel::progressChanged(const QString &appId, int progress)
365{
334{
366 const int idx = findApplication(appId);
335 const int idx = findApplication(appId);
@@ -408,7 +377,7 @@ void LauncherModel::countVisibleChanged(const QString &appId, bool countVisible)
408 }
377 }
409 } else {
378 } else {
410 // Need to create a new LauncherItem and show the highlight
379 // Need to create a new LauncherItem and show the highlight
411− auto appInfo = getApplicationInfo(appId);
380+ UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(appId);
412 if (countVisible && appInfo.valid) {
381 if (countVisible && appInfo.valid) {
413 LauncherItem *item = new LauncherItem(appId,
382 LauncherItem *item = new LauncherItem(appId,
414 appInfo.name,
383 appInfo.name,
@@ -428,7 +397,7 @@ void LauncherModel::refresh()
428 // First walk through all the existing items and see if we need to remove something
397 // First walk through all the existing items and see if we need to remove something
429 QList<LauncherItem*> toBeRemoved;
398 QList<LauncherItem*> toBeRemoved;
430 Q_FOREACH (LauncherItem* item, m_list) {
399 Q_FOREACH (LauncherItem* item, m_list) {
431− auto appInfo = getApplicationInfo(item->appId());
400+ UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(item->appId());
432 if (!appInfo.valid) {
401 if (!appInfo.valid) {
433 // Application no longer available => drop it!
402 // Application no longer available => drop it!
434 toBeRemoved << item;
403 toBeRemoved << item;
@@ -480,7 +449,7 @@ void LauncherModel::refresh()
480 if (itemIndex == -1) {
449 if (itemIndex == -1) {
481 // Need to add it. Just add it into the addedIndex to keep same ordering as the list
450 // Need to add it. Just add it into the addedIndex to keep same ordering as the list
482 // in the settings.
451 // in the settings.
483− auto appInfo = getApplicationInfo(entry);
452+ UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(entry);
484 if (!appInfo.valid) {
453 if (!appInfo.valid) {
485 continue;
454 continue;
486 }
455 }
plugins/Unity/Launcher/launchermodel.h−14
@@ -27,12 +27,6 @@ class GSettings;
27class DBusInterface;
27class DBusInterface;
28class ASAdapter;
28class ASAdapter;
29
29
30−namespace ubuntu {
31− namespace app_launch {
32− class Registry;
33− }
34−}
35−
36using namespace unity::shell::launcher;
30using namespace unity::shell::launcher;
37using namespace unity::shell::application;
31using namespace unity::shell::application;
38
32
@@ -73,13 +67,6 @@ private:
73
67
74 void unpin(const QString &appId);
68 void unpin(const QString &appId);
75
69
76− struct AppInfo {
77− bool valid = false;
78− QString name;
79− QString icon;
80− };
81− AppInfo getApplicationInfo(const QString &appId);
82−
83private Q_SLOTS:
70private Q_SLOTS:
84 void countChanged(const QString &appId, int count);
71 void countChanged(const QString &appId, int count);
85 void countVisibleChanged(const QString &appId, bool count);
72 void countVisibleChanged(const QString &appId, bool count);
@@ -98,7 +85,6 @@ private:
98 ASAdapter *m_asAdapter;
85 ASAdapter *m_asAdapter;
99
86
100 ApplicationManagerInterface *m_appManager;
87 ApplicationManagerInterface *m_appManager;
101− std::shared_ptr<ubuntu::app_launch::Registry> m_ualRegistry;
102
88
103 friend class LauncherModelTest;
89 friend class LauncherModelTest;
104};
90};
plugins/Unity/Launcher/plugin.cpp+2−1
@@ -26,7 +26,7 @@
26// local
26// local
27#include "launchermodel.h"
27#include "launchermodel.h"
28#include "launcheritem.h"
28#include "launcheritem.h"
29−
29+#include "appdrawermodel.h"
30
30
31using namespace unity::shell::launcher;
31using namespace unity::shell::launcher;
32
32
@@ -46,4 +46,5 @@ void UnityLauncherPlugin::registerTypes(const char *uri)
46 qmlRegisterSingletonType<LauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
46 qmlRegisterSingletonType<LauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
47 qmlRegisterUncreatableType<LauncherItem>(uri, 0, 1, "LauncherItem", QStringLiteral("Can't create new Launcher Items in QML. Get them from the LauncherModel."));
47 qmlRegisterUncreatableType<LauncherItem>(uri, 0, 1, "LauncherItem", QStringLiteral("Can't create new Launcher Items in QML. Get them from the LauncherModel."));
48 qmlRegisterUncreatableType<QuickListModel>(uri, 0, 1, "QuickListModel", QStringLiteral("Can't create a QuickListModel in QML. Get them from the LauncherItems."));
48 qmlRegisterUncreatableType<QuickListModel>(uri, 0, 1, "QuickListModel", QStringLiteral("Can't create a QuickListModel in QML. Get them from the LauncherItems."));
49+ qmlRegisterType<AppDrawerModel>(uri, 0, 1, "AppDrawerModel");
49}
50}
plugins/Unity/Launcher/ualwrapper.cppnew file+73
@@ -0,0 +1,73 @@
1+/*
2+ * Copyright (C) 2016 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 <QDebug>
18+
19+#include "ualwrapper.h"
20+
21+#include <ubuntu-app-launch/registry.h>
22+using namespace ubuntu::app_launch;
23+
24+UalWrapper::UalWrapper(QObject *parent):
25+ QObject(parent)
26+{
27+
28+}
29+
30+QStringList UalWrapper::installedApps()
31+{
32+ QStringList appIds;
33+ try {
34+ for (const std::shared_ptr<Application> &app : Registry::installedApps()) {
35+ if (!app->appId().package.value().empty()) {
36+ appIds << QString::fromStdString(app->appId().package.value() + "_" + app->appId().appname.value());
37+ } else {
38+ appIds << QString::fromStdString(app->appId().appname);
39+ }
40+ }
41+ } catch (const std::runtime_error &e) {
42+ qWarning() << "ubuntu-all-launch threw an exception listing apps:" << e.what();
43+ }
44+
45+ return appIds;
46+}
47+
48+UalWrapper::AppInfo UalWrapper::getApplicationInfo(const QString &appId)
49+{
50+ AppInfo info;
51+
52+ try {
53+ AppID ualAppId = AppID::find(appId.toStdString());
54+ if (ualAppId.empty()) {
55+ qWarning() << "Empty ualAppId result for" << appId;
56+ return info;
57+ }
58+
59+ std::shared_ptr<Application> ualApp;
60+ ualApp = Application::create(ualAppId, Registry::getDefault());
61+
62+ info.name = QString::fromStdString(ualApp->info()->name());
63+ info.icon = QString::fromStdString(ualApp->info()->iconPath());
64+ for (const std::string &keyword : ualApp->info()->keywords().value()) {
65+ info.keywords << QString::fromStdString(keyword);
66+ }
67+ info.valid = true;
68+ } catch (const std::runtime_error &e) {
69+ qWarning() << "ubuntu-app-launch threw an exception getting app info for appId:" << appId << ":" << e.what();
70+ }
71+
72+ return info;
73+}
plugins/Unity/Launcher/ualwrapper.hnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (C) 2016 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 <QObject>
18+
19+class UalWrapper: public QObject
20+{
21+ Q_OBJECT
22+public:
23+ struct AppInfo {
24+ bool valid = false;
25+ QString name;
26+ QString icon;
27+ QStringList keywords;
28+ };
29+
30+ UalWrapper(QObject* parent = nullptr);
31+
32+ static QStringList installedApps();
33+ static AppInfo getApplicationInfo(const QString &appId);
34+
35+};
plugins/Utils/CMakeLists.txt+4
@@ -15,6 +15,10 @@ set(QMLPLUGIN_SRC
15 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
15 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
16 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
16 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
17 applicationsfiltermodel.cpp
17 applicationsfiltermodel.cpp
18+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/AppDrawerModelInterface.h
19+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
20+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
21+ appdrawerproxymodel.cpp
18 constants.cpp
22 constants.cpp
19 WindowInputMonitor.cpp
23 WindowInputMonitor.cpp
20 inputwatcher.cpp
24 inputwatcher.cpp
plugins/Utils/appdrawerproxymodel.cppnew file+189
@@ -0,0 +1,189 @@
1+/*
2+ * Copyright (C) 2016 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 "appdrawerproxymodel.h"
18+
19+#include <unity/shell/launcher/LauncherItemInterface.h>
20+
21+#include <QDebug>
22+
23+AppDrawerProxyModel::AppDrawerProxyModel(QObject *parent):
24+ QSortFilterProxyModel(parent)
25+{
26+ setSortRole(AppDrawerModelInterface::RoleName);
27+ setSortLocaleAware(true);
28+ sort(0);
29+
30+ connect(this, &QAbstractListModel::rowsInserted, this, &AppDrawerProxyModel::countChanged);
31+ connect(this, &QAbstractListModel::rowsRemoved, this, &AppDrawerProxyModel::countChanged);
32+ connect(this, &QAbstractListModel::layoutChanged, this, &AppDrawerProxyModel::countChanged);
33+}
34+
35+QAbstractItemModel *AppDrawerProxyModel::source() const
36+{
37+ return m_source;
38+}
39+
40+void AppDrawerProxyModel::setSource(QAbstractItemModel *source)
41+{
42+ if (m_source != source) {
43+ m_source = source;
44+ setSourceModel(m_source);
45+ setSortRole(m_sortBy == SortByAToZ ? AppDrawerModelInterface::RoleName : AppDrawerModelInterface::RoleUsage);
46+ connect(m_source, &QAbstractItemModel::rowsRemoved, this, &AppDrawerProxyModel::invalidateFilter);
47+ connect(m_source, &QAbstractItemModel::rowsInserted, this, &AppDrawerProxyModel::invalidateFilter);
48+ Q_EMIT sourceChanged();
49+ }
50+}
51+
52+AppDrawerProxyModel::GroupBy AppDrawerProxyModel::group() const
53+{
54+ return m_group;
55+}
56+
57+void AppDrawerProxyModel::setGroup(AppDrawerProxyModel::GroupBy group)
58+{
59+ if (m_group != group) {
60+ m_group = group;
61+ Q_EMIT groupChanged();
62+ invalidateFilter();
63+ }
64+}
65+
66+QString AppDrawerProxyModel::filterLetter() const
67+{
68+ return m_filterLetter;
69+}
70+
71+void AppDrawerProxyModel::setFilterLetter(const QString &filterLetter)
72+{
73+ if (m_filterLetter != filterLetter) {
74+ m_filterLetter = filterLetter;
75+ Q_EMIT filterLetterChanged();
76+ invalidateFilter();
77+ }
78+}
79+
80+QString AppDrawerProxyModel::filterString() const
81+{
82+ return m_filterString;
83+}
84+
85+void AppDrawerProxyModel::setFilterString(const QString &filterString)
86+{
87+ if (m_filterString != filterString) {
88+ m_filterString = filterString;
89+ Q_EMIT filterStringChanged();
90+ invalidateFilter();
91+ }
92+}
93+
94+AppDrawerProxyModel::SortBy AppDrawerProxyModel::sortBy() const
95+{
96+ return m_sortBy;
97+}
98+
99+void AppDrawerProxyModel::setSortBy(AppDrawerProxyModel::SortBy sortBy)
100+{
101+ if (m_sortBy != sortBy) {
102+ m_sortBy = sortBy;
103+ Q_EMIT sortByChanged();
104+ setSortRole(m_sortBy == SortByAToZ ? AppDrawerModelInterface::RoleName : AppDrawerModelInterface::RoleUsage);
105+ sort(0);
106+ }
107+}
108+
109+int AppDrawerProxyModel::count() const
110+{
111+ return rowCount();
112+}
113+
114+QVariant AppDrawerProxyModel::data(const QModelIndex &index, int role) const
115+{
116+ QModelIndex idx = mapToSource(index);
117+ if (role == Qt::UserRole) {
118+ QString name = m_source->data(idx, AppDrawerModelInterface::RoleName).toString();
119+ return name.length() > 0 ? QString(name.at(0)).toUpper() : QChar();
120+ }
121+ return m_source->data(idx, role);
122+}
123+
124+QHash<int, QByteArray> AppDrawerProxyModel::roleNames() const
125+{
126+ if (m_source) {
127+ QHash<int, QByteArray> roles = m_source->roleNames();
128+ roles.insert(Qt::UserRole, "letter");
129+ return roles;
130+ }
131+ return QHash<int, QByteArray>();
132+}
133+
134+bool AppDrawerProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
135+{
136+ Q_UNUSED(source_parent)
137+
138+ if (m_group == GroupByAToZ && source_row > 0) {
139+ QString currentName = m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleName).toString();
140+ QChar currentLetter = currentName.length() > 0 ? currentName.at(0) : QChar();
141+ QString previousName = m_source->data(m_source->index(source_row - 1,0 ), AppDrawerModelInterface::RoleName).toString();
142+ QChar previousLetter = previousName.length() > 0 ? previousName.at(0) : QChar();
143+ if (currentLetter.toLower() == previousLetter.toLower()) {
144+ return false;
145+ }
146+ } else if(m_group == GroupByAll && source_row > 0) {
147+ return false;
148+ }
149+ if (!m_filterLetter.isEmpty()) {
150+ QString currentName = m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleName).toString();
151+ QString currentLetter = currentName.length() > 0 ? QString(currentName.at(0)) : QString();
152+ if (currentLetter.toLower() != m_filterLetter.toLower()) {
153+ return false;
154+ }
155+ }
156+ if (!m_filterString.isEmpty()) {
157+ QStringList allWords = m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleKeywords).toStringList();
158+ allWords.prepend(m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleName).toString());
159+ bool found = false;
160+ Q_FOREACH (const QString currentWord, allWords) {
161+ if (currentWord.toLower().startsWith(m_filterString.toLower())) {
162+ found = true;
163+ break;
164+ }
165+ }
166+ if (!found) {
167+ return false;
168+ }
169+ }
170+ return true;
171+}
172+
173+QString AppDrawerProxyModel::appId(int index) const
174+{
175+ if (index >= 0 && index < rowCount()) {
176+ QModelIndex sourceIndex = mapToSource(this->index(index, 0));
177+
178+ AppDrawerModelInterface* adm = dynamic_cast<AppDrawerModelInterface*>(m_source);
179+ if (adm) {
180+ return adm->data(sourceIndex, AppDrawerModelInterface::RoleAppId).toString();
181+ }
182+
183+ AppDrawerProxyModel* adpm = qobject_cast<AppDrawerProxyModel*>(m_source);
184+ if (adpm) {
185+ return adpm->appId(sourceIndex.row());
186+ }
187+ }
188+ return nullptr;
189+}
plugins/Utils/appdrawerproxymodel.hnew file+87
@@ -0,0 +1,87 @@
1+/*
2+ * Copyright (C) 2016 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 <QSortFilterProxyModel>
18+
19+#include <unity/shell/launcher/AppDrawerModelInterface.h>
20+
21+using namespace unity::shell::launcher;
22+
23+class AppDrawerProxyModel: public QSortFilterProxyModel
24+{
25+ Q_OBJECT
26+ Q_ENUMS(GroupBy)
27+ Q_ENUMS(SortBy)
28+ Q_PROPERTY(QAbstractItemModel* source READ source WRITE setSource NOTIFY sourceChanged)
29+ Q_PROPERTY(GroupBy group READ group WRITE setGroup NOTIFY groupChanged)
30+ Q_PROPERTY(QString filterLetter READ filterLetter WRITE setFilterLetter NOTIFY filterLetterChanged)
31+ Q_PROPERTY(QString filterString READ filterString WRITE setFilterString NOTIFY filterStringChanged)
32+ Q_PROPERTY(SortBy sortBy READ sortBy WRITE setSortBy NOTIFY sortByChanged)
33+ Q_PROPERTY(int count READ count NOTIFY countChanged)
34+
35+public:
36+ enum GroupBy {
37+ GroupByNone,
38+ GroupByAll,
39+ GroupByAToZ
40+ };
41+ enum SortBy {
42+ SortByAToZ,
43+ SortByUsage
44+ };
45+
46+ AppDrawerProxyModel(QObject* parent = nullptr);
47+
48+ QAbstractItemModel* source() const;
49+ void setSource(QAbstractItemModel* source);
50+
51+ GroupBy group() const;
52+ void setGroup(GroupBy group);
53+
54+ QString filterLetter() const;
55+ void setFilterLetter(const QString &filterLetter);
56+
57+ QString filterString() const;
58+ void setFilterString(const QString &filterString);
59+
60+ SortBy sortBy() const;
61+ void setSortBy(SortBy sortBy);
62+
63+ int count() const;
64+
65+ QVariant data(const QModelIndex &index, int role) const override;
66+ QHash<int, QByteArray> roleNames() const override;
67+
68+ Q_INVOKABLE QString appId(int index) const;
69+
70+protected:
71+ bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
72+
73+Q_SIGNALS:
74+ void sourceChanged();
75+ void groupChanged();
76+ void filterLetterChanged();
77+ void filterStringChanged();
78+ void sortByChanged();
79+ void countChanged();
80+
81+private:
82+ QAbstractItemModel* m_source = nullptr;
83+ GroupBy m_group = GroupByNone;
84+ QString m_filterLetter;
85+ QString m_filterString;
86+ SortBy m_sortBy = SortByAToZ;
87+};
plugins/Utils/plugin.cpp+2
@@ -39,6 +39,7 @@
39#include "deviceconfigparser.h"
39#include "deviceconfigparser.h"
40#include "globalfunctions.h"
40#include "globalfunctions.h"
41#include "URLDispatcher.h"
41#include "URLDispatcher.h"
42+#include "appdrawerproxymodel.h"
42
43
43static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
44static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
44{
45{
@@ -82,4 +83,5 @@ void UtilsPlugin::registerTypes(const char *uri)
82 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
83 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
83 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
84 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
84 qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher");
85 qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher");
86+ qmlRegisterType<AppDrawerProxyModel>(uri, 0, 1, "AppDrawerProxyModel");
85}
87}
qml/Components/KeyboardShortcutsOverlay.qml+13
@@ -173,6 +173,19 @@ Rectangle {
173 Layout.maximumWidth: maxTextSize
173 Layout.maximumWidth: maxTextSize
174 }
174 }
175
175
176+ Label {
177+ text: i18n.tr("Super + A")
178+ fontSize: "small"
179+ font.weight: Font.Medium
180+ }
181+ Label {
182+ text: i18n.tr("Opens the Application Drawer.")
183+ fontSize: "small"
184+ font.weight: Font.Light
185+ wrapMode: Text.Wrap
186+ Layout.maximumWidth: maxTextSize
187+ }
188+
176
189
177 // Scopes section
190 // Scopes section
178 Item { Layout.columnSpan: 2; height: units.gu(2) }
191 Item { Layout.columnSpan: 2; height: units.gu(2) }
qml/Launcher/BackgroundBlur.qmlnew file+81
@@ -0,0 +1,81 @@
1+/*
2+ * Copyright (C) 2016 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.4
18+import Ubuntu.Components 1.3
19+import QtGraphicalEffects 1.0
20+
21+Item {
22+ id: root
23+
24+ property int blurAmount: 32
25+ property Item sourceItem
26+ property rect blurRect: Qt.rect(0,0,0,0)
27+ property alias cached: fastBlur.cached
28+
29+ Rectangle {
30+ id: blurMask
31+ color: "yellow"
32+ x: blurRect.x
33+ y: blurRect.y
34+ width: blurRect.width
35+ height: blurRect.height
36+ }
37+
38+ ShaderEffect {
39+ id: maskedBlurEffect
40+ x: blurRect.x
41+ y: blurRect.y
42+ width: blurRect.width
43+ height: blurRect.height
44+
45+ property variant source: ShaderEffectSource {
46+ id: shaderEffectSource
47+ sourceItem: root.sourceItem
48+ hideSource: false
49+ sourceRect: root.blurRect
50+ }
51+
52+ property var mask: ShaderEffectSource {
53+ sourceItem: blurMask
54+ hideSource: true
55+ }
56+
57+ fragmentShader: "
58+ varying highp vec2 qt_TexCoord0;
59+ uniform sampler2D source;
60+ uniform sampler2D mask;
61+ void main(void)
62+ {
63+ highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
64+ highp vec4 maskColor = texture2D(mask, qt_TexCoord0);
65+
66+ sourceColor *= maskColor.a;
67+
68+ gl_FragColor = sourceColor;
69+ }"
70+ }
71+
72+ FastBlur {
73+ id: fastBlur
74+ x: blurRect.x
75+ y: blurRect.y
76+ width: blurRect.width
77+ height: blurRect.height
78+ source: maskedBlurEffect
79+ radius: Math.min(blurAmount, 128)
80+ }
81+ }
qml/Launcher/Drawer.qmlnew file+306
@@ -0,0 +1,306 @@
1+/*
2+ * Copyright (C) 2016 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.4
18+import Ubuntu.Components 1.3
19+import Unity.Launcher 0.1
20+import Utils 0.1
21+import "../Components"
22+import Qt.labs.settings 1.0
23+
24+FocusScope {
25+ id: root
26+
27+ property int panelWidth: 0
28+ readonly property bool moving: listLoader.item && listLoader.item.moving
29+
30+ signal applicationSelected(string appId)
31+
32+ property bool draggingHorizontally: false
33+ property int dragDistance: 0
34+
35+ onFocusChanged: {
36+ if (focus) {
37+ searchField.selectAll();
38+ }
39+ }
40+
41+ function focusInput() {
42+ searchField.selectAll();
43+ searchField.focus = true;
44+ }
45+
46+ Settings {
47+ property alias selectedTab: sections.selectedIndex
48+ }
49+
50+ MouseArea {
51+ anchors.fill: parent
52+ hoverEnabled: true
53+ acceptedButtons: Qt.AllButtons
54+ onWheel: wheel.accepted = true
55+ }
56+
57+ Rectangle {
58+ anchors.fill: parent
59+ color: "#BF000000"
60+
61+ AppDrawerModel {
62+ id: appDrawerModel
63+ }
64+
65+ AppDrawerProxyModel {
66+ id: sortProxyModel
67+ source: appDrawerModel
68+ filterString: searchField.displayText
69+ sortBy: AppDrawerProxyModel.SortByAToZ
70+ }
71+
72+ Item {
73+ id: contentContainer
74+ anchors.fill: parent
75+ anchors.leftMargin: root.panelWidth
76+
77+ TextField {
78+ id: searchField
79+ anchors { left: parent.left; top: parent.top; right: parent.right; margins: units.gu(1) }
80+ placeholderText: i18n.tr("Search…")
81+ focus: true
82+ onAccepted: {
83+ if (searchField.displayText != "" && listLoader.item && listLoader.item.currentItem) {
84+ root.applicationSelected(listLoader.item.getFirstAppId());
85+ }
86+ }
87+ }
88+
89+ Item {
90+ id: sectionsContainer
91+ anchors { left: parent.left; top: searchField.bottom; right: parent.right; }
92+ height: sections.height
93+ clip: true
94+ z: 2
95+
96+ Sections {
97+ id: sections
98+ width: parent.width
99+ actions: [
100+ Action {
101+ text: i18n.ctr("Apps sorted alphabetically", "A-Z")
102+ // TODO: Disabling this for now as we don't get the right input from u-a-l yet.
103+// },
104+// Action {
105+// text: i18n.ctr("Most used apps", "Most used")
106+ }
107+ ]
108+
109+ Rectangle {
110+ anchors.bottom: parent.bottom
111+ height: units.dp(1)
112+ color: 'gray'
113+ width: contentContainer.width
114+ }
115+ }
116+ }
117+
118+ Loader {
119+ id: listLoader
120+ anchors { left: parent.left; top: sectionsContainer.bottom; right: parent.right; bottom: parent.bottom; leftMargin: units.gu(1); rightMargin: units.gu(1) }
121+ sourceComponent: {
122+ switch (sections.selectedIndex) {
123+ case 0: return aToZComponent;
124+ case 1: return mostUsedComponent;
125+ }
126+ }
127+ Binding {
128+ target: listLoader.item || null
129+ property: "objectName"
130+ value: "drawerItemList"
131+ }
132+ }
133+
134+ MouseArea {
135+ parent: listLoader.item ? listLoader.item : null
136+ anchors.fill: parent
137+ propagateComposedEvents: true
138+ property int oldX: 0
139+ onPressed: {
140+ oldX = mouseX;
141+ }
142+ onMouseXChanged: {
143+ var diff = oldX - mouseX;
144+ root.draggingHorizontally |= diff > units.gu(2);
145+ if (!root.draggingHorizontally) {
146+ return;
147+ }
148+ propagateComposedEvents = false;
149+ parent.interactive = false;
150+ root.dragDistance += diff;
151+ oldX = mouseX
152+ }
153+ onReleased: {
154+ if (root.draggingHorizontally) {
155+ root.draggingHorizontally = false;
156+ parent.interactive = true;
157+ }
158+ reactivateTimer.start();
159+ }
160+ Timer {
161+ id: reactivateTimer
162+ interval: 0
163+ onTriggered: parent.propagateComposedEvents = true;
164+ }
165+ }
166+
167+ Component {
168+ id: mostUsedComponent
169+ DrawerListView {
170+
171+ header: MoreAppsHeader {
172+ width: parent.width
173+ height: units.gu(6)
174+ }
175+
176+ model: AppDrawerProxyModel {
177+ source: sortProxyModel
178+ group: AppDrawerProxyModel.GroupByAll
179+ sortBy: AppDrawerProxyModel.SortByUsage
180+ }
181+
182+ delegate: UbuntuShape {
183+ width: parent.width
184+ color: "#20ffffff"
185+ aspect: UbuntuShape.Flat
186+ // NOTE: Cannot use gridView.rows here as it would evaluate to 0 at first and only update later,
187+ // which messes up the ListView.
188+ height: (Math.ceil(mostUsedGridView.model.count / mostUsedGridView.columns) * mostUsedGridView.delegateHeight) + units.gu(2)
189+
190+ readonly property string appId: model.appId
191+
192+ DrawerGridView {
193+ id: mostUsedGridView
194+ anchors.fill: parent
195+ topMargin: units.gu(1)
196+ bottomMargin: units.gu(1)
197+ clip: true
198+
199+ model: sortProxyModel
200+
201+ delegateWidth: units.gu(8)
202+ delegateHeight: units.gu(10)
203+ delegate: drawerDelegateComponent
204+ }
205+ }
206+ }
207+ }
208+
209+ Component {
210+ id: aToZComponent
211+ DrawerListView {
212+
213+ header: MoreAppsHeader {
214+ width: parent.width
215+ height: units.gu(6)
216+ }
217+
218+ model: AppDrawerProxyModel {
219+ source: sortProxyModel
220+ sortBy: AppDrawerProxyModel.SortByAToZ
221+ group: AppDrawerProxyModel.GroupByAToZ
222+ }
223+
224+ delegate: UbuntuShape {
225+ width: parent.width
226+ color: "#20ffffff"
227+ aspect: UbuntuShape.Flat
228+
229+ readonly property string appId: model.appId
230+
231+ // NOTE: Cannot use gridView.rows here as it would evaluate to 0 at first and only update later,
232+ // which messes up the ListView.
233+ height: (Math.ceil(gridView.model.count / gridView.columns) * gridView.delegateHeight) +
234+ categoryNameLabel.implicitHeight + units.gu(2)
235+
236+ Label {
237+ id: categoryNameLabel
238+ anchors { left: parent.left; top: parent.top; right: parent.right; margins: units.gu(1) }
239+ text: model.letter
240+ }
241+
242+ DrawerGridView {
243+ id: gridView
244+ anchors { left: parent.left; top: categoryNameLabel.bottom; right: parent.right; topMargin: units.gu(1) }
245+ height: rows * delegateHeight
246+
247+ interactive: false
248+
249+ model: AppDrawerProxyModel {
250+ id: categoryModel
251+ source: sortProxyModel
252+ filterLetter: model.letter
253+ }
254+ delegateWidth: units.gu(8)
255+ delegateHeight: units.gu(10)
256+ delegate: drawerDelegateComponent
257+ }
258+ }
259+ }
260+ }
261+ }
262+
263+ Component {
264+ id: drawerDelegateComponent
265+ AbstractButton {
266+ width: GridView.view.cellWidth
267+ height: units.gu(10)
268+ objectName: "drawerItem_" + model.appId
269+
270+ onClicked: root.applicationSelected(model.appId)
271+
272+ Column {
273+ width: units.gu(8)
274+ anchors.horizontalCenter: parent.horizontalCenter
275+ height: childrenRect.height
276+ spacing: units.gu(1)
277+
278+ UbuntuShape {
279+ id: appIcon
280+ width: units.gu(6)
281+ height: 7.5 / 8 * width
282+ anchors.horizontalCenter: parent.horizontalCenter
283+ backgroundMode: UbuntuShape.SolidColor
284+ backgroundColor: UbuntuColors.lightGrey
285+ radius: "medium"
286+ borderSource: 'undefined'
287+ source: Image {
288+ id: sourceImage
289+ sourceSize.width: appIcon.width
290+ source: model.icon
291+ }
292+ sourceFillMode: UbuntuShape.PreserveAspectCrop
293+ }
294+
295+ Label {
296+ text: model.name
297+ width: parent.width
298+ horizontalAlignment: Text.AlignHCenter
299+ fontSize: "small"
300+ elide: Text.ElideRight
301+ }
302+ }
303+ }
304+ }
305+ }
306+}
qml/Launcher/DrawerGridView.qmlnew file+47
@@ -0,0 +1,47 @@
1+/*
2+ * Copyright (C) 2016 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.4
18+import "../Components"
19+
20+Item {
21+ id: root
22+
23+ property int delegateWidth: units.gu(10)
24+ property int delegateHeight: units.gu(10)
25+ property alias delegate: gridView.delegate
26+ property alias model: gridView.model
27+ property alias interactive: gridView.interactive
28+
29+ property alias header: gridView.header
30+ property alias topMargin: gridView.topMargin
31+ property alias bottomMargin: gridView.bottomMargin
32+
33+ readonly property int columns: width / delegateWidth
34+ readonly property int rows: Math.ceil(gridView.model.count / root.columns)
35+
36+ GridView {
37+ id: gridView
38+ anchors.fill: parent
39+ leftMargin: spacing
40+
41+ readonly property int overflow: width - (root.columns * root.delegateWidth)
42+ readonly property real spacing: overflow / (root.columns)
43+
44+ cellWidth: root.delegateWidth + spacing
45+ cellHeight: root.delegateHeight
46+ }
47+}
qml/Launcher/DrawerListView.qmlnew file+32
@@ -0,0 +1,32 @@
1+/*
2+ * Copyright (C) 2016 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.4
18+import Ubuntu.Components 1.3
19+import "../Components"
20+
21+ListView {
22+ id: root
23+ anchors.fill: parent
24+ topMargin: units.gu(1)
25+ bottomMargin: units.gu(1)
26+ spacing: units.gu(1)
27+ clip: true
28+
29+ function getFirstAppId() {
30+ return model.appId(0);
31+ }
32+}
qml/Launcher/Launcher.qml+173−31
@@ -27,10 +27,12 @@ FocusScope {
27 property bool lockedVisible: false
27 property bool lockedVisible: false
28 property bool available: true // can be used to disable all interactions
28 property bool available: true // can be used to disable all interactions
29 property alias inverted: panel.inverted
29 property alias inverted: panel.inverted
30+ property Item blurSource: null
31+ property int topPanelHeight: 0
32+ property bool drawerEnabled: true
30
33
31 property int panelWidth: units.gu(10)
34 property int panelWidth: units.gu(10)
32 property int dragAreaWidth: units.gu(1)
35 property int dragAreaWidth: units.gu(1)
33− property int minimizeDistance: units.gu(26)
34 property real progress: dragArea.dragging && dragArea.touchPosition.x > panelWidth ?
36 property real progress: dragArea.dragging && dragArea.touchPosition.x > panelWidth ?
35 (width * (dragArea.touchPosition.x-panelWidth) / (width - panelWidth)) : 0
37 (width * (dragArea.touchPosition.x-panelWidth) / (width - panelWidth)) : 0
36
38
@@ -43,13 +45,11 @@ FocusScope {
43 readonly property alias shortcutHintsShown: panel.shortcutHintsShown
45 readonly property alias shortcutHintsShown: panel.shortcutHintsShown
44
46
45 readonly property bool shown: panel.x > -panel.width
47 readonly property bool shown: panel.x > -panel.width
48+ readonly property bool drawerShown: drawer.x == 0
46
49
47 // emitted when an application is selected
50 // emitted when an application is selected
48 signal launcherApplicationSelected(string appId)
51 signal launcherApplicationSelected(string appId)
49
52
50− // emitted when the apps dash should be shown because of a swipe gesture
51− signal dash()
52−
53 // emitted when the dash icon in the launcher has been tapped
53 // emitted when the dash icon in the launcher has been tapped
54 signal showDashHome()
54 signal showDashHome()
55
55
@@ -62,6 +62,9 @@ FocusScope {
62 }
62 }
63
63
64 onSuperPressedChanged: {
64 onSuperPressedChanged: {
65+ if (state == "drawer")
66+ return;
67+
65 if (superPressed) {
68 if (superPressed) {
66 superPressTimer.start();
69 superPressTimer.start();
67 superLongPressTimer.start();
70 superLongPressTimer.start();
@@ -132,7 +135,7 @@ FocusScope {
132 }
135 }
133
136
134 function pushEdge(amount) {
137 function pushEdge(amount) {
135− if (root.state === "") {
138+ if (root.state === "" || root.state == "visible" || root.state == "visibleTemporary") {
136 edgeBarrier.push(amount);
139 edgeBarrier.push(amount);
137 }
140 }
138 }
141 }
@@ -143,6 +146,22 @@ FocusScope {
143 switchToNextState("visible")
146 switchToNextState("visible")
144 }
147 }
145
148
149+ function openDrawer(focusInputField) {
150+ if (!drawerEnabled) {
151+ return;
152+ }
153+
154+ panel.shortcutHintsShown = false;
155+ superPressTimer.stop();
156+ superLongPressTimer.stop();
157+ root.focus = true;
158+ drawer.focus = true;
159+ if (focusInputField) {
160+ drawer.focusInput();
161+ }
162+ switchToNextState("drawer")
163+ }
164+
146 Keys.onPressed: {
165 Keys.onPressed: {
147 switch (event.key) {
166 switch (event.key) {
148 case Qt.Key_Backtab:
167 case Qt.Key_Backtab:
@@ -278,7 +297,7 @@ FocusScope {
278 InverseMouseArea {
297 InverseMouseArea {
279 id: closeMouseArea
298 id: closeMouseArea
280 anchors.fill: panel
299 anchors.fill: panel
281− enabled: root.state == "visible" && (!root.lockedVisible || panel.highlightIndex >= -1)
300+ enabled: root.state == "visible" || root.state == "drawer"
282 visible: enabled
301 visible: enabled
283 onPressed: {
302 onPressed: {
284 mouse.accepted = false;
303 mouse.accepted = false;
@@ -307,23 +326,61 @@ FocusScope {
307 }
326 }
308 }
327 }
309
328
310− EdgeBarrier {
329+ BackgroundBlur {
311− id: edgeBarrier
330+ id: backgroundBlur
312− edge: Qt.LeftEdge
331+ anchors.fill: parent
332+ anchors.topMargin: root.inverted ? 0 : -root.topPanelHeight
333+ visible: root.blurSource && drawer.x > -drawer.width
334+ blurAmount: units.gu(6)
335+ sourceItem: root.blurSource
336+ blurRect: Qt.rect(panel.width,
337+ root.topPanelHeight,
338+ drawer.width + drawer.x - panel.width,
339+ height - root.topPanelHeight)
340+ cached: drawer.moving
341+ }
342+
343+ Drawer {
344+ id: drawer
345+ objectName: "drawer"
346+ anchors {
313− target: parent
347+ top: parent.top
314− enabled: root.available
348+ topMargin: root.inverted ? root.topPanelHeight : 0
349+ bottom: parent.bottom
350+ right: parent.left
351+ }
352+ width: Math.min(root.width, units.gu(90)) * .9
353+ panelWidth: panel.width
354+ visible: x > -width
355+
356+ Behavior on anchors.rightMargin {
357+ enabled: !dragArea.dragging && !launcherDragArea.drag.active && panel.animate && !drawer.draggingHorizontally
358+ NumberAnimation {
359+ duration: 300
360+ easing.type: Easing.OutCubic
361+ }
362+ }
363+
364+ onApplicationSelected: {
365+ root.hide();
366+ root.launcherApplicationSelected(appId)
367+ root.focus = false;
368+ }
369+
370+ Keys.onEscapePressed: {
315− onPassed: { root.switchToNextState("visibleTemporary"); }
371+ switchToNextState("");
316− material: Component {
372+ root.focus = false;
317− Item {
373+ }
318− Rectangle {
374+
319− width: parent.height
375+ onDragDistanceChanged: {
320− height: parent.width
376+ anchors.rightMargin = Math.max(-drawer.width, anchors.rightMargin + dragDistance);
321− rotation: -90
377+ }
322− anchors.centerIn: parent
378+ onDraggingHorizontallyChanged: {
323− gradient: Gradient {
379+ if (!draggingHorizontally) {
324− GradientStop { position: 0.0; color: Qt.rgba(panel.color.r, panel.color.g, panel.color.b, .5)}
380+ if (drawer.x < -units.gu(10)) {
325− GradientStop { position: 1.0; color: Qt.rgba(panel.color.r,panel.color.g,panel.color.b,0)}
381+ root.hide();
326− }
382+ } else {
383+ root.openDrawer();
327 }
384 }
328 }
385 }
329 }
386 }
@@ -332,7 +389,7 @@ FocusScope {
332 LauncherPanel {
389 LauncherPanel {
333 id: panel
390 id: panel
334 objectName: "launcherPanel"
391 objectName: "launcherPanel"
335− enabled: root.available && root.state == "visible" || root.state == "visibleTemporary"
392+ enabled: root.available && (root.state == "visible" || root.state == "visibleTemporary" || root.state == "drawer")
336 width: root.panelWidth
393 width: root.panelWidth
337 anchors {
394 anchors {
338 top: parent.top
395 top: parent.top
@@ -394,6 +451,38 @@ FocusScope {
394 }
451 }
395 }
452 }
396
453
454+ EdgeBarrier {
455+ id: edgeBarrier
456+ edge: Qt.LeftEdge
457+ target: parent
458+ enabled: root.available
459+ onProgressChanged: {
460+ if (progress > .5 && root.state != "visibleTemporary" && root.state != "drawer" && root.state != "visible") {
461+ root.switchToNextState("visibleTemporary");
462+ }
463+ }
464+ onPassed: {
465+ if (root.drawerEnabled) {
466+ root.switchToNextState("drawer");
467+ }
468+ }
469+
470+ material: Component {
471+ Item {
472+ Rectangle {
473+ width: parent.height
474+ height: parent.width
475+ rotation: -90
476+ anchors.centerIn: parent
477+ gradient: Gradient {
478+ GradientStop { position: 0.0; color: Qt.rgba(panel.color.r, panel.color.g, panel.color.b, .5)}
479+ GradientStop { position: 1.0; color: Qt.rgba(panel.color.r,panel.color.g,panel.color.b,0)}
480+ }
481+ }
482+ }
483+ }
484+ }
485+
397 SwipeArea {
486 SwipeArea {
398 id: dragArea
487 id: dragArea
399 objectName: "launcherDragArea"
488 objectName: "launcherDragArea"
@@ -405,25 +494,62 @@ FocusScope {
405 width: root.dragAreaWidth
494 width: root.dragAreaWidth
406 height: root.height
495 height: root.height
407
496
497+ function easeInOutCubic(t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 }
498+
499+ property var lastDragPoints: []
500+
501+ function dragDirection() {
502+ if (lastDragPoints.length < 5) {
503+ return "unknown";
504+ }
505+
506+ var toRight = true;
507+ var toLeft = true;
508+ for (var i = lastDragPoints.length - 5; i < lastDragPoints.length; i++) {
509+ if (toRight && lastDragPoints[i] < lastDragPoints[i-1]) {
510+ toRight = false;
511+ }
512+ if (toLeft && lastDragPoints[i] > lastDragPoints[i-1]) {
513+ toLeft = false;
514+ }
515+ }
516+ return toRight ? "right" : toLeft ? "left" : "unknown";
517+ }
518+
408 onDistanceChanged: {
519 onDistanceChanged: {
409− if (!dragging || launcher.state == "visible")
520+ if (dragging && launcher.state != "visible" && launcher.state != "drawer") {
410− return;
521+ panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
522+ }
411
523
524+ if (root.drawerEnabled && dragging && launcher.state != "drawer") {
525+ lastDragPoints.push(distance)
526+ var drawerHintDistance = panel.width + units.gu(1)
527+ if (distance < drawerHintDistance) {
528+ drawer.anchors.rightMargin = -Math.min(Math.max(0, distance), drawer.width);
529+ } else {
412− panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
530+ var linearDrawerX = Math.min(Math.max(0, distance - drawerHintDistance), drawer.width);
531+ var linearDrawerProgress = linearDrawerX / (drawer.width)
532+ var easedDrawerProgress = easeInOutCubic(linearDrawerProgress);
533+ drawer.anchors.rightMargin = -(drawerHintDistance + easedDrawerProgress * (drawer.width - drawerHintDistance));
534+ }
535+ }
413 }
536 }
414
537
415 onDraggingChanged: {
538 onDraggingChanged: {
416 if (!dragging) {
539 if (!dragging) {
417 if (distance > panel.width / 2) {
540 if (distance > panel.width / 2) {
418− root.switchToNextState("visible")
541+ if (root.drawerEnabled && distance > panel.width * 3 && dragDirection() !== "left") {
419− if (distance > minimizeDistance) {
542+ root.switchToNextState("drawer");
420− root.dash();
543+ root.focus = true;
544+ } else {
545+ root.switchToNextState("visible");
421 }
546 }
422 } else if (root.state === "") {
547 } else if (root.state === "") {
423 // didn't drag far enough. rollback
548 // didn't drag far enough. rollback
424− root.switchToNextState("")
549+ root.switchToNextState("");
425 }
550 }
426 }
551 }
552+ lastDragPoints = [];
427 }
553 }
428 }
554 }
429
555
@@ -434,6 +560,10 @@ FocusScope {
434 target: panel
560 target: panel
435 x: -root.panelWidth
561 x: -root.panelWidth
436 }
562 }
563+ PropertyChanges {
564+ target: drawer
565+ anchors.rightMargin: 0
566+ }
437 },
567 },
438 State {
568 State {
439 name: "visible"
569 name: "visible"
@@ -441,6 +571,18 @@ FocusScope {
441 target: panel
571 target: panel
442 x: -root.x // so we never go past panelWidth, even when teased by tutorial
572 x: -root.x // so we never go past panelWidth, even when teased by tutorial
443 }
573 }
574+ PropertyChanges {
575+ target: drawer
576+ anchors.rightMargin: 0
577+ }
578+ },
579+ State {
580+ name: "drawer"
581+ extend: "visible"
582+ PropertyChanges {
583+ target: drawer
584+ anchors.rightMargin: -drawer.width + root.x // so we never go past panelWidth, even when teased by tutorial
585+ }
444 },
586 },
445 State {
587 State {
446 name: "visibleTemporary"
588 name: "visibleTemporary"
qml/Launcher/MoreAppsHeader.qmlnew file+46
@@ -0,0 +1,46 @@
1+import QtQuick 2.4
2+import Ubuntu.Components 1.3
3+
4+AbstractButton {
5+ id: root
6+
7+ onClicked: {
8+ // TODO: Make this point to the snappy store as soon as we stop landing to vivid
9+ Qt.openUrlExternally("scope://com.canonical.scopes.clickstore")
10+ }
11+
12+ UbuntuShape {
13+ width: parent.width
14+ height: parent.height - units.gu(1)
15+ color: "#20ffffff"
16+ aspect: UbuntuShape.Flat
17+
18+ Row {
19+ anchors.fill: parent
20+ anchors.margins: units.gu(1)
21+ spacing: units.gu(1)
22+
23+ Icon {
24+ height: units.gu(2.2)
25+ width: height
26+ name: "stock_application"
27+ anchors.verticalCenter: parent.verticalCenter
28+ color: "white"
29+ }
30+
31+ Label {
32+ text: i18n.tr("More apps in the store")
33+ anchors.verticalCenter: parent.verticalCenter
34+ fontSize: "small"
35+ }
36+
37+ Icon {
38+ height: units.gu(2.5)
39+ width: height
40+ anchors.verticalCenter: parent.verticalCenter
41+ name: "go-next"
42+ color: "white"
43+ }
44+ }
45+ }
46+}
qml/Shell.qml+10−34
@@ -278,8 +278,6 @@ StyledItem {
278
278
279 dragAreaWidth: shell.edgeSize
279 dragAreaWidth: shell.edgeSize
280 background: wallpaperResolver.background
280 background: wallpaperResolver.background
281− leftEdgeDragProgress: !greeter || greeter.locked || !tutorial.launcherLongSwipeEnabled ? 0 :
282− Math.max(0, (launcher.dragDistance * (stage.width - launcher.panelWidth) / stage.width) - launcher.panelWidth)
283
281
284 applicationManager: ApplicationManager
282 applicationManager: ApplicationManager
285 topLevelSurfaceList: topLevelSurfaceList
283 topLevelSurfaceList: topLevelSurfaceList
@@ -301,14 +299,12 @@ StyledItem {
301
299
302 interactive: (!greeter || !greeter.shown)
300 interactive: (!greeter || !greeter.shown)
303 && panel.indicators.fullyClosed
301 && panel.indicators.fullyClosed
304− && launcher.progress == 0
305 && !notifications.useModal
302 && !notifications.useModal
306
303
307 onInteractiveChanged: { if (interactive) { focus = true; } }
304 onInteractiveChanged: { if (interactive) { focus = true; } }
308
305
309 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
306 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
310 suspended: greeter.shown
307 suspended: greeter.shown
311− keepDashRunning: launcher.shown || launcher.dashSwipe
312 altTabPressed: physicalKeysMapper.altTabPressed
308 altTabPressed: physicalKeysMapper.altTabPressed
313 oskEnabled: shell.oskEnabled
309 oskEnabled: shell.oskEnabled
314 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
310 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
@@ -323,7 +319,7 @@ StyledItem {
323 topMargin: panel.panelHeight
319 topMargin: panel.panelHeight
324 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
320 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
325 }
321 }
326− z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
322+ z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
327 }
323 }
328
324
329 Loader {
325 Loader {
@@ -345,7 +341,6 @@ StyledItem {
345 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
341 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
346 hides: [launcher, panel.indicators]
342 hides: [launcher, panel.indicators]
347 tabletMode: shell.usageScenario != "phone"
343 tabletMode: shell.usageScenario != "phone"
348− launcherOffset: launcher.progress
349 forcedUnlock: wizard.active || shell.mode === "full-shell"
344 forcedUnlock: wizard.active || shell.mode === "full-shell"
350 background: wallpaperResolver.cachedBackground
345 background: wallpaperResolver.cachedBackground
351 hasCustomBackground: wallpaperResolver.hasCustomBackground
346 hasCustomBackground: wallpaperResolver.hasCustomBackground
@@ -434,19 +429,6 @@ StyledItem {
434 }
429 }
435 }
430 }
436
431
437− function showDash() {
438− if (greeter.notifyShowingDashFromDrag()) {
439− launcher.fadeOut();
440− }
441−
442− if (!greeter.locked && tutorial.launcherLongSwipeEnabled
443− && (ApplicationManager.focusedApplicationId != "unity8-dash" || stage.spreadShown)) {
444− ApplicationManager.requestFocusApplication("unity8-dash")
445− launcher.fadeOut();
446− stage.closeSpread();
447− }
448− }
449−
450 Item {
432 Item {
451 id: overlay
433 id: overlay
452 z: 10
434 z: 10
@@ -497,15 +479,6 @@ StyledItem {
497 id: launcher
479 id: launcher
498 objectName: "launcher"
480 objectName: "launcher"
499
481
500− /*
501− * Since the Dash doesn't have the same controll over surfaces that the
502− * Shell does, it can't slowly move the scope out of the way, as the shell
503− * does with apps, and the dash is show instantly. This allows for some
504− * leeway and prevents accidental home swipes.
505− */
506− readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
507− readonly property bool dashSwipe: progress > offset
508−
509 anchors.top: parent.top
482 anchors.top: parent.top
510 anchors.topMargin: inverted ? 0 : panel.panelHeight
483 anchors.topMargin: inverted ? 0 : panel.panelHeight
511 anchors.bottom: parent.bottom
484 anchors.bottom: parent.bottom
@@ -521,14 +494,11 @@ StyledItem {
521 superTabPressed: physicalKeysMapper.superTabPressed
494 superTabPressed: physicalKeysMapper.superTabPressed
522 panelWidth: units.gu(settings.launcherWidth)
495 panelWidth: units.gu(settings.launcherWidth)
523 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
496 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
497+ blurSource: greeter.shown ? greeter : stages
498+ topPanelHeight: panel.panelHeight
499+ drawerEnabled: !greeter.shown
524
500
525 onShowDashHome: showHome()
501 onShowDashHome: showHome()
526− onDash: showDash()
527− onDashSwipeChanged: {
528− if (dashSwipe) {
529− dash.setCurrentScope(0, false, true)
530− }
531− }
532 onLauncherApplicationSelected: {
502 onLauncherApplicationSelected: {
533 greeter.notifyUserRequestedApp();
503 greeter.notifyUserRequestedApp();
534 shell.activateApplication(appId);
504 shell.activateApplication(appId);
@@ -544,6 +514,12 @@ StyledItem {
544 }
514 }
545 }
515 }
546
516
517+ GlobalShortcut {
518+ shortcut: Qt.MetaModifier | Qt.Key_A
519+ onTriggered: {
520+ launcher.openDrawer(true);
521+ }
522+ }
547 GlobalShortcut {
523 GlobalShortcut {
548 shortcut: Qt.AltModifier | Qt.Key_F1
524 shortcut: Qt.AltModifier | Qt.Key_F1
549 onTriggered: {
525 onTriggered: {
qml/Stage/Stage.qml+3−6
@@ -36,7 +36,6 @@ FocusScope {
36 property url background
36 property url background
37 property int dragAreaWidth
37 property int dragAreaWidth
38 property bool interactive
38 property bool interactive
39− property bool keepDashRunning: true
40 property real nativeHeight
39 property real nativeHeight
41 property real nativeWidth
40 property real nativeWidth
42 property QtObject orientations
41 property QtObject orientations
@@ -50,7 +49,6 @@ FocusScope {
50
49
51 // Configuration
50 // Configuration
52 property string mode: "staged"
51 property string mode: "staged"
53− property real leftEdgeDragProgress: 0
54
52
55 // Used by the tutorial code
53 // Used by the tutorial code
56 readonly property real rightEdgeDragProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0 // How far left the stage has been dragged
54 readonly property real rightEdgeDragProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0 // How far left the stage has been dragged
@@ -415,7 +413,7 @@ FocusScope {
415 // in staged mode, when it switches to Windowed mode it will suddenly
413 // in staged mode, when it switches to Windowed mode it will suddenly
416 // resume all those apps at once. We might want to avoid that.
414 // resume all those apps at once. We might want to avoid that.
417 value: root.mode === "windowed"
415 value: root.mode === "windowed"
418− || (isDash && root.keepDashRunning)
416+ || isDash
419 || (!root.suspended && model.application && priv.focusedAppDelegate &&
417 || (!root.suspended && model.application && priv.focusedAppDelegate &&
420 (priv.focusedAppDelegate.appId === model.application.appId ||
418 (priv.focusedAppDelegate.appId === model.application.appId ||
421 priv.mainStageAppId === model.application.appId ||
419 priv.mainStageAppId === model.application.appId ||
@@ -478,7 +476,7 @@ FocusScope {
478 visible: true
476 visible: true
479 blurRadius: 32
477 blurRadius: 32
480 brightness: .65
478 brightness: .65
481− opacity: MathUtils.linearAnimation(spreadItem.rightEdgeBreakPoint, 1, 0, 1, Math.max(rightEdgeDragArea.progress, edgeBarrier.progress))
479+ opacity: MathUtils.linearAnimation(spreadItem.rightEdgeBreakPoint, 1, 0, 1, Math.max(rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0, edgeBarrier.progress))
482 }
480 }
483 },
481 },
484 State {
482 State {
@@ -1061,7 +1059,6 @@ FocusScope {
1061 sideStageX: sideStage.x
1059 sideStageX: sideStage.x
1062 itemIndex: appDelegate.itemIndex
1060 itemIndex: appDelegate.itemIndex
1063 nextInStack: priv.nextInStack
1061 nextInStack: priv.nextInStack
1064− leftEdgeDragProgress: root.leftEdgeDragProgress
1065 }
1062 }
1066
1063
1067 StagedRightEdgeMaths {
1064 StagedRightEdgeMaths {
@@ -1164,7 +1161,7 @@ FocusScope {
1164 when: root.mode == "windowed" && (root.state == "windowedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running || edgeBarrier.progress > 0)
1161 when: root.mode == "windowed" && (root.state == "windowedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running || edgeBarrier.progress > 0)
1165 PropertyChanges {
1162 PropertyChanges {
1166 target: windowedRightEdgeMaths
1163 target: windowedRightEdgeMaths
1167− progress: Math.max(rightEdgeDragArea.progress, edgeBarrier.progress)
1164+ progress: Math.max(rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0, edgeBarrier.progress)
1168 }
1165 }
1169 PropertyChanges {
1166 PropertyChanges {
1170 target: appDelegate
1167 target: appDelegate
qml/Stage/StageMaths.qml+2−7
@@ -12,7 +12,6 @@ QtObject {
12 property int sideStageWidth: 0
12 property int sideStageWidth: 0
13 property int sideStageX: sceneWidth
13 property int sideStageX: sceneWidth
14 property bool animateX: false
14 property bool animateX: false
15− property int leftEdgeDragProgress: 0
16
15
17 property int stage: ApplicationInfoInterface.MainStage
16 property int stage: ApplicationInfoInterface.MainStage
18 property var thisDelegate: null
17 property var thisDelegate: null
@@ -26,10 +25,6 @@ QtObject {
26 // of the last focused order.
25 // of the last focused order.
27 readonly property int itemZ: {
26 readonly property int itemZ: {
28 // only shuffle when we've got a main and side stage
27 // only shuffle when we've got a main and side stage
29− if (thisDelegate.isDash && thisDelegate != mainStageDelegate && leftEdgeDragProgress > 0) {
30− return -1; // Keep the dash behind all other apps for the left edge gesture
31− }
32−
33 if (!sideStageDelegate) return itemIndex;
28 if (!sideStageDelegate) return itemIndex;
34
29
35 // don't shuffle indexes greater than "actives or next"
30 // don't shuffle indexes greater than "actives or next"
@@ -64,12 +59,12 @@ QtObject {
64
59
65 property int itemX: {
60 property int itemX: {
66 if (mainStageDelegate == thisDelegate) {
61 if (mainStageDelegate == thisDelegate) {
67− return thisDelegate.isDash ? 0 : leftEdgeDragProgress;
62+ return 0
68 }
63 }
69 if (sideStageDelegate == thisDelegate) {
64 if (sideStageDelegate == thisDelegate) {
70 return sideStageX;
65 return sideStageX;
71 }
66 }
72− return thisDelegate.isDash && leftEdgeDragProgress > 0 ? 0 : sceneWidth;
67+ return sceneWidth;
73 }
68 }
74 Behavior on itemX { enabled: root.animateX; UbuntuNumberAnimation {} }
69 Behavior on itemX { enabled: root.animateX; UbuntuNumberAnimation {} }
75
70
qml/Tutorial/TutorialLeftLong.qml+1−1
@@ -25,7 +25,7 @@ TutorialPage {
25
25
26 // Unlike other tutorials, this one can't be skipped before we show it, so
26 // Unlike other tutorials, this one can't be skipped before we show it, so
27 // only set opacityOverride if we're already shown.
27 // only set opacityOverride if we're already shown.
28− opacityOverride: shown ? 1 - launcher.dragDistance / launcher.minimizeDistance : 1
28+ opacityOverride: shown ? 1 - launcher.dragDistance / (launcher.panelWidth * 3) : 1
29
29
30 mouseArea {
30 mouseArea {
31 anchors.leftMargin: launcher.dragAreaWidth
31 anchors.leftMargin: launcher.dragAreaWidth
tests/mocks/Unity/Launcher/CMakeLists.txt+5−1
@@ -1,7 +1,8 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=10)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=11)
2
2
3include_directories(
3include_directories(
4 ${CMAKE_CURRENT_SOURCE_DIR}
4 ${CMAKE_CURRENT_SOURCE_DIR}
5+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/
5)
6)
6
7
7set(MockLauncherModel_SOURCES
8set(MockLauncherModel_SOURCES
@@ -9,10 +10,13 @@ set(MockLauncherModel_SOURCES
9 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
10 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
10 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
11 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
11 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
12 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
13+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/AppDrawerModelInterface.h
12 plugin.cpp
14 plugin.cpp
13 MockLauncherModel.cpp
15 MockLauncherModel.cpp
14 MockLauncherItem.cpp
16 MockLauncherItem.cpp
15 MockQuickListModel.cpp
17 MockQuickListModel.cpp
18+ MockAppDrawerModel.cpp
19+ ${CMAKE_SOURCE_DIR}/plugins/Utils/applicationsfiltermodel.cpp
16)
20)
17
21
18add_library(MockLauncherPlugin MODULE ${MockLauncherModel_SOURCES})
22add_library(MockLauncherPlugin MODULE ${MockLauncherModel_SOURCES})
tests/mocks/Unity/Launcher/MockAppDrawerModel.cppnew file+75
@@ -0,0 +1,75 @@
1+/*
2+ * Copyright (C) 2016 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 "MockAppDrawerModel.h"
18+
19+#include <QDebug>
20+#include <QDateTime>
21+
22+MockAppDrawerModel::MockAppDrawerModel(QObject *parent):
23+ AppDrawerModelInterface(parent)
24+{
25+ MockLauncherItem *item = new MockLauncherItem("dialer-app", "/usr/share/applications/dialer-app.desktop", "Dialer", "dialer-app", this);
26+ m_list.append(item);
27+ item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
28+ m_list.append(item);
29+ item = new MockLauncherItem("camera-app2", "/usr/share/applications/camera-app2.desktop", "Camera2", "camera", this);
30+ m_list.append(item);
31+ item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
32+ m_list.append(item);
33+ item = new MockLauncherItem("music-app", "/usr/share/applications/music-app.desktop", "Music", "soundcloud", this);
34+ m_list.append(item);
35+ item = new MockLauncherItem("facebook-webapp", "/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook", this);
36+ m_list.append(item);
37+ item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
38+ m_list.append(item);
39+ item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
40+ m_list.append(item);
41+ item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
42+ m_list.append(item);
43+ item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
44+ m_list.append(item);
45+ item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
46+ m_list.append(item);
47+ item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
48+ m_list.append(item);
49+ item = new MockLauncherItem("libreoffice", "/usr/share/applications/libreoffice.desktop","Libre Office", "libreoffice", this);
50+ m_list.append(item);
51+ qDebug() << "mock model created";
52+
53+ qsrand(QDateTime::currentMSecsSinceEpoch() / 1000);
54+}
55+
56+int MockAppDrawerModel::rowCount(const QModelIndex &parent) const
57+{
58+ return m_list.count();
59+}
60+
61+QVariant MockAppDrawerModel::data(const QModelIndex &index, int role) const
62+{
63+ switch (role) {
64+ case RoleAppId:
65+ return m_list.at(index.row())->appId();
66+ case RoleName:
67+ return m_list.at(index.row())->name();
68+ case RoleIcon:
69+ return m_list.at(index.row())->icon();
70+ case RoleUsage:
71+ return qrand();
72+ }
73+
74+ return QVariant();
75+}
tests/mocks/Unity/Launcher/MockAppDrawerModel.hnew file+32
@@ -0,0 +1,32 @@
1+/*
2+ * Copyright (C) 2016 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 <unity/shell/launcher/AppDrawerModelInterface.h>
18+
19+#include "MockLauncherItem.h"
20+
21+class MockAppDrawerModel: public AppDrawerModelInterface
22+{
23+ Q_OBJECT
24+public:
25+ MockAppDrawerModel(QObject* parent = nullptr);
26+
27+ int rowCount(const QModelIndex &parent) const override;
28+ QVariant data(const QModelIndex &index, int role) const override;
29+
30+private:
31+ QList<MockLauncherItem*> m_list;
32+};
tests/mocks/Unity/Launcher/MockLauncherItem.cpp+13
@@ -67,6 +67,11 @@ QString MockLauncherItem::icon() const
67 return m_icon;
67 return m_icon;
68}
68}
69
69
70+QStringList MockLauncherItem::keywords() const
71+{
72+ return m_keywords;
73+}
74+
70bool MockLauncherItem::pinned() const
75bool MockLauncherItem::pinned() const
71{
76{
72 return m_pinned;
77 return m_pinned;
@@ -200,3 +205,11 @@ unity::shell::launcher::QuickListModelInterface *MockLauncherItem::quickList() c
200{
205{
201 return m_quickList;
206 return m_quickList;
202}
207}
208+
209+void MockLauncherItem::setKeywords(const QStringList &keywords)
210+{
211+ if (m_keywords != keywords) {
212+ m_keywords = keywords;
213+ Q_EMIT keywordsChanged(keywords);
214+ }
215+}
tests/mocks/Unity/Launcher/MockLauncherItem.h+6
@@ -22,6 +22,8 @@
22
22
23#include <unity/shell/launcher/LauncherItemInterface.h>
23#include <unity/shell/launcher/LauncherItemInterface.h>
24
24
25+#include <QStringList>
26+
25class MockQuickListModel;
27class MockQuickListModel;
26
28
27using namespace unity::shell::launcher;
29using namespace unity::shell::launcher;
@@ -37,6 +39,7 @@ public:
37 QString desktopFile() const;
39 QString desktopFile() const;
38 QString name() const override;
40 QString name() const override;
39 QString icon() const override;
41 QString icon() const override;
42+ QStringList keywords() const override;
40
43
41 bool pinned() const override;
44 bool pinned() const override;
42 bool running() const override;
45 bool running() const override;
@@ -51,6 +54,7 @@ public:
51 unity::shell::launcher::QuickListModelInterface *quickList() const override;
54 unity::shell::launcher::QuickListModelInterface *quickList() const override;
52
55
53private:
56private:
57+ void setKeywords(const QStringList &keywords);
54 void setPinned(bool pinned);
58 void setPinned(bool pinned);
55 void setRunning(bool running);
59 void setRunning(bool running);
56 void setRecent(bool recent);
60 void setRecent(bool recent);
@@ -65,6 +69,7 @@ private:
65 QString m_desktopFile;
69 QString m_desktopFile;
66 QString m_name;
70 QString m_name;
67 QString m_icon;
71 QString m_icon;
72+ QStringList m_keywords;
68 bool m_pinned;
73 bool m_pinned;
69 bool m_running;
74 bool m_running;
70 bool m_recent;
75 bool m_recent;
@@ -77,6 +82,7 @@ private:
77 MockQuickListModel *m_quickList;
82 MockQuickListModel *m_quickList;
78
83
79 friend class MockLauncherModel;
84 friend class MockLauncherModel;
85+ friend class MockAppDrawerModel;
80};
86};
81
87
82#endif // MOCKLAUNCHERITEM_H
88#endif // MOCKLAUNCHERITEM_H
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+3
@@ -36,6 +36,9 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
36 item = new MockLauncherItem("camera-app2", "/usr/share/applications/camera-app2.desktop", "Camera2", "camera", this);
36 item = new MockLauncherItem("camera-app2", "/usr/share/applications/camera-app2.desktop", "Camera2", "camera", this);
37 item->setPinned(true);
37 item->setPinned(true);
38 m_list.append(item);
38 m_list.append(item);
39+ item = new MockLauncherItem("camera-app3", "/usr/share/applications/camera-app2.desktop", "Camera2", "camera", this);
40+ item->setPinned(true);
41+ m_list.append(item);
39 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
42 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
40 item->setProgress(50);
43 item->setProgress(50);
41 item->setCountVisible(true);
44 item->setCountVisible(true);
tests/mocks/Unity/Launcher/plugin.cpp+4
@@ -21,9 +21,11 @@
21#include "MockLauncherModel.h"
21#include "MockLauncherModel.h"
22#include "MockLauncherItem.h"
22#include "MockLauncherItem.h"
23#include "MockQuickListModel.h"
23#include "MockQuickListModel.h"
24+#include "MockAppDrawerModel.h"
24
25
25#include <unity/shell/launcher/LauncherModelInterface.h>
26#include <unity/shell/launcher/LauncherModelInterface.h>
26#include <unity/shell/launcher/LauncherItemInterface.h>
27#include <unity/shell/launcher/LauncherItemInterface.h>
28+#include <unity/shell/launcher/AppDrawerModelInterface.h>
27
29
28#include <QtQml/qqml.h>
30#include <QtQml/qqml.h>
29
31
@@ -41,8 +43,10 @@ void TestLauncherPlugin::registerTypes(const char* uri)
41 qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
43 qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
42 qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
44 qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
43 qmlRegisterUncreatableType<QuickListModelInterface>(uri, 0, 1, "QuickListModelInterface", "Abstract Interface. Cannot be instantiated.");
45 qmlRegisterUncreatableType<QuickListModelInterface>(uri, 0, 1, "QuickListModelInterface", "Abstract Interface. Cannot be instantiated.");
46+ qmlRegisterUncreatableType<AppDrawerModelInterface>(uri, 0, 1, "AppDrawerModelInterface", "Abstract Interface. Cannot be instantiated.");
44
47
45 qmlRegisterSingletonType<MockLauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
48 qmlRegisterSingletonType<MockLauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
46 qmlRegisterUncreatableType<MockLauncherItem>(uri, 0, 1, "LauncherItem", "Can't create LauncherItems in QML. Get them from the LauncherModel");
49 qmlRegisterUncreatableType<MockLauncherItem>(uri, 0, 1, "LauncherItem", "Can't create LauncherItems in QML. Get them from the LauncherModel");
47 qmlRegisterUncreatableType<MockQuickListModel>(uri, 0, 1, "QuickListModel", "Can't create QuickLists in QML. Get them from the LauncherItems");
50 qmlRegisterUncreatableType<MockQuickListModel>(uri, 0, 1, "QuickListModel", "Can't create QuickLists in QML. Get them from the LauncherItems");
51+ qmlRegisterType<MockAppDrawerModel>(uri, 0, 1, "AppDrawerModel");
48}
52}
tests/mocks/Utils/CMakeLists.txt+4
@@ -24,11 +24,15 @@ set(QMLPLUGIN_SRC
24 ${CMAKE_SOURCE_DIR}/plugins/Utils/inputeventgenerator.cpp
24 ${CMAKE_SOURCE_DIR}/plugins/Utils/inputeventgenerator.cpp
25 ${CMAKE_SOURCE_DIR}/plugins/Utils/deviceconfigparser.cpp
25 ${CMAKE_SOURCE_DIR}/plugins/Utils/deviceconfigparser.cpp
26 ${CMAKE_SOURCE_DIR}/plugins/Utils/globalfunctions.cpp
26 ${CMAKE_SOURCE_DIR}/plugins/Utils/globalfunctions.cpp
27+ ${CMAKE_SOURCE_DIR}/plugins/Utils/appdrawerproxymodel.cpp
27 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
28 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
28 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
29 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
29 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
30 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
30 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
31 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
31 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
32 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
33+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/AppDrawerModelInterface.h
34+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
35+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
32 constants.cpp
36 constants.cpp
33 plugin.cpp
37 plugin.cpp
34 windowstatestorage.cpp
38 windowstatestorage.cpp
tests/mocks/Utils/plugin.cpp+2
@@ -40,6 +40,7 @@
40#include <inputeventgenerator.h>
40#include <inputeventgenerator.h>
41#include <deviceconfigparser.h>
41#include <deviceconfigparser.h>
42#include <globalfunctions.h>
42#include <globalfunctions.h>
43+#include <appdrawerproxymodel.h>
43
44
44static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
45static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
45{
46{
@@ -82,4 +83,5 @@ void FakeUtilsPlugin::registerTypes(const char *uri)
82 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
83 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
83 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
84 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
84 qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher");
85 qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher");
86+ qmlRegisterType<AppDrawerProxyModel>(uri, 0, 1, "AppDrawerProxyModel");
85}
87}
tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=10)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=11)
2
2
3include_directories(
3include_directories(
4 ${CMAKE_CURRENT_SOURCE_DIR}
4 ${CMAKE_CURRENT_SOURCE_DIR}
tests/plugins/Unity/Launcher/CMakeLists.txt+2−1
@@ -1,5 +1,5 @@
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=10)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=11)
3
3
4include_directories(
4include_directories(
5 ${CMAKE_CURRENT_SOURCE_DIR}
5 ${CMAKE_CURRENT_SOURCE_DIR}
@@ -30,6 +30,7 @@ add_executable(launchermodeltestExec
30 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistmodel.cpp
30 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistmodel.cpp
31 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/dbusinterface.cpp
31 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/dbusinterface.cpp
32 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistentry.cpp
32 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistentry.cpp
33+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/ualwrapper.cpp
33 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
34 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
34 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
35 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
35 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
36 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
tests/qmltests/CMakeLists.txt+1
@@ -71,6 +71,7 @@ add_unity8_qmltest(Greeter Infographics)
71add_unity8_qmltest(Greeter NarrowView)
71add_unity8_qmltest(Greeter NarrowView)
72add_unity8_qmltest(Greeter WideView)
72add_unity8_qmltest(Greeter WideView)
73add_unity8_qmltest(Launcher Launcher)
73add_unity8_qmltest(Launcher Launcher)
74+add_unity8_qmltest(Launcher Drawer)
74add_unity8_qmltest(Notifications Notifications)
75add_unity8_qmltest(Notifications Notifications)
75add_unity8_qmltest(Notifications VisualSnapDecisionsQueue)
76add_unity8_qmltest(Notifications VisualSnapDecisionsQueue)
76add_unity8_qmltest(Notifications OptionToggle)
77add_unity8_qmltest(Notifications OptionToggle)
tests/qmltests/Dash/tst_DashShell.qml−29
@@ -112,34 +112,5 @@ Item {
112 mouseClick(buttonShowDashHome);
112 mouseClick(buttonShowDashHome);
113 tryCompare(dashContentList, "currentIndex", 0);
113 tryCompare(dashContentList, "currentIndex", 0);
114 }
114 }
115−
116− function test_setShortLauncherMoveResetNonActiveDash() {
117− var dashContentList = findChild(dash, "dashContentList");
118− var startX = dash.width - units.gu(1);
119− var startY = dash.height / 2;
120− var stopX = units.gu(1)
121− var stopY = startY;
122− waitForRendering(dashContentList);
123− touchFlick(dash, startX, startY, stopX, stopY);
124− touchFlick(dash, startX, startY, stopX, stopY);
125− compare(dashContentList.currentIndex, 2, "Could not flick to scope id 2");
126−
127− // Pretend the dash is not active
128− dash.windowActive = false;
129−
130− // Flick the greeter away
131− var startX = shell.width - units.gu(1);
132− var startY = shell.height / 2;
133− var stopX = units.gu(1)
134− var stopY = startY;
135− touchFlick(shell, startX, startY, stopX, stopY);
136−
137− var greeter = findChild(shell, "greeter");
138− tryCompare(greeter, "shown", false);
139−
140− // Now do a small launcher movement
141− touchFlick(shell, stopX, startY, startX, stopY);
142− compare(dashContentList.currentIndex, 0, "Small launcher move did not reset dash on non active window");
143− }
144 }
115 }
145}
116}
tests/qmltests/Launcher/tst_Drawer.qmlnew file+264
@@ -0,0 +1,264 @@
1+/*
2+ * Copyright 2013-2016 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.4
18+import QtQuick.Layouts 1.1
19+import Ubuntu.Components 1.3
20+import ".."
21+import "../../../qml/Launcher"
22+import Unity.Launcher 0.1
23+import Utils 0.1 // For EdgeBarrierSettings
24+import Unity.Test 0.1
25+
26+StyledItem {
27+ id: root
28+ theme.name: "Ubuntu.Components.Themes.SuruDark"
29+
30+ width: units.gu(140)
31+ height: units.gu(70)
32+ Rectangle {
33+ anchors.fill: parent
34+ color: UbuntuColors.graphite // something neither white nor black
35+ }
36+
37+ Launcher {
38+ id: launcher
39+ x: 0
40+ y: 0
41+ width: units.gu(40)
42+ height: root.height
43+
44+ lockedVisible: lockedVisibleCheckBox.checked
45+
46+ property string lastSelectedApplication
47+ onLauncherApplicationSelected: {
48+ lastSelectedApplication = appId
49+ }
50+
51+ Component.onCompleted: {
52+ launcher.focus = true
53+ edgeBarrierControls.target = testCase.findChild(this, "edgeBarrierController");
54+ }
55+ }
56+
57+ ColumnLayout {
58+ anchors { bottom: parent.bottom; right: parent.right; margins: units.gu(1) }
59+ spacing: units.gu(1)
60+ width: childrenRect.width
61+
62+ RowLayout {
63+ CheckBox {
64+ id: lockedVisibleCheckBox
65+ checked: false
66+ }
67+ Label {
68+ text: "Launcher always visible"
69+ }
70+ }
71+
72+ Slider {
73+ id: widthSlider
74+ Layout.fillWidth: true
75+ minimumValue: 6
76+ maximumValue: 12
77+ value: 10
78+ }
79+
80+ MouseTouchEmulationCheckbox {}
81+
82+ EdgeBarrierControls {
83+ id: edgeBarrierControls
84+ text: "Drag here to pull out launcher"
85+ onDragged: { launcher.pushEdge(amount); }
86+ }
87+ }
88+
89+ UnityTestCase {
90+ id: testCase
91+ when: windowShown
92+ name: "Drawer"
93+
94+ function dragDrawerIntoView() {
95+ var startX = launcher.dragAreaWidth/2;
96+ var startY = launcher.height/2;
97+ touchFlick(launcher,
98+ startX, startY,
99+ startX+units.gu(35), startY);
100+
101+ var drawer = findChild(launcher, "drawer");
102+ verify(!!drawer);
103+
104+ // wait until it gets fully extended
105+ // a tryCompare doesn't work since
106+ // compare(-0.000005917593600024418, 0);
107+ // is true and in this case we want exactly 0 or will have pain later on
108+ tryCompareFunction( function(){ return drawer.x === 0; }, true );
109+ tryCompare(launcher, "state", "drawer");
110+ tryCompare(launcher, "drawerShown", true);
111+ }
112+
113+ function revealByEdgePush() {
114+ // Place the mouse against the window/screen edge and push beyond the barrier threshold
115+ // If the mouse did not move between two revealByEdgePush(), we need it to move out the
116+ // area to make sure we're not just accumulating to the previous push
117+ mouseMove(root, root.width, root.height / 2);
118+ mouseMove(root, 1, root.height / 2);
119+
120+ launcher.pushEdge(EdgeBarrierSettings.pushThreshold * 1.1);
121+
122+ var drawer = findChild(launcher, "drawer");
123+ verify(!!drawer);
124+
125+ // wait until it gets fully extended
126+ tryCompare(drawer, "x", 0);
127+ tryCompare(launcher, "state", "drawer");
128+ tryCompare(launcher, "drawerShown", true);
129+ }
130+
131+ function init() {
132+ launcher.lastSelectedApplication = "";
133+ launcher.lockedVisible = false;
134+ launcher.hide();
135+ var drawer = findChild(launcher, "drawer");
136+ tryCompare(drawer, "x", -drawer.width);
137+ }
138+
139+ function test_revealByEdgeDrag() {
140+ dragDrawerIntoView();
141+ }
142+
143+ function test_revealByEdgePush_data() {
144+ return [
145+ { tag: "autohide launcher", autohide: true },
146+ { tag: "locked launcher", autohide: false }
147+ ]
148+ }
149+
150+ function test_revealByEdgePush(data) {
151+ launcher.lockedVisible = !data.autohide;
152+
153+ var panel = findChild(launcher, "launcherPanel");
154+ tryCompare(panel, "x", data.autohide ? -panel.width : 0);
155+ tryCompare(launcher, "state", data.autohide ? "" : "visible");
156+ waitForRendering(launcher)
157+
158+ revealByEdgePush();
159+ }
160+
161+ function test_hideByDraggingDrawer_data() {
162+ return [
163+ {tag: "autohide", autohide: true, endState: ""},
164+ {tag: "locked", autohide: false, endState: "visible"}
165+ ]
166+ }
167+
168+ function test_hideByDraggingDrawer(data) {
169+ launcher.lockedVisible = !data.autohide;
170+
171+ dragDrawerIntoView();
172+ waitForRendering(launcher);
173+
174+ var drawer = findChild(launcher, "drawer");
175+
176+ mouseFlick(root, drawer.width - units.gu(1), drawer.height / 2, units.gu(2), drawer.height / 2, true, true);
177+
178+ tryCompare(drawer.anchors, "rightMargin", 0);
179+ tryCompare(launcher, "state", data.endState);
180+ launcher.lockedVisible = false;
181+ }
182+
183+ function test_hideByClickingOutside() {
184+ dragDrawerIntoView();
185+
186+ var drawer = findChild(launcher, "drawer");
187+
188+ mouseClick(root, drawer.width + units.gu(1), root.height / 2);
189+
190+ tryCompare(launcher, "state", "");
191+ }
192+
193+ function test_launchAppFromDrawer() {
194+ dragDrawerIntoView();
195+
196+ var drawerList = findChild(launcher, "drawerItemList");
197+ var dialerApp = findChild(drawerList, "drawerItem_dialer-app");
198+ mouseClick(dialerApp, dialerApp.width / 2, dialerApp.height / 2);
199+
200+ tryCompare(launcher, "lastSelectedApplication", "dialer-app");
201+
202+ tryCompare(launcher, "state", "");
203+ }
204+
205+ function test_launcherGivesUpFocusAfterLaunchingFromDrawer() {
206+ dragDrawerIntoView();
207+
208+ tryCompare(launcher, "focus", true);
209+
210+ var drawerList = findChild(launcher, "drawerItemList");
211+ var dialerApp = findChild(drawerList, "drawerItem_dialer-app");
212+ mouseClick(dialerApp, dialerApp.width / 2, dialerApp.height / 2);
213+
214+ tryCompare(launcher, "focus", false);
215+ }
216+
217+ function test_drawerDisabled() {
218+ launcher.drawerEnabled = false;
219+
220+ var startX = launcher.dragAreaWidth/2;
221+ var startY = launcher.height/2;
222+ touchFlick(launcher,
223+ startX, startY,
224+ startX+units.gu(35), startY);
225+
226+ var drawer = findChild(launcher, "drawer");
227+ verify(!!drawer);
228+
229+ tryCompare(launcher, "state", "visible");
230+ tryCompare(launcher, "drawerShown", false);
231+
232+ launcher.drawerEnabled = true;
233+ }
234+
235+ function test_search() {
236+ compare(launcher.lastSelectedApplication, "");
237+
238+ launcher.openDrawer(true)
239+ typeString("cam");
240+ keyClick(Qt.Key_Enter);
241+
242+ compare(launcher.lastSelectedApplication, "camera-app");
243+ }
244+
245+ function test_dragDirectionOnLeftEdgeDrag_data() {
246+ return [
247+ { tag: "reveal", direction: "right", endState: "drawer" },
248+ { tag: "cancel", direction: "left", endState: "visible" },
249+ ]
250+ }
251+
252+ function test_dragDirectionOnLeftEdgeDrag(data) {
253+ var startX = launcher.dragAreaWidth/2;
254+ var startY = launcher.height/2;
255+ var stopX = startX + units.gu(35);
256+ var endX = stopX + (units.gu(4) * (data.direction === "left" ? -1 : 1))
257+
258+ touchFlick(launcher, startX, startY, stopX, startY, true, false);
259+ touchFlick(launcher, stopX, startY, endX, startY, false, true);
260+
261+ tryCompare(launcher, "state", data.endState)
262+ }
263+ }
264+}
tests/qmltests/Launcher/tst_Launcher.qml+1−1
@@ -322,7 +322,7 @@ Rectangle {
322 function revealByEdgePush() {
322 function revealByEdgePush() {
323 // Place the mouse against the window/screen edge and push beyond the barrier threshold
323 // Place the mouse against the window/screen edge and push beyond the barrier threshold
324 mouseMove(root, 1, root.height / 2);
324 mouseMove(root, 1, root.height / 2);
325− launcher.pushEdge(EdgeBarrierSettings.pushThreshold * 1.1);
325+ launcher.pushEdge(EdgeBarrierSettings.pushThreshold * .6);
326
326
327 var panel = findChild(launcher, "launcherPanel");
327 var panel = findChild(launcher, "launcherPanel");
328 verify(!!panel);
328 verify(!!panel);
tests/qmltests/Stage/tst_PhoneStage.qml−26
@@ -334,32 +334,6 @@ Item {
334 tryCompare(firstApp, "focused", true);
334 tryCompare(firstApp, "focused", true);
335 }
335 }
336
336
337− function test_leftEdge_data() {
338− return [
339− { tag: "normal", inSpread: false, leftEdgeDragWidth: units.gu(20), shouldMoveApp: true },
340− { tag: "inSpread", inSpread: true, leftEdgeDragWidth: units.gu(5), shouldMoveApp: false }
341− ]
342− }
343−
344− function test_leftEdge(data) {
345− addApps(2);
346−
347− if (data.inSpread) {
348− performEdgeSwipeToShowAppSpread();
349− }
350−
351− var focusedDelegate = findChild(stage, "appDelegate_" + topLevelSurfaceList.idAt(0));
352− var currentX = focusedDelegate.x;
353−
354− stage.leftEdgeDragProgress = data.leftEdgeDragWidth;
355−
356− tryCompare(focusedDelegate, "x", data.shouldMoveApp ? data.leftEdgeDragWidth : currentX);
357−
358− stage.leftEdgeDragProgress = 0;
359−
360− tryCompare(focusedDelegate, "x", currentX);
361− }
362−
363 function test_focusedAppIsTheOnlyRunningApp() {
337 function test_focusedAppIsTheOnlyRunningApp() {
364 addApps(2);
338 addApps(2);
365
339
tests/qmltests/tst_OrientedShell.qml+1−13
@@ -999,7 +999,7 @@ Rectangle {
999 var rotationStates = findInvisibleChild(orientedShell, "rotationStates");
999 var rotationStates = findInvisibleChild(orientedShell, "rotationStates");
1000 waitUntilTransitionsEnd(rotationStates);
1000 waitUntilTransitionsEnd(rotationStates);
1001
1001
1002− performLeftEdgeSwipeToSwitchToDash();
1002+ ApplicationManager.requestFocusApplication("unity8-dash");
1003
1003
1004 // Should be back to portrait
1004 // Should be back to portrait
1005 tryCompare(shell, "transformRotationAngle", 0);
1005 tryCompare(shell, "transformRotationAngle", 0);
@@ -1391,18 +1391,6 @@ Rectangle {
1391 waitUntilTransitionsEnd(rotationStates);
1391 waitUntilTransitionsEnd(rotationStates);
1392 }
1392 }
1393
1393
1394− function performLeftEdgeSwipeToSwitchToDash() {
1395− var swipeLength = shell.width * 0.7;
1396−
1397− var touchStartX = 1;
1398− var touchStartY = shell.height / 2;
1399− touchFlick(shell,
1400− touchStartX, touchStartY,
1401− touchStartX + swipeLength, touchStartY);
1402−
1403− tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
1404− }
1405−
1406 function performEdgeSwipeToSwitchToPreviousApp() {
1394 function performEdgeSwipeToSwitchToPreviousApp() {
1407 // swipe just enough to ensure an app switch action.
1395 // swipe just enough to ensure an app switch action.
1408 // If we swipe too much we will trigger the spread mode
1396 // If we swipe too much we will trigger the spread mode
tests/qmltests/tst_Shell.qml+2−143
@@ -605,38 +605,6 @@ Rectangle {
605 compare(ApplicationManager.get(0).appId, "unity8-dash");
605 compare(ApplicationManager.get(0).appId, "unity8-dash");
606 }
606 }
607
607
608− function test_phoneLeftEdgeDrag_data() {
609− return [
610− {tag: "without launcher",
611− revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
612− launcherHides: true, greeterShown: false},
613−
614− {tag: "with launcher",
615− revealLauncher: true, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
616− launcherHides: true, greeterShown: false},
617−
618− {tag: "small swipe",
619− revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app",
620− launcherHides: false, greeterShown: false},
621−
622− {tag: "long swipe",
623− revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
624− launcherHides: true, greeterShown: false},
625−
626− {tag: "small swipe with greeter",
627− revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app",
628− launcherHides: false, greeterShown: true},
629−
630− {tag: "long swipe with greeter",
631− revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
632− launcherHides: true, greeterShown: true},
633−
634− {tag: "swipe over dash",
635− revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "unity8-dash",
636− launcherHides: false, greeterShown: false},
637− ];
638− }
639−
640 function test_snapDecisionDismissalReturnsFocus() {
608 function test_snapDecisionDismissalReturnsFocus() {
641 loadShell("phone");
609 loadShell("phone");
642 swipeAwayGreeter();
610 swipeAwayGreeter();
@@ -701,80 +669,6 @@ Rectangle {
701 mockNotificationsModel.append(n)
669 mockNotificationsModel.append(n)
702 }
670 }
703
671
704− function test_phoneLeftEdgeDrag(data) {
705− loadShell("phone");
706− swipeAwayGreeter();
707− dragLauncherIntoView();
708− var appSurfaceId = topLevelSurfaceList.nextId;
709− tapOnAppIconInLauncher();
710− waitUntilAppWindowIsFullyLoaded(appSurfaceId);
711−
712− var greeter = findChild(shell, "greeter");
713− if (data.greeterShown) {
714− showGreeter();
715− }
716−
717− if (data.revealLauncher) {
718− dragLauncherIntoView();
719− }
720−
721− swipeFromLeftEdge(data.swipeLength);
722− if (data.appHides) {
723− waitUntilDashIsFocused();
724− tryCompare(greeter, "shown", false);
725− } else {
726− tryCompare(greeter, "fullyShown", data.greeterShown);
727− }
728−
729− var launcher = findChild(shell, "launcherPanel");
730− tryCompare(launcher, "x", data.launcherHides ? -launcher.width : 0)
731−
732− //FIXME: This check fails. Don't understand the rationale behind it.
733− /*
734− // Make sure the helper for sliding out the launcher wasn't touched. We want to fade it out here.
735− var animateTimer = findInvisibleChild(shell, "animateTimer");
736− compare(animateTimer.nextState, "visible");
737− */
738− }
739−
740− function test_tabletLeftEdgeDrag_data() {
741− return [
742− {tag: "without password", user: "no-password", loggedIn: true},
743− {tag: "with password", user: "has-password", loggedIn: false},
744− ]
745− }
746−
747− function test_tabletLeftEdgeDrag(data) {
748− setLightDMMockMode("full");
749− loadShell("tablet");
750−
751− selectUser(data.user)
752−
753− swipeFromLeftEdge(shell.width * 0.75)
754− wait(500) // to give time to handle dash() signal from Launcher
755− confirmLoggedIn(data.loggedIn)
756− }
757−
758− function test_longLeftEdgeSwipeTakesToAppsAndResetSearchString() {
759− loadShell("phone");
760− swipeAwayGreeter();
761− dragLauncherIntoView();
762− dashCommunicatorSpy.clear();
763−
764− var appSurfaceId = topLevelSurfaceList.nextId;
765− tapOnAppIconInLauncher();
766− waitUntilAppWindowIsFullyLoaded(appSurfaceId);
767−
768− tryCompareFunction(function() { return ApplicationManager.focusedApplicationId !== "unity8-dash"; }, true);
769−
770− //Long left swipe
771− swipeFromLeftEdge(units.gu(30));
772−
773− tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
774−
775− compare(dashCommunicatorSpy.count, 1);
776− }
777−
778 function test_ClickUbuntuIconInLauncherTakesToAppsAndResetSearchString() {
672 function test_ClickUbuntuIconInLauncherTakesToAppsAndResetSearchString() {
779 loadShell("phone");
673 loadShell("phone");
780 swipeAwayGreeter();
674 swipeAwayGreeter();
@@ -913,39 +807,6 @@ Rectangle {
913 LightDM.Users.mockMode = mode;
807 LightDM.Users.mockMode = mode;
914 }
808 }
915
809
916− /*
917− Regression test for bug https://bugs.launchpad.net/touch-preview-images/+bug/1193419
918−
919− When the user minimizes an application (left-edge swipe) he should always end up in the
920− "Applications" scope view.
921−
922− Steps:
923− - reveal launcher and launch an app that covers the dash
924− - perform long left edge swipe to go minimize the app and go back to the dash.
925− - verify the setCurrentScope() D-Bus call to the dash has been called for the correct scope id.
926− */
927− function test_minimizingAppTakesToDashApps() {
928− loadShell("phone");
929− swipeAwayGreeter();
930− dragLauncherIntoView();
931−
932− // Launch an app from the launcher
933− var appSurfaceId = topLevelSurfaceList.nextId;
934− tapOnAppIconInLauncher();
935− waitUntilAppWindowIsFullyLoaded(appSurfaceId);
936−
937− verify(ApplicationManager.focusedApplicationId !== "unity8-dash")
938−
939− dashCommunicatorSpy.clear();
940− // Minimize the application we just launched
941− swipeFromLeftEdge(shell.width * 0.75);
942−
943− tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
944−
945− compare(dashCommunicatorSpy.count, 1);
946− compare(dashCommunicatorSpy.signalArguments[0][0], 0);
947− }
948−
949 function test_showInputMethod() {
810 function test_showInputMethod() {
950 loadShell("phone");
811 loadShell("phone");
951 swipeAwayGreeter();
812 swipeAwayGreeter();
@@ -1166,7 +1027,7 @@ Rectangle {
1166
1027
1167 // Place the mouse against the window/screen edge and push beyond the barrier threshold
1028 // Place the mouse against the window/screen edge and push beyond the barrier threshold
1168 mouseMove(shell, 0, shell.height / 2);
1029 mouseMove(shell, 0, shell.height / 2);
1169− launcher.pushEdge(EdgeBarrierSettings.pushThreshold * 1.1);
1030+ launcher.pushEdge(EdgeBarrierSettings.pushThreshold * .8);
1170
1031
1171 var panel = findChild(launcher, "launcherPanel");
1032 var panel = findChild(launcher, "launcherPanel");
1172 verify(panel);
1033 verify(panel);
@@ -1186,9 +1047,7 @@ Rectangle {
1186 waitUntilAppWindowIsFullyLoaded(appSurfaceId);
1047 waitUntilAppWindowIsFullyLoaded(appSurfaceId);
1187
1048
1188 // Minimize the application we just launched
1049 // Minimize the application we just launched
1189− swipeFromLeftEdge(shell.width * 0.75);
1050+ swipeFromLeftEdge(units.gu(15));
1190−
1191− waitUntilDashIsFocused();
1192
1051
1193 showGreeter();
1052 showGreeter();
1194
1053
tests/qmltests/tst_ShellWithPin.qml−66
@@ -547,71 +547,5 @@ Item {
547 var lockscreen = findChild(shell, "lockscreen");
547 var lockscreen = findChild(shell, "lockscreen");
548 verify(lockscreen.shown);
548 verify(lockscreen.shown);
549 }
549 }
550−
551− /*
552− Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1393447
553−
554− Do a left edge drag that is long enough to start displacing the greeter
555− but short engough so that the greeter comes back into place once the
556− finger is lifted.
557−
558− In this situation the launcher should remaing fully shown and hide itself
559− only after its idle timeout is triggered.
560− */
561− function test_shortLeftEdgeSwipeMakesLauncherStayVisible() {
562− showGreeter();
563−
564− var launcher = testCase.findChild(shell, "launcher")
565− var launcherPanel = testCase.findChild(launcher, "launcherPanel");
566−
567− var toX = shell.width * 0.45;
568− touchFlick(shell,
569− 1 /* fromX */, shell.height * 0.5 /* fromY */,
570− toX /* toX */, shell.height * 0.5 /* toY */,
571− true /* beginTouch */, false /* endTouch */,
572− 50, 100);
573−
574− // Launcher must be fully shown by now
575− tryCompare(launcherPanel, "x", 0);
576−
577− // Greeter should be displaced
578− var coverPage = findChild(shell, "coverPage");
579− tryCompareFunction(function() { return coverPage.mapToItem(shell, 0, 0).x > shell.width*0.2; }, true);
580−
581− touchRelease(shell, toX, shell.height * 0.5);
582−
583− // Upon release the greeter should have slid back into full view
584− tryCompareFunction(function() { return coverPage.mapToItem(shell, 0, 0).x === 0; }, true);
585−
586− // And the launcher should stay fully shown
587− for (var i = 0; i < 10; ++i) {
588− wait(50);
589− compare(launcherPanel.x, 0);
590− }
591− }
592−
593− function test_longLeftEdgeDrags() {
594− var coverPage = findChild(shell, "coverPage");
595− var lockscreen = findChild(shell, "lockscreen");
596− var launcher = findChild(shell, "launcherPanel");
597− var galleryApp = ApplicationManager.startApplication("gallery-app");
598− tryCompare(shell, "mainApp", galleryApp);
599−
600− // Show greeter
601− showGreeter();
602−
603− // Swipe cover page away
604− touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);
605− tryCompare(launcher, "x", -launcher.width);
606− tryCompare(coverPage, "showProgress", 0);
607− compare(lockscreen.shown, true);
608− tryCompare(shell, "mainApp", galleryApp);
609−
610− // Now attempt a swipe on lockscreen
611− touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);
612− tryCompare(launcher, "x", 0);
613− compare(lockscreen.shown, true);
614− tryCompare(shell, "mainApp", galleryApp);
615− }
616 }
550 }
617}
551}