Merge lp:~mzanetti/unity8/tune-right-edge-push into lp:unity8

Proposed by Michael Zanetti
Status: Superseded
Proposed branch: lp:~mzanetti/unity8/tune-right-edge-push
Merge into: lp:unity8
Diff against target: 3039 lines (+1686/-421)
50 files modified
data/com.canonical.Unity8.gschema.xml (+2/-2)
debian/control (+3/-1)
plugins/Greeter/Unity/Launcher/CMakeLists.txt (+1/-1)
plugins/Greeter/Unity/Launcher/launcheritem.cpp (+13/-0)
plugins/Greeter/Unity/Launcher/launcheritem.h (+3/-0)
plugins/Unity/Launcher/CMakeLists.txt (+4/-1)
plugins/Unity/Launcher/appdrawermodel.cpp (+62/-0)
plugins/Unity/Launcher/appdrawermodel.h (+33/-0)
plugins/Unity/Launcher/launcheritem.cpp (+13/-0)
plugins/Unity/Launcher/launcheritem.h (+4/-0)
plugins/Unity/Launcher/launchermodel.cpp (+6/-37)
plugins/Unity/Launcher/launchermodel.h (+0/-14)
plugins/Unity/Launcher/plugin.cpp (+2/-1)
plugins/Unity/Launcher/ualwrapper.cpp (+73/-0)
plugins/Unity/Launcher/ualwrapper.h (+35/-0)
plugins/Utils/CMakeLists.txt (+4/-0)
plugins/Utils/appdrawerproxymodel.cpp (+189/-0)
plugins/Utils/appdrawerproxymodel.h (+87/-0)
plugins/Utils/plugin.cpp (+2/-0)
qml/Components/KeyboardShortcutsOverlay.qml (+13/-0)
qml/Launcher/BackgroundBlur.qml (+81/-0)
qml/Launcher/Drawer.qml (+306/-0)
qml/Launcher/DrawerGridView.qml (+47/-0)
qml/Launcher/DrawerListView.qml (+32/-0)
qml/Launcher/Launcher.qml (+173/-31)
qml/Launcher/MoreAppsHeader.qml (+46/-0)
qml/Shell.qml (+10/-34)
qml/Stage/Spread/WindowedRightEdgeMaths.qml (+19/-4)
qml/Stage/Stage.qml (+4/-6)
qml/Stage/StageMaths.qml (+2/-7)
qml/Tutorial/TutorialLeftLong.qml (+1/-1)
tests/mocks/Unity/Launcher/CMakeLists.txt (+5/-1)
tests/mocks/Unity/Launcher/MockAppDrawerModel.cpp (+75/-0)
tests/mocks/Unity/Launcher/MockAppDrawerModel.h (+32/-0)
tests/mocks/Unity/Launcher/MockLauncherItem.cpp (+13/-0)
tests/mocks/Unity/Launcher/MockLauncherItem.h (+6/-0)
tests/mocks/Unity/Launcher/MockLauncherModel.cpp (+3/-0)
tests/mocks/Unity/Launcher/plugin.cpp (+4/-0)
tests/mocks/Utils/CMakeLists.txt (+4/-0)
tests/mocks/Utils/plugin.cpp (+2/-0)
tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt (+1/-1)
tests/plugins/Unity/Launcher/CMakeLists.txt (+2/-1)
tests/qmltests/CMakeLists.txt (+1/-0)
tests/qmltests/Dash/tst_DashShell.qml (+0/-29)
tests/qmltests/Launcher/tst_Drawer.qml (+264/-0)
tests/qmltests/Launcher/tst_Launcher.qml (+1/-1)
tests/qmltests/Stage/tst_PhoneStage.qml (+0/-26)
tests/qmltests/tst_OrientedShell.qml (+1/-13)
tests/qmltests/tst_Shell.qml (+2/-143)
tests/qmltests/tst_ShellWithPin.qml (+0/-66)
To merge this branch: bzr merge lp:~mzanetti/unity8/tune-right-edge-push
Reviewer Review Type Date Requested Status
Unity8 CI Bot continuous-integration Approve
Albert Astals Cid (community) Approve
Review via email: mp+311536@code.launchpad.net

This proposal has been superseded by a proposal from 2016-12-06.

Commit message

tune right edge push

make it less intrusive when accidentally hitting the edge with the mouse
tweak visuals for the mouse case

Description of the change

 * Are there any related MPs required for this MP to build/function as expected? Please list.
no
 * Did you perform an exploratory manual test run of your code change and any related functionality?
yes
 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
no
 * If you changed the UI, has there been a design review?
Showed a video to Vesa, he likes it. Added him as reviewer for confirmation

To post a comment you must log in.
2704. By Michael Zanetti

tune right edge push as per design request

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

PASSED: Continuous integration, rev:2704
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/2524/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/3329
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=qmluitests.sh/1905
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/1905
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=zesty,testname=qmluitests.sh/1905
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/3357
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/3209
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/3209/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/3209
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/3209/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/3209
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/3209/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/3209
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/3209/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/3209
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/3209/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/3209
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/3209/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/3209
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/3209/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/3209
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/3209/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/3209
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/3209/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/2524/rebuild

review: Approve (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

Feels better, but please make progress a readonly property.

review: Needs Fixing
2705. By Michael Zanetti

make property readonly

Revision history for this message
Albert Astals Cid (aacid) wrote :

 * Did you perform an exploratory manual test run of the code change and any related functionality?
Yes

 * Did CI run pass? If not, please explain why.
Yes

review: Approve
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

PASSED: Continuous integration, rev:2705
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/2584/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/3399
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=qmluitests.sh/1956
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/1956
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=zesty,testname=qmluitests.sh/1956
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/3427
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/3278
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/3278/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/3278
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/3278/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/3278
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/3278/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/3278
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/3278/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/3278
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/3278/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/3278
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/3278/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/3278
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/3278/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/3278
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/3278/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/3278
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/3278/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/2584/rebuild

review: Approve (continuous-integration)
2706. By Michael Zanetti

merge appdrawer as prereq

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/com.canonical.Unity8.gschema.xml'
2--- data/com.canonical.Unity8.gschema.xml 2016-11-16 05:54:50 +0000
3+++ data/com.canonical.Unity8.gschema.xml 2016-12-06 13:47:05 +0000
4@@ -12,7 +12,7 @@
5 <description>The usage mode chosen will affect the Window Management behaviour.</description>
6 </key>
7 <key type="y" name="edge-barrier-sensitivity">
8- <default>35</default>
9+ <default>50</default>
10 <range min="1" max="100"/>
11 <summary>Sensitivity of screen edge barriers for the mouse pointer.</summary>
12 <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>
13@@ -23,7 +23,7 @@
14 <description>How much you have to push (in grid units)the mouse against an edge barrier when sensibility is 100.</description>
15 </key>
16 <key type="u" name="edge-barrier-max-push">
17- <default>60</default>
18+ <default>120</default>
19 <summary>Maximum push needed to overcome edge barrier</summary>
20 <description>How much you have to push (in grid units) the mouse against an edge barrier when sensibility is 1.</description>
21 </key>
22
23=== modified file 'debian/control'
24--- debian/control 2016-11-29 09:40:16 +0000
25+++ debian/control 2016-12-06 13:47:05 +0000
26@@ -38,7 +38,7 @@
27 libubuntugestures5-private-dev (>= 1.3.2030),
28 libudev-dev,
29 libudm-common-dev,
30- libunity-api-dev (>= 7.120),
31+ libunity-api-dev (>= 7.121),
32 libusermetricsoutput1-dev,
33 # Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
34 libx11-dev[!arm64 !armhf],
35@@ -49,6 +49,7 @@
36 python3-all:any,
37 python3-setuptools,
38 qml-module-qt-labs-folderlistmodel,
39+ qml-module-qt-labs-settings,
40 qml-module-qtqml-statemachine,
41 qml-module-qtmultimedia (>= 5.4.1-1ubuntu19~overlay2),
42 qml-module-qtquick-layouts,
43@@ -122,6 +123,7 @@
44 qmenumodel-qml (>= 0.2.10),
45 qml-module-biometryd,
46 qml-module-qt-labs-folderlistmodel,
47+ qml-module-qt-labs-settings,
48 qml-module-qtqml-statemachine,
49 qml-module-qtquick-xmllistmodel,
50 qml-module-qtsysteminfo,
51
52=== modified file 'plugins/Greeter/Unity/Launcher/CMakeLists.txt'
53--- plugins/Greeter/Unity/Launcher/CMakeLists.txt 2016-10-28 12:08:59 +0000
54+++ plugins/Greeter/Unity/Launcher/CMakeLists.txt 2016-12-06 13:47:05 +0000
55@@ -1,4 +1,4 @@
56-pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=10)
57+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=11)
58 pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
59
60 add_definitions(-DSM_BUSNAME=systemBus)
61
62=== modified file 'plugins/Greeter/Unity/Launcher/launcheritem.cpp'
63--- plugins/Greeter/Unity/Launcher/launcheritem.cpp 2016-05-10 16:23:34 +0000
64+++ plugins/Greeter/Unity/Launcher/launcheritem.cpp 2016-12-06 13:47:05 +0000
65@@ -76,6 +76,19 @@
66 }
67 }
68
69+QStringList LauncherItem::keywords() const
70+{
71+ return m_keywords;
72+}
73+
74+void LauncherItem::setKeywords(const QStringList &keywords)
75+{
76+ if (m_keywords != keywords) {
77+ m_keywords = keywords;
78+ Q_EMIT keywordsChanged(keywords);
79+ }
80+}
81+
82 bool LauncherItem::pinned() const
83 {
84 return m_pinned;
85
86=== modified file 'plugins/Greeter/Unity/Launcher/launcheritem.h'
87--- plugins/Greeter/Unity/Launcher/launcheritem.h 2016-05-10 15:51:00 +0000
88+++ plugins/Greeter/Unity/Launcher/launcheritem.h 2016-12-06 13:47:05 +0000
89@@ -34,6 +34,7 @@
90 QString appId() const override;
91 QString name() const override;
92 QString icon() const override;
93+ QStringList keywords() const override;
94 bool pinned() const override;
95 bool running() const override;
96 bool recent() const override;
97@@ -49,6 +50,7 @@
98 private:
99 void setName(const QString &name);
100 void setIcon(const QString &icon);
101+ void setKeywords(const QStringList &keywords);
102 void setPinned(bool pinned);
103 void setRunning(bool running);
104 void setRecent(bool recent);
105@@ -63,6 +65,7 @@
106 QString m_appId;
107 QString m_name;
108 QString m_icon;
109+ QStringList m_keywords;
110 bool m_pinned;
111 bool m_running;
112 bool m_recent;
113
114=== modified file 'plugins/Unity/Launcher/CMakeLists.txt'
115--- plugins/Unity/Launcher/CMakeLists.txt 2016-10-28 12:08:59 +0000
116+++ plugins/Unity/Launcher/CMakeLists.txt 2016-12-06 13:47:05 +0000
117@@ -1,4 +1,4 @@
118-pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=10)
119+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=11)
120 pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
121
122 add_definitions(-DSM_BUSNAME=systemBus)
123@@ -25,6 +25,8 @@
124 dbusinterface.cpp
125 gsettings.cpp
126 asadapter.cpp
127+ appdrawermodel.cpp
128+ ualwrapper.cpp
129 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
130 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
131 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
132@@ -32,6 +34,7 @@
133 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
134 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
135 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
136+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/AppDrawerModelInterface.h
137 )
138
139 add_library(UnityLauncher-qml MODULE
140
141=== added file 'plugins/Unity/Launcher/appdrawermodel.cpp'
142--- plugins/Unity/Launcher/appdrawermodel.cpp 1970-01-01 00:00:00 +0000
143+++ plugins/Unity/Launcher/appdrawermodel.cpp 2016-12-06 13:47:05 +0000
144@@ -0,0 +1,62 @@
145+/*
146+ * Copyright (C) 2016 Canonical, Ltd.
147+ *
148+ * This program is free software; you can redistribute it and/or modify
149+ * it under the terms of the GNU General Public License as published by
150+ * the Free Software Foundation; version 3.
151+ *
152+ * This program is distributed in the hope that it will be useful,
153+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
154+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
155+ * GNU General Public License for more details.
156+ *
157+ * You should have received a copy of the GNU General Public License
158+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
159+ */
160+
161+#include "appdrawermodel.h"
162+#include "ualwrapper.h"
163+
164+#include <QDebug>
165+#include <QDateTime>
166+
167+AppDrawerModel::AppDrawerModel(QObject *parent):
168+ AppDrawerModelInterface(parent)
169+{
170+ Q_FOREACH (const QString &appId, UalWrapper::installedApps()) {
171+ UalWrapper::AppInfo info = UalWrapper::getApplicationInfo(appId);
172+ if (!info.valid) {
173+ qWarning() << "Failed to get app info for app" << appId;
174+ continue;
175+ }
176+ m_list.append(new LauncherItem(appId, info.name, info.icon, this));
177+ m_list.last()->setKeywords(info.keywords);
178+ }
179+ qsrand(QDateTime::currentMSecsSinceEpoch() / 100);
180+}
181+
182+int AppDrawerModel::rowCount(const QModelIndex &parent) const
183+{
184+ Q_UNUSED(parent)
185+ return m_list.count();
186+}
187+
188+QVariant AppDrawerModel::data(const QModelIndex &index, int role) const
189+{
190+ switch (role) {
191+ case RoleAppId:
192+ return m_list.at(index.row())->appId();
193+ case RoleName:
194+ return m_list.at(index.row())->name();
195+ case RoleIcon:
196+ return m_list.at(index.row())->icon();
197+ case RoleKeywords:
198+ return m_list.at(index.row())->keywords();
199+ case RoleUsage:
200+ // FIXME: u-a-l needs to provide API for usage stats.
201+ // don't forget to drop the qsrand() call in the ctor when dropping this.
202+ return qrand();
203+ }
204+
205+ return QVariant();
206+}
207
208=== added file 'plugins/Unity/Launcher/appdrawermodel.h'
209--- plugins/Unity/Launcher/appdrawermodel.h 1970-01-01 00:00:00 +0000
210+++ plugins/Unity/Launcher/appdrawermodel.h 2016-12-06 13:47:05 +0000
211@@ -0,0 +1,33 @@
212+/*
213+ * Copyright (C) 2016 Canonical, Ltd.
214+ *
215+ * This program is free software; you can redistribute it and/or modify
216+ * it under the terms of the GNU General Public License as published by
217+ * the Free Software Foundation; version 3.
218+ *
219+ * This program is distributed in the hope that it will be useful,
220+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
221+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
222+ * GNU General Public License for more details.
223+ *
224+ * You should have received a copy of the GNU General Public License
225+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
226+ */
227+
228+
229+#include <unity/shell/launcher/AppDrawerModelInterface.h>
230+
231+#include "launcheritem.h"
232+
233+class AppDrawerModel: public AppDrawerModelInterface
234+{
235+ Q_OBJECT
236+public:
237+ AppDrawerModel(QObject* parent = nullptr);
238+
239+ int rowCount(const QModelIndex &parent) const override;
240+ QVariant data(const QModelIndex &index, int role) const override;
241+
242+private:
243+ QList<LauncherItem*> m_list;
244+};
245
246=== modified file 'plugins/Unity/Launcher/launcheritem.cpp'
247--- plugins/Unity/Launcher/launcheritem.cpp 2016-10-28 11:54:16 +0000
248+++ plugins/Unity/Launcher/launcheritem.cpp 2016-12-06 13:47:05 +0000
249@@ -90,6 +90,19 @@
250 }
251 }
252
253+QStringList LauncherItem::keywords() const
254+{
255+ return m_keywords;
256+}
257+
258+void LauncherItem::setKeywords(const QStringList &keywords)
259+{
260+ if (m_keywords != keywords) {
261+ m_keywords = keywords;
262+ Q_EMIT keywordsChanged(keywords);
263+ }
264+}
265+
266 bool LauncherItem::pinned() const
267 {
268 return m_pinned;
269
270=== modified file 'plugins/Unity/Launcher/launcheritem.h'
271--- plugins/Unity/Launcher/launcheritem.h 2016-05-10 15:51:00 +0000
272+++ plugins/Unity/Launcher/launcheritem.h 2016-12-06 13:47:05 +0000
273@@ -37,6 +37,7 @@
274 QString appId() const override;
275 QString name() const override;
276 QString icon() const override;
277+ QStringList keywords() const override;
278 bool pinned() const override;
279 bool running() const override;
280 bool recent() const override;
281@@ -52,6 +53,7 @@
282 private:
283 void setName(const QString &name);
284 void setIcon(const QString &icon);
285+ void setKeywords(const QStringList &keywords);
286 void setPinned(bool pinned);
287 void setRunning(bool running);
288 void setRecent(bool recent);
289@@ -66,6 +68,7 @@
290 QString m_appId;
291 QString m_name;
292 QString m_icon;
293+ QStringList m_keywords;
294 bool m_pinned;
295 bool m_running;
296 bool m_recent;
297@@ -79,6 +82,7 @@
298 QuickListEntry m_quitAction;
299
300 friend class LauncherModel;
301+ friend class AppDrawerModel;
302 };
303
304 #endif // LAUNCHERITEM_H
305
306=== modified file 'plugins/Unity/Launcher/launchermodel.cpp'
307--- plugins/Unity/Launcher/launchermodel.cpp 2016-09-23 15:10:26 +0000
308+++ plugins/Unity/Launcher/launchermodel.cpp 2016-12-06 13:47:05 +0000
309@@ -19,18 +19,14 @@
310 #include "gsettings.h"
311 #include "dbusinterface.h"
312 #include "asadapter.h"
313+#include "ualwrapper.h"
314
315-#include <ubuntu-app-launch/appid.h>
316-#include <ubuntu-app-launch/application.h>
317-#include <ubuntu-app-launch/registry.h>
318 #include <unity/shell/application/ApplicationInfoInterface.h>
319 #include <unity/shell/application/MirSurfaceListInterface.h>
320
321 #include <QDesktopServices>
322 #include <QDebug>
323
324-namespace ual = ubuntu::app_launch;
325-
326 using namespace unity::shell::application;
327
328 LauncherModel::LauncherModel(QObject *parent):
329@@ -38,8 +34,7 @@
330 m_settings(new GSettings(this)),
331 m_dbusIface(new DBusInterface(this)),
332 m_asAdapter(new ASAdapter()),
333- m_appManager(nullptr),
334- m_ualRegistry(std::make_shared<ual::Registry>())
335+ m_appManager(nullptr)
336 {
337 connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged);
338 connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged);
339@@ -159,7 +154,7 @@
340 index = m_list.count();
341 }
342
343- auto appInfo = getApplicationInfo(appId);
344+ UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(appId);
345 if (!appInfo.valid) {
346 qWarning() << "Can't pin application, appId not found:" << appId;
347 return;
348@@ -335,32 +330,6 @@
349 return -1;
350 }
351
352-LauncherModel::AppInfo LauncherModel::getApplicationInfo(const QString &appId)
353-{
354- AppInfo info;
355-
356- ual::AppID ualAppId = ual::AppID::find(m_ualRegistry, appId.toStdString());
357- if (ualAppId.empty()) {
358- return info;
359- }
360-
361- std::shared_ptr<ual::Application> ualApp;
362- try
363- {
364- ualApp = ual::Application::create(ualAppId, m_ualRegistry);
365- }
366- catch (std::runtime_error &e)
367- {
368- qWarning() << "Couldn't find application info for" << appId << "-" << e.what();
369- return info;
370- }
371-
372- info.valid = true;
373- info.name = QString::fromStdString(ualApp->info()->name());
374- info.icon = QString::fromStdString(ualApp->info()->iconPath());
375- return info;
376-}
377-
378 void LauncherModel::progressChanged(const QString &appId, int progress)
379 {
380 const int idx = findApplication(appId);
381@@ -408,7 +377,7 @@
382 }
383 } else {
384 // Need to create a new LauncherItem and show the highlight
385- auto appInfo = getApplicationInfo(appId);
386+ UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(appId);
387 if (countVisible && appInfo.valid) {
388 LauncherItem *item = new LauncherItem(appId,
389 appInfo.name,
390@@ -428,7 +397,7 @@
391 // First walk through all the existing items and see if we need to remove something
392 QList<LauncherItem*> toBeRemoved;
393 Q_FOREACH (LauncherItem* item, m_list) {
394- auto appInfo = getApplicationInfo(item->appId());
395+ UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(item->appId());
396 if (!appInfo.valid) {
397 // Application no longer available => drop it!
398 toBeRemoved << item;
399@@ -480,7 +449,7 @@
400 if (itemIndex == -1) {
401 // Need to add it. Just add it into the addedIndex to keep same ordering as the list
402 // in the settings.
403- auto appInfo = getApplicationInfo(entry);
404+ UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(entry);
405 if (!appInfo.valid) {
406 continue;
407 }
408
409=== modified file 'plugins/Unity/Launcher/launchermodel.h'
410--- plugins/Unity/Launcher/launchermodel.h 2016-09-23 14:44:26 +0000
411+++ plugins/Unity/Launcher/launchermodel.h 2016-12-06 13:47:05 +0000
412@@ -27,12 +27,6 @@
413 class DBusInterface;
414 class ASAdapter;
415
416-namespace ubuntu {
417- namespace app_launch {
418- class Registry;
419- }
420-}
421-
422 using namespace unity::shell::launcher;
423 using namespace unity::shell::application;
424
425@@ -73,13 +67,6 @@
426
427 void unpin(const QString &appId);
428
429- struct AppInfo {
430- bool valid = false;
431- QString name;
432- QString icon;
433- };
434- AppInfo getApplicationInfo(const QString &appId);
435-
436 private Q_SLOTS:
437 void countChanged(const QString &appId, int count);
438 void countVisibleChanged(const QString &appId, bool count);
439@@ -98,7 +85,6 @@
440 ASAdapter *m_asAdapter;
441
442 ApplicationManagerInterface *m_appManager;
443- std::shared_ptr<ubuntu::app_launch::Registry> m_ualRegistry;
444
445 friend class LauncherModelTest;
446 };
447
448=== modified file 'plugins/Unity/Launcher/plugin.cpp'
449--- plugins/Unity/Launcher/plugin.cpp 2015-09-14 09:11:08 +0000
450+++ plugins/Unity/Launcher/plugin.cpp 2016-12-06 13:47:05 +0000
451@@ -26,7 +26,7 @@
452 // local
453 #include "launchermodel.h"
454 #include "launcheritem.h"
455-
456+#include "appdrawermodel.h"
457
458 using namespace unity::shell::launcher;
459
460@@ -46,4 +46,5 @@
461 qmlRegisterSingletonType<LauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
462 qmlRegisterUncreatableType<LauncherItem>(uri, 0, 1, "LauncherItem", QStringLiteral("Can't create new Launcher Items in QML. Get them from the LauncherModel."));
463 qmlRegisterUncreatableType<QuickListModel>(uri, 0, 1, "QuickListModel", QStringLiteral("Can't create a QuickListModel in QML. Get them from the LauncherItems."));
464+ qmlRegisterType<AppDrawerModel>(uri, 0, 1, "AppDrawerModel");
465 }
466
467=== added file 'plugins/Unity/Launcher/ualwrapper.cpp'
468--- plugins/Unity/Launcher/ualwrapper.cpp 1970-01-01 00:00:00 +0000
469+++ plugins/Unity/Launcher/ualwrapper.cpp 2016-12-06 13:47:05 +0000
470@@ -0,0 +1,73 @@
471+/*
472+ * Copyright (C) 2016 Canonical, Ltd.
473+ *
474+ * This program is free software; you can redistribute it and/or modify
475+ * it under the terms of the GNU General Public License as published by
476+ * the Free Software Foundation; version 3.
477+ *
478+ * This program is distributed in the hope that it will be useful,
479+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
480+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
481+ * GNU General Public License for more details.
482+ *
483+ * You should have received a copy of the GNU General Public License
484+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
485+ */
486+
487+#include <QDebug>
488+
489+#include "ualwrapper.h"
490+
491+#include <ubuntu-app-launch/registry.h>
492+using namespace ubuntu::app_launch;
493+
494+UalWrapper::UalWrapper(QObject *parent):
495+ QObject(parent)
496+{
497+
498+}
499+
500+QStringList UalWrapper::installedApps()
501+{
502+ QStringList appIds;
503+ try {
504+ for (const std::shared_ptr<Application> &app : Registry::installedApps()) {
505+ if (!app->appId().package.value().empty()) {
506+ appIds << QString::fromStdString(app->appId().package.value() + "_" + app->appId().appname.value());
507+ } else {
508+ appIds << QString::fromStdString(app->appId().appname);
509+ }
510+ }
511+ } catch (const std::runtime_error &e) {
512+ qWarning() << "ubuntu-all-launch threw an exception listing apps:" << e.what();
513+ }
514+
515+ return appIds;
516+}
517+
518+UalWrapper::AppInfo UalWrapper::getApplicationInfo(const QString &appId)
519+{
520+ AppInfo info;
521+
522+ try {
523+ AppID ualAppId = AppID::find(appId.toStdString());
524+ if (ualAppId.empty()) {
525+ qWarning() << "Empty ualAppId result for" << appId;
526+ return info;
527+ }
528+
529+ std::shared_ptr<Application> ualApp;
530+ ualApp = Application::create(ualAppId, Registry::getDefault());
531+
532+ info.name = QString::fromStdString(ualApp->info()->name());
533+ info.icon = QString::fromStdString(ualApp->info()->iconPath());
534+ for (const std::string &keyword : ualApp->info()->keywords().value()) {
535+ info.keywords << QString::fromStdString(keyword);
536+ }
537+ info.valid = true;
538+ } catch (const std::runtime_error &e) {
539+ qWarning() << "ubuntu-app-launch threw an exception getting app info for appId:" << appId << ":" << e.what();
540+ }
541+
542+ return info;
543+}
544
545=== added file 'plugins/Unity/Launcher/ualwrapper.h'
546--- plugins/Unity/Launcher/ualwrapper.h 1970-01-01 00:00:00 +0000
547+++ plugins/Unity/Launcher/ualwrapper.h 2016-12-06 13:47:05 +0000
548@@ -0,0 +1,35 @@
549+/*
550+ * Copyright (C) 2016 Canonical, Ltd.
551+ *
552+ * This program is free software; you can redistribute it and/or modify
553+ * it under the terms of the GNU General Public License as published by
554+ * the Free Software Foundation; version 3.
555+ *
556+ * This program is distributed in the hope that it will be useful,
557+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
558+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
559+ * GNU General Public License for more details.
560+ *
561+ * You should have received a copy of the GNU General Public License
562+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
563+ */
564+
565+#include <QObject>
566+
567+class UalWrapper: public QObject
568+{
569+ Q_OBJECT
570+public:
571+ struct AppInfo {
572+ bool valid = false;
573+ QString name;
574+ QString icon;
575+ QStringList keywords;
576+ };
577+
578+ UalWrapper(QObject* parent = nullptr);
579+
580+ static QStringList installedApps();
581+ static AppInfo getApplicationInfo(const QString &appId);
582+
583+};
584
585=== modified file 'plugins/Utils/CMakeLists.txt'
586--- plugins/Utils/CMakeLists.txt 2016-06-29 18:05:44 +0000
587+++ plugins/Utils/CMakeLists.txt 2016-12-06 13:47:05 +0000
588@@ -15,6 +15,10 @@
589 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
590 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
591 applicationsfiltermodel.cpp
592+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/AppDrawerModelInterface.h
593+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
594+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
595+ appdrawerproxymodel.cpp
596 constants.cpp
597 WindowInputMonitor.cpp
598 inputwatcher.cpp
599
600=== added file 'plugins/Utils/appdrawerproxymodel.cpp'
601--- plugins/Utils/appdrawerproxymodel.cpp 1970-01-01 00:00:00 +0000
602+++ plugins/Utils/appdrawerproxymodel.cpp 2016-12-06 13:47:05 +0000
603@@ -0,0 +1,189 @@
604+/*
605+ * Copyright (C) 2016 Canonical, Ltd.
606+ *
607+ * This program is free software; you can redistribute it and/or modify
608+ * it under the terms of the GNU General Public License as published by
609+ * the Free Software Foundation; version 3.
610+ *
611+ * This program is distributed in the hope that it will be useful,
612+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
613+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
614+ * GNU General Public License for more details.
615+ *
616+ * You should have received a copy of the GNU General Public License
617+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
618+ */
619+
620+#include "appdrawerproxymodel.h"
621+
622+#include <unity/shell/launcher/LauncherItemInterface.h>
623+
624+#include <QDebug>
625+
626+AppDrawerProxyModel::AppDrawerProxyModel(QObject *parent):
627+ QSortFilterProxyModel(parent)
628+{
629+ setSortRole(AppDrawerModelInterface::RoleName);
630+ setSortLocaleAware(true);
631+ sort(0);
632+
633+ connect(this, &QAbstractListModel::rowsInserted, this, &AppDrawerProxyModel::countChanged);
634+ connect(this, &QAbstractListModel::rowsRemoved, this, &AppDrawerProxyModel::countChanged);
635+ connect(this, &QAbstractListModel::layoutChanged, this, &AppDrawerProxyModel::countChanged);
636+}
637+
638+QAbstractItemModel *AppDrawerProxyModel::source() const
639+{
640+ return m_source;
641+}
642+
643+void AppDrawerProxyModel::setSource(QAbstractItemModel *source)
644+{
645+ if (m_source != source) {
646+ m_source = source;
647+ setSourceModel(m_source);
648+ setSortRole(m_sortBy == SortByAToZ ? AppDrawerModelInterface::RoleName : AppDrawerModelInterface::RoleUsage);
649+ connect(m_source, &QAbstractItemModel::rowsRemoved, this, &AppDrawerProxyModel::invalidateFilter);
650+ connect(m_source, &QAbstractItemModel::rowsInserted, this, &AppDrawerProxyModel::invalidateFilter);
651+ Q_EMIT sourceChanged();
652+ }
653+}
654+
655+AppDrawerProxyModel::GroupBy AppDrawerProxyModel::group() const
656+{
657+ return m_group;
658+}
659+
660+void AppDrawerProxyModel::setGroup(AppDrawerProxyModel::GroupBy group)
661+{
662+ if (m_group != group) {
663+ m_group = group;
664+ Q_EMIT groupChanged();
665+ invalidateFilter();
666+ }
667+}
668+
669+QString AppDrawerProxyModel::filterLetter() const
670+{
671+ return m_filterLetter;
672+}
673+
674+void AppDrawerProxyModel::setFilterLetter(const QString &filterLetter)
675+{
676+ if (m_filterLetter != filterLetter) {
677+ m_filterLetter = filterLetter;
678+ Q_EMIT filterLetterChanged();
679+ invalidateFilter();
680+ }
681+}
682+
683+QString AppDrawerProxyModel::filterString() const
684+{
685+ return m_filterString;
686+}
687+
688+void AppDrawerProxyModel::setFilterString(const QString &filterString)
689+{
690+ if (m_filterString != filterString) {
691+ m_filterString = filterString;
692+ Q_EMIT filterStringChanged();
693+ invalidateFilter();
694+ }
695+}
696+
697+AppDrawerProxyModel::SortBy AppDrawerProxyModel::sortBy() const
698+{
699+ return m_sortBy;
700+}
701+
702+void AppDrawerProxyModel::setSortBy(AppDrawerProxyModel::SortBy sortBy)
703+{
704+ if (m_sortBy != sortBy) {
705+ m_sortBy = sortBy;
706+ Q_EMIT sortByChanged();
707+ setSortRole(m_sortBy == SortByAToZ ? AppDrawerModelInterface::RoleName : AppDrawerModelInterface::RoleUsage);
708+ sort(0);
709+ }
710+}
711+
712+int AppDrawerProxyModel::count() const
713+{
714+ return rowCount();
715+}
716+
717+QVariant AppDrawerProxyModel::data(const QModelIndex &index, int role) const
718+{
719+ QModelIndex idx = mapToSource(index);
720+ if (role == Qt::UserRole) {
721+ QString name = m_source->data(idx, AppDrawerModelInterface::RoleName).toString();
722+ return name.length() > 0 ? QString(name.at(0)).toUpper() : QChar();
723+ }
724+ return m_source->data(idx, role);
725+}
726+
727+QHash<int, QByteArray> AppDrawerProxyModel::roleNames() const
728+{
729+ if (m_source) {
730+ QHash<int, QByteArray> roles = m_source->roleNames();
731+ roles.insert(Qt::UserRole, "letter");
732+ return roles;
733+ }
734+ return QHash<int, QByteArray>();
735+}
736+
737+bool AppDrawerProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
738+{
739+ Q_UNUSED(source_parent)
740+
741+ if (m_group == GroupByAToZ && source_row > 0) {
742+ QString currentName = m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleName).toString();
743+ QChar currentLetter = currentName.length() > 0 ? currentName.at(0) : QChar();
744+ QString previousName = m_source->data(m_source->index(source_row - 1,0 ), AppDrawerModelInterface::RoleName).toString();
745+ QChar previousLetter = previousName.length() > 0 ? previousName.at(0) : QChar();
746+ if (currentLetter.toLower() == previousLetter.toLower()) {
747+ return false;
748+ }
749+ } else if(m_group == GroupByAll && source_row > 0) {
750+ return false;
751+ }
752+ if (!m_filterLetter.isEmpty()) {
753+ QString currentName = m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleName).toString();
754+ QString currentLetter = currentName.length() > 0 ? QString(currentName.at(0)) : QString();
755+ if (currentLetter.toLower() != m_filterLetter.toLower()) {
756+ return false;
757+ }
758+ }
759+ if (!m_filterString.isEmpty()) {
760+ QStringList allWords = m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleKeywords).toStringList();
761+ allWords.prepend(m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleName).toString());
762+ bool found = false;
763+ Q_FOREACH (const QString currentWord, allWords) {
764+ if (currentWord.toLower().startsWith(m_filterString.toLower())) {
765+ found = true;
766+ break;
767+ }
768+ }
769+ if (!found) {
770+ return false;
771+ }
772+ }
773+ return true;
774+}
775+
776+QString AppDrawerProxyModel::appId(int index) const
777+{
778+ if (index >= 0 && index < rowCount()) {
779+ QModelIndex sourceIndex = mapToSource(this->index(index, 0));
780+
781+ AppDrawerModelInterface* adm = dynamic_cast<AppDrawerModelInterface*>(m_source);
782+ if (adm) {
783+ return adm->data(sourceIndex, AppDrawerModelInterface::RoleAppId).toString();
784+ }
785+
786+ AppDrawerProxyModel* adpm = qobject_cast<AppDrawerProxyModel*>(m_source);
787+ if (adpm) {
788+ return adpm->appId(sourceIndex.row());
789+ }
790+ }
791+ return nullptr;
792+}
793
794=== added file 'plugins/Utils/appdrawerproxymodel.h'
795--- plugins/Utils/appdrawerproxymodel.h 1970-01-01 00:00:00 +0000
796+++ plugins/Utils/appdrawerproxymodel.h 2016-12-06 13:47:05 +0000
797@@ -0,0 +1,87 @@
798+/*
799+ * Copyright (C) 2016 Canonical, Ltd.
800+ *
801+ * This program is free software; you can redistribute it and/or modify
802+ * it under the terms of the GNU General Public License as published by
803+ * the Free Software Foundation; version 3.
804+ *
805+ * This program is distributed in the hope that it will be useful,
806+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
807+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
808+ * GNU General Public License for more details.
809+ *
810+ * You should have received a copy of the GNU General Public License
811+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
812+ */
813+
814+#include <QSortFilterProxyModel>
815+
816+#include <unity/shell/launcher/AppDrawerModelInterface.h>
817+
818+using namespace unity::shell::launcher;
819+
820+class AppDrawerProxyModel: public QSortFilterProxyModel
821+{
822+ Q_OBJECT
823+ Q_ENUMS(GroupBy)
824+ Q_ENUMS(SortBy)
825+ Q_PROPERTY(QAbstractItemModel* source READ source WRITE setSource NOTIFY sourceChanged)
826+ Q_PROPERTY(GroupBy group READ group WRITE setGroup NOTIFY groupChanged)
827+ Q_PROPERTY(QString filterLetter READ filterLetter WRITE setFilterLetter NOTIFY filterLetterChanged)
828+ Q_PROPERTY(QString filterString READ filterString WRITE setFilterString NOTIFY filterStringChanged)
829+ Q_PROPERTY(SortBy sortBy READ sortBy WRITE setSortBy NOTIFY sortByChanged)
830+ Q_PROPERTY(int count READ count NOTIFY countChanged)
831+
832+public:
833+ enum GroupBy {
834+ GroupByNone,
835+ GroupByAll,
836+ GroupByAToZ
837+ };
838+ enum SortBy {
839+ SortByAToZ,
840+ SortByUsage
841+ };
842+
843+ AppDrawerProxyModel(QObject* parent = nullptr);
844+
845+ QAbstractItemModel* source() const;
846+ void setSource(QAbstractItemModel* source);
847+
848+ GroupBy group() const;
849+ void setGroup(GroupBy group);
850+
851+ QString filterLetter() const;
852+ void setFilterLetter(const QString &filterLetter);
853+
854+ QString filterString() const;
855+ void setFilterString(const QString &filterString);
856+
857+ SortBy sortBy() const;
858+ void setSortBy(SortBy sortBy);
859+
860+ int count() const;
861+
862+ QVariant data(const QModelIndex &index, int role) const override;
863+ QHash<int, QByteArray> roleNames() const override;
864+
865+ Q_INVOKABLE QString appId(int index) const;
866+
867+protected:
868+ bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
869+
870+Q_SIGNALS:
871+ void sourceChanged();
872+ void groupChanged();
873+ void filterLetterChanged();
874+ void filterStringChanged();
875+ void sortByChanged();
876+ void countChanged();
877+
878+private:
879+ QAbstractItemModel* m_source = nullptr;
880+ GroupBy m_group = GroupByNone;
881+ QString m_filterLetter;
882+ QString m_filterString;
883+ SortBy m_sortBy = SortByAToZ;
884+};
885
886=== modified file 'plugins/Utils/plugin.cpp'
887--- plugins/Utils/plugin.cpp 2016-06-28 20:38:00 +0000
888+++ plugins/Utils/plugin.cpp 2016-12-06 13:47:05 +0000
889@@ -39,6 +39,7 @@
890 #include "deviceconfigparser.h"
891 #include "globalfunctions.h"
892 #include "URLDispatcher.h"
893+#include "appdrawerproxymodel.h"
894
895 static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
896 {
897@@ -82,4 +83,5 @@
898 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
899 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
900 qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher");
901+ qmlRegisterType<AppDrawerProxyModel>(uri, 0, 1, "AppDrawerProxyModel");
902 }
903
904=== modified file 'qml/Components/KeyboardShortcutsOverlay.qml'
905--- qml/Components/KeyboardShortcutsOverlay.qml 2016-05-11 11:09:42 +0000
906+++ qml/Components/KeyboardShortcutsOverlay.qml 2016-12-06 13:47:05 +0000
907@@ -173,6 +173,19 @@
908 Layout.maximumWidth: maxTextSize
909 }
910
911+ Label {
912+ text: i18n.tr("Super + A")
913+ fontSize: "small"
914+ font.weight: Font.Medium
915+ }
916+ Label {
917+ text: i18n.tr("Opens the Application Drawer.")
918+ fontSize: "small"
919+ font.weight: Font.Light
920+ wrapMode: Text.Wrap
921+ Layout.maximumWidth: maxTextSize
922+ }
923+
924
925 // Scopes section
926 Item { Layout.columnSpan: 2; height: units.gu(2) }
927
928=== added file 'qml/Launcher/BackgroundBlur.qml'
929--- qml/Launcher/BackgroundBlur.qml 1970-01-01 00:00:00 +0000
930+++ qml/Launcher/BackgroundBlur.qml 2016-12-06 13:47:05 +0000
931@@ -0,0 +1,81 @@
932+/*
933+ * Copyright (C) 2016 Canonical, Ltd.
934+ *
935+ * This program is free software; you can redistribute it and/or modify
936+ * it under the terms of the GNU General Public License as published by
937+ * the Free Software Foundation; version 3.
938+ *
939+ * This program is distributed in the hope that it will be useful,
940+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
941+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
942+ * GNU General Public License for more details.
943+ *
944+ * You should have received a copy of the GNU General Public License
945+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
946+ */
947+
948+import QtQuick 2.4
949+import Ubuntu.Components 1.3
950+import QtGraphicalEffects 1.0
951+
952+Item {
953+ id: root
954+
955+ property int blurAmount: 32
956+ property Item sourceItem
957+ property rect blurRect: Qt.rect(0,0,0,0)
958+ property alias cached: fastBlur.cached
959+
960+ Rectangle {
961+ id: blurMask
962+ color: "yellow"
963+ x: blurRect.x
964+ y: blurRect.y
965+ width: blurRect.width
966+ height: blurRect.height
967+ }
968+
969+ ShaderEffect {
970+ id: maskedBlurEffect
971+ x: blurRect.x
972+ y: blurRect.y
973+ width: blurRect.width
974+ height: blurRect.height
975+
976+ property variant source: ShaderEffectSource {
977+ id: shaderEffectSource
978+ sourceItem: root.sourceItem
979+ hideSource: false
980+ sourceRect: root.blurRect
981+ }
982+
983+ property var mask: ShaderEffectSource {
984+ sourceItem: blurMask
985+ hideSource: true
986+ }
987+
988+ fragmentShader: "
989+ varying highp vec2 qt_TexCoord0;
990+ uniform sampler2D source;
991+ uniform sampler2D mask;
992+ void main(void)
993+ {
994+ highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
995+ highp vec4 maskColor = texture2D(mask, qt_TexCoord0);
996+
997+ sourceColor *= maskColor.a;
998+
999+ gl_FragColor = sourceColor;
1000+ }"
1001+ }
1002+
1003+ FastBlur {
1004+ id: fastBlur
1005+ x: blurRect.x
1006+ y: blurRect.y
1007+ width: blurRect.width
1008+ height: blurRect.height
1009+ source: maskedBlurEffect
1010+ radius: Math.min(blurAmount, 128)
1011+ }
1012+ }
1013
1014=== added file 'qml/Launcher/Drawer.qml'
1015--- qml/Launcher/Drawer.qml 1970-01-01 00:00:00 +0000
1016+++ qml/Launcher/Drawer.qml 2016-12-06 13:47:05 +0000
1017@@ -0,0 +1,306 @@
1018+/*
1019+ * Copyright (C) 2016 Canonical, Ltd.
1020+ *
1021+ * This program is free software; you can redistribute it and/or modify
1022+ * it under the terms of the GNU General Public License as published by
1023+ * the Free Software Foundation; version 3.
1024+ *
1025+ * This program is distributed in the hope that it will be useful,
1026+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1027+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1028+ * GNU General Public License for more details.
1029+ *
1030+ * You should have received a copy of the GNU General Public License
1031+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1032+ */
1033+
1034+import QtQuick 2.4
1035+import Ubuntu.Components 1.3
1036+import Unity.Launcher 0.1
1037+import Utils 0.1
1038+import "../Components"
1039+import Qt.labs.settings 1.0
1040+
1041+FocusScope {
1042+ id: root
1043+
1044+ property int panelWidth: 0
1045+ readonly property bool moving: listLoader.item && listLoader.item.moving
1046+
1047+ signal applicationSelected(string appId)
1048+
1049+ property bool draggingHorizontally: false
1050+ property int dragDistance: 0
1051+
1052+ onFocusChanged: {
1053+ if (focus) {
1054+ searchField.selectAll();
1055+ }
1056+ }
1057+
1058+ function focusInput() {
1059+ searchField.selectAll();
1060+ searchField.focus = true;
1061+ }
1062+
1063+ Settings {
1064+ property alias selectedTab: sections.selectedIndex
1065+ }
1066+
1067+ MouseArea {
1068+ anchors.fill: parent
1069+ hoverEnabled: true
1070+ acceptedButtons: Qt.AllButtons
1071+ onWheel: wheel.accepted = true
1072+ }
1073+
1074+ Rectangle {
1075+ anchors.fill: parent
1076+ color: "#BF000000"
1077+
1078+ AppDrawerModel {
1079+ id: appDrawerModel
1080+ }
1081+
1082+ AppDrawerProxyModel {
1083+ id: sortProxyModel
1084+ source: appDrawerModel
1085+ filterString: searchField.displayText
1086+ sortBy: AppDrawerProxyModel.SortByAToZ
1087+ }
1088+
1089+ Item {
1090+ id: contentContainer
1091+ anchors.fill: parent
1092+ anchors.leftMargin: root.panelWidth
1093+
1094+ TextField {
1095+ id: searchField
1096+ anchors { left: parent.left; top: parent.top; right: parent.right; margins: units.gu(1) }
1097+ placeholderText: i18n.tr("Search…")
1098+ focus: true
1099+ onAccepted: {
1100+ if (searchField.displayText != "" && listLoader.item && listLoader.item.currentItem) {
1101+ root.applicationSelected(listLoader.item.getFirstAppId());
1102+ }
1103+ }
1104+ }
1105+
1106+ Item {
1107+ id: sectionsContainer
1108+ anchors { left: parent.left; top: searchField.bottom; right: parent.right; }
1109+ height: sections.height
1110+ clip: true
1111+ z: 2
1112+
1113+ Sections {
1114+ id: sections
1115+ width: parent.width
1116+ actions: [
1117+ Action {
1118+ text: i18n.ctr("Apps sorted alphabetically", "A-Z")
1119+ // TODO: Disabling this for now as we don't get the right input from u-a-l yet.
1120+// },
1121+// Action {
1122+// text: i18n.ctr("Most used apps", "Most used")
1123+ }
1124+ ]
1125+
1126+ Rectangle {
1127+ anchors.bottom: parent.bottom
1128+ height: units.dp(1)
1129+ color: 'gray'
1130+ width: contentContainer.width
1131+ }
1132+ }
1133+ }
1134+
1135+ Loader {
1136+ id: listLoader
1137+ anchors { left: parent.left; top: sectionsContainer.bottom; right: parent.right; bottom: parent.bottom; leftMargin: units.gu(1); rightMargin: units.gu(1) }
1138+ sourceComponent: {
1139+ switch (sections.selectedIndex) {
1140+ case 0: return aToZComponent;
1141+ case 1: return mostUsedComponent;
1142+ }
1143+ }
1144+ Binding {
1145+ target: listLoader.item || null
1146+ property: "objectName"
1147+ value: "drawerItemList"
1148+ }
1149+ }
1150+
1151+ MouseArea {
1152+ parent: listLoader.item ? listLoader.item : null
1153+ anchors.fill: parent
1154+ propagateComposedEvents: true
1155+ property int oldX: 0
1156+ onPressed: {
1157+ oldX = mouseX;
1158+ }
1159+ onMouseXChanged: {
1160+ var diff = oldX - mouseX;
1161+ root.draggingHorizontally |= diff > units.gu(2);
1162+ if (!root.draggingHorizontally) {
1163+ return;
1164+ }
1165+ propagateComposedEvents = false;
1166+ parent.interactive = false;
1167+ root.dragDistance += diff;
1168+ oldX = mouseX
1169+ }
1170+ onReleased: {
1171+ if (root.draggingHorizontally) {
1172+ root.draggingHorizontally = false;
1173+ parent.interactive = true;
1174+ }
1175+ reactivateTimer.start();
1176+ }
1177+ Timer {
1178+ id: reactivateTimer
1179+ interval: 0
1180+ onTriggered: parent.propagateComposedEvents = true;
1181+ }
1182+ }
1183+
1184+ Component {
1185+ id: mostUsedComponent
1186+ DrawerListView {
1187+
1188+ header: MoreAppsHeader {
1189+ width: parent.width
1190+ height: units.gu(6)
1191+ }
1192+
1193+ model: AppDrawerProxyModel {
1194+ source: sortProxyModel
1195+ group: AppDrawerProxyModel.GroupByAll
1196+ sortBy: AppDrawerProxyModel.SortByUsage
1197+ }
1198+
1199+ delegate: UbuntuShape {
1200+ width: parent.width
1201+ color: "#20ffffff"
1202+ aspect: UbuntuShape.Flat
1203+ // NOTE: Cannot use gridView.rows here as it would evaluate to 0 at first and only update later,
1204+ // which messes up the ListView.
1205+ height: (Math.ceil(mostUsedGridView.model.count / mostUsedGridView.columns) * mostUsedGridView.delegateHeight) + units.gu(2)
1206+
1207+ readonly property string appId: model.appId
1208+
1209+ DrawerGridView {
1210+ id: mostUsedGridView
1211+ anchors.fill: parent
1212+ topMargin: units.gu(1)
1213+ bottomMargin: units.gu(1)
1214+ clip: true
1215+
1216+ model: sortProxyModel
1217+
1218+ delegateWidth: units.gu(8)
1219+ delegateHeight: units.gu(10)
1220+ delegate: drawerDelegateComponent
1221+ }
1222+ }
1223+ }
1224+ }
1225+
1226+ Component {
1227+ id: aToZComponent
1228+ DrawerListView {
1229+
1230+ header: MoreAppsHeader {
1231+ width: parent.width
1232+ height: units.gu(6)
1233+ }
1234+
1235+ model: AppDrawerProxyModel {
1236+ source: sortProxyModel
1237+ sortBy: AppDrawerProxyModel.SortByAToZ
1238+ group: AppDrawerProxyModel.GroupByAToZ
1239+ }
1240+
1241+ delegate: UbuntuShape {
1242+ width: parent.width
1243+ color: "#20ffffff"
1244+ aspect: UbuntuShape.Flat
1245+
1246+ readonly property string appId: model.appId
1247+
1248+ // NOTE: Cannot use gridView.rows here as it would evaluate to 0 at first and only update later,
1249+ // which messes up the ListView.
1250+ height: (Math.ceil(gridView.model.count / gridView.columns) * gridView.delegateHeight) +
1251+ categoryNameLabel.implicitHeight + units.gu(2)
1252+
1253+ Label {
1254+ id: categoryNameLabel
1255+ anchors { left: parent.left; top: parent.top; right: parent.right; margins: units.gu(1) }
1256+ text: model.letter
1257+ }
1258+
1259+ DrawerGridView {
1260+ id: gridView
1261+ anchors { left: parent.left; top: categoryNameLabel.bottom; right: parent.right; topMargin: units.gu(1) }
1262+ height: rows * delegateHeight
1263+
1264+ interactive: false
1265+
1266+ model: AppDrawerProxyModel {
1267+ id: categoryModel
1268+ source: sortProxyModel
1269+ filterLetter: model.letter
1270+ }
1271+ delegateWidth: units.gu(8)
1272+ delegateHeight: units.gu(10)
1273+ delegate: drawerDelegateComponent
1274+ }
1275+ }
1276+ }
1277+ }
1278+ }
1279+
1280+ Component {
1281+ id: drawerDelegateComponent
1282+ AbstractButton {
1283+ width: GridView.view.cellWidth
1284+ height: units.gu(10)
1285+ objectName: "drawerItem_" + model.appId
1286+
1287+ onClicked: root.applicationSelected(model.appId)
1288+
1289+ Column {
1290+ width: units.gu(8)
1291+ anchors.horizontalCenter: parent.horizontalCenter
1292+ height: childrenRect.height
1293+ spacing: units.gu(1)
1294+
1295+ UbuntuShape {
1296+ id: appIcon
1297+ width: units.gu(6)
1298+ height: 7.5 / 8 * width
1299+ anchors.horizontalCenter: parent.horizontalCenter
1300+ backgroundMode: UbuntuShape.SolidColor
1301+ backgroundColor: UbuntuColors.lightGrey
1302+ radius: "medium"
1303+ borderSource: 'undefined'
1304+ source: Image {
1305+ id: sourceImage
1306+ sourceSize.width: appIcon.width
1307+ source: model.icon
1308+ }
1309+ sourceFillMode: UbuntuShape.PreserveAspectCrop
1310+ }
1311+
1312+ Label {
1313+ text: model.name
1314+ width: parent.width
1315+ horizontalAlignment: Text.AlignHCenter
1316+ fontSize: "small"
1317+ elide: Text.ElideRight
1318+ }
1319+ }
1320+ }
1321+ }
1322+ }
1323+}
1324
1325=== added file 'qml/Launcher/DrawerGridView.qml'
1326--- qml/Launcher/DrawerGridView.qml 1970-01-01 00:00:00 +0000
1327+++ qml/Launcher/DrawerGridView.qml 2016-12-06 13:47:05 +0000
1328@@ -0,0 +1,47 @@
1329+/*
1330+ * Copyright (C) 2016 Canonical, Ltd.
1331+ *
1332+ * This program is free software; you can redistribute it and/or modify
1333+ * it under the terms of the GNU General Public License as published by
1334+ * the Free Software Foundation; version 3.
1335+ *
1336+ * This program is distributed in the hope that it will be useful,
1337+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1338+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1339+ * GNU General Public License for more details.
1340+ *
1341+ * You should have received a copy of the GNU General Public License
1342+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1343+ */
1344+
1345+import QtQuick 2.4
1346+import "../Components"
1347+
1348+Item {
1349+ id: root
1350+
1351+ property int delegateWidth: units.gu(10)
1352+ property int delegateHeight: units.gu(10)
1353+ property alias delegate: gridView.delegate
1354+ property alias model: gridView.model
1355+ property alias interactive: gridView.interactive
1356+
1357+ property alias header: gridView.header
1358+ property alias topMargin: gridView.topMargin
1359+ property alias bottomMargin: gridView.bottomMargin
1360+
1361+ readonly property int columns: width / delegateWidth
1362+ readonly property int rows: Math.ceil(gridView.model.count / root.columns)
1363+
1364+ GridView {
1365+ id: gridView
1366+ anchors.fill: parent
1367+ leftMargin: spacing
1368+
1369+ readonly property int overflow: width - (root.columns * root.delegateWidth)
1370+ readonly property real spacing: overflow / (root.columns)
1371+
1372+ cellWidth: root.delegateWidth + spacing
1373+ cellHeight: root.delegateHeight
1374+ }
1375+}
1376
1377=== added file 'qml/Launcher/DrawerListView.qml'
1378--- qml/Launcher/DrawerListView.qml 1970-01-01 00:00:00 +0000
1379+++ qml/Launcher/DrawerListView.qml 2016-12-06 13:47:05 +0000
1380@@ -0,0 +1,32 @@
1381+/*
1382+ * Copyright (C) 2016 Canonical, Ltd.
1383+ *
1384+ * This program is free software; you can redistribute it and/or modify
1385+ * it under the terms of the GNU General Public License as published by
1386+ * the Free Software Foundation; version 3.
1387+ *
1388+ * This program is distributed in the hope that it will be useful,
1389+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1390+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1391+ * GNU General Public License for more details.
1392+ *
1393+ * You should have received a copy of the GNU General Public License
1394+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1395+ */
1396+
1397+import QtQuick 2.4
1398+import Ubuntu.Components 1.3
1399+import "../Components"
1400+
1401+ListView {
1402+ id: root
1403+ anchors.fill: parent
1404+ topMargin: units.gu(1)
1405+ bottomMargin: units.gu(1)
1406+ spacing: units.gu(1)
1407+ clip: true
1408+
1409+ function getFirstAppId() {
1410+ return model.appId(0);
1411+ }
1412+}
1413
1414=== modified file 'qml/Launcher/Launcher.qml'
1415--- qml/Launcher/Launcher.qml 2016-09-22 10:14:18 +0000
1416+++ qml/Launcher/Launcher.qml 2016-12-06 13:47:05 +0000
1417@@ -27,10 +27,12 @@
1418 property bool lockedVisible: false
1419 property bool available: true // can be used to disable all interactions
1420 property alias inverted: panel.inverted
1421+ property Item blurSource: null
1422+ property int topPanelHeight: 0
1423+ property bool drawerEnabled: true
1424
1425 property int panelWidth: units.gu(10)
1426 property int dragAreaWidth: units.gu(1)
1427- property int minimizeDistance: units.gu(26)
1428 property real progress: dragArea.dragging && dragArea.touchPosition.x > panelWidth ?
1429 (width * (dragArea.touchPosition.x-panelWidth) / (width - panelWidth)) : 0
1430
1431@@ -43,13 +45,11 @@
1432 readonly property alias shortcutHintsShown: panel.shortcutHintsShown
1433
1434 readonly property bool shown: panel.x > -panel.width
1435+ readonly property bool drawerShown: drawer.x == 0
1436
1437 // emitted when an application is selected
1438 signal launcherApplicationSelected(string appId)
1439
1440- // emitted when the apps dash should be shown because of a swipe gesture
1441- signal dash()
1442-
1443 // emitted when the dash icon in the launcher has been tapped
1444 signal showDashHome()
1445
1446@@ -62,6 +62,9 @@
1447 }
1448
1449 onSuperPressedChanged: {
1450+ if (state == "drawer")
1451+ return;
1452+
1453 if (superPressed) {
1454 superPressTimer.start();
1455 superLongPressTimer.start();
1456@@ -132,7 +135,7 @@
1457 }
1458
1459 function pushEdge(amount) {
1460- if (root.state === "") {
1461+ if (root.state === "" || root.state == "visible" || root.state == "visibleTemporary") {
1462 edgeBarrier.push(amount);
1463 }
1464 }
1465@@ -143,6 +146,22 @@
1466 switchToNextState("visible")
1467 }
1468
1469+ function openDrawer(focusInputField) {
1470+ if (!drawerEnabled) {
1471+ return;
1472+ }
1473+
1474+ panel.shortcutHintsShown = false;
1475+ superPressTimer.stop();
1476+ superLongPressTimer.stop();
1477+ root.focus = true;
1478+ drawer.focus = true;
1479+ if (focusInputField) {
1480+ drawer.focusInput();
1481+ }
1482+ switchToNextState("drawer")
1483+ }
1484+
1485 Keys.onPressed: {
1486 switch (event.key) {
1487 case Qt.Key_Backtab:
1488@@ -278,7 +297,7 @@
1489 InverseMouseArea {
1490 id: closeMouseArea
1491 anchors.fill: panel
1492- enabled: root.state == "visible" && (!root.lockedVisible || panel.highlightIndex >= -1)
1493+ enabled: root.state == "visible" || root.state == "drawer"
1494 visible: enabled
1495 onPressed: {
1496 mouse.accepted = false;
1497@@ -307,23 +326,61 @@
1498 }
1499 }
1500
1501- EdgeBarrier {
1502- id: edgeBarrier
1503- edge: Qt.LeftEdge
1504- target: parent
1505- enabled: root.available
1506- onPassed: { root.switchToNextState("visibleTemporary"); }
1507- material: Component {
1508- Item {
1509- Rectangle {
1510- width: parent.height
1511- height: parent.width
1512- rotation: -90
1513- anchors.centerIn: parent
1514- gradient: Gradient {
1515- GradientStop { position: 0.0; color: Qt.rgba(panel.color.r, panel.color.g, panel.color.b, .5)}
1516- GradientStop { position: 1.0; color: Qt.rgba(panel.color.r,panel.color.g,panel.color.b,0)}
1517- }
1518+ BackgroundBlur {
1519+ id: backgroundBlur
1520+ anchors.fill: parent
1521+ anchors.topMargin: root.inverted ? 0 : -root.topPanelHeight
1522+ visible: root.blurSource && drawer.x > -drawer.width
1523+ blurAmount: units.gu(6)
1524+ sourceItem: root.blurSource
1525+ blurRect: Qt.rect(panel.width,
1526+ root.topPanelHeight,
1527+ drawer.width + drawer.x - panel.width,
1528+ height - root.topPanelHeight)
1529+ cached: drawer.moving
1530+ }
1531+
1532+ Drawer {
1533+ id: drawer
1534+ objectName: "drawer"
1535+ anchors {
1536+ top: parent.top
1537+ topMargin: root.inverted ? root.topPanelHeight : 0
1538+ bottom: parent.bottom
1539+ right: parent.left
1540+ }
1541+ width: Math.min(root.width, units.gu(90)) * .9
1542+ panelWidth: panel.width
1543+ visible: x > -width
1544+
1545+ Behavior on anchors.rightMargin {
1546+ enabled: !dragArea.dragging && !launcherDragArea.drag.active && panel.animate && !drawer.draggingHorizontally
1547+ NumberAnimation {
1548+ duration: 300
1549+ easing.type: Easing.OutCubic
1550+ }
1551+ }
1552+
1553+ onApplicationSelected: {
1554+ root.hide();
1555+ root.launcherApplicationSelected(appId)
1556+ root.focus = false;
1557+ }
1558+
1559+ Keys.onEscapePressed: {
1560+ switchToNextState("");
1561+ root.focus = false;
1562+ }
1563+
1564+ onDragDistanceChanged: {
1565+ anchors.rightMargin = Math.max(-drawer.width, anchors.rightMargin + dragDistance);
1566+ }
1567+ onDraggingHorizontallyChanged: {
1568+ if (!draggingHorizontally) {
1569+ if (drawer.x < -units.gu(10)) {
1570+ root.hide();
1571+ } else {
1572+ root.openDrawer();
1573 }
1574 }
1575 }
1576@@ -332,7 +389,7 @@
1577 LauncherPanel {
1578 id: panel
1579 objectName: "launcherPanel"
1580- enabled: root.available && root.state == "visible" || root.state == "visibleTemporary"
1581+ enabled: root.available && (root.state == "visible" || root.state == "visibleTemporary" || root.state == "drawer")
1582 width: root.panelWidth
1583 anchors {
1584 top: parent.top
1585@@ -394,6 +451,38 @@
1586 }
1587 }
1588
1589+ EdgeBarrier {
1590+ id: edgeBarrier
1591+ edge: Qt.LeftEdge
1592+ target: parent
1593+ enabled: root.available
1594+ onProgressChanged: {
1595+ if (progress > .5 && root.state != "visibleTemporary" && root.state != "drawer" && root.state != "visible") {
1596+ root.switchToNextState("visibleTemporary");
1597+ }
1598+ }
1599+ onPassed: {
1600+ if (root.drawerEnabled) {
1601+ root.switchToNextState("drawer");
1602+ }
1603+ }
1604+
1605+ material: Component {
1606+ Item {
1607+ Rectangle {
1608+ width: parent.height
1609+ height: parent.width
1610+ rotation: -90
1611+ anchors.centerIn: parent
1612+ gradient: Gradient {
1613+ GradientStop { position: 0.0; color: Qt.rgba(panel.color.r, panel.color.g, panel.color.b, .5)}
1614+ GradientStop { position: 1.0; color: Qt.rgba(panel.color.r,panel.color.g,panel.color.b,0)}
1615+ }
1616+ }
1617+ }
1618+ }
1619+ }
1620+
1621 SwipeArea {
1622 id: dragArea
1623 objectName: "launcherDragArea"
1624@@ -405,25 +494,62 @@
1625 width: root.dragAreaWidth
1626 height: root.height
1627
1628+ function easeInOutCubic(t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 }
1629+
1630+ property var lastDragPoints: []
1631+
1632+ function dragDirection() {
1633+ if (lastDragPoints.length < 5) {
1634+ return "unknown";
1635+ }
1636+
1637+ var toRight = true;
1638+ var toLeft = true;
1639+ for (var i = lastDragPoints.length - 5; i < lastDragPoints.length; i++) {
1640+ if (toRight && lastDragPoints[i] < lastDragPoints[i-1]) {
1641+ toRight = false;
1642+ }
1643+ if (toLeft && lastDragPoints[i] > lastDragPoints[i-1]) {
1644+ toLeft = false;
1645+ }
1646+ }
1647+ return toRight ? "right" : toLeft ? "left" : "unknown";
1648+ }
1649+
1650 onDistanceChanged: {
1651- if (!dragging || launcher.state == "visible")
1652- return;
1653+ if (dragging && launcher.state != "visible" && launcher.state != "drawer") {
1654+ panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
1655+ }
1656
1657- panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
1658+ if (root.drawerEnabled && dragging && launcher.state != "drawer") {
1659+ lastDragPoints.push(distance)
1660+ var drawerHintDistance = panel.width + units.gu(1)
1661+ if (distance < drawerHintDistance) {
1662+ drawer.anchors.rightMargin = -Math.min(Math.max(0, distance), drawer.width);
1663+ } else {
1664+ var linearDrawerX = Math.min(Math.max(0, distance - drawerHintDistance), drawer.width);
1665+ var linearDrawerProgress = linearDrawerX / (drawer.width)
1666+ var easedDrawerProgress = easeInOutCubic(linearDrawerProgress);
1667+ drawer.anchors.rightMargin = -(drawerHintDistance + easedDrawerProgress * (drawer.width - drawerHintDistance));
1668+ }
1669+ }
1670 }
1671
1672 onDraggingChanged: {
1673 if (!dragging) {
1674 if (distance > panel.width / 2) {
1675- root.switchToNextState("visible")
1676- if (distance > minimizeDistance) {
1677- root.dash();
1678+ if (root.drawerEnabled && distance > panel.width * 3 && dragDirection() !== "left") {
1679+ root.switchToNextState("drawer");
1680+ root.focus = true;
1681+ } else {
1682+ root.switchToNextState("visible");
1683 }
1684 } else if (root.state === "") {
1685 // didn't drag far enough. rollback
1686- root.switchToNextState("")
1687+ root.switchToNextState("");
1688 }
1689 }
1690+ lastDragPoints = [];
1691 }
1692 }
1693
1694@@ -434,6 +560,10 @@
1695 target: panel
1696 x: -root.panelWidth
1697 }
1698+ PropertyChanges {
1699+ target: drawer
1700+ anchors.rightMargin: 0
1701+ }
1702 },
1703 State {
1704 name: "visible"
1705@@ -441,6 +571,18 @@
1706 target: panel
1707 x: -root.x // so we never go past panelWidth, even when teased by tutorial
1708 }
1709+ PropertyChanges {
1710+ target: drawer
1711+ anchors.rightMargin: 0
1712+ }
1713+ },
1714+ State {
1715+ name: "drawer"
1716+ extend: "visible"
1717+ PropertyChanges {
1718+ target: drawer
1719+ anchors.rightMargin: -drawer.width + root.x // so we never go past panelWidth, even when teased by tutorial
1720+ }
1721 },
1722 State {
1723 name: "visibleTemporary"
1724
1725=== added file 'qml/Launcher/MoreAppsHeader.qml'
1726--- qml/Launcher/MoreAppsHeader.qml 1970-01-01 00:00:00 +0000
1727+++ qml/Launcher/MoreAppsHeader.qml 2016-12-06 13:47:05 +0000
1728@@ -0,0 +1,46 @@
1729+import QtQuick 2.4
1730+import Ubuntu.Components 1.3
1731+
1732+AbstractButton {
1733+ id: root
1734+
1735+ onClicked: {
1736+ // TODO: Make this point to the snappy store as soon as we stop landing to vivid
1737+ Qt.openUrlExternally("scope://com.canonical.scopes.clickstore")
1738+ }
1739+
1740+ UbuntuShape {
1741+ width: parent.width
1742+ height: parent.height - units.gu(1)
1743+ color: "#20ffffff"
1744+ aspect: UbuntuShape.Flat
1745+
1746+ Row {
1747+ anchors.fill: parent
1748+ anchors.margins: units.gu(1)
1749+ spacing: units.gu(1)
1750+
1751+ Icon {
1752+ height: units.gu(2.2)
1753+ width: height
1754+ name: "stock_application"
1755+ anchors.verticalCenter: parent.verticalCenter
1756+ color: "white"
1757+ }
1758+
1759+ Label {
1760+ text: i18n.tr("More apps in the store")
1761+ anchors.verticalCenter: parent.verticalCenter
1762+ fontSize: "small"
1763+ }
1764+
1765+ Icon {
1766+ height: units.gu(2.5)
1767+ width: height
1768+ anchors.verticalCenter: parent.verticalCenter
1769+ name: "go-next"
1770+ color: "white"
1771+ }
1772+ }
1773+ }
1774+}
1775
1776=== modified file 'qml/Shell.qml'
1777--- qml/Shell.qml 2016-11-29 09:39:56 +0000
1778+++ qml/Shell.qml 2016-12-06 13:47:05 +0000
1779@@ -278,8 +278,6 @@
1780
1781 dragAreaWidth: shell.edgeSize
1782 background: wallpaperResolver.background
1783- leftEdgeDragProgress: !greeter || greeter.locked || !tutorial.launcherLongSwipeEnabled ? 0 :
1784- Math.max(0, (launcher.dragDistance * (stage.width - launcher.panelWidth) / stage.width) - launcher.panelWidth)
1785
1786 applicationManager: ApplicationManager
1787 topLevelSurfaceList: topLevelSurfaceList
1788@@ -301,14 +299,12 @@
1789
1790 interactive: (!greeter || !greeter.shown)
1791 && panel.indicators.fullyClosed
1792- && launcher.progress == 0
1793 && !notifications.useModal
1794
1795 onInteractiveChanged: { if (interactive) { focus = true; } }
1796
1797 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
1798 suspended: greeter.shown
1799- keepDashRunning: launcher.shown || launcher.dashSwipe
1800 altTabPressed: physicalKeysMapper.altTabPressed
1801 oskEnabled: shell.oskEnabled
1802 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
1803@@ -323,7 +319,7 @@
1804 topMargin: panel.panelHeight
1805 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
1806 }
1807- z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
1808+ z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
1809 }
1810
1811 Loader {
1812@@ -345,7 +341,6 @@
1813 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
1814 hides: [launcher, panel.indicators]
1815 tabletMode: shell.usageScenario != "phone"
1816- launcherOffset: launcher.progress
1817 forcedUnlock: wizard.active || shell.mode === "full-shell"
1818 background: wallpaperResolver.cachedBackground
1819 hasCustomBackground: wallpaperResolver.hasCustomBackground
1820@@ -434,19 +429,6 @@
1821 }
1822 }
1823
1824- function showDash() {
1825- if (greeter.notifyShowingDashFromDrag()) {
1826- launcher.fadeOut();
1827- }
1828-
1829- if (!greeter.locked && tutorial.launcherLongSwipeEnabled
1830- && (ApplicationManager.focusedApplicationId != "unity8-dash" || stage.spreadShown)) {
1831- ApplicationManager.requestFocusApplication("unity8-dash")
1832- launcher.fadeOut();
1833- stage.closeSpread();
1834- }
1835- }
1836-
1837 Item {
1838 id: overlay
1839 z: 10
1840@@ -497,15 +479,6 @@
1841 id: launcher
1842 objectName: "launcher"
1843
1844- /*
1845- * Since the Dash doesn't have the same controll over surfaces that the
1846- * Shell does, it can't slowly move the scope out of the way, as the shell
1847- * does with apps, and the dash is show instantly. This allows for some
1848- * leeway and prevents accidental home swipes.
1849- */
1850- readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
1851- readonly property bool dashSwipe: progress > offset
1852-
1853 anchors.top: parent.top
1854 anchors.topMargin: inverted ? 0 : panel.panelHeight
1855 anchors.bottom: parent.bottom
1856@@ -521,14 +494,11 @@
1857 superTabPressed: physicalKeysMapper.superTabPressed
1858 panelWidth: units.gu(settings.launcherWidth)
1859 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
1860+ blurSource: greeter.shown ? greeter : stages
1861+ topPanelHeight: panel.panelHeight
1862+ drawerEnabled: !greeter.shown
1863
1864 onShowDashHome: showHome()
1865- onDash: showDash()
1866- onDashSwipeChanged: {
1867- if (dashSwipe) {
1868- dash.setCurrentScope(0, false, true)
1869- }
1870- }
1871 onLauncherApplicationSelected: {
1872 greeter.notifyUserRequestedApp();
1873 shell.activateApplication(appId);
1874@@ -545,6 +515,12 @@
1875 }
1876
1877 GlobalShortcut {
1878+ shortcut: Qt.MetaModifier | Qt.Key_A
1879+ onTriggered: {
1880+ launcher.openDrawer(true);
1881+ }
1882+ }
1883+ GlobalShortcut {
1884 shortcut: Qt.AltModifier | Qt.Key_F1
1885 onTriggered: {
1886 launcher.openForKeyboardNavigation();
1887
1888=== modified file 'qml/Stage/Spread/WindowedRightEdgeMaths.qml'
1889--- qml/Stage/Spread/WindowedRightEdgeMaths.qml 2016-09-20 12:03:51 +0000
1890+++ qml/Stage/Spread/WindowedRightEdgeMaths.qml 2016-12-06 13:47:05 +0000
1891@@ -25,21 +25,31 @@
1892 // Input
1893 property int itemIndex: 0
1894 property int normalZ: 0
1895- property real progress: 0
1896 property int startWidth: 0
1897 property int startHeight: 0
1898 property int startX: 0
1899 property int targetX: 0
1900 property int startY: 0
1901 property int targetY: 0
1902-// property real startAngle: 40
1903 property real targetAngle: 0
1904 property int targetHeight: 0
1905 property real targetScale: 0
1906+ property real swipeProgress: 0
1907+ property real pushProgress: 0
1908
1909 // Config
1910 property real breakPoint: 0.4
1911
1912+ // internal
1913+ readonly property real progress: {
1914+ if (pushProgress > 0) {
1915+ // we don't do the full animation when pushing, just a little bit
1916+ return MathUtils.linearAnimation(0, 1, 0, breakPoint + .1, pushProgress)
1917+ } else {
1918+ return swipeProgress;
1919+ }
1920+ }
1921+
1922 // Output
1923
1924 readonly property real scaleToPreviewProgress: {
1925@@ -68,12 +78,17 @@
1926
1927 readonly property int animatedZ: {
1928 if (progress < breakPoint + (1 - breakPoint) / 2) {
1929- return itemIndex == 1 ? normalZ + 2 : normalZ
1930+ if (swipeProgress > 0) {
1931+ return itemIndex == 1 ? normalZ + 2 : normalZ
1932+ }
1933+ if (pushProgress > 0) {
1934+ return normalZ;
1935+ }
1936 }
1937 return itemIndex
1938 }
1939
1940- readonly property real opacityMask: itemIndex == 1 ? MathUtils.linearAnimation(0, breakPoint, 0, 1, progress) : 1
1941+ readonly property real opacityMask: (swipeProgress > 0 && itemIndex == 1) ? MathUtils.linearAnimation(0, breakPoint, 0, 1, progress) : 1
1942
1943 readonly property real animatedScale: progress < breakPoint ? 1 : MathUtils.linearAnimation(breakPoint, 1, 1, targetScale, progress)
1944
1945
1946=== modified file 'qml/Stage/Stage.qml'
1947--- qml/Stage/Stage.qml 2016-11-29 09:39:25 +0000
1948+++ qml/Stage/Stage.qml 2016-12-06 13:47:05 +0000
1949@@ -36,7 +36,6 @@
1950 property url background
1951 property int dragAreaWidth
1952 property bool interactive
1953- property bool keepDashRunning: true
1954 property real nativeHeight
1955 property real nativeWidth
1956 property QtObject orientations
1957@@ -50,7 +49,6 @@
1958
1959 // Configuration
1960 property string mode: "staged"
1961- property real leftEdgeDragProgress: 0
1962
1963 // Used by the tutorial code
1964 readonly property real rightEdgeDragProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0 // How far left the stage has been dragged
1965@@ -415,7 +413,7 @@
1966 // in staged mode, when it switches to Windowed mode it will suddenly
1967 // resume all those apps at once. We might want to avoid that.
1968 value: root.mode === "windowed"
1969- || (isDash && root.keepDashRunning)
1970+ || isDash
1971 || (!root.suspended && model.application && priv.focusedAppDelegate &&
1972 (priv.focusedAppDelegate.appId === model.application.appId ||
1973 priv.mainStageAppId === model.application.appId ||
1974@@ -478,7 +476,7 @@
1975 visible: true
1976 blurRadius: 32
1977 brightness: .65
1978- opacity: MathUtils.linearAnimation(spreadItem.rightEdgeBreakPoint, 1, 0, 1, Math.max(rightEdgeDragArea.progress, edgeBarrier.progress))
1979+ opacity: MathUtils.linearAnimation(spreadItem.rightEdgeBreakPoint, 1, 0, 1, Math.max(rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0, edgeBarrier.progress))
1980 }
1981 },
1982 State {
1983@@ -1061,7 +1059,6 @@
1984 sideStageX: sideStage.x
1985 itemIndex: appDelegate.itemIndex
1986 nextInStack: priv.nextInStack
1987- leftEdgeDragProgress: root.leftEdgeDragProgress
1988 }
1989
1990 StagedRightEdgeMaths {
1991@@ -1164,7 +1161,8 @@
1992 when: root.mode == "windowed" && (root.state == "windowedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running || edgeBarrier.progress > 0)
1993 PropertyChanges {
1994 target: windowedRightEdgeMaths
1995- progress: Math.max(rightEdgeDragArea.progress, edgeBarrier.progress)
1996+ swipeProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0
1997+ pushProgress: edgeBarrier.progress
1998 }
1999 PropertyChanges {
2000 target: appDelegate
2001
2002=== modified file 'qml/Stage/StageMaths.qml'
2003--- qml/Stage/StageMaths.qml 2016-09-13 13:15:09 +0000
2004+++ qml/Stage/StageMaths.qml 2016-12-06 13:47:05 +0000
2005@@ -12,7 +12,6 @@
2006 property int sideStageWidth: 0
2007 property int sideStageX: sceneWidth
2008 property bool animateX: false
2009- property int leftEdgeDragProgress: 0
2010
2011 property int stage: ApplicationInfoInterface.MainStage
2012 property var thisDelegate: null
2013@@ -26,10 +25,6 @@
2014 // of the last focused order.
2015 readonly property int itemZ: {
2016 // only shuffle when we've got a main and side stage
2017- if (thisDelegate.isDash && thisDelegate != mainStageDelegate && leftEdgeDragProgress > 0) {
2018- return -1; // Keep the dash behind all other apps for the left edge gesture
2019- }
2020-
2021 if (!sideStageDelegate) return itemIndex;
2022
2023 // don't shuffle indexes greater than "actives or next"
2024@@ -64,12 +59,12 @@
2025
2026 property int itemX: {
2027 if (mainStageDelegate == thisDelegate) {
2028- return thisDelegate.isDash ? 0 : leftEdgeDragProgress;
2029+ return 0
2030 }
2031 if (sideStageDelegate == thisDelegate) {
2032 return sideStageX;
2033 }
2034- return thisDelegate.isDash && leftEdgeDragProgress > 0 ? 0 : sceneWidth;
2035+ return sceneWidth;
2036 }
2037 Behavior on itemX { enabled: root.animateX; UbuntuNumberAnimation {} }
2038
2039
2040=== modified file 'qml/Tutorial/TutorialLeftLong.qml'
2041--- qml/Tutorial/TutorialLeftLong.qml 2016-04-29 09:12:21 +0000
2042+++ qml/Tutorial/TutorialLeftLong.qml 2016-12-06 13:47:05 +0000
2043@@ -25,7 +25,7 @@
2044
2045 // Unlike other tutorials, this one can't be skipped before we show it, so
2046 // only set opacityOverride if we're already shown.
2047- opacityOverride: shown ? 1 - launcher.dragDistance / launcher.minimizeDistance : 1
2048+ opacityOverride: shown ? 1 - launcher.dragDistance / (launcher.panelWidth * 3) : 1
2049
2050 mouseArea {
2051 anchors.leftMargin: launcher.dragAreaWidth
2052
2053=== modified file 'tests/mocks/Unity/Launcher/CMakeLists.txt'
2054--- tests/mocks/Unity/Launcher/CMakeLists.txt 2016-10-28 12:08:59 +0000
2055+++ tests/mocks/Unity/Launcher/CMakeLists.txt 2016-12-06 13:47:05 +0000
2056@@ -1,7 +1,8 @@
2057-pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=10)
2058+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=11)
2059
2060 include_directories(
2061 ${CMAKE_CURRENT_SOURCE_DIR}
2062+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/
2063 )
2064
2065 set(MockLauncherModel_SOURCES
2066@@ -9,10 +10,13 @@
2067 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
2068 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
2069 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
2070+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/AppDrawerModelInterface.h
2071 plugin.cpp
2072 MockLauncherModel.cpp
2073 MockLauncherItem.cpp
2074 MockQuickListModel.cpp
2075+ MockAppDrawerModel.cpp
2076+ ${CMAKE_SOURCE_DIR}/plugins/Utils/applicationsfiltermodel.cpp
2077 )
2078
2079 add_library(MockLauncherPlugin MODULE ${MockLauncherModel_SOURCES})
2080
2081=== added file 'tests/mocks/Unity/Launcher/MockAppDrawerModel.cpp'
2082--- tests/mocks/Unity/Launcher/MockAppDrawerModel.cpp 1970-01-01 00:00:00 +0000
2083+++ tests/mocks/Unity/Launcher/MockAppDrawerModel.cpp 2016-12-06 13:47:05 +0000
2084@@ -0,0 +1,75 @@
2085+/*
2086+ * Copyright (C) 2016 Canonical, Ltd.
2087+ *
2088+ * This program is free software; you can redistribute it and/or modify
2089+ * it under the terms of the GNU General Public License as published by
2090+ * the Free Software Foundation; version 3.
2091+ *
2092+ * This program is distributed in the hope that it will be useful,
2093+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2094+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2095+ * GNU General Public License for more details.
2096+ *
2097+ * You should have received a copy of the GNU General Public License
2098+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2099+ */
2100+
2101+#include "MockAppDrawerModel.h"
2102+
2103+#include <QDebug>
2104+#include <QDateTime>
2105+
2106+MockAppDrawerModel::MockAppDrawerModel(QObject *parent):
2107+ AppDrawerModelInterface(parent)
2108+{
2109+ MockLauncherItem *item = new MockLauncherItem("dialer-app", "/usr/share/applications/dialer-app.desktop", "Dialer", "dialer-app", this);
2110+ m_list.append(item);
2111+ item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
2112+ m_list.append(item);
2113+ item = new MockLauncherItem("camera-app2", "/usr/share/applications/camera-app2.desktop", "Camera2", "camera", this);
2114+ m_list.append(item);
2115+ item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
2116+ m_list.append(item);
2117+ item = new MockLauncherItem("music-app", "/usr/share/applications/music-app.desktop", "Music", "soundcloud", this);
2118+ m_list.append(item);
2119+ item = new MockLauncherItem("facebook-webapp", "/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook", this);
2120+ m_list.append(item);
2121+ item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
2122+ m_list.append(item);
2123+ item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
2124+ m_list.append(item);
2125+ item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
2126+ m_list.append(item);
2127+ item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
2128+ m_list.append(item);
2129+ item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
2130+ m_list.append(item);
2131+ item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
2132+ m_list.append(item);
2133+ item = new MockLauncherItem("libreoffice", "/usr/share/applications/libreoffice.desktop","Libre Office", "libreoffice", this);
2134+ m_list.append(item);
2135+ qDebug() << "mock model created";
2136+
2137+ qsrand(QDateTime::currentMSecsSinceEpoch() / 1000);
2138+}
2139+
2140+int MockAppDrawerModel::rowCount(const QModelIndex &parent) const
2141+{
2142+ return m_list.count();
2143+}
2144+
2145+QVariant MockAppDrawerModel::data(const QModelIndex &index, int role) const
2146+{
2147+ switch (role) {
2148+ case RoleAppId:
2149+ return m_list.at(index.row())->appId();
2150+ case RoleName:
2151+ return m_list.at(index.row())->name();
2152+ case RoleIcon:
2153+ return m_list.at(index.row())->icon();
2154+ case RoleUsage:
2155+ return qrand();
2156+ }
2157+
2158+ return QVariant();
2159+}
2160
2161=== added file 'tests/mocks/Unity/Launcher/MockAppDrawerModel.h'
2162--- tests/mocks/Unity/Launcher/MockAppDrawerModel.h 1970-01-01 00:00:00 +0000
2163+++ tests/mocks/Unity/Launcher/MockAppDrawerModel.h 2016-12-06 13:47:05 +0000
2164@@ -0,0 +1,32 @@
2165+/*
2166+ * Copyright (C) 2016 Canonical, Ltd.
2167+ *
2168+ * This program is free software; you can redistribute it and/or modify
2169+ * it under the terms of the GNU General Public License as published by
2170+ * the Free Software Foundation; version 3.
2171+ *
2172+ * This program is distributed in the hope that it will be useful,
2173+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2174+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2175+ * GNU General Public License for more details.
2176+ *
2177+ * You should have received a copy of the GNU General Public License
2178+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2179+ */
2180+
2181+#include <unity/shell/launcher/AppDrawerModelInterface.h>
2182+
2183+#include "MockLauncherItem.h"
2184+
2185+class MockAppDrawerModel: public AppDrawerModelInterface
2186+{
2187+ Q_OBJECT
2188+public:
2189+ MockAppDrawerModel(QObject* parent = nullptr);
2190+
2191+ int rowCount(const QModelIndex &parent) const override;
2192+ QVariant data(const QModelIndex &index, int role) const override;
2193+
2194+private:
2195+ QList<MockLauncherItem*> m_list;
2196+};
2197
2198=== modified file 'tests/mocks/Unity/Launcher/MockLauncherItem.cpp'
2199--- tests/mocks/Unity/Launcher/MockLauncherItem.cpp 2016-05-13 11:28:56 +0000
2200+++ tests/mocks/Unity/Launcher/MockLauncherItem.cpp 2016-12-06 13:47:05 +0000
2201@@ -67,6 +67,11 @@
2202 return m_icon;
2203 }
2204
2205+QStringList MockLauncherItem::keywords() const
2206+{
2207+ return m_keywords;
2208+}
2209+
2210 bool MockLauncherItem::pinned() const
2211 {
2212 return m_pinned;
2213@@ -200,3 +205,11 @@
2214 {
2215 return m_quickList;
2216 }
2217+
2218+void MockLauncherItem::setKeywords(const QStringList &keywords)
2219+{
2220+ if (m_keywords != keywords) {
2221+ m_keywords = keywords;
2222+ Q_EMIT keywordsChanged(keywords);
2223+ }
2224+}
2225
2226=== modified file 'tests/mocks/Unity/Launcher/MockLauncherItem.h'
2227--- tests/mocks/Unity/Launcher/MockLauncherItem.h 2016-05-10 15:51:00 +0000
2228+++ tests/mocks/Unity/Launcher/MockLauncherItem.h 2016-12-06 13:47:05 +0000
2229@@ -22,6 +22,8 @@
2230
2231 #include <unity/shell/launcher/LauncherItemInterface.h>
2232
2233+#include <QStringList>
2234+
2235 class MockQuickListModel;
2236
2237 using namespace unity::shell::launcher;
2238@@ -37,6 +39,7 @@
2239 QString desktopFile() const;
2240 QString name() const override;
2241 QString icon() const override;
2242+ QStringList keywords() const override;
2243
2244 bool pinned() const override;
2245 bool running() const override;
2246@@ -51,6 +54,7 @@
2247 unity::shell::launcher::QuickListModelInterface *quickList() const override;
2248
2249 private:
2250+ void setKeywords(const QStringList &keywords);
2251 void setPinned(bool pinned);
2252 void setRunning(bool running);
2253 void setRecent(bool recent);
2254@@ -65,6 +69,7 @@
2255 QString m_desktopFile;
2256 QString m_name;
2257 QString m_icon;
2258+ QStringList m_keywords;
2259 bool m_pinned;
2260 bool m_running;
2261 bool m_recent;
2262@@ -77,6 +82,7 @@
2263 MockQuickListModel *m_quickList;
2264
2265 friend class MockLauncherModel;
2266+ friend class MockAppDrawerModel;
2267 };
2268
2269 #endif // MOCKLAUNCHERITEM_H
2270
2271=== modified file 'tests/mocks/Unity/Launcher/MockLauncherModel.cpp'
2272--- tests/mocks/Unity/Launcher/MockLauncherModel.cpp 2016-06-17 01:13:55 +0000
2273+++ tests/mocks/Unity/Launcher/MockLauncherModel.cpp 2016-12-06 13:47:05 +0000
2274@@ -36,6 +36,9 @@
2275 item = new MockLauncherItem("camera-app2", "/usr/share/applications/camera-app2.desktop", "Camera2", "camera", this);
2276 item->setPinned(true);
2277 m_list.append(item);
2278+ item = new MockLauncherItem("camera-app3", "/usr/share/applications/camera-app2.desktop", "Camera2", "camera", this);
2279+ item->setPinned(true);
2280+ m_list.append(item);
2281 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
2282 item->setProgress(50);
2283 item->setCountVisible(true);
2284
2285=== modified file 'tests/mocks/Unity/Launcher/plugin.cpp'
2286--- tests/mocks/Unity/Launcher/plugin.cpp 2013-08-19 15:10:58 +0000
2287+++ tests/mocks/Unity/Launcher/plugin.cpp 2016-12-06 13:47:05 +0000
2288@@ -21,9 +21,11 @@
2289 #include "MockLauncherModel.h"
2290 #include "MockLauncherItem.h"
2291 #include "MockQuickListModel.h"
2292+#include "MockAppDrawerModel.h"
2293
2294 #include <unity/shell/launcher/LauncherModelInterface.h>
2295 #include <unity/shell/launcher/LauncherItemInterface.h>
2296+#include <unity/shell/launcher/AppDrawerModelInterface.h>
2297
2298 #include <QtQml/qqml.h>
2299
2300@@ -41,8 +43,10 @@
2301 qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
2302 qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
2303 qmlRegisterUncreatableType<QuickListModelInterface>(uri, 0, 1, "QuickListModelInterface", "Abstract Interface. Cannot be instantiated.");
2304+ qmlRegisterUncreatableType<AppDrawerModelInterface>(uri, 0, 1, "AppDrawerModelInterface", "Abstract Interface. Cannot be instantiated.");
2305
2306 qmlRegisterSingletonType<MockLauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
2307 qmlRegisterUncreatableType<MockLauncherItem>(uri, 0, 1, "LauncherItem", "Can't create LauncherItems in QML. Get them from the LauncherModel");
2308 qmlRegisterUncreatableType<MockQuickListModel>(uri, 0, 1, "QuickListModel", "Can't create QuickLists in QML. Get them from the LauncherItems");
2309+ qmlRegisterType<MockAppDrawerModel>(uri, 0, 1, "AppDrawerModel");
2310 }
2311
2312=== modified file 'tests/mocks/Utils/CMakeLists.txt'
2313--- tests/mocks/Utils/CMakeLists.txt 2016-06-29 18:05:44 +0000
2314+++ tests/mocks/Utils/CMakeLists.txt 2016-12-06 13:47:05 +0000
2315@@ -24,11 +24,15 @@
2316 ${CMAKE_SOURCE_DIR}/plugins/Utils/inputeventgenerator.cpp
2317 ${CMAKE_SOURCE_DIR}/plugins/Utils/deviceconfigparser.cpp
2318 ${CMAKE_SOURCE_DIR}/plugins/Utils/globalfunctions.cpp
2319+ ${CMAKE_SOURCE_DIR}/plugins/Utils/appdrawerproxymodel.cpp
2320 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
2321 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
2322 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
2323 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
2324 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
2325+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/AppDrawerModelInterface.h
2326+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
2327+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
2328 constants.cpp
2329 plugin.cpp
2330 windowstatestorage.cpp
2331
2332=== modified file 'tests/mocks/Utils/plugin.cpp'
2333--- tests/mocks/Utils/plugin.cpp 2016-06-28 20:38:00 +0000
2334+++ tests/mocks/Utils/plugin.cpp 2016-12-06 13:47:05 +0000
2335@@ -40,6 +40,7 @@
2336 #include <inputeventgenerator.h>
2337 #include <deviceconfigparser.h>
2338 #include <globalfunctions.h>
2339+#include <appdrawerproxymodel.h>
2340
2341 static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
2342 {
2343@@ -82,4 +83,5 @@
2344 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
2345 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
2346 qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher");
2347+ qmlRegisterType<AppDrawerProxyModel>(uri, 0, 1, "AppDrawerProxyModel");
2348 }
2349
2350=== modified file 'tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt'
2351--- tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt 2016-10-28 12:08:59 +0000
2352+++ tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt 2016-12-06 13:47:05 +0000
2353@@ -1,4 +1,4 @@
2354-pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=10)
2355+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=11)
2356
2357 include_directories(
2358 ${CMAKE_CURRENT_SOURCE_DIR}
2359
2360=== modified file 'tests/plugins/Unity/Launcher/CMakeLists.txt'
2361--- tests/plugins/Unity/Launcher/CMakeLists.txt 2016-10-28 12:08:59 +0000
2362+++ tests/plugins/Unity/Launcher/CMakeLists.txt 2016-12-06 13:47:05 +0000
2363@@ -1,5 +1,5 @@
2364 pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2365-pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=10)
2366+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=11)
2367
2368 include_directories(
2369 ${CMAKE_CURRENT_SOURCE_DIR}
2370@@ -30,6 +30,7 @@
2371 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistmodel.cpp
2372 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/dbusinterface.cpp
2373 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistentry.cpp
2374+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/ualwrapper.cpp
2375 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
2376 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
2377 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
2378
2379=== modified file 'tests/qmltests/CMakeLists.txt'
2380--- tests/qmltests/CMakeLists.txt 2016-09-22 14:19:36 +0000
2381+++ tests/qmltests/CMakeLists.txt 2016-12-06 13:47:05 +0000
2382@@ -71,6 +71,7 @@
2383 add_unity8_qmltest(Greeter NarrowView)
2384 add_unity8_qmltest(Greeter WideView)
2385 add_unity8_qmltest(Launcher Launcher)
2386+add_unity8_qmltest(Launcher Drawer)
2387 add_unity8_qmltest(Notifications Notifications)
2388 add_unity8_qmltest(Notifications VisualSnapDecisionsQueue)
2389 add_unity8_qmltest(Notifications OptionToggle)
2390
2391=== modified file 'tests/qmltests/Dash/tst_DashShell.qml'
2392--- tests/qmltests/Dash/tst_DashShell.qml 2016-07-08 20:44:09 +0000
2393+++ tests/qmltests/Dash/tst_DashShell.qml 2016-12-06 13:47:05 +0000
2394@@ -112,34 +112,5 @@
2395 mouseClick(buttonShowDashHome);
2396 tryCompare(dashContentList, "currentIndex", 0);
2397 }
2398-
2399- function test_setShortLauncherMoveResetNonActiveDash() {
2400- var dashContentList = findChild(dash, "dashContentList");
2401- var startX = dash.width - units.gu(1);
2402- var startY = dash.height / 2;
2403- var stopX = units.gu(1)
2404- var stopY = startY;
2405- waitForRendering(dashContentList);
2406- touchFlick(dash, startX, startY, stopX, stopY);
2407- touchFlick(dash, startX, startY, stopX, stopY);
2408- compare(dashContentList.currentIndex, 2, "Could not flick to scope id 2");
2409-
2410- // Pretend the dash is not active
2411- dash.windowActive = false;
2412-
2413- // Flick the greeter away
2414- var startX = shell.width - units.gu(1);
2415- var startY = shell.height / 2;
2416- var stopX = units.gu(1)
2417- var stopY = startY;
2418- touchFlick(shell, startX, startY, stopX, stopY);
2419-
2420- var greeter = findChild(shell, "greeter");
2421- tryCompare(greeter, "shown", false);
2422-
2423- // Now do a small launcher movement
2424- touchFlick(shell, stopX, startY, startX, stopY);
2425- compare(dashContentList.currentIndex, 0, "Small launcher move did not reset dash on non active window");
2426- }
2427 }
2428 }
2429
2430=== added file 'tests/qmltests/Launcher/tst_Drawer.qml'
2431--- tests/qmltests/Launcher/tst_Drawer.qml 1970-01-01 00:00:00 +0000
2432+++ tests/qmltests/Launcher/tst_Drawer.qml 2016-12-06 13:47:05 +0000
2433@@ -0,0 +1,264 @@
2434+/*
2435+ * Copyright 2013-2016 Canonical Ltd.
2436+ *
2437+ * This program is free software; you can redistribute it and/or modify
2438+ * it under the terms of the GNU General Public License as published by
2439+ * the Free Software Foundation; version 3.
2440+ *
2441+ * This program is distributed in the hope that it will be useful,
2442+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2443+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2444+ * GNU General Public License for more details.
2445+ *
2446+ * You should have received a copy of the GNU General Public License
2447+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2448+ */
2449+
2450+import QtQuick 2.4
2451+import QtQuick.Layouts 1.1
2452+import Ubuntu.Components 1.3
2453+import ".."
2454+import "../../../qml/Launcher"
2455+import Unity.Launcher 0.1
2456+import Utils 0.1 // For EdgeBarrierSettings
2457+import Unity.Test 0.1
2458+
2459+StyledItem {
2460+ id: root
2461+ theme.name: "Ubuntu.Components.Themes.SuruDark"
2462+
2463+ width: units.gu(140)
2464+ height: units.gu(70)
2465+ Rectangle {
2466+ anchors.fill: parent
2467+ color: UbuntuColors.graphite // something neither white nor black
2468+ }
2469+
2470+ Launcher {
2471+ id: launcher
2472+ x: 0
2473+ y: 0
2474+ width: units.gu(40)
2475+ height: root.height
2476+
2477+ lockedVisible: lockedVisibleCheckBox.checked
2478+
2479+ property string lastSelectedApplication
2480+ onLauncherApplicationSelected: {
2481+ lastSelectedApplication = appId
2482+ }
2483+
2484+ Component.onCompleted: {
2485+ launcher.focus = true
2486+ edgeBarrierControls.target = testCase.findChild(this, "edgeBarrierController");
2487+ }
2488+ }
2489+
2490+ ColumnLayout {
2491+ anchors { bottom: parent.bottom; right: parent.right; margins: units.gu(1) }
2492+ spacing: units.gu(1)
2493+ width: childrenRect.width
2494+
2495+ RowLayout {
2496+ CheckBox {
2497+ id: lockedVisibleCheckBox
2498+ checked: false
2499+ }
2500+ Label {
2501+ text: "Launcher always visible"
2502+ }
2503+ }
2504+
2505+ Slider {
2506+ id: widthSlider
2507+ Layout.fillWidth: true
2508+ minimumValue: 6
2509+ maximumValue: 12
2510+ value: 10
2511+ }
2512+
2513+ MouseTouchEmulationCheckbox {}
2514+
2515+ EdgeBarrierControls {
2516+ id: edgeBarrierControls
2517+ text: "Drag here to pull out launcher"
2518+ onDragged: { launcher.pushEdge(amount); }
2519+ }
2520+ }
2521+
2522+ UnityTestCase {
2523+ id: testCase
2524+ when: windowShown
2525+ name: "Drawer"
2526+
2527+ function dragDrawerIntoView() {
2528+ var startX = launcher.dragAreaWidth/2;
2529+ var startY = launcher.height/2;
2530+ touchFlick(launcher,
2531+ startX, startY,
2532+ startX+units.gu(35), startY);
2533+
2534+ var drawer = findChild(launcher, "drawer");
2535+ verify(!!drawer);
2536+
2537+ // wait until it gets fully extended
2538+ // a tryCompare doesn't work since
2539+ // compare(-0.000005917593600024418, 0);
2540+ // is true and in this case we want exactly 0 or will have pain later on
2541+ tryCompareFunction( function(){ return drawer.x === 0; }, true );
2542+ tryCompare(launcher, "state", "drawer");
2543+ tryCompare(launcher, "drawerShown", true);
2544+ }
2545+
2546+ function revealByEdgePush() {
2547+ // Place the mouse against the window/screen edge and push beyond the barrier threshold
2548+ // If the mouse did not move between two revealByEdgePush(), we need it to move out the
2549+ // area to make sure we're not just accumulating to the previous push
2550+ mouseMove(root, root.width, root.height / 2);
2551+ mouseMove(root, 1, root.height / 2);
2552+
2553+ launcher.pushEdge(EdgeBarrierSettings.pushThreshold * 1.1);
2554+
2555+ var drawer = findChild(launcher, "drawer");
2556+ verify(!!drawer);
2557+
2558+ // wait until it gets fully extended
2559+ tryCompare(drawer, "x", 0);
2560+ tryCompare(launcher, "state", "drawer");
2561+ tryCompare(launcher, "drawerShown", true);
2562+ }
2563+
2564+ function init() {
2565+ launcher.lastSelectedApplication = "";
2566+ launcher.lockedVisible = false;
2567+ launcher.hide();
2568+ var drawer = findChild(launcher, "drawer");
2569+ tryCompare(drawer, "x", -drawer.width);
2570+ }
2571+
2572+ function test_revealByEdgeDrag() {
2573+ dragDrawerIntoView();
2574+ }
2575+
2576+ function test_revealByEdgePush_data() {
2577+ return [
2578+ { tag: "autohide launcher", autohide: true },
2579+ { tag: "locked launcher", autohide: false }
2580+ ]
2581+ }
2582+
2583+ function test_revealByEdgePush(data) {
2584+ launcher.lockedVisible = !data.autohide;
2585+
2586+ var panel = findChild(launcher, "launcherPanel");
2587+ tryCompare(panel, "x", data.autohide ? -panel.width : 0);
2588+ tryCompare(launcher, "state", data.autohide ? "" : "visible");
2589+ waitForRendering(launcher)
2590+
2591+ revealByEdgePush();
2592+ }
2593+
2594+ function test_hideByDraggingDrawer_data() {
2595+ return [
2596+ {tag: "autohide", autohide: true, endState: ""},
2597+ {tag: "locked", autohide: false, endState: "visible"}
2598+ ]
2599+ }
2600+
2601+ function test_hideByDraggingDrawer(data) {
2602+ launcher.lockedVisible = !data.autohide;
2603+
2604+ dragDrawerIntoView();
2605+ waitForRendering(launcher);
2606+
2607+ var drawer = findChild(launcher, "drawer");
2608+
2609+ mouseFlick(root, drawer.width - units.gu(1), drawer.height / 2, units.gu(2), drawer.height / 2, true, true);
2610+
2611+ tryCompare(drawer.anchors, "rightMargin", 0);
2612+ tryCompare(launcher, "state", data.endState);
2613+ launcher.lockedVisible = false;
2614+ }
2615+
2616+ function test_hideByClickingOutside() {
2617+ dragDrawerIntoView();
2618+
2619+ var drawer = findChild(launcher, "drawer");
2620+
2621+ mouseClick(root, drawer.width + units.gu(1), root.height / 2);
2622+
2623+ tryCompare(launcher, "state", "");
2624+ }
2625+
2626+ function test_launchAppFromDrawer() {
2627+ dragDrawerIntoView();
2628+
2629+ var drawerList = findChild(launcher, "drawerItemList");
2630+ var dialerApp = findChild(drawerList, "drawerItem_dialer-app");
2631+ mouseClick(dialerApp, dialerApp.width / 2, dialerApp.height / 2);
2632+
2633+ tryCompare(launcher, "lastSelectedApplication", "dialer-app");
2634+
2635+ tryCompare(launcher, "state", "");
2636+ }
2637+
2638+ function test_launcherGivesUpFocusAfterLaunchingFromDrawer() {
2639+ dragDrawerIntoView();
2640+
2641+ tryCompare(launcher, "focus", true);
2642+
2643+ var drawerList = findChild(launcher, "drawerItemList");
2644+ var dialerApp = findChild(drawerList, "drawerItem_dialer-app");
2645+ mouseClick(dialerApp, dialerApp.width / 2, dialerApp.height / 2);
2646+
2647+ tryCompare(launcher, "focus", false);
2648+ }
2649+
2650+ function test_drawerDisabled() {
2651+ launcher.drawerEnabled = false;
2652+
2653+ var startX = launcher.dragAreaWidth/2;
2654+ var startY = launcher.height/2;
2655+ touchFlick(launcher,
2656+ startX, startY,
2657+ startX+units.gu(35), startY);
2658+
2659+ var drawer = findChild(launcher, "drawer");
2660+ verify(!!drawer);
2661+
2662+ tryCompare(launcher, "state", "visible");
2663+ tryCompare(launcher, "drawerShown", false);
2664+
2665+ launcher.drawerEnabled = true;
2666+ }
2667+
2668+ function test_search() {
2669+ compare(launcher.lastSelectedApplication, "");
2670+
2671+ launcher.openDrawer(true)
2672+ typeString("cam");
2673+ keyClick(Qt.Key_Enter);
2674+
2675+ compare(launcher.lastSelectedApplication, "camera-app");
2676+ }
2677+
2678+ function test_dragDirectionOnLeftEdgeDrag_data() {
2679+ return [
2680+ { tag: "reveal", direction: "right", endState: "drawer" },
2681+ { tag: "cancel", direction: "left", endState: "visible" },
2682+ ]
2683+ }
2684+
2685+ function test_dragDirectionOnLeftEdgeDrag(data) {
2686+ var startX = launcher.dragAreaWidth/2;
2687+ var startY = launcher.height/2;
2688+ var stopX = startX + units.gu(35);
2689+ var endX = stopX + (units.gu(4) * (data.direction === "left" ? -1 : 1))
2690+
2691+ touchFlick(launcher, startX, startY, stopX, startY, true, false);
2692+ touchFlick(launcher, stopX, startY, endX, startY, false, true);
2693+
2694+ tryCompare(launcher, "state", data.endState)
2695+ }
2696+ }
2697+}
2698
2699=== modified file 'tests/qmltests/Launcher/tst_Launcher.qml'
2700--- tests/qmltests/Launcher/tst_Launcher.qml 2016-11-24 16:06:15 +0000
2701+++ tests/qmltests/Launcher/tst_Launcher.qml 2016-12-06 13:47:05 +0000
2702@@ -322,7 +322,7 @@
2703 function revealByEdgePush() {
2704 // Place the mouse against the window/screen edge and push beyond the barrier threshold
2705 mouseMove(root, 1, root.height / 2);
2706- launcher.pushEdge(EdgeBarrierSettings.pushThreshold * 1.1);
2707+ launcher.pushEdge(EdgeBarrierSettings.pushThreshold * .6);
2708
2709 var panel = findChild(launcher, "launcherPanel");
2710 verify(!!panel);
2711
2712=== modified file 'tests/qmltests/Stage/tst_PhoneStage.qml'
2713--- tests/qmltests/Stage/tst_PhoneStage.qml 2016-10-08 16:45:37 +0000
2714+++ tests/qmltests/Stage/tst_PhoneStage.qml 2016-12-06 13:47:05 +0000
2715@@ -334,32 +334,6 @@
2716 tryCompare(firstApp, "focused", true);
2717 }
2718
2719- function test_leftEdge_data() {
2720- return [
2721- { tag: "normal", inSpread: false, leftEdgeDragWidth: units.gu(20), shouldMoveApp: true },
2722- { tag: "inSpread", inSpread: true, leftEdgeDragWidth: units.gu(5), shouldMoveApp: false }
2723- ]
2724- }
2725-
2726- function test_leftEdge(data) {
2727- addApps(2);
2728-
2729- if (data.inSpread) {
2730- performEdgeSwipeToShowAppSpread();
2731- }
2732-
2733- var focusedDelegate = findChild(stage, "appDelegate_" + topLevelSurfaceList.idAt(0));
2734- var currentX = focusedDelegate.x;
2735-
2736- stage.leftEdgeDragProgress = data.leftEdgeDragWidth;
2737-
2738- tryCompare(focusedDelegate, "x", data.shouldMoveApp ? data.leftEdgeDragWidth : currentX);
2739-
2740- stage.leftEdgeDragProgress = 0;
2741-
2742- tryCompare(focusedDelegate, "x", currentX);
2743- }
2744-
2745 function test_focusedAppIsTheOnlyRunningApp() {
2746 addApps(2);
2747
2748
2749=== modified file 'tests/qmltests/tst_OrientedShell.qml'
2750--- tests/qmltests/tst_OrientedShell.qml 2016-11-14 01:39:43 +0000
2751+++ tests/qmltests/tst_OrientedShell.qml 2016-12-06 13:47:05 +0000
2752@@ -999,7 +999,7 @@
2753 var rotationStates = findInvisibleChild(orientedShell, "rotationStates");
2754 waitUntilTransitionsEnd(rotationStates);
2755
2756- performLeftEdgeSwipeToSwitchToDash();
2757+ ApplicationManager.requestFocusApplication("unity8-dash");
2758
2759 // Should be back to portrait
2760 tryCompare(shell, "transformRotationAngle", 0);
2761@@ -1391,18 +1391,6 @@
2762 waitUntilTransitionsEnd(rotationStates);
2763 }
2764
2765- function performLeftEdgeSwipeToSwitchToDash() {
2766- var swipeLength = shell.width * 0.7;
2767-
2768- var touchStartX = 1;
2769- var touchStartY = shell.height / 2;
2770- touchFlick(shell,
2771- touchStartX, touchStartY,
2772- touchStartX + swipeLength, touchStartY);
2773-
2774- tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
2775- }
2776-
2777 function performEdgeSwipeToSwitchToPreviousApp() {
2778 // swipe just enough to ensure an app switch action.
2779 // If we swipe too much we will trigger the spread mode
2780
2781=== modified file 'tests/qmltests/tst_Shell.qml'
2782--- tests/qmltests/tst_Shell.qml 2016-11-22 17:00:33 +0000
2783+++ tests/qmltests/tst_Shell.qml 2016-12-06 13:47:05 +0000
2784@@ -605,38 +605,6 @@
2785 compare(ApplicationManager.get(0).appId, "unity8-dash");
2786 }
2787
2788- function test_phoneLeftEdgeDrag_data() {
2789- return [
2790- {tag: "without launcher",
2791- revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
2792- launcherHides: true, greeterShown: false},
2793-
2794- {tag: "with launcher",
2795- revealLauncher: true, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
2796- launcherHides: true, greeterShown: false},
2797-
2798- {tag: "small swipe",
2799- revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app",
2800- launcherHides: false, greeterShown: false},
2801-
2802- {tag: "long swipe",
2803- revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
2804- launcherHides: true, greeterShown: false},
2805-
2806- {tag: "small swipe with greeter",
2807- revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app",
2808- launcherHides: false, greeterShown: true},
2809-
2810- {tag: "long swipe with greeter",
2811- revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
2812- launcherHides: true, greeterShown: true},
2813-
2814- {tag: "swipe over dash",
2815- revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "unity8-dash",
2816- launcherHides: false, greeterShown: false},
2817- ];
2818- }
2819-
2820 function test_snapDecisionDismissalReturnsFocus() {
2821 loadShell("phone");
2822 swipeAwayGreeter();
2823@@ -701,80 +669,6 @@
2824 mockNotificationsModel.append(n)
2825 }
2826
2827- function test_phoneLeftEdgeDrag(data) {
2828- loadShell("phone");
2829- swipeAwayGreeter();
2830- dragLauncherIntoView();
2831- var appSurfaceId = topLevelSurfaceList.nextId;
2832- tapOnAppIconInLauncher();
2833- waitUntilAppWindowIsFullyLoaded(appSurfaceId);
2834-
2835- var greeter = findChild(shell, "greeter");
2836- if (data.greeterShown) {
2837- showGreeter();
2838- }
2839-
2840- if (data.revealLauncher) {
2841- dragLauncherIntoView();
2842- }
2843-
2844- swipeFromLeftEdge(data.swipeLength);
2845- if (data.appHides) {
2846- waitUntilDashIsFocused();
2847- tryCompare(greeter, "shown", false);
2848- } else {
2849- tryCompare(greeter, "fullyShown", data.greeterShown);
2850- }
2851-
2852- var launcher = findChild(shell, "launcherPanel");
2853- tryCompare(launcher, "x", data.launcherHides ? -launcher.width : 0)
2854-
2855- //FIXME: This check fails. Don't understand the rationale behind it.
2856- /*
2857- // Make sure the helper for sliding out the launcher wasn't touched. We want to fade it out here.
2858- var animateTimer = findInvisibleChild(shell, "animateTimer");
2859- compare(animateTimer.nextState, "visible");
2860- */
2861- }
2862-
2863- function test_tabletLeftEdgeDrag_data() {
2864- return [
2865- {tag: "without password", user: "no-password", loggedIn: true},
2866- {tag: "with password", user: "has-password", loggedIn: false},
2867- ]
2868- }
2869-
2870- function test_tabletLeftEdgeDrag(data) {
2871- setLightDMMockMode("full");
2872- loadShell("tablet");
2873-
2874- selectUser(data.user)
2875-
2876- swipeFromLeftEdge(shell.width * 0.75)
2877- wait(500) // to give time to handle dash() signal from Launcher
2878- confirmLoggedIn(data.loggedIn)
2879- }
2880-
2881- function test_longLeftEdgeSwipeTakesToAppsAndResetSearchString() {
2882- loadShell("phone");
2883- swipeAwayGreeter();
2884- dragLauncherIntoView();
2885- dashCommunicatorSpy.clear();
2886-
2887- var appSurfaceId = topLevelSurfaceList.nextId;
2888- tapOnAppIconInLauncher();
2889- waitUntilAppWindowIsFullyLoaded(appSurfaceId);
2890-
2891- tryCompareFunction(function() { return ApplicationManager.focusedApplicationId !== "unity8-dash"; }, true);
2892-
2893- //Long left swipe
2894- swipeFromLeftEdge(units.gu(30));
2895-
2896- tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
2897-
2898- compare(dashCommunicatorSpy.count, 1);
2899- }
2900-
2901 function test_ClickUbuntuIconInLauncherTakesToAppsAndResetSearchString() {
2902 loadShell("phone");
2903 swipeAwayGreeter();
2904@@ -913,39 +807,6 @@
2905 LightDM.Users.mockMode = mode;
2906 }
2907
2908- /*
2909- Regression test for bug https://bugs.launchpad.net/touch-preview-images/+bug/1193419
2910-
2911- When the user minimizes an application (left-edge swipe) he should always end up in the
2912- "Applications" scope view.
2913-
2914- Steps:
2915- - reveal launcher and launch an app that covers the dash
2916- - perform long left edge swipe to go minimize the app and go back to the dash.
2917- - verify the setCurrentScope() D-Bus call to the dash has been called for the correct scope id.
2918- */
2919- function test_minimizingAppTakesToDashApps() {
2920- loadShell("phone");
2921- swipeAwayGreeter();
2922- dragLauncherIntoView();
2923-
2924- // Launch an app from the launcher
2925- var appSurfaceId = topLevelSurfaceList.nextId;
2926- tapOnAppIconInLauncher();
2927- waitUntilAppWindowIsFullyLoaded(appSurfaceId);
2928-
2929- verify(ApplicationManager.focusedApplicationId !== "unity8-dash")
2930-
2931- dashCommunicatorSpy.clear();
2932- // Minimize the application we just launched
2933- swipeFromLeftEdge(shell.width * 0.75);
2934-
2935- tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
2936-
2937- compare(dashCommunicatorSpy.count, 1);
2938- compare(dashCommunicatorSpy.signalArguments[0][0], 0);
2939- }
2940-
2941 function test_showInputMethod() {
2942 loadShell("phone");
2943 swipeAwayGreeter();
2944@@ -1166,7 +1027,7 @@
2945
2946 // Place the mouse against the window/screen edge and push beyond the barrier threshold
2947 mouseMove(shell, 0, shell.height / 2);
2948- launcher.pushEdge(EdgeBarrierSettings.pushThreshold * 1.1);
2949+ launcher.pushEdge(EdgeBarrierSettings.pushThreshold * .8);
2950
2951 var panel = findChild(launcher, "launcherPanel");
2952 verify(panel);
2953@@ -1186,9 +1047,7 @@
2954 waitUntilAppWindowIsFullyLoaded(appSurfaceId);
2955
2956 // Minimize the application we just launched
2957- swipeFromLeftEdge(shell.width * 0.75);
2958-
2959- waitUntilDashIsFocused();
2960+ swipeFromLeftEdge(units.gu(15));
2961
2962 showGreeter();
2963
2964
2965=== modified file 'tests/qmltests/tst_ShellWithPin.qml'
2966--- tests/qmltests/tst_ShellWithPin.qml 2016-09-22 10:33:39 +0000
2967+++ tests/qmltests/tst_ShellWithPin.qml 2016-12-06 13:47:05 +0000
2968@@ -547,71 +547,5 @@
2969 var lockscreen = findChild(shell, "lockscreen");
2970 verify(lockscreen.shown);
2971 }
2972-
2973- /*
2974- Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1393447
2975-
2976- Do a left edge drag that is long enough to start displacing the greeter
2977- but short engough so that the greeter comes back into place once the
2978- finger is lifted.
2979-
2980- In this situation the launcher should remaing fully shown and hide itself
2981- only after its idle timeout is triggered.
2982- */
2983- function test_shortLeftEdgeSwipeMakesLauncherStayVisible() {
2984- showGreeter();
2985-
2986- var launcher = testCase.findChild(shell, "launcher")
2987- var launcherPanel = testCase.findChild(launcher, "launcherPanel");
2988-
2989- var toX = shell.width * 0.45;
2990- touchFlick(shell,
2991- 1 /* fromX */, shell.height * 0.5 /* fromY */,
2992- toX /* toX */, shell.height * 0.5 /* toY */,
2993- true /* beginTouch */, false /* endTouch */,
2994- 50, 100);
2995-
2996- // Launcher must be fully shown by now
2997- tryCompare(launcherPanel, "x", 0);
2998-
2999- // Greeter should be displaced
3000- var coverPage = findChild(shell, "coverPage");
3001- tryCompareFunction(function() { return coverPage.mapToItem(shell, 0, 0).x > shell.width*0.2; }, true);
3002-
3003- touchRelease(shell, toX, shell.height * 0.5);
3004-
3005- // Upon release the greeter should have slid back into full view
3006- tryCompareFunction(function() { return coverPage.mapToItem(shell, 0, 0).x === 0; }, true);
3007-
3008- // And the launcher should stay fully shown
3009- for (var i = 0; i < 10; ++i) {
3010- wait(50);
3011- compare(launcherPanel.x, 0);
3012- }
3013- }
3014-
3015- function test_longLeftEdgeDrags() {
3016- var coverPage = findChild(shell, "coverPage");
3017- var lockscreen = findChild(shell, "lockscreen");
3018- var launcher = findChild(shell, "launcherPanel");
3019- var galleryApp = ApplicationManager.startApplication("gallery-app");
3020- tryCompare(shell, "mainApp", galleryApp);
3021-
3022- // Show greeter
3023- showGreeter();
3024-
3025- // Swipe cover page away
3026- touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);
3027- tryCompare(launcher, "x", -launcher.width);
3028- tryCompare(coverPage, "showProgress", 0);
3029- compare(lockscreen.shown, true);
3030- tryCompare(shell, "mainApp", galleryApp);
3031-
3032- // Now attempt a swipe on lockscreen
3033- touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);
3034- tryCompare(launcher, "x", 0);
3035- compare(lockscreen.shown, true);
3036- tryCompare(shell, "mainApp", galleryApp);
3037- }
3038 }
3039 }

Subscribers

People subscribed via source and target branches