on xenial help sign in
Merge from trunk
4afcfe2Michael Terry
CMakeLists.txt+13
@@ -40,6 +40,19 @@ if(cmake_build_type_lower MATCHES coverage)
40 ENABLE_COVERAGE_REPORT(TARGETS ${SHELL_APP} FILTER /usr/include ${CMAKE_SOURCE_DIR}/tests/* ${CMAKE_BINARY_DIR}/*)
40 ENABLE_COVERAGE_REPORT(TARGETS ${SHELL_APP} FILTER /usr/include ${CMAKE_SOURCE_DIR}/tests/* ${CMAKE_BINARY_DIR}/*)
41endif()
41endif()
42
42
43+find_package (ECM 1.7.0 QUIET NO_MODULE)
44+if (ECM_FOUND)
45+ # Provides us with -DECM_ENABLE_SANITIZERS='X'
46+ # Where X can be address, thread, memory, leak, undefined
47+ include("${ECM_MODULE_DIR}/ECMEnableSanitizers.cmake")
48+endif()
49+
50+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
51+ if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.0.0")
52+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override" )
53+ endif()
54+endif()
55+
43# Make sure we have all the needed symbols
56# Make sure we have all the needed symbols
44set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,defs")
57set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,defs")
45set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,defs")
58set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,defs")
data/com.canonical.Unity8.gschema.xml+2−2
@@ -12,7 +12,7 @@
12 <description>The usage mode chosen will affect the Window Management behaviour.</description>
12 <description>The usage mode chosen will affect the Window Management behaviour.</description>
13 </key>
13 </key>
14 <key type="y" name="edge-barrier-sensitivity">
14 <key type="y" name="edge-barrier-sensitivity">
15− <default>35</default>
15+ <default>50</default>
16 <range min="1" max="100"/>
16 <range min="1" max="100"/>
17 <summary>Sensitivity of screen edge barriers for the mouse pointer.</summary>
17 <summary>Sensitivity of screen edge barriers for the mouse pointer.</summary>
18 <description>Some UI actions like revealing the launcher or the applications spread are triggered by pushing the mouse pointer against a screen edge. This key defines how much you have to push in order to trigger the associated action.</description>
18 <description>Some UI actions like revealing the launcher or the applications spread are triggered by pushing the mouse pointer against a screen edge. This key defines how much you have to push in order to trigger the associated action.</description>
@@ -23,7 +23,7 @@
23 <description>How much you have to push (in grid units)the mouse against an edge barrier when sensibility is 100.</description>
23 <description>How much you have to push (in grid units)the mouse against an edge barrier when sensibility is 100.</description>
24 </key>
24 </key>
25 <key type="u" name="edge-barrier-max-push">
25 <key type="u" name="edge-barrier-max-push">
26− <default>60</default>
26+ <default>120</default>
27 <summary>Maximum push needed to overcome edge barrier</summary>
27 <summary>Maximum push needed to overcome edge barrier</summary>
28 <description>How much you have to push (in grid units) the mouse against an edge barrier when sensibility is 1.</description>
28 <description>How much you have to push (in grid units) the mouse against an edge barrier when sensibility is 1.</description>
29 </key>
29 </key>
debian/changelog+33
@@ -1,3 +1,36 @@
1+unity8 (8.15+17.04.20161207.1-0ubuntu1) zesty; urgency=medium
2+
3+ [ Albert Astals Cid ]
4+ * Add the Wsuggest-override flag to gcc
5+ * Add support for compiler sanitizers via ECM
6+ * Use timeStep as delay time (LP: #1642919)
7+ * Bring back fix for 1517830 (LP: #1517830)
8+ * Fix compile warnings in mocks
9+ * Do not hide panel when launching an application if the mouse is on
10+ the panel (LP: #1591311)
11+
12+ [ Daniel d'Andrada ]
13+ * Fix "make tryApplicationWindow"
14+ * Take save/restore functions out of WindowResizeArea
15+
16+ [ Josh Arenson ]
17+ * Enable the greeter to remember which session the user last logged
18+ into (LP: #1631365)
19+
20+ [ Lukáš Tinkl ]
21+ * Fix the Super key not invoking the dash scope home (LP: #1607427)
22+
23+ [ Michael Zanetti ]
24+ * Add the ApplicationDrawer
25+ * tune right edge push (LP: #1646094)
26+ * improve close button visiblity when hovering with the mouse
27+ * Update virtual touchpad visuals and add a tutorial. (LP: #1585220)
28+
29+ [ Pete Woods ]
30+ * MenuItemFactory: Add subtitle support to SwitchItem widget
31+
32+ -- Michał Sawicz <michal.sawicz@canonical.com> Wed, 07 Dec 2016 13:49:24 +0000
33+
1unity8 (8.15+17.04.20161129-0ubuntu1) zesty; urgency=medium
34unity8 (8.15+17.04.20161129-0ubuntu1) zesty; urgency=medium
2
35
3 [ Albert Astals Cid ]
36 [ Albert Astals Cid ]
debian/control+3−1
@@ -39,7 +39,7 @@ Build-Depends: cmake,
39 libubuntugestures5-private-dev (>= 1.3.2030),
39 libubuntugestures5-private-dev (>= 1.3.2030),
40 libudev-dev,
40 libudev-dev,
41 libudm-common-dev,
41 libudm-common-dev,
42− libunity-api-dev (>= 7.120),
42+ libunity-api-dev (>= 7.121),
43 libusermetricsoutput1-dev,
43 libusermetricsoutput1-dev,
44# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
44# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
45 libx11-dev[!arm64 !armhf],
45 libx11-dev[!arm64 !armhf],
@@ -50,6 +50,7 @@ Build-Depends: cmake,
50 python3-all:any,
50 python3-all:any,
51 python3-setuptools,
51 python3-setuptools,
52 qml-module-qt-labs-folderlistmodel,
52 qml-module-qt-labs-folderlistmodel,
53+ qml-module-qt-labs-settings,
53 qml-module-qtqml-statemachine,
54 qml-module-qtqml-statemachine,
54 qml-module-qtmultimedia (>= 5.4.1-1ubuntu19~overlay2),
55 qml-module-qtmultimedia (>= 5.4.1-1ubuntu19~overlay2),
55 qml-module-qtquick-layouts,
56 qml-module-qtquick-layouts,
@@ -124,6 +125,7 @@ Depends: dmz-cursor-theme,
124 qmenumodel-qml (>= 0.2.10),
125 qmenumodel-qml (>= 0.2.10),
125 qml-module-biometryd,
126 qml-module-biometryd,
126 qml-module-qt-labs-folderlistmodel,
127 qml-module-qt-labs-folderlistmodel,
128+ qml-module-qt-labs-settings,
127 qml-module-qtqml-statemachine,
129 qml-module-qtqml-statemachine,
128 qml-module-qtquick-xmllistmodel,
130 qml-module-qtquick-xmllistmodel,
129 qml-module-qtsysteminfo,
131 qml-module-qtsysteminfo,
plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=10)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=11)
2pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
3
3
4add_definitions(-DSM_BUSNAME=systemBus)
4add_definitions(-DSM_BUSNAME=systemBus)
plugins/Greeter/Unity/Launcher/launcheritem.cpp+13
@@ -76,6 +76,19 @@ void LauncherItem::setIcon(const QString &icon)
76 }
76 }
77}
77}
78
78
79+QStringList LauncherItem::keywords() const
80+{
81+ return m_keywords;
82+}
83+
84+void LauncherItem::setKeywords(const QStringList &keywords)
85+{
86+ if (m_keywords != keywords) {
87+ m_keywords = keywords;
88+ Q_EMIT keywordsChanged(keywords);
89+ }
90+}
91+
79bool LauncherItem::pinned() const
92bool LauncherItem::pinned() const
80{
93{
81 return m_pinned;
94 return m_pinned;
plugins/Greeter/Unity/Launcher/launcheritem.h+3
@@ -34,6 +34,7 @@ public:
34 QString appId() const override;
34 QString appId() const override;
35 QString name() const override;
35 QString name() const override;
36 QString icon() const override;
36 QString icon() const override;
37+ QStringList keywords() const override;
37 bool pinned() const override;
38 bool pinned() const override;
38 bool running() const override;
39 bool running() const override;
39 bool recent() const override;
40 bool recent() const override;
@@ -49,6 +50,7 @@ public:
49private:
50private:
50 void setName(const QString &name);
51 void setName(const QString &name);
51 void setIcon(const QString &icon);
52 void setIcon(const QString &icon);
53+ void setKeywords(const QStringList &keywords);
52 void setPinned(bool pinned);
54 void setPinned(bool pinned);
53 void setRunning(bool running);
55 void setRunning(bool running);
54 void setRecent(bool recent);
56 void setRecent(bool recent);
@@ -63,6 +65,7 @@ private:
63 QString m_appId;
65 QString m_appId;
64 QString m_name;
66 QString m_name;
65 QString m_icon;
67 QString m_icon;
68+ QStringList m_keywords;
66 bool m_pinned;
69 bool m_pinned;
67 bool m_running;
70 bool m_running;
68 bool m_recent;
71 bool m_recent;
plugins/LightDM/FullLightDM/CMakeLists.txt+5−1
@@ -6,10 +6,14 @@ add_definitions(-DFULL_LIGHTDM)
6
6
7include_directories(
7include_directories(
8 ../
8 ../
9− ${LIBLIGHTDM_INCLUDE_DIRS}
10 ${CMAKE_CURRENT_BINARY_DIR}
9 ${CMAKE_CURRENT_BINARY_DIR}
11 )
10 )
12
11
12+include_directories(
13+ SYSTEM
14+ ${LIBLIGHTDM_INCLUDE_DIRS}
15+ )
16+
13foreach(source_file ${QMLPLUGIN_SRC})
17foreach(source_file ${QMLPLUGIN_SRC})
14 list(APPEND RELATIVE_QMLPLUGIN_SRC ../${source_file})
18 list(APPEND RELATIVE_QMLPLUGIN_SRC ../${source_file})
15endforeach()
19endforeach()
plugins/Unity/Indicators/CMakeLists.txt+3
@@ -11,6 +11,9 @@ include_directories(
11include_directories(
11include_directories(
12 SYSTEM
12 SYSTEM
13 ${GLIB_INCLUDE_DIRS}
13 ${GLIB_INCLUDE_DIRS}
14+)
15+include_directories(
16+ SYSTEM
14 ${GIO_INCLUDE_DIRS}
17 ${GIO_INCLUDE_DIRS}
15 ${QMENUMODEL_INCLUDE_DIRS}
18 ${QMENUMODEL_INCLUDE_DIRS}
16)
19)
plugins/Unity/Launcher/CMakeLists.txt+4−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=10)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=11)
2pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
3
3
4add_definitions(-DSM_BUSNAME=systemBus)
4add_definitions(-DSM_BUSNAME=systemBus)
@@ -25,6 +25,8 @@ set(QMLLAUNCHERPLUGIN_SRC
25 dbusinterface.cpp
25 dbusinterface.cpp
26 gsettings.cpp
26 gsettings.cpp
27 asadapter.cpp
27 asadapter.cpp
28+ appdrawermodel.cpp
29+ ualwrapper.cpp
28 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
30 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
29 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
31 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
30 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
32 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
@@ -32,6 +34,7 @@ set(QMLLAUNCHERPLUGIN_SRC
32 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
34 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
33 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
35 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
34 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
36 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
37+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/AppDrawerModelInterface.h
35 )
38 )
36
39
37add_library(UnityLauncher-qml MODULE
40add_library(UnityLauncher-qml MODULE
plugins/Unity/Launcher/appdrawermodel.cppnew file+62
@@ -0,0 +1,62 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "appdrawermodel.h"
18+#include "ualwrapper.h"
19+
20+#include <QDebug>
21+#include <QDateTime>
22+
23+AppDrawerModel::AppDrawerModel(QObject *parent):
24+ AppDrawerModelInterface(parent)
25+{
26+ Q_FOREACH (const QString &appId, UalWrapper::installedApps()) {
27+ UalWrapper::AppInfo info = UalWrapper::getApplicationInfo(appId);
28+ if (!info.valid) {
29+ qWarning() << "Failed to get app info for app" << appId;
30+ continue;
31+ }
32+ m_list.append(new LauncherItem(appId, info.name, info.icon, this));
33+ m_list.last()->setKeywords(info.keywords);
34+ }
35+ qsrand(QDateTime::currentMSecsSinceEpoch() / 100);
36+}
37+
38+int AppDrawerModel::rowCount(const QModelIndex &parent) const
39+{
40+ Q_UNUSED(parent)
41+ return m_list.count();
42+}
43+
44+QVariant AppDrawerModel::data(const QModelIndex &index, int role) const
45+{
46+ switch (role) {
47+ case RoleAppId:
48+ return m_list.at(index.row())->appId();
49+ case RoleName:
50+ return m_list.at(index.row())->name();
51+ case RoleIcon:
52+ return m_list.at(index.row())->icon();
53+ case RoleKeywords:
54+ return m_list.at(index.row())->keywords();
55+ case RoleUsage:
56+ // FIXME: u-a-l needs to provide API for usage stats.
57+ // don't forget to drop the qsrand() call in the ctor when dropping this.
58+ return qrand();
59+ }
60+
61+ return QVariant();
62+}
plugins/Unity/Launcher/appdrawermodel.hnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+
18+#include <unity/shell/launcher/AppDrawerModelInterface.h>
19+
20+#include "launcheritem.h"
21+
22+class AppDrawerModel: public AppDrawerModelInterface
23+{
24+ Q_OBJECT
25+public:
26+ AppDrawerModel(QObject* parent = nullptr);
27+
28+ int rowCount(const QModelIndex &parent) const override;
29+ QVariant data(const QModelIndex &index, int role) const override;
30+
31+private:
32+ QList<LauncherItem*> m_list;
33+};
plugins/Unity/Launcher/launcheritem.cpp+13
@@ -90,6 +90,19 @@ void LauncherItem::setIcon(const QString &icon)
90 }
90 }
91}
91}
92
92
93+QStringList LauncherItem::keywords() const
94+{
95+ return m_keywords;
96+}
97+
98+void LauncherItem::setKeywords(const QStringList &keywords)
99+{
100+ if (m_keywords != keywords) {
101+ m_keywords = keywords;
102+ Q_EMIT keywordsChanged(keywords);
103+ }
104+}
105+
93bool LauncherItem::pinned() const
106bool LauncherItem::pinned() const
94{
107{
95 return m_pinned;
108 return m_pinned;
plugins/Unity/Launcher/launcheritem.h+4
@@ -37,6 +37,7 @@ public:
37 QString appId() const override;
37 QString appId() const override;
38 QString name() const override;
38 QString name() const override;
39 QString icon() const override;
39 QString icon() const override;
40+ QStringList keywords() const override;
40 bool pinned() const override;
41 bool pinned() const override;
41 bool running() const override;
42 bool running() const override;
42 bool recent() const override;
43 bool recent() const override;
@@ -52,6 +53,7 @@ public:
52private:
53private:
53 void setName(const QString &name);
54 void setName(const QString &name);
54 void setIcon(const QString &icon);
55 void setIcon(const QString &icon);
56+ void setKeywords(const QStringList &keywords);
55 void setPinned(bool pinned);
57 void setPinned(bool pinned);
56 void setRunning(bool running);
58 void setRunning(bool running);
57 void setRecent(bool recent);
59 void setRecent(bool recent);
@@ -66,6 +68,7 @@ private:
66 QString m_appId;
68 QString m_appId;
67 QString m_name;
69 QString m_name;
68 QString m_icon;
70 QString m_icon;
71+ QStringList m_keywords;
69 bool m_pinned;
72 bool m_pinned;
70 bool m_running;
73 bool m_running;
71 bool m_recent;
74 bool m_recent;
@@ -79,6 +82,7 @@ private:
79 QuickListEntry m_quitAction;
82 QuickListEntry m_quitAction;
80
83
81 friend class LauncherModel;
84 friend class LauncherModel;
85+ friend class AppDrawerModel;
82};
86};
83
87
84#endif // LAUNCHERITEM_H
88#endif // LAUNCHERITEM_H
plugins/Unity/Launcher/launchermodel.cpp+6−37
@@ -19,18 +19,14 @@
19#include "gsettings.h"
19#include "gsettings.h"
20#include "dbusinterface.h"
20#include "dbusinterface.h"
21#include "asadapter.h"
21#include "asadapter.h"
22+#include "ualwrapper.h"
22
23
23−#include <ubuntu-app-launch/appid.h>
24−#include <ubuntu-app-launch/application.h>
25−#include <ubuntu-app-launch/registry.h>
26#include <unity/shell/application/ApplicationInfoInterface.h>
24#include <unity/shell/application/ApplicationInfoInterface.h>
27#include <unity/shell/application/MirSurfaceListInterface.h>
25#include <unity/shell/application/MirSurfaceListInterface.h>
28
26
29#include <QDesktopServices>
27#include <QDesktopServices>
30#include <QDebug>
28#include <QDebug>
31
29
32−namespace ual = ubuntu::app_launch;
33−
34using namespace unity::shell::application;
30using namespace unity::shell::application;
35
31
36LauncherModel::LauncherModel(QObject *parent):
32LauncherModel::LauncherModel(QObject *parent):
@@ -38,8 +34,7 @@ LauncherModel::LauncherModel(QObject *parent):
38 m_settings(new GSettings(this)),
34 m_settings(new GSettings(this)),
39 m_dbusIface(new DBusInterface(this)),
35 m_dbusIface(new DBusInterface(this)),
40 m_asAdapter(new ASAdapter()),
36 m_asAdapter(new ASAdapter()),
41− m_appManager(nullptr),
37+ m_appManager(nullptr)
42− m_ualRegistry(std::make_shared<ual::Registry>())
43{
38{
44 connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged);
39 connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged);
45 connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged);
40 connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged);
@@ -159,7 +154,7 @@ void LauncherModel::pin(const QString &appId, int index)
159 index = m_list.count();
154 index = m_list.count();
160 }
155 }
161
156
162− auto appInfo = getApplicationInfo(appId);
157+ UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(appId);
163 if (!appInfo.valid) {
158 if (!appInfo.valid) {
164 qWarning() << "Can't pin application, appId not found:" << appId;
159 qWarning() << "Can't pin application, appId not found:" << appId;
165 return;
160 return;
@@ -335,32 +330,6 @@ int LauncherModel::findApplication(const QString &appId)
335 return -1;
330 return -1;
336}
331}
337
332
338−LauncherModel::AppInfo LauncherModel::getApplicationInfo(const QString &appId)
339−{
340− AppInfo info;
341−
342− ual::AppID ualAppId = ual::AppID::find(m_ualRegistry, appId.toStdString());
343− if (ualAppId.empty()) {
344− return info;
345− }
346−
347− std::shared_ptr<ual::Application> ualApp;
348− try
349− {
350− ualApp = ual::Application::create(ualAppId, m_ualRegistry);
351− }
352− catch (std::runtime_error &e)
353− {
354− qWarning() << "Couldn't find application info for" << appId << "-" << e.what();
355− return info;
356− }
357−
358− info.valid = true;
359− info.name = QString::fromStdString(ualApp->info()->name());
360− info.icon = QString::fromStdString(ualApp->info()->iconPath());
361− return info;
362−}
363−
364void LauncherModel::progressChanged(const QString &appId, int progress)
333void LauncherModel::progressChanged(const QString &appId, int progress)
365{
334{
366 const int idx = findApplication(appId);
335 const int idx = findApplication(appId);
@@ -408,7 +377,7 @@ void LauncherModel::countVisibleChanged(const QString &appId, bool countVisible)
408 }
377 }
409 } else {
378 } else {
410 // Need to create a new LauncherItem and show the highlight
379 // Need to create a new LauncherItem and show the highlight
411− auto appInfo = getApplicationInfo(appId);
380+ UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(appId);
412 if (countVisible && appInfo.valid) {
381 if (countVisible && appInfo.valid) {
413 LauncherItem *item = new LauncherItem(appId,
382 LauncherItem *item = new LauncherItem(appId,
414 appInfo.name,
383 appInfo.name,
@@ -428,7 +397,7 @@ void LauncherModel::refresh()
428 // First walk through all the existing items and see if we need to remove something
397 // First walk through all the existing items and see if we need to remove something
429 QList<LauncherItem*> toBeRemoved;
398 QList<LauncherItem*> toBeRemoved;
430 Q_FOREACH (LauncherItem* item, m_list) {
399 Q_FOREACH (LauncherItem* item, m_list) {
431− auto appInfo = getApplicationInfo(item->appId());
400+ UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(item->appId());
432 if (!appInfo.valid) {
401 if (!appInfo.valid) {
433 // Application no longer available => drop it!
402 // Application no longer available => drop it!
434 toBeRemoved << item;
403 toBeRemoved << item;
@@ -480,7 +449,7 @@ void LauncherModel::refresh()
480 if (itemIndex == -1) {
449 if (itemIndex == -1) {
481 // Need to add it. Just add it into the addedIndex to keep same ordering as the list
450 // Need to add it. Just add it into the addedIndex to keep same ordering as the list
482 // in the settings.
451 // in the settings.
483− auto appInfo = getApplicationInfo(entry);
452+ UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(entry);
484 if (!appInfo.valid) {
453 if (!appInfo.valid) {
485 continue;
454 continue;
486 }
455 }
plugins/Unity/Launcher/launchermodel.h−14
@@ -27,12 +27,6 @@ class GSettings;
27class DBusInterface;
27class DBusInterface;
28class ASAdapter;
28class ASAdapter;
29
29
30−namespace ubuntu {
31− namespace app_launch {
32− class Registry;
33− }
34−}
35−
36using namespace unity::shell::launcher;
30using namespace unity::shell::launcher;
37using namespace unity::shell::application;
31using namespace unity::shell::application;
38
32
@@ -73,13 +67,6 @@ private:
73
67
74 void unpin(const QString &appId);
68 void unpin(const QString &appId);
75
69
76− struct AppInfo {
77− bool valid = false;
78− QString name;
79− QString icon;
80− };
81− AppInfo getApplicationInfo(const QString &appId);
82−
83private Q_SLOTS:
70private Q_SLOTS:
84 void countChanged(const QString &appId, int count);
71 void countChanged(const QString &appId, int count);
85 void countVisibleChanged(const QString &appId, bool count);
72 void countVisibleChanged(const QString &appId, bool count);
@@ -98,7 +85,6 @@ private:
98 ASAdapter *m_asAdapter;
85 ASAdapter *m_asAdapter;
99
86
100 ApplicationManagerInterface *m_appManager;
87 ApplicationManagerInterface *m_appManager;
101− std::shared_ptr<ubuntu::app_launch::Registry> m_ualRegistry;
102
88
103 friend class LauncherModelTest;
89 friend class LauncherModelTest;
104};
90};
plugins/Unity/Launcher/plugin.cpp+2−1
@@ -26,7 +26,7 @@
26// local
26// local
27#include "launchermodel.h"
27#include "launchermodel.h"
28#include "launcheritem.h"
28#include "launcheritem.h"
29−
29+#include "appdrawermodel.h"
30
30
31using namespace unity::shell::launcher;
31using namespace unity::shell::launcher;
32
32
@@ -46,4 +46,5 @@ void UnityLauncherPlugin::registerTypes(const char *uri)
46 qmlRegisterSingletonType<LauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
46 qmlRegisterSingletonType<LauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
47 qmlRegisterUncreatableType<LauncherItem>(uri, 0, 1, "LauncherItem", QStringLiteral("Can't create new Launcher Items in QML. Get them from the LauncherModel."));
47 qmlRegisterUncreatableType<LauncherItem>(uri, 0, 1, "LauncherItem", QStringLiteral("Can't create new Launcher Items in QML. Get them from the LauncherModel."));
48 qmlRegisterUncreatableType<QuickListModel>(uri, 0, 1, "QuickListModel", QStringLiteral("Can't create a QuickListModel in QML. Get them from the LauncherItems."));
48 qmlRegisterUncreatableType<QuickListModel>(uri, 0, 1, "QuickListModel", QStringLiteral("Can't create a QuickListModel in QML. Get them from the LauncherItems."));
49+ qmlRegisterType<AppDrawerModel>(uri, 0, 1, "AppDrawerModel");
49}
50}
plugins/Unity/Launcher/ualwrapper.cppnew file+73
@@ -0,0 +1,73 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <QDebug>
18+
19+#include "ualwrapper.h"
20+
21+#include <ubuntu-app-launch/registry.h>
22+using namespace ubuntu::app_launch;
23+
24+UalWrapper::UalWrapper(QObject *parent):
25+ QObject(parent)
26+{
27+
28+}
29+
30+QStringList UalWrapper::installedApps()
31+{
32+ QStringList appIds;
33+ try {
34+ for (const std::shared_ptr<Application> &app : Registry::installedApps()) {
35+ if (!app->appId().package.value().empty()) {
36+ appIds << QString::fromStdString(app->appId().package.value() + "_" + app->appId().appname.value());
37+ } else {
38+ appIds << QString::fromStdString(app->appId().appname);
39+ }
40+ }
41+ } catch (const std::runtime_error &e) {
42+ qWarning() << "ubuntu-all-launch threw an exception listing apps:" << e.what();
43+ }
44+
45+ return appIds;
46+}
47+
48+UalWrapper::AppInfo UalWrapper::getApplicationInfo(const QString &appId)
49+{
50+ AppInfo info;
51+
52+ try {
53+ AppID ualAppId = AppID::find(appId.toStdString());
54+ if (ualAppId.empty()) {
55+ qWarning() << "Empty ualAppId result for" << appId;
56+ return info;
57+ }
58+
59+ std::shared_ptr<Application> ualApp;
60+ ualApp = Application::create(ualAppId, Registry::getDefault());
61+
62+ info.name = QString::fromStdString(ualApp->info()->name());
63+ info.icon = QString::fromStdString(ualApp->info()->iconPath());
64+ for (const std::string &keyword : ualApp->info()->keywords().value()) {
65+ info.keywords << QString::fromStdString(keyword);
66+ }
67+ info.valid = true;
68+ } catch (const std::runtime_error &e) {
69+ qWarning() << "ubuntu-app-launch threw an exception getting app info for appId:" << appId << ":" << e.what();
70+ }
71+
72+ return info;
73+}
plugins/Unity/Launcher/ualwrapper.hnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <QObject>
18+
19+class UalWrapper: public QObject
20+{
21+ Q_OBJECT
22+public:
23+ struct AppInfo {
24+ bool valid = false;
25+ QString name;
26+ QString icon;
27+ QStringList keywords;
28+ };
29+
30+ UalWrapper(QObject* parent = nullptr);
31+
32+ static QStringList installedApps();
33+ static AppInfo getApplicationInfo(const QString &appId);
34+
35+};
plugins/Utils/CMakeLists.txt+4
@@ -15,6 +15,10 @@ set(QMLPLUGIN_SRC
15 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
15 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
16 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
16 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
17 applicationsfiltermodel.cpp
17 applicationsfiltermodel.cpp
18+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/AppDrawerModelInterface.h
19+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
20+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
21+ appdrawerproxymodel.cpp
18 constants.cpp
22 constants.cpp
19 WindowInputMonitor.cpp
23 WindowInputMonitor.cpp
20 inputwatcher.cpp
24 inputwatcher.cpp
plugins/Utils/ElapsedTimer.h+1−1
@@ -41,7 +41,7 @@ class ElapsedTimer : public AbstractElapsedTimer {
41public:
41public:
42 void start() override { m_timer.start(); }
42 void start() override { m_timer.start(); }
43 qint64 msecsSinceReference() const override { return m_timer.msecsSinceReference(); }
43 qint64 msecsSinceReference() const override { return m_timer.msecsSinceReference(); }
44− qint64 elapsed() const override { return m_timer.elapsed(); }
44+ qint64 elapsed() const override { return m_timer.isValid() ? m_timer.elapsed() : 0; }
45private:
45private:
46 QElapsedTimer m_timer;
46 QElapsedTimer m_timer;
47};
47};
plugins/Utils/Timer.cpp+5−2
@@ -37,13 +37,16 @@ void Timer::setInterval(int msecs)
37void Timer::start()
37void Timer::start()
38{
38{
39 m_timer.start();
39 m_timer.start();
40− AbstractTimer::start();
41}
40}
42
41
43void Timer::stop()
42void Timer::stop()
44{
43{
45 m_timer.stop();
44 m_timer.stop();
46− AbstractTimer::stop();
45+}
46+
47+bool Timer::isRunning() const
48+{
49+ return m_timer.isActive();
47}
50}
48
51
49bool Timer::isSingleShot() const
52bool Timer::isSingleShot() const
plugins/Utils/Timer.h+5−6
@@ -30,18 +30,16 @@ class AbstractTimer : public QObject
30{
30{
31 Q_OBJECT
31 Q_OBJECT
32public:
32public:
33− AbstractTimer(QObject *parent) : QObject(parent), m_isRunning(false) {}
33+ AbstractTimer(QObject *parent) : QObject(parent) {}
34 virtual int interval() const = 0;
34 virtual int interval() const = 0;
35 virtual void setInterval(int msecs) = 0;
35 virtual void setInterval(int msecs) = 0;
36− virtual void start() { m_isRunning = true; }
36+ virtual void start() = 0;
37− virtual void stop() { m_isRunning = false; }
37+ virtual void stop() = 0;
38− bool isRunning() const { return m_isRunning; }
38+ virtual bool isRunning() const = 0;
39 virtual bool isSingleShot() const = 0;
39 virtual bool isSingleShot() const = 0;
40 virtual void setSingleShot(bool value) = 0;
40 virtual void setSingleShot(bool value) = 0;
41Q_SIGNALS:
41Q_SIGNALS:
42 void timeout();
42 void timeout();
43−private:
44− bool m_isRunning;
45};
43};
46
44
47/** A QTimer wrapper */
45/** A QTimer wrapper */
@@ -55,6 +53,7 @@ public:
55 void setInterval(int msecs) override;
53 void setInterval(int msecs) override;
56 void start() override;
54 void start() override;
57 void stop() override;
55 void stop() override;
56+ bool isRunning() const override;
58 bool isSingleShot() const override;
57 bool isSingleShot() const override;
59 void setSingleShot(bool value) override;
58 void setSingleShot(bool value) override;
60private:
59private:
plugins/Utils/appdrawerproxymodel.cppnew file+189
@@ -0,0 +1,189 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "appdrawerproxymodel.h"
18+
19+#include <unity/shell/launcher/LauncherItemInterface.h>
20+
21+#include <QDebug>
22+
23+AppDrawerProxyModel::AppDrawerProxyModel(QObject *parent):
24+ QSortFilterProxyModel(parent)
25+{
26+ setSortRole(AppDrawerModelInterface::RoleName);
27+ setSortLocaleAware(true);
28+ sort(0);
29+
30+ connect(this, &QAbstractListModel::rowsInserted, this, &AppDrawerProxyModel::countChanged);
31+ connect(this, &QAbstractListModel::rowsRemoved, this, &AppDrawerProxyModel::countChanged);
32+ connect(this, &QAbstractListModel::layoutChanged, this, &AppDrawerProxyModel::countChanged);
33+}
34+
35+QAbstractItemModel *AppDrawerProxyModel::source() const
36+{
37+ return m_source;
38+}
39+
40+void AppDrawerProxyModel::setSource(QAbstractItemModel *source)
41+{
42+ if (m_source != source) {
43+ m_source = source;
44+ setSourceModel(m_source);
45+ setSortRole(m_sortBy == SortByAToZ ? AppDrawerModelInterface::RoleName : AppDrawerModelInterface::RoleUsage);
46+ connect(m_source, &QAbstractItemModel::rowsRemoved, this, &AppDrawerProxyModel::invalidateFilter);
47+ connect(m_source, &QAbstractItemModel::rowsInserted, this, &AppDrawerProxyModel::invalidateFilter);
48+ Q_EMIT sourceChanged();
49+ }
50+}
51+
52+AppDrawerProxyModel::GroupBy AppDrawerProxyModel::group() const
53+{
54+ return m_group;
55+}
56+
57+void AppDrawerProxyModel::setGroup(AppDrawerProxyModel::GroupBy group)
58+{
59+ if (m_group != group) {
60+ m_group = group;
61+ Q_EMIT groupChanged();
62+ invalidateFilter();
63+ }
64+}
65+
66+QString AppDrawerProxyModel::filterLetter() const
67+{
68+ return m_filterLetter;
69+}
70+
71+void AppDrawerProxyModel::setFilterLetter(const QString &filterLetter)
72+{
73+ if (m_filterLetter != filterLetter) {
74+ m_filterLetter = filterLetter;
75+ Q_EMIT filterLetterChanged();
76+ invalidateFilter();
77+ }
78+}
79+
80+QString AppDrawerProxyModel::filterString() const
81+{
82+ return m_filterString;
83+}
84+
85+void AppDrawerProxyModel::setFilterString(const QString &filterString)
86+{
87+ if (m_filterString != filterString) {
88+ m_filterString = filterString;
89+ Q_EMIT filterStringChanged();
90+ invalidateFilter();
91+ }
92+}
93+
94+AppDrawerProxyModel::SortBy AppDrawerProxyModel::sortBy() const
95+{
96+ return m_sortBy;
97+}
98+
99+void AppDrawerProxyModel::setSortBy(AppDrawerProxyModel::SortBy sortBy)
100+{
101+ if (m_sortBy != sortBy) {
102+ m_sortBy = sortBy;
103+ Q_EMIT sortByChanged();
104+ setSortRole(m_sortBy == SortByAToZ ? AppDrawerModelInterface::RoleName : AppDrawerModelInterface::RoleUsage);
105+ sort(0);
106+ }
107+}
108+
109+int AppDrawerProxyModel::count() const
110+{
111+ return rowCount();
112+}
113+
114+QVariant AppDrawerProxyModel::data(const QModelIndex &index, int role) const
115+{
116+ QModelIndex idx = mapToSource(index);
117+ if (role == Qt::UserRole) {
118+ QString name = m_source->data(idx, AppDrawerModelInterface::RoleName).toString();
119+ return name.length() > 0 ? QString(name.at(0)).toUpper() : QChar();
120+ }
121+ return m_source->data(idx, role);
122+}
123+
124+QHash<int, QByteArray> AppDrawerProxyModel::roleNames() const
125+{
126+ if (m_source) {
127+ QHash<int, QByteArray> roles = m_source->roleNames();
128+ roles.insert(Qt::UserRole, "letter");
129+ return roles;
130+ }
131+ return QHash<int, QByteArray>();
132+}
133+
134+bool AppDrawerProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
135+{
136+ Q_UNUSED(source_parent)
137+
138+ if (m_group == GroupByAToZ && source_row > 0) {
139+ QString currentName = m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleName).toString();
140+ QChar currentLetter = currentName.length() > 0 ? currentName.at(0) : QChar();
141+ QString previousName = m_source->data(m_source->index(source_row - 1,0 ), AppDrawerModelInterface::RoleName).toString();
142+ QChar previousLetter = previousName.length() > 0 ? previousName.at(0) : QChar();
143+ if (currentLetter.toLower() == previousLetter.toLower()) {
144+ return false;
145+ }
146+ } else if(m_group == GroupByAll && source_row > 0) {
147+ return false;
148+ }
149+ if (!m_filterLetter.isEmpty()) {
150+ QString currentName = m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleName).toString();
151+ QString currentLetter = currentName.length() > 0 ? QString(currentName.at(0)) : QString();
152+ if (currentLetter.toLower() != m_filterLetter.toLower()) {
153+ return false;
154+ }
155+ }
156+ if (!m_filterString.isEmpty()) {
157+ QStringList allWords = m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleKeywords).toStringList();
158+ allWords.prepend(m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleName).toString());
159+ bool found = false;
160+ Q_FOREACH (const QString currentWord, allWords) {
161+ if (currentWord.toLower().startsWith(m_filterString.toLower())) {
162+ found = true;
163+ break;
164+ }
165+ }
166+ if (!found) {
167+ return false;
168+ }
169+ }
170+ return true;
171+}
172+
173+QString AppDrawerProxyModel::appId(int index) const
174+{
175+ if (index >= 0 && index < rowCount()) {
176+ QModelIndex sourceIndex = mapToSource(this->index(index, 0));
177+
178+ AppDrawerModelInterface* adm = dynamic_cast<AppDrawerModelInterface*>(m_source);
179+ if (adm) {
180+ return adm->data(sourceIndex, AppDrawerModelInterface::RoleAppId).toString();
181+ }
182+
183+ AppDrawerProxyModel* adpm = qobject_cast<AppDrawerProxyModel*>(m_source);
184+ if (adpm) {
185+ return adpm->appId(sourceIndex.row());
186+ }
187+ }
188+ return nullptr;
189+}
plugins/Utils/appdrawerproxymodel.hnew file+87
@@ -0,0 +1,87 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <QSortFilterProxyModel>
18+
19+#include <unity/shell/launcher/AppDrawerModelInterface.h>
20+
21+using namespace unity::shell::launcher;
22+
23+class AppDrawerProxyModel: public QSortFilterProxyModel
24+{
25+ Q_OBJECT
26+ Q_ENUMS(GroupBy)
27+ Q_ENUMS(SortBy)
28+ Q_PROPERTY(QAbstractItemModel* source READ source WRITE setSource NOTIFY sourceChanged)
29+ Q_PROPERTY(GroupBy group READ group WRITE setGroup NOTIFY groupChanged)
30+ Q_PROPERTY(QString filterLetter READ filterLetter WRITE setFilterLetter NOTIFY filterLetterChanged)
31+ Q_PROPERTY(QString filterString READ filterString WRITE setFilterString NOTIFY filterStringChanged)
32+ Q_PROPERTY(SortBy sortBy READ sortBy WRITE setSortBy NOTIFY sortByChanged)
33+ Q_PROPERTY(int count READ count NOTIFY countChanged)
34+
35+public:
36+ enum GroupBy {
37+ GroupByNone,
38+ GroupByAll,
39+ GroupByAToZ
40+ };
41+ enum SortBy {
42+ SortByAToZ,
43+ SortByUsage
44+ };
45+
46+ AppDrawerProxyModel(QObject* parent = nullptr);
47+
48+ QAbstractItemModel* source() const;
49+ void setSource(QAbstractItemModel* source);
50+
51+ GroupBy group() const;
52+ void setGroup(GroupBy group);
53+
54+ QString filterLetter() const;
55+ void setFilterLetter(const QString &filterLetter);
56+
57+ QString filterString() const;
58+ void setFilterString(const QString &filterString);
59+
60+ SortBy sortBy() const;
61+ void setSortBy(SortBy sortBy);
62+
63+ int count() const;
64+
65+ QVariant data(const QModelIndex &index, int role) const override;
66+ QHash<int, QByteArray> roleNames() const override;
67+
68+ Q_INVOKABLE QString appId(int index) const;
69+
70+protected:
71+ bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
72+
73+Q_SIGNALS:
74+ void sourceChanged();
75+ void groupChanged();
76+ void filterLetterChanged();
77+ void filterStringChanged();
78+ void sortByChanged();
79+ void countChanged();
80+
81+private:
82+ QAbstractItemModel* m_source = nullptr;
83+ GroupBy m_group = GroupByNone;
84+ QString m_filterLetter;
85+ QString m_filterString;
86+ SortBy m_sortBy = SortByAToZ;
87+};
plugins/Utils/globalfunctions.cpp+5
@@ -51,3 +51,8 @@ QQuickItem *GlobalFunctions::itemAt(QQuickItem* parent, int x, int y, QJSValue m
51 }
51 }
52 return nullptr;
52 return nullptr;
53}
53}
54+
55+bool GlobalFunctions::itemUnderMouse(QQuickItem* item)
56+{
57+ return item && item->isUnderMouse();
58+}
plugins/Utils/globalfunctions.h+2
@@ -37,6 +37,8 @@ public:
37 int x,
37 int x,
38 int y,
38 int y,
39 QJSValue matcher);
39 QJSValue matcher);
40+
41+ static Q_INVOKABLE bool itemUnderMouse(QQuickItem* item);
40};
42};
41
43
42#endif // GLOBALFUNCTIONS_H
44#endif // GLOBALFUNCTIONS_H
plugins/Utils/plugin.cpp+2
@@ -39,6 +39,7 @@
39#include "deviceconfigparser.h"
39#include "deviceconfigparser.h"
40#include "globalfunctions.h"
40#include "globalfunctions.h"
41#include "URLDispatcher.h"
41#include "URLDispatcher.h"
42+#include "appdrawerproxymodel.h"
42
43
43static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
44static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
44{
45{
@@ -82,4 +83,5 @@ void UtilsPlugin::registerTypes(const char *uri)
82 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
83 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
83 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
84 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
84 qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher");
85 qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher");
86+ qmlRegisterType<AppDrawerProxyModel>(uri, 0, 1, "AppDrawerProxyModel");
85}
87}
po/unity8.pot+80−43
@@ -8,7 +8,7 @@ msgid ""
8msgstr ""
8msgstr ""
9"Project-Id-Version: unity8\n"
9"Project-Id-Version: unity8\n"
10"Report-Msgid-Bugs-To: \n"
10"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2016-11-29 09:43+0000\n"
11+"POT-Creation-Date: 2016-12-07 13:49+0000\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -23,7 +23,7 @@ msgid "Password: "
23msgstr ""
23msgstr ""
24
24
25#: plugins/Unity/Launcher/launcheritem.cpp:50
25#: plugins/Unity/Launcher/launcheritem.cpp:50
26−#: plugins/Unity/Launcher/launcheritem.cpp:109
26+#: plugins/Unity/Launcher/launcheritem.cpp:122
27msgid "Pin shortcut"
27msgid "Pin shortcut"
28msgstr ""
28msgstr ""
29
29
@@ -31,7 +31,7 @@ msgstr ""
31msgid "Quit"
31msgid "Quit"
32msgstr ""
32msgstr ""
33
33
34−#: plugins/Unity/Launcher/launcheritem.cpp:109
34+#: plugins/Unity/Launcher/launcheritem.cpp:122
35msgid "Unpin shortcut"
35msgid "Unpin shortcut"
36msgstr ""
36msgstr ""
37
37
@@ -175,87 +175,95 @@ msgstr ""
175msgid "Same as clicking on a launcher icon."
175msgid "Same as clicking on a launcher icon."
176msgstr ""
176msgstr ""
177
177
178−#: qml/Components/KeyboardShortcutsOverlay.qml:181
178+#: qml/Components/KeyboardShortcutsOverlay.qml:177
179+msgid "Super + A"
180+msgstr ""
181+
182+#: qml/Components/KeyboardShortcutsOverlay.qml:182
183+msgid "Opens the Application Drawer."
184+msgstr ""
185+
186+#: qml/Components/KeyboardShortcutsOverlay.qml:194
179msgid "Scopes"
187msgid "Scopes"
180msgstr ""
188msgstr ""
181
189
182−#: qml/Components/KeyboardShortcutsOverlay.qml:188
190+#: qml/Components/KeyboardShortcutsOverlay.qml:201
183msgid "Super (Tap)"
191msgid "Super (Tap)"
184msgstr ""
192msgstr ""
185
193
186−#: qml/Components/KeyboardShortcutsOverlay.qml:193
194+#: qml/Components/KeyboardShortcutsOverlay.qml:206
187msgid "Opens the Scopes home."
195msgid "Opens the Scopes home."
188msgstr ""
196msgstr ""
189
197
190−#: qml/Components/KeyboardShortcutsOverlay.qml:209
198+#: qml/Components/KeyboardShortcutsOverlay.qml:222
191msgid "Switching"
199msgid "Switching"
192msgstr ""
200msgstr ""
193
201
194−#: qml/Components/KeyboardShortcutsOverlay.qml:216
202+#: qml/Components/KeyboardShortcutsOverlay.qml:229
195msgid "Alt + Tab"
203msgid "Alt + Tab"
196msgstr ""
204msgstr ""
197
205
198−#: qml/Components/KeyboardShortcutsOverlay.qml:221
206+#: qml/Components/KeyboardShortcutsOverlay.qml:234
199msgid "Switches between applications."
207msgid "Switches between applications."
200msgstr ""
208msgstr ""
201
209
202−#: qml/Components/KeyboardShortcutsOverlay.qml:229
210+#: qml/Components/KeyboardShortcutsOverlay.qml:242
203msgid "Super + W"
211msgid "Super + W"
204msgstr ""
212msgstr ""
205
213
206−#: qml/Components/KeyboardShortcutsOverlay.qml:234
214+#: qml/Components/KeyboardShortcutsOverlay.qml:247
207msgid "Opens the desktop spread."
215msgid "Opens the desktop spread."
208msgstr ""
216msgstr ""
209
217
210−#: qml/Components/KeyboardShortcutsOverlay.qml:242
218+#: qml/Components/KeyboardShortcutsOverlay.qml:255
211msgid "Cursor Left or Right"
219msgid "Cursor Left or Right"
212msgstr ""
220msgstr ""
213
221
214−#: qml/Components/KeyboardShortcutsOverlay.qml:247
222+#: qml/Components/KeyboardShortcutsOverlay.qml:260
215msgid "Moves the focus."
223msgid "Moves the focus."
216msgstr ""
224msgstr ""
217
225
218−#: qml/Components/KeyboardShortcutsOverlay.qml:259
226+#: qml/Components/KeyboardShortcutsOverlay.qml:272
219msgid "Windows"
227msgid "Windows"
220msgstr ""
228msgstr ""
221
229
222−#: qml/Components/KeyboardShortcutsOverlay.qml:266
230+#: qml/Components/KeyboardShortcutsOverlay.qml:279
223msgid "Ctrl + Super + D"
231msgid "Ctrl + Super + D"
224msgstr ""
232msgstr ""
225
233
226−#: qml/Components/KeyboardShortcutsOverlay.qml:271
234+#: qml/Components/KeyboardShortcutsOverlay.qml:284
227msgid "Minimizes all windows."
235msgid "Minimizes all windows."
228msgstr ""
236msgstr ""
229
237
230−#: qml/Components/KeyboardShortcutsOverlay.qml:279
238+#: qml/Components/KeyboardShortcutsOverlay.qml:292
231msgid "Ctrl + Super + Up"
239msgid "Ctrl + Super + Up"
232msgstr ""
240msgstr ""
233
241
234−#: qml/Components/KeyboardShortcutsOverlay.qml:284
242+#: qml/Components/KeyboardShortcutsOverlay.qml:297
235msgid "Maximizes the current window."
243msgid "Maximizes the current window."
236msgstr ""
244msgstr ""
237
245
238−#: qml/Components/KeyboardShortcutsOverlay.qml:292
246+#: qml/Components/KeyboardShortcutsOverlay.qml:305
239msgid "Ctrl + Super + Down"
247msgid "Ctrl + Super + Down"
240msgstr ""
248msgstr ""
241
249
242−#: qml/Components/KeyboardShortcutsOverlay.qml:297
250+#: qml/Components/KeyboardShortcutsOverlay.qml:310
243msgid "Minimizes or restores the current window."
251msgid "Minimizes or restores the current window."
244msgstr ""
252msgstr ""
245
253
246−#: qml/Components/KeyboardShortcutsOverlay.qml:305
254+#: qml/Components/KeyboardShortcutsOverlay.qml:318
247msgid "Ctrl + Super + Left or Right"
255msgid "Ctrl + Super + Left or Right"
248msgstr ""
256msgstr ""
249
257
250−#: qml/Components/KeyboardShortcutsOverlay.qml:310
258+#: qml/Components/KeyboardShortcutsOverlay.qml:323
251msgid "Semi-maximizes the current window."
259msgid "Semi-maximizes the current window."
252msgstr ""
260msgstr ""
253
261
254−#: qml/Components/KeyboardShortcutsOverlay.qml:318
262+#: qml/Components/KeyboardShortcutsOverlay.qml:331
255msgid "Alt + F4"
263msgid "Alt + F4"
256msgstr ""
264msgstr ""
257
265
258−#: qml/Components/KeyboardShortcutsOverlay.qml:323
266+#: qml/Components/KeyboardShortcutsOverlay.qml:336
259msgid "Closes the current window."
267msgid "Closes the current window."
260msgstr ""
268msgstr ""
261
269
@@ -311,6 +319,28 @@ msgstr ""
311msgid "Preview Share Item"
319msgid "Preview Share Item"
312msgstr ""
320msgstr ""
313
321
322+#: qml/Components/VirtualTouchPad.qml:317
323+msgid ""
324+"Your device is now connected to an external display. Use this screen as a "
325+"touch pad to interact with the pointer."
326+msgstr ""
327+
328+#: qml/Components/VirtualTouchPad.qml:327
329+msgid "Tap left button to click."
330+msgstr ""
331+
332+#: qml/Components/VirtualTouchPad.qml:339
333+msgid "Tap right button to right click."
334+msgstr ""
335+
336+#: qml/Components/VirtualTouchPad.qml:351
337+msgid "Swipe with two fingers to scroll."
338+msgstr ""
339+
340+#: qml/Components/VirtualTouchPad.qml:396
341+msgid "Find more settings in the system settings."
342+msgstr ""
343+
314#: qml/Dash/DashPageHeader.qml:361
344#: qml/Dash/DashPageHeader.qml:361
315msgctxt "Button: Open the Ubuntu Store"
345msgctxt "Button: Open the Ubuntu Store"
316msgid "Store"
346msgid "Store"
@@ -408,12 +438,6 @@ msgstr ""
408msgid "Also installed"
438msgid "Also installed"
409msgstr ""
439msgstr ""
410
440
411−#: qml/DisabledScreenNotice.qml:109
412−msgid ""
413−"Your device is now connected to an external display. Use this screen as a "
414−"touch pad to interact with the pointer."
415−msgstr ""
416−
417#: qml/Greeter/CoverPage.qml:127
441#: qml/Greeter/CoverPage.qml:127
418msgid "Unlock"
442msgid "Unlock"
419msgstr ""
443msgstr ""
@@ -437,7 +461,7 @@ msgid_plural "Please wait %1 minutes and then try again…"
437msgstr[0] ""
461msgstr[0] ""
438msgstr[1] ""
462msgstr[1] ""
439
463
440−#: qml/Greeter/Greeter.qml:596
464+#: qml/Greeter/Greeter.qml:592
441msgid "Try again"
465msgid "Try again"
442msgstr ""
466msgstr ""
443
467
@@ -465,6 +489,19 @@ msgstr ""
465msgid "Select desktop environment"
489msgid "Select desktop environment"
466msgstr ""
490msgstr ""
467
491
492+#: qml/Launcher/Drawer.qml:80
493+msgid "Search…"
494+msgstr ""
495+
496+#: qml/Launcher/Drawer.qml:101
497+msgctxt "Apps sorted alphabetically"
498+msgid "A-Z"
499+msgstr ""
500+
501+#: qml/Launcher/MoreAppsHeader.qml:32
502+msgid "More apps in the store"
503+msgstr ""
504+
468#: qml/Notifications/NotificationMenuItemFactory.qml:124
505#: qml/Notifications/NotificationMenuItemFactory.qml:124
469msgid "Show password"
506msgid "Show password"
470msgstr ""
507msgstr ""
@@ -477,62 +514,62 @@ msgstr ""
477msgid "Conference"
514msgid "Conference"
478msgstr ""
515msgstr ""
479
516
480−#: qml/Panel/Indicators/MenuItemFactory.qml:847
517+#: qml/Panel/Indicators/MenuItemFactory.qml:868
481msgid "Nothing is playing"
518msgid "Nothing is playing"
482msgstr ""
519msgstr ""
483
520
484−#: qml/Panel/Indicators/MenuItemFactory.qml:975
521+#: qml/Panel/Indicators/MenuItemFactory.qml:996
485#, qt-format
522#, qt-format
486msgid "%1 hour"
523msgid "%1 hour"
487msgid_plural "%1 hours"
524msgid_plural "%1 hours"
488msgstr[0] ""
525msgstr[0] ""
489msgstr[1] ""
526msgstr[1] ""
490
527
491−#: qml/Panel/Indicators/MenuItemFactory.qml:979
528+#: qml/Panel/Indicators/MenuItemFactory.qml:1000
492#, qt-format
529#, qt-format
493msgid "%1 minute"
530msgid "%1 minute"
494msgid_plural "%1 minutes"
531msgid_plural "%1 minutes"
495msgstr[0] ""
532msgstr[0] ""
496msgstr[1] ""
533msgstr[1] ""
497
534
498−#: qml/Panel/Indicators/MenuItemFactory.qml:984
535+#: qml/Panel/Indicators/MenuItemFactory.qml:1005
499#, qt-format
536#, qt-format
500msgid "%1 second"
537msgid "%1 second"
501msgid_plural "%1 seconds"
538msgid_plural "%1 seconds"
502msgstr[0] ""
539msgstr[0] ""
503msgstr[1] ""
540msgstr[1] ""
504
541
505−#: qml/Panel/Indicators/MenuItemFactory.qml:987
542+#: qml/Panel/Indicators/MenuItemFactory.qml:1008
506msgid "0 seconds"
543msgid "0 seconds"
507msgstr ""
544msgstr ""
508
545
509#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
546#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
510−#: qml/Panel/Indicators/MenuItemFactory.qml:989
547+#: qml/Panel/Indicators/MenuItemFactory.qml:1010
511#, qt-format
548#, qt-format
512msgid "%1 remaining"
549msgid "%1 remaining"
513msgstr ""
550msgstr ""
514
551
515−#: qml/Panel/Indicators/MenuItemFactory.qml:995
552+#: qml/Panel/Indicators/MenuItemFactory.qml:1016
516msgid "In queue…"
553msgid "In queue…"
517msgstr ""
554msgstr ""
518
555
519−#: qml/Panel/Indicators/MenuItemFactory.qml:999
556+#: qml/Panel/Indicators/MenuItemFactory.qml:1020
520msgid "Downloading"
557msgid "Downloading"
521msgstr ""
558msgstr ""
522
559
523−#: qml/Panel/Indicators/MenuItemFactory.qml:1001
560+#: qml/Panel/Indicators/MenuItemFactory.qml:1022
524msgid "Paused, tap to resume"
561msgid "Paused, tap to resume"
525msgstr ""
562msgstr ""
526
563
527−#: qml/Panel/Indicators/MenuItemFactory.qml:1003
564+#: qml/Panel/Indicators/MenuItemFactory.qml:1024
528msgid "Canceled"
565msgid "Canceled"
529msgstr ""
566msgstr ""
530
567
531−#: qml/Panel/Indicators/MenuItemFactory.qml:1005
568+#: qml/Panel/Indicators/MenuItemFactory.qml:1026
532msgid "Finished"
569msgid "Finished"
533msgstr ""
570msgstr ""
534
571
535−#: qml/Panel/Indicators/MenuItemFactory.qml:1007
572+#: qml/Panel/Indicators/MenuItemFactory.qml:1028
536msgid "Failed, tap to retry"
573msgid "Failed, tap to retry"
537msgstr ""
574msgstr ""
538
575
qml/Components/KeyboardShortcutsOverlay.qml+13
@@ -173,6 +173,19 @@ Rectangle {
173 Layout.maximumWidth: maxTextSize
173 Layout.maximumWidth: maxTextSize
174 }
174 }
175
175
176+ Label {
177+ text: i18n.tr("Super + A")
178+ fontSize: "small"
179+ font.weight: Font.Medium
180+ }
181+ Label {
182+ text: i18n.tr("Opens the Application Drawer.")
183+ fontSize: "small"
184+ font.weight: Font.Light
185+ wrapMode: Text.Wrap
186+ Layout.maximumWidth: maxTextSize
187+ }
188+
176
189
177 // Scopes section
190 // Scopes section
178 Item { Layout.columnSpan: 2; height: units.gu(2) }
191 Item { Layout.columnSpan: 2; height: units.gu(2) }
qml/Components/VirtualTouchPad.qml+269−1
@@ -15,20 +15,58 @@
15 */
15 */
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18+import QtQuick.Layouts 1.1
18import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20+import Qt.labs.settings 1.0
21+import Unity.Screens 0.1
19import UInput 0.1
22import UInput 0.1
23+import "../Components"
20
24
21Item {
25Item {
26+ id: root
22 property var uinput: UInput {
27 property var uinput: UInput {
23 Component.onCompleted: createMouse();
28 Component.onCompleted: createMouse();
24 Component.onDestruction: removeMouse();
29 Component.onDestruction: removeMouse();
25 }
30 }
26
31
32+ Component.onCompleted: {
33+ if (!settings.touchpadTutorialHasRun) {
34+ root.runTutorial()
35+ }
36+ }
37+
38+ function runTutorial() {
39+ // If the tutorial animation is started too early, e.g. in Component.onCompleted,
40+ // root width & height might be reported as 0x0 still. As animations read their
41+ // values at startup and won't update them, lets make sure to only start once
42+ // we have some actual size.
43+ if (root.width > 0 && root.height > 0) {
44+ tutorial.start();
45+ } else {
46+ tutorialTimer.start();
47+ }
48+ }
49+
50+ Timer {
51+ id: tutorialTimer
52+ interval: 50
53+ repeat: false
54+ running: false
55+ onTriggered: root.runTutorial();
56+ }
57+
27− readonly property bool pressed: point1.pressed || point2.pressed
58+ readonly property bool pressed: point1.pressed || point2.pressed || leftButton.pressed || rightButton.pressed
59+
60+ property var settings: Settings {
61+ objectName: "virtualTouchPadSettings"
62+ property bool touchpadTutorialHasRun: false
63+ property bool oskEnabled: true
64+ }
28
65
29 MultiPointTouchArea {
66 MultiPointTouchArea {
30 objectName: "touchPadArea"
67 objectName: "touchPadArea"
31 anchors.fill: parent
68 anchors.fill: parent
69+ enabled: !tutorial.running || tutorial.paused
32
70
33 // FIXME: Once we have Qt DPR support, this should be Qt.styleHints.startDragDistance
71 // FIXME: Once we have Qt DPR support, this should be Qt.styleHints.startDragDistance
34 readonly property int clickThreshold: internalGu * 1.5
72 readonly property int clickThreshold: internalGu * 1.5
@@ -37,6 +75,11 @@ Item {
37 property bool isDrag: false
75 property bool isDrag: false
38
76
39 onPressed: {
77 onPressed: {
78+ if (tutorial.paused) {
79+ tutorial.resume();
80+ return;
81+ }
82+
40 // If double-tapping *really* fast, it could happen that we end up having only point2 pressed
83 // If double-tapping *really* fast, it could happen that we end up having only point2 pressed
41 // Make sure we check for both combos, only point1 or only point2
84 // Make sure we check for both combos, only point1 or only point2
42 if (((point1.pressed && !point2.pressed) || (!point1.pressed && point2.pressed))
85 if (((point1.pressed && !point2.pressed) || (!point1.pressed && point2.pressed))
@@ -135,4 +178,229 @@ Item {
135 }
178 }
136 ]
179 ]
137 }
180 }
181+
182+ RowLayout {
183+ anchors { left: parent.left; right: parent.right; bottom: parent.bottom; margins: -internalGu * 1 }
184+ height: internalGu * 10
185+ spacing: internalGu * 1
186+
187+ MouseArea {
188+ id: leftButton
189+ objectName: "leftButton"
190+ Layout.fillWidth: true
191+ Layout.fillHeight: true
192+ onPressed: uinput.pressMouse(UInput.ButtonLeft);
193+ onReleased: uinput.releaseMouse(UInput.ButtonLeft);
194+ property bool highlight: false
195+ UbuntuShape {
196+ anchors.fill: parent
197+ backgroundColor: leftButton.highlight || leftButton.pressed ? UbuntuColors.ash : UbuntuColors.inkstone
198+ Behavior on backgroundColor { ColorAnimation { duration: UbuntuAnimation.FastDuration } }
199+ }
200+ }
201+
202+ MouseArea {
203+ id: rightButton
204+ objectName: "rightButton"
205+ Layout.fillWidth: true
206+ Layout.fillHeight: true
207+ onPressed: uinput.pressMouse(UInput.ButtonRight);
208+ onReleased: uinput.releaseMouse(UInput.ButtonRight);
209+ property bool highlight: false
210+ UbuntuShape {
211+ anchors.fill: parent
212+ backgroundColor: rightButton.highlight || rightButton.pressed ? UbuntuColors.ash : UbuntuColors.inkstone
213+ Behavior on backgroundColor { ColorAnimation { duration: UbuntuAnimation.FastDuration } }
214+ }
215+ }
216+ }
217+
218+ AbstractButton {
219+ id: oskButton
220+ objectName: "oskButton"
221+ anchors { right: parent.right; top: parent.top; margins: internalGu * 2 }
222+ height: internalGu * 6
223+ width: height
224+
225+ onClicked: {
226+ settings.oskEnabled = !settings.oskEnabled
227+ }
228+
229+ Rectangle {
230+ anchors.fill: parent
231+ radius: width / 2
232+ color: UbuntuColors.inkstone
233+ }
234+
235+ Icon {
236+ anchors.fill: parent
237+ anchors.margins: internalGu * 1.5
238+ name: "input-keyboard-symbolic"
239+ }
240+ }
241+
242+ Screens {
243+ id: screens
244+ }
245+
246+ InputMethod {
247+ id: inputMethod
248+ // Don't resize when there is only one screen to avoid resize clashing with the InputMethod in the Shell.
249+ enabled: screens.count > 1 && settings.oskEnabled && !tutorial.running
250+ objectName: "inputMethod"
251+ anchors.fill: parent
252+ }
253+
254+ Label {
255+ id: tutorialLabel
256+ objectName: "tutorialLabel"
257+ anchors { left: parent.left; top: parent.top; right: parent.right; margins: internalGu * 4; topMargin: internalGu * 10 }
258+ opacity: 0
259+ visible: opacity > 0
260+ font.pixelSize: 2 * internalGu
261+ color: "white"
262+ wrapMode: Text.WordWrap
263+ }
264+
265+ Icon {
266+ id: tutorialImage
267+ objectName: "tutorialImage"
268+ height: internalGu * 8
269+ width: height
270+ name: "input-touchpad-symbolic"
271+ color: "white"
272+ opacity: 0
273+ visible: opacity > 0
274+ anchors { top: tutorialLabel.bottom; horizontalCenter: parent.horizontalCenter; margins: internalGu * 2 }
275+ }
276+
277+ Item {
278+ id: tutorialFinger1
279+ objectName: "tutorialFinger1"
280+ width: internalGu * 5
281+ height: width
282+ property real scale: 1
283+ opacity: 0
284+ visible: opacity > 0
285+ Rectangle {
286+ width: parent.width * parent.scale
287+ height: width
288+ anchors.centerIn: parent
289+ radius: width / 2
290+ color: UbuntuColors.inkstone
291+ }
292+ }
293+
294+ Item {
295+ id: tutorialFinger2
296+ objectName: "tutorialFinger2"
297+ width: internalGu * 5
298+ height: width
299+ property real scale: 1
300+ opacity: 0
301+ visible: opacity > 0
302+ Rectangle {
303+ width: parent.width * parent.scale
304+ height: width
305+ anchors.centerIn: parent
306+ radius: width / 2
307+ color: UbuntuColors.inkstone
308+ }
309+ }
310+
311+ SequentialAnimation {
312+ id: tutorial
313+ objectName: "tutorialAnimation"
314+
315+ PropertyAction { targets: [leftButton, rightButton, oskButton]; property: "enabled"; value: false }
316+ PropertyAction { targets: [leftButton, rightButton, oskButton]; property: "opacity"; value: 0 }
317+ PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Your device is now connected to an external display. Use this screen as a touch pad to interact with the pointer.") }
318+ UbuntuNumberAnimation { targets: [tutorialLabel, tutorialImage]; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
319+ PropertyAction { target: tutorial; property: "paused"; value: true }
320+ PauseAnimation { duration: 500 } // it takes a bit until pausing actually takes effect
321+ UbuntuNumberAnimation { targets: [tutorialLabel, tutorialImage]; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
322+
323+ UbuntuNumberAnimation { target: leftButton; property: "opacity"; to: 1 }
324+ UbuntuNumberAnimation { target: rightButton; property: "opacity"; to: 1 }
325+
326+ PauseAnimation { duration: UbuntuAnimation.SleepyDuration }
327+ PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Tap left button to click.") }
328+ UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
329+ SequentialAnimation {
330+ loops: 2
331+ PropertyAction { target: leftButton; property: "highlight"; value: true }
332+ PauseAnimation { duration: UbuntuAnimation.FastDuration }
333+ PropertyAction { target: leftButton; property: "highlight"; value: false }
334+ PauseAnimation { duration: UbuntuAnimation.SleepyDuration }
335+ }
336+ UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
337+
338+ PauseAnimation { duration: UbuntuAnimation.SleepyDuration }
339+ PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Tap right button to right click.") }
340+ UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
341+ SequentialAnimation {
342+ loops: 2
343+ PropertyAction { target: rightButton; property: "highlight"; value: true }
344+ PauseAnimation { duration: UbuntuAnimation.FastDuration }
345+ PropertyAction { target: rightButton; property: "highlight"; value: false }
346+ PauseAnimation { duration: UbuntuAnimation.SleepyDuration }
347+ }
348+ UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
349+
350+ PauseAnimation { duration: UbuntuAnimation.SleepyDuration }
351+ PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Swipe with two fingers to scroll.") }
352+ UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
353+ PropertyAction { target: tutorialFinger1; property: "x"; value: root.width / 2 - tutorialFinger1.width - internalGu * 1 }
354+ PropertyAction { target: tutorialFinger2; property: "x"; value: root.width / 2 + tutorialFinger1.width + internalGu * 1 - tutorialFinger2.width }
355+ PropertyAction { target: tutorialFinger1; property: "y"; value: root.height / 2 - internalGu * 10 }
356+ PropertyAction { target: tutorialFinger2; property: "y"; value: root.height / 2 - internalGu * 10 }
357+ SequentialAnimation {
358+ ParallelAnimation {
359+ UbuntuNumberAnimation { target: tutorialFinger1; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
360+ UbuntuNumberAnimation { target: tutorialFinger2; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
361+ UbuntuNumberAnimation { target: tutorialFinger1; property: "scale"; from: 0; to: 1; duration: UbuntuAnimation.FastDuration }
362+ UbuntuNumberAnimation { target: tutorialFinger2; property: "scale"; from: 0; to: 1; duration: UbuntuAnimation.FastDuration }
363+ }
364+ ParallelAnimation {
365+ UbuntuNumberAnimation { target: tutorialFinger1; property: "y"; to: root.height / 2 + internalGu * 10; duration: UbuntuAnimation.SleepyDuration }
366+ UbuntuNumberAnimation { target: tutorialFinger2; property: "y"; to: root.height / 2 + internalGu * 10; duration: UbuntuAnimation.SleepyDuration }
367+ }
368+ ParallelAnimation {
369+ UbuntuNumberAnimation { target: tutorialFinger1; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
370+ UbuntuNumberAnimation { target: tutorialFinger2; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
371+ UbuntuNumberAnimation { target: tutorialFinger1; property: "scale"; from: 1; to: 0; duration: UbuntuAnimation.FastDuration }
372+ UbuntuNumberAnimation { target: tutorialFinger2; property: "scale"; from: 1; to: 0; duration: UbuntuAnimation.FastDuration }
373+ }
374+ PauseAnimation { duration: UbuntuAnimation.SlowDuration }
375+ ParallelAnimation {
376+ UbuntuNumberAnimation { target: tutorialFinger1; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
377+ UbuntuNumberAnimation { target: tutorialFinger2; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
378+ UbuntuNumberAnimation { target: tutorialFinger1; property: "scale"; from: 0; to: 1; duration: UbuntuAnimation.FastDuration }
379+ UbuntuNumberAnimation { target: tutorialFinger2; property: "scale"; from: 0; to: 1; duration: UbuntuAnimation.FastDuration }
380+ }
381+ ParallelAnimation {
382+ UbuntuNumberAnimation { target: tutorialFinger1; property: "y"; to: root.height / 2 - internalGu * 10; duration: UbuntuAnimation.SleepyDuration }
383+ UbuntuNumberAnimation { target: tutorialFinger2; property: "y"; to: root.height / 2 - internalGu * 10; duration: UbuntuAnimation.SleepyDuration }
384+ }
385+ ParallelAnimation {
386+ UbuntuNumberAnimation { target: tutorialFinger1; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
387+ UbuntuNumberAnimation { target: tutorialFinger2; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
388+ UbuntuNumberAnimation { target: tutorialFinger1; property: "scale"; from: 1; to: 0; duration: UbuntuAnimation.FastDuration }
389+ UbuntuNumberAnimation { target: tutorialFinger2; property: "scale"; from: 1; to: 0; duration: UbuntuAnimation.FastDuration }
390+ }
391+ PauseAnimation { duration: UbuntuAnimation.SlowDuration }
392+ }
393+ UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
394+
395+ PauseAnimation { duration: UbuntuAnimation.SleepyDuration }
396+ PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Find more settings in the system settings.") }
397+ UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
398+ PauseAnimation { duration: 2000 }
399+ UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
400+
401+ UbuntuNumberAnimation { target: oskButton; property: "opacity"; to: 1 }
402+ PropertyAction { targets: [leftButton, rightButton, oskButton]; property: "enabled"; value: true }
403+
404+ PropertyAction { target: settings; property: "touchpadTutorialHasRun"; value: true }
405+ }
138}
406}
qml/Dash/Previews/PreviewAudioPlayback.qml+1
@@ -70,6 +70,7 @@ PreviewWidget {
70 objectName: "playButton"
70 objectName: "playButton"
71 width: trackRow.column1Width
71 width: trackRow.column1Width
72 height: width
72 height: width
73+ activeFocusOnPress: false
73
74
74 onClicked: {
75 onClicked: {
75 if (trackItem.isPlayingItem) {
76 if (trackItem.isPlayingItem) {
qml/DisabledScreenNotice.qml+3−55
@@ -18,15 +18,12 @@ import QtQuick 2.4
18import QtQuick.Layouts 1.1
18import QtQuick.Layouts 1.1
19import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20import Unity.Session 0.1
20import Unity.Session 0.1
21−import Unity.Screens 0.1
22import QtQuick.Window 2.2
21import QtQuick.Window 2.2
23import "Components"
22import "Components"
24
23
25Item {
24Item {
26 id: root
25 id: root
27
26
28− property bool infoNoteDisplayed: true
29−
30 // For testing
27 // For testing
31 property var screen: Screen
28 property var screen: Screen
32 property var orientationLock: OrientationLock
29 property var orientationLock: OrientationLock
@@ -36,10 +33,6 @@ Item {
36 name: applicationArguments.deviceName
33 name: applicationArguments.deviceName
37 }
34 }
38
35
39− Screens {
40− id: screens
41− }
42−
43 Item {
36 Item {
44 id: contentContainer
37 id: contentContainer
45 objectName: "contentContainer"
38 objectName: "contentContainer"
@@ -75,58 +68,13 @@ Item {
75 }
68 }
76 transformOrigin: Item.Center
69 transformOrigin: Item.Center
77
70
78− VirtualTouchPad {
79− anchors.fill: parent
80−
81− onPressedChanged: {
82− if (pressed && infoNoteDisplayed) {
83− infoNoteDisplayed = false;
84− }
85− }
86− }
87−
88 Rectangle {
71 Rectangle {
89 anchors.fill: parent
72 anchors.fill: parent
90− color: "#3b3b3b"
73+ color: UbuntuColors.jet
91 }
74 }
92
75
93− Item {
76+ VirtualTouchPad {
94− objectName: "infoNoticeArea"
95− anchors.fill: parent
96− opacity: infoNoteDisplayed ? 1 : 0
97− visible: opacity > 0
98− Behavior on opacity {
99− UbuntuNumberAnimation { }
100− }
101−
102− Column {
103− anchors.centerIn: parent
104− width: parent.width - (internalGu * 8)
105− spacing: internalGu * 4
106−
107− Label {
108− id: text
109− text: i18n.tr("Your device is now connected to an external display. Use this screen as a touch pad to interact with the pointer.")
110− color: "white"
111− width: parent.width
112− font.pixelSize: 2.5 * internalGu
113− wrapMode: Text.Wrap
114− }
115− Icon {
116− height: internalGu * 8
117− width: height
118− name: "input-touchpad-symbolic"
119− color: "white"
120− anchors.horizontalCenter: parent.horizontalCenter
121− }
122− }
123− }
124−
125− InputMethod {
126− id: inputMethod
127− // Don't resize when there is only one screen to avoid resize clashing with the InputMethod in the Shell.
128− enabled: screens.count > 1
129− objectName: "inputMethod"
77+ objectName: "virtualTouchPad"
130 anchors.fill: parent
78 anchors.fill: parent
131 }
79 }
132 }
80 }
qml/Greeter/Greeter.qml+1−5
@@ -145,11 +145,7 @@ Showable {
145 }
145 }
146 }
146 }
147
147
148− if (loader.item.sessionToStart === LightDMService.greeter.defaultSession) {
149− return LightDMService.greeter.defaultSession;
148+ return LightDMService.greeter.defaultSession;
150− } else {
151− return "ubuntu"; // The default / fallback
152− }
153 }
149 }
154
150
155 QtObject {
151 QtObject {
qml/Greeter/LoginList.qml+1−1
@@ -165,7 +165,7 @@ StyledItem {
165 }
165 }
166
166
167 delegate: Item {
167 delegate: Item {
168− width: parent.width
168+ width: userList.width
169 height: root.cellHeight
169 height: root.cellHeight
170
170
171 readonly property bool belowHighlight: (userList.currentIndex < 0 && index > 0) || (userList.currentIndex >= 0 && index > userList.currentIndex)
171 readonly property bool belowHighlight: (userList.currentIndex < 0 && index > 0) || (userList.currentIndex >= 0 && index > userList.currentIndex)
qml/Greeter/WideView.qml+7−2
@@ -125,6 +125,8 @@ FocusScope {
125 id: loginList
125 id: loginList
126 objectName: "loginList"
126 objectName: "loginList"
127
127
128+ property int selectedUserIndex: 0
129+
128 width: units.gu(40)
130 width: units.gu(40)
129 anchors {
131 anchors {
130 left: parent.left
132 left: parent.left
@@ -139,9 +141,12 @@ FocusScope {
139 Behavior on boxVerticalOffset { UbuntuNumberAnimation {} }
141 Behavior on boxVerticalOffset { UbuntuNumberAnimation {} }
140
142
141 model: root.userModel
143 model: root.userModel
142− currentSession: LightDMService.greeter.defaultSession
144+ currentSession: LightDMService.users.data(selectedUserIndex, LightDMService.userRoles.SessionRole);
143 onResponded: root.responded(response)
145 onResponded: root.responded(response)
146+ onSelected: {
144− onSelected: root.selected(index)
147+ root.selected(index)
148+ loginList.selectedUserIndex = index;
149+ }
145 onSessionChooserButtonClicked: parent.state = "SessionsList"
150 onSessionChooserButtonClicked: parent.state = "SessionsList"
146
151
147 Keys.forwardTo: [sessionChooserLoader.item]
152 Keys.forwardTo: [sessionChooserLoader.item]
qml/Launcher/BackgroundBlur.qmlnew file+81
@@ -0,0 +1,81 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import QtGraphicalEffects 1.0
20+
21+Item {
22+ id: root
23+
24+ property int blurAmount: 32
25+ property Item sourceItem
26+ property rect blurRect: Qt.rect(0,0,0,0)
27+ property alias cached: fastBlur.cached
28+
29+ Rectangle {
30+ id: blurMask
31+ color: "yellow"
32+ x: blurRect.x
33+ y: blurRect.y
34+ width: blurRect.width
35+ height: blurRect.height
36+ }
37+
38+ ShaderEffect {
39+ id: maskedBlurEffect
40+ x: blurRect.x
41+ y: blurRect.y
42+ width: blurRect.width
43+ height: blurRect.height
44+
45+ property variant source: ShaderEffectSource {
46+ id: shaderEffectSource
47+ sourceItem: root.sourceItem
48+ hideSource: false
49+ sourceRect: root.blurRect
50+ }
51+
52+ property var mask: ShaderEffectSource {
53+ sourceItem: blurMask
54+ hideSource: true
55+ }
56+
57+ fragmentShader: "
58+ varying highp vec2 qt_TexCoord0;
59+ uniform sampler2D source;
60+ uniform sampler2D mask;
61+ void main(void)
62+ {
63+ highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
64+ highp vec4 maskColor = texture2D(mask, qt_TexCoord0);
65+
66+ sourceColor *= maskColor.a;
67+
68+ gl_FragColor = sourceColor;
69+ }"
70+ }
71+
72+ FastBlur {
73+ id: fastBlur
74+ x: blurRect.x
75+ y: blurRect.y
76+ width: blurRect.width
77+ height: blurRect.height
78+ source: maskedBlurEffect
79+ radius: Math.min(blurAmount, 128)
80+ }
81+ }
qml/Launcher/Drawer.qmlnew file+306
@@ -0,0 +1,306 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import Unity.Launcher 0.1
20+import Utils 0.1
21+import "../Components"
22+import Qt.labs.settings 1.0
23+
24+FocusScope {
25+ id: root
26+
27+ property int panelWidth: 0
28+ readonly property bool moving: listLoader.item && listLoader.item.moving
29+
30+ signal applicationSelected(string appId)
31+
32+ property bool draggingHorizontally: false
33+ property int dragDistance: 0
34+
35+ onFocusChanged: {
36+ if (focus) {
37+ searchField.selectAll();
38+ }
39+ }
40+
41+ function focusInput() {
42+ searchField.selectAll();
43+ searchField.focus = true;
44+ }
45+
46+ Settings {
47+ property alias selectedTab: sections.selectedIndex
48+ }
49+
50+ MouseArea {
51+ anchors.fill: parent
52+ hoverEnabled: true
53+ acceptedButtons: Qt.AllButtons
54+ onWheel: wheel.accepted = true
55+ }
56+
57+ Rectangle {
58+ anchors.fill: parent
59+ color: "#BF000000"
60+
61+ AppDrawerModel {
62+ id: appDrawerModel
63+ }
64+
65+ AppDrawerProxyModel {
66+ id: sortProxyModel
67+ source: appDrawerModel
68+ filterString: searchField.displayText
69+ sortBy: AppDrawerProxyModel.SortByAToZ
70+ }
71+
72+ Item {
73+ id: contentContainer
74+ anchors.fill: parent
75+ anchors.leftMargin: root.panelWidth
76+
77+ TextField {
78+ id: searchField
79+ anchors { left: parent.left; top: parent.top; right: parent.right; margins: units.gu(1) }
80+ placeholderText: i18n.tr("Search…")
81+ focus: true
82+ onAccepted: {
83+ if (searchField.displayText != "" && listLoader.item && listLoader.item.currentItem) {
84+ root.applicationSelected(listLoader.item.getFirstAppId());
85+ }
86+ }
87+ }
88+
89+ Item {
90+ id: sectionsContainer
91+ anchors { left: parent.left; top: searchField.bottom; right: parent.right; }
92+ height: sections.height
93+ clip: true
94+ z: 2
95+
96+ Sections {
97+ id: sections
98+ width: parent.width
99+ actions: [
100+ Action {
101+ text: i18n.ctr("Apps sorted alphabetically", "A-Z")
102+ // TODO: Disabling this for now as we don't get the right input from u-a-l yet.
103+// },
104+// Action {
105+// text: i18n.ctr("Most used apps", "Most used")
106+ }
107+ ]
108+
109+ Rectangle {
110+ anchors.bottom: parent.bottom
111+ height: units.dp(1)
112+ color: 'gray'
113+ width: contentContainer.width
114+ }
115+ }
116+ }
117+
118+ Loader {
119+ id: listLoader
120+ anchors { left: parent.left; top: sectionsContainer.bottom; right: parent.right; bottom: parent.bottom; leftMargin: units.gu(1); rightMargin: units.gu(1) }
121+ sourceComponent: {
122+ switch (sections.selectedIndex) {
123+ case 0: return aToZComponent;
124+ case 1: return mostUsedComponent;
125+ }
126+ }
127+ Binding {
128+ target: listLoader.item || null
129+ property: "objectName"
130+ value: "drawerItemList"
131+ }
132+ }
133+
134+ MouseArea {
135+ parent: listLoader.item ? listLoader.item : null
136+ anchors.fill: parent
137+ propagateComposedEvents: true
138+ property int oldX: 0
139+ onPressed: {
140+ oldX = mouseX;
141+ }
142+ onMouseXChanged: {
143+ var diff = oldX - mouseX;
144+ root.draggingHorizontally |= diff > units.gu(2);
145+ if (!root.draggingHorizontally) {
146+ return;
147+ }
148+ propagateComposedEvents = false;
149+ parent.interactive = false;
150+ root.dragDistance += diff;
151+ oldX = mouseX
152+ }
153+ onReleased: {
154+ if (root.draggingHorizontally) {
155+ root.draggingHorizontally = false;
156+ parent.interactive = true;
157+ }
158+ reactivateTimer.start();
159+ }
160+ Timer {
161+ id: reactivateTimer
162+ interval: 0
163+ onTriggered: parent.propagateComposedEvents = true;
164+ }
165+ }
166+
167+ Component {
168+ id: mostUsedComponent
169+ DrawerListView {
170+
171+ header: MoreAppsHeader {
172+ width: parent.width
173+ height: units.gu(6)
174+ }
175+
176+ model: AppDrawerProxyModel {
177+ source: sortProxyModel
178+ group: AppDrawerProxyModel.GroupByAll
179+ sortBy: AppDrawerProxyModel.SortByUsage
180+ }
181+
182+ delegate: UbuntuShape {
183+ width: parent.width
184+ color: "#20ffffff"
185+ aspect: UbuntuShape.Flat
186+ // NOTE: Cannot use gridView.rows here as it would evaluate to 0 at first and only update later,
187+ // which messes up the ListView.
188+ height: (Math.ceil(mostUsedGridView.model.count / mostUsedGridView.columns) * mostUsedGridView.delegateHeight) + units.gu(2)
189+
190+ readonly property string appId: model.appId
191+
192+ DrawerGridView {
193+ id: mostUsedGridView
194+ anchors.fill: parent
195+ topMargin: units.gu(1)
196+ bottomMargin: units.gu(1)
197+ clip: true
198+
199+ model: sortProxyModel
200+
201+ delegateWidth: units.gu(8)
202+ delegateHeight: units.gu(10)
203+ delegate: drawerDelegateComponent
204+ }
205+ }
206+ }
207+ }
208+
209+ Component {
210+ id: aToZComponent
211+ DrawerListView {
212+
213+ header: MoreAppsHeader {
214+ width: parent.width
215+ height: units.gu(6)
216+ }
217+
218+ model: AppDrawerProxyModel {
219+ source: sortProxyModel
220+ sortBy: AppDrawerProxyModel.SortByAToZ
221+ group: AppDrawerProxyModel.GroupByAToZ
222+ }
223+
224+ delegate: UbuntuShape {
225+ width: parent.width
226+ color: "#20ffffff"
227+ aspect: UbuntuShape.Flat
228+
229+ readonly property string appId: model.appId
230+
231+ // NOTE: Cannot use gridView.rows here as it would evaluate to 0 at first and only update later,
232+ // which messes up the ListView.
233+ height: (Math.ceil(gridView.model.count / gridView.columns) * gridView.delegateHeight) +
234+ categoryNameLabel.implicitHeight + units.gu(2)
235+
236+ Label {
237+ id: categoryNameLabel
238+ anchors { left: parent.left; top: parent.top; right: parent.right; margins: units.gu(1) }
239+ text: model.letter
240+ }
241+
242+ DrawerGridView {
243+ id: gridView
244+ anchors { left: parent.left; top: categoryNameLabel.bottom; right: parent.right; topMargin: units.gu(1) }
245+ height: rows * delegateHeight
246+
247+ interactive: false
248+
249+ model: AppDrawerProxyModel {
250+ id: categoryModel
251+ source: sortProxyModel
252+ filterLetter: model.letter
253+ }
254+ delegateWidth: units.gu(8)
255+ delegateHeight: units.gu(10)
256+ delegate: drawerDelegateComponent
257+ }
258+ }
259+ }
260+ }
261+ }
262+
263+ Component {
264+ id: drawerDelegateComponent
265+ AbstractButton {
266+ width: GridView.view.cellWidth
267+ height: units.gu(10)
268+ objectName: "drawerItem_" + model.appId
269+
270+ onClicked: root.applicationSelected(model.appId)
271+
272+ Column {
273+ width: units.gu(8)
274+ anchors.horizontalCenter: parent.horizontalCenter
275+ height: childrenRect.height
276+ spacing: units.gu(1)
277+
278+ UbuntuShape {
279+ id: appIcon
280+ width: units.gu(6)
281+ height: 7.5 / 8 * width
282+ anchors.horizontalCenter: parent.horizontalCenter
283+ backgroundMode: UbuntuShape.SolidColor
284+ backgroundColor: UbuntuColors.lightGrey
285+ radius: "medium"
286+ borderSource: 'undefined'
287+ source: Image {
288+ id: sourceImage
289+ sourceSize.width: appIcon.width
290+ source: model.icon
291+ }
292+ sourceFillMode: UbuntuShape.PreserveAspectCrop
293+ }
294+
295+ Label {
296+ text: model.name
297+ width: parent.width
298+ horizontalAlignment: Text.AlignHCenter
299+ fontSize: "small"
300+ elide: Text.ElideRight
301+ }
302+ }
303+ }
304+ }
305+ }
306+}
qml/Launcher/DrawerGridView.qmlnew file+47
@@ -0,0 +1,47 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import "../Components"
19+
20+Item {
21+ id: root
22+
23+ property int delegateWidth: units.gu(10)
24+ property int delegateHeight: units.gu(10)
25+ property alias delegate: gridView.delegate
26+ property alias model: gridView.model
27+ property alias interactive: gridView.interactive
28+
29+ property alias header: gridView.header
30+ property alias topMargin: gridView.topMargin
31+ property alias bottomMargin: gridView.bottomMargin
32+
33+ readonly property int columns: width / delegateWidth
34+ readonly property int rows: Math.ceil(gridView.model.count / root.columns)
35+
36+ GridView {
37+ id: gridView
38+ anchors.fill: parent
39+ leftMargin: spacing
40+
41+ readonly property int overflow: width - (root.columns * root.delegateWidth)
42+ readonly property real spacing: overflow / (root.columns)
43+
44+ cellWidth: root.delegateWidth + spacing
45+ cellHeight: root.delegateHeight
46+ }
47+}
qml/Launcher/DrawerListView.qmlnew file+32
@@ -0,0 +1,32 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import "../Components"
20+
21+ListView {
22+ id: root
23+ anchors.fill: parent
24+ topMargin: units.gu(1)
25+ bottomMargin: units.gu(1)
26+ spacing: units.gu(1)
27+ clip: true
28+
29+ function getFirstAppId() {
30+ return model.appId(0);
31+ }
32+}
qml/Launcher/Launcher.qml+182−34
@@ -19,18 +19,23 @@ import "../Components"
19import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20import Ubuntu.Gestures 0.1
20import Ubuntu.Gestures 0.1
21import Unity.Launcher 0.1
21import Unity.Launcher 0.1
22+import Utils 0.1 as Utils
22
23
23FocusScope {
24FocusScope {
24 id: root
25 id: root
25
26
27+ readonly property int ignoreHideIfMouseOverLauncher: 1
28+
26 property bool autohideEnabled: false
29 property bool autohideEnabled: false
27 property bool lockedVisible: false
30 property bool lockedVisible: false
28 property bool available: true // can be used to disable all interactions
31 property bool available: true // can be used to disable all interactions
29 property alias inverted: panel.inverted
32 property alias inverted: panel.inverted
33+ property Item blurSource: null
34+ property int topPanelHeight: 0
35+ property bool drawerEnabled: true
30
36
31 property int panelWidth: units.gu(10)
37 property int panelWidth: units.gu(10)
32 property int dragAreaWidth: units.gu(1)
38 property int dragAreaWidth: units.gu(1)
33− property int minimizeDistance: units.gu(26)
34 property real progress: dragArea.dragging && dragArea.touchPosition.x > panelWidth ?
39 property real progress: dragArea.dragging && dragArea.touchPosition.x > panelWidth ?
35 (width * (dragArea.touchPosition.x-panelWidth) / (width - panelWidth)) : 0
40 (width * (dragArea.touchPosition.x-panelWidth) / (width - panelWidth)) : 0
36
41
@@ -43,13 +48,11 @@ FocusScope {
43 readonly property alias shortcutHintsShown: panel.shortcutHintsShown
48 readonly property alias shortcutHintsShown: panel.shortcutHintsShown
44
49
45 readonly property bool shown: panel.x > -panel.width
50 readonly property bool shown: panel.x > -panel.width
51+ readonly property bool drawerShown: drawer.x == 0
46
52
47 // emitted when an application is selected
53 // emitted when an application is selected
48 signal launcherApplicationSelected(string appId)
54 signal launcherApplicationSelected(string appId)
49
55
50− // emitted when the apps dash should be shown because of a swipe gesture
51− signal dash()
52−
53 // emitted when the dash icon in the launcher has been tapped
56 // emitted when the dash icon in the launcher has been tapped
54 signal showDashHome()
57 signal showDashHome()
55
58
@@ -62,6 +65,9 @@ FocusScope {
62 }
65 }
63
66
64 onSuperPressedChanged: {
67 onSuperPressedChanged: {
68+ if (state == "drawer")
69+ return;
70+
65 if (superPressed) {
71 if (superPressed) {
66 superPressTimer.start();
72 superPressTimer.start();
67 superLongPressTimer.start();
73 superLongPressTimer.start();
@@ -102,7 +108,10 @@ FocusScope {
102 }
108 }
103 }
109 }
104
110
105− function hide() {
111+ function hide(flags) {
112+ if ((flags & ignoreHideIfMouseOverLauncher) && Utils.Functions.itemUnderMouse(panel)) {
113+ return;
114+ }
106 switchToNextState("")
115 switchToNextState("")
107 }
116 }
108
117
@@ -132,7 +141,7 @@ FocusScope {
132 }
141 }
133
142
134 function pushEdge(amount) {
143 function pushEdge(amount) {
135− if (root.state === "") {
144+ if (root.state === "" || root.state == "visible" || root.state == "visibleTemporary") {
136 edgeBarrier.push(amount);
145 edgeBarrier.push(amount);
137 }
146 }
138 }
147 }
@@ -143,6 +152,22 @@ FocusScope {
143 switchToNextState("visible")
152 switchToNextState("visible")
144 }
153 }
145
154
155+ function openDrawer(focusInputField) {
156+ if (!drawerEnabled) {
157+ return;
158+ }
159+
160+ panel.shortcutHintsShown = false;
161+ superPressTimer.stop();
162+ superLongPressTimer.stop();
163+ root.focus = true;
164+ drawer.focus = true;
165+ if (focusInputField) {
166+ drawer.focusInput();
167+ }
168+ switchToNextState("drawer")
169+ }
170+
146 Keys.onPressed: {
171 Keys.onPressed: {
147 switch (event.key) {
172 switch (event.key) {
148 case Qt.Key_Backtab:
173 case Qt.Key_Backtab:
@@ -278,7 +303,7 @@ FocusScope {
278 InverseMouseArea {
303 InverseMouseArea {
279 id: closeMouseArea
304 id: closeMouseArea
280 anchors.fill: panel
305 anchors.fill: panel
281− enabled: root.state == "visible" && (!root.lockedVisible || panel.highlightIndex >= -1)
306+ enabled: root.state == "visible" || root.state == "drawer"
282 visible: enabled
307 visible: enabled
283 onPressed: {
308 onPressed: {
284 mouse.accepted = false;
309 mouse.accepted = false;
@@ -307,23 +332,61 @@ FocusScope {
307 }
332 }
308 }
333 }
309
334
310− EdgeBarrier {
335+ BackgroundBlur {
311− id: edgeBarrier
336+ id: backgroundBlur
312− edge: Qt.LeftEdge
337+ anchors.fill: parent
338+ anchors.topMargin: root.inverted ? 0 : -root.topPanelHeight
339+ visible: root.blurSource && drawer.x > -drawer.width
340+ blurAmount: units.gu(6)
341+ sourceItem: root.blurSource
342+ blurRect: Qt.rect(panel.width,
343+ root.topPanelHeight,
344+ drawer.width + drawer.x - panel.width,
345+ height - root.topPanelHeight)
346+ cached: drawer.moving
347+ }
348+
349+ Drawer {
350+ id: drawer
351+ objectName: "drawer"
352+ anchors {
313− target: parent
353+ top: parent.top
314− enabled: root.available
354+ topMargin: root.inverted ? root.topPanelHeight : 0
355+ bottom: parent.bottom
356+ right: parent.left
357+ }
358+ width: Math.min(root.width, units.gu(90)) * .9
359+ panelWidth: panel.width
360+ visible: x > -width
361+
362+ Behavior on anchors.rightMargin {
363+ enabled: !dragArea.dragging && !launcherDragArea.drag.active && panel.animate && !drawer.draggingHorizontally
364+ NumberAnimation {
365+ duration: 300
366+ easing.type: Easing.OutCubic
367+ }
368+ }
369+
370+ onApplicationSelected: {
371+ root.hide();
372+ root.launcherApplicationSelected(appId)
373+ root.focus = false;
374+ }
375+
376+ Keys.onEscapePressed: {
315− onPassed: { root.switchToNextState("visibleTemporary"); }
377+ switchToNextState("");
316− material: Component {
378+ root.focus = false;
317− Item {
379+ }
318− Rectangle {
380+
319− width: parent.height
381+ onDragDistanceChanged: {
320− height: parent.width
382+ anchors.rightMargin = Math.max(-drawer.width, anchors.rightMargin + dragDistance);
321− rotation: -90
383+ }
322− anchors.centerIn: parent
384+ onDraggingHorizontallyChanged: {
323− gradient: Gradient {
385+ if (!draggingHorizontally) {
324− GradientStop { position: 0.0; color: Qt.rgba(panel.color.r, panel.color.g, panel.color.b, .5)}
386+ if (drawer.x < -units.gu(10)) {
325− GradientStop { position: 1.0; color: Qt.rgba(panel.color.r,panel.color.g,panel.color.b,0)}
387+ root.hide();
326− }
388+ } else {
389+ root.openDrawer();
327 }
390 }
328 }
391 }
329 }
392 }
@@ -332,7 +395,7 @@ FocusScope {
332 LauncherPanel {
395 LauncherPanel {
333 id: panel
396 id: panel
334 objectName: "launcherPanel"
397 objectName: "launcherPanel"
335− enabled: root.available && root.state == "visible" || root.state == "visibleTemporary"
398+ enabled: root.available && (root.state == "visible" || root.state == "visibleTemporary" || root.state == "drawer")
336 width: root.panelWidth
399 width: root.panelWidth
337 anchors {
400 anchors {
338 top: parent.top
401 top: parent.top
@@ -359,11 +422,11 @@ FocusScope {
359 property bool animate: true
422 property bool animate: true
360
423
361 onApplicationSelected: {
424 onApplicationSelected: {
362− root.hide();
425+ root.hide(ignoreHideIfMouseOverLauncher);
363 launcherApplicationSelected(appId)
426 launcherApplicationSelected(appId)
364 }
427 }
365 onShowDashHome: {
428 onShowDashHome: {
366− root.hide();
429+ root.hide(ignoreHideIfMouseOverLauncher);
367 root.showDashHome();
430 root.showDashHome();
368 }
431 }
369
432
@@ -394,6 +457,38 @@ FocusScope {
394 }
457 }
395 }
458 }
396
459
460+ EdgeBarrier {
461+ id: edgeBarrier
462+ edge: Qt.LeftEdge
463+ target: parent
464+ enabled: root.available
465+ onProgressChanged: {
466+ if (progress > .5 && root.state != "visibleTemporary" && root.state != "drawer" && root.state != "visible") {
467+ root.switchToNextState("visibleTemporary");
468+ }
469+ }
470+ onPassed: {
471+ if (root.drawerEnabled) {
472+ root.switchToNextState("drawer");
473+ }
474+ }
475+
476+ material: Component {
477+ Item {
478+ Rectangle {
479+ width: parent.height
480+ height: parent.width
481+ rotation: -90
482+ anchors.centerIn: parent
483+ gradient: Gradient {
484+ GradientStop { position: 0.0; color: Qt.rgba(panel.color.r, panel.color.g, panel.color.b, .5)}
485+ GradientStop { position: 1.0; color: Qt.rgba(panel.color.r,panel.color.g,panel.color.b,0)}
486+ }
487+ }
488+ }
489+ }
490+ }
491+
397 SwipeArea {
492 SwipeArea {
398 id: dragArea
493 id: dragArea
399 objectName: "launcherDragArea"
494 objectName: "launcherDragArea"
@@ -405,25 +500,62 @@ FocusScope {
405 width: root.dragAreaWidth
500 width: root.dragAreaWidth
406 height: root.height
501 height: root.height
407
502
503+ function easeInOutCubic(t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 }
504+
505+ property var lastDragPoints: []
506+
507+ function dragDirection() {
508+ if (lastDragPoints.length < 5) {
509+ return "unknown";
510+ }
511+
512+ var toRight = true;
513+ var toLeft = true;
514+ for (var i = lastDragPoints.length - 5; i < lastDragPoints.length; i++) {
515+ if (toRight && lastDragPoints[i] < lastDragPoints[i-1]) {
516+ toRight = false;
517+ }
518+ if (toLeft && lastDragPoints[i] > lastDragPoints[i-1]) {
519+ toLeft = false;
520+ }
521+ }
522+ return toRight ? "right" : toLeft ? "left" : "unknown";
523+ }
524+
408 onDistanceChanged: {
525 onDistanceChanged: {
409− if (!dragging || launcher.state == "visible")
526+ if (dragging && launcher.state != "visible" && launcher.state != "drawer") {
410− return;
527+ panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
528+ }
411
529
530+ if (root.drawerEnabled && dragging && launcher.state != "drawer") {
531+ lastDragPoints.push(distance)
532+ var drawerHintDistance = panel.width + units.gu(1)
533+ if (distance < drawerHintDistance) {
534+ drawer.anchors.rightMargin = -Math.min(Math.max(0, distance), drawer.width);
535+ } else {
412− panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
536+ var linearDrawerX = Math.min(Math.max(0, distance - drawerHintDistance), drawer.width);
537+ var linearDrawerProgress = linearDrawerX / (drawer.width)
538+ var easedDrawerProgress = easeInOutCubic(linearDrawerProgress);
539+ drawer.anchors.rightMargin = -(drawerHintDistance + easedDrawerProgress * (drawer.width - drawerHintDistance));
540+ }
541+ }
413 }
542 }
414
543
415 onDraggingChanged: {
544 onDraggingChanged: {
416 if (!dragging) {
545 if (!dragging) {
417 if (distance > panel.width / 2) {
546 if (distance > panel.width / 2) {
418− root.switchToNextState("visible")
547+ if (root.drawerEnabled && distance > panel.width * 3 && dragDirection() !== "left") {
419− if (distance > minimizeDistance) {
548+ root.switchToNextState("drawer");
420− root.dash();
549+ root.focus = true;
550+ } else {
551+ root.switchToNextState("visible");
421 }
552 }
422 } else if (root.state === "") {
553 } else if (root.state === "") {
423 // didn't drag far enough. rollback
554 // didn't drag far enough. rollback
424− root.switchToNextState("")
555+ root.switchToNextState("");
425 }
556 }
426 }
557 }
558+ lastDragPoints = [];
427 }
559 }
428 }
560 }
429
561
@@ -434,6 +566,10 @@ FocusScope {
434 target: panel
566 target: panel
435 x: -root.panelWidth
567 x: -root.panelWidth
436 }
568 }
569+ PropertyChanges {
570+ target: drawer
571+ anchors.rightMargin: 0
572+ }
437 },
573 },
438 State {
574 State {
439 name: "visible"
575 name: "visible"
@@ -441,6 +577,18 @@ FocusScope {
441 target: panel
577 target: panel
442 x: -root.x // so we never go past panelWidth, even when teased by tutorial
578 x: -root.x // so we never go past panelWidth, even when teased by tutorial
443 }
579 }
580+ PropertyChanges {
581+ target: drawer
582+ anchors.rightMargin: 0
583+ }
584+ },
585+ State {
586+ name: "drawer"
587+ extend: "visible"
588+ PropertyChanges {
589+ target: drawer
590+ anchors.rightMargin: -drawer.width + root.x // so we never go past panelWidth, even when teased by tutorial
591+ }
444 },
592 },
445 State {
593 State {
446 name: "visibleTemporary"
594 name: "visibleTemporary"
qml/Launcher/MoreAppsHeader.qmlnew file+46
@@ -0,0 +1,46 @@
1+import QtQuick 2.4
2+import Ubuntu.Components 1.3
3+
4+AbstractButton {
5+ id: root
6+
7+ onClicked: {
8+ // TODO: Make this point to the snappy store as soon as we stop landing to vivid
9+ Qt.openUrlExternally("scope://com.canonical.scopes.clickstore")
10+ }
11+
12+ UbuntuShape {
13+ width: parent.width
14+ height: parent.height - units.gu(1)
15+ color: "#20ffffff"
16+ aspect: UbuntuShape.Flat
17+
18+ Row {
19+ anchors.fill: parent
20+ anchors.margins: units.gu(1)
21+ spacing: units.gu(1)
22+
23+ Icon {
24+ height: units.gu(2.2)
25+ width: height
26+ name: "stock_application"
27+ anchors.verticalCenter: parent.verticalCenter
28+ color: "white"
29+ }
30+
31+ Label {
32+ text: i18n.tr("More apps in the store")
33+ anchors.verticalCenter: parent.verticalCenter
34+ fontSize: "small"
35+ }
36+
37+ Icon {
38+ height: units.gu(2.5)
39+ width: height
40+ anchors.verticalCenter: parent.verticalCenter
41+ name: "go-next"
42+ color: "white"
43+ }
44+ }
45+ }
46+}
qml/Panel/Indicators/MenuItemFactory.qml+22−1
@@ -106,7 +106,7 @@ Item {
106 }
106 }
107
107
108 if (component === undefined) {
108 if (component === undefined) {
109− console.debug("Don't know how to make " + modelData.type + " for " + indicator);
109+ console.debug("Don't know how to make " + type + " for " + indicator);
110 }
110 }
111
111
112 return component
112 return component
@@ -398,7 +398,9 @@ Item {
398 id: switchItem
398 id: switchItem
399 objectName: "switchMenu"
399 objectName: "switchMenu"
400 property QtObject menuData: null
400 property QtObject menuData: null
401+ property var menuModel: menuFactory.menuModel
401 property int menuIndex: -1
402 property int menuIndex: -1
403+ property var extendedData: menuData && menuData.ext || undefined
402 property bool serverChecked: menuData && menuData.isToggled || false
404 property bool serverChecked: menuData && menuData.isToggled || false
403
405
404 text: menuData && menuData.label || ""
406 text: menuData && menuData.label || ""
@@ -407,6 +409,25 @@ Item {
407 checked: serverChecked
409 checked: serverChecked
408 highlightWhenPressed: false
410 highlightWhenPressed: false
409
411
412+ property var subtitleAction: UnityMenuAction {
413+ model: menuModel
414+ index: menuIndex
415+ name: getExtendedProperty(extendedData, "xCanonicalSubtitleAction", "")
416+ }
417+ subtitle.text: subtitleAction.valid ? subtitleAction.state : ""
418+
419+ onMenuModelChanged: {
420+ loadAttributes();
421+ }
422+ onMenuIndexChanged: {
423+ loadAttributes();
424+ }
425+
426+ function loadAttributes() {
427+ if (!menuModel || menuIndex == -1) return;
428+ menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-subtitle-action': 'string'});
429+ }
430+
410 ServerPropertySynchroniser {
431 ServerPropertySynchroniser {
411 objectName: "sync"
432 objectName: "sync"
412 syncTimeout: Utils.Constants.indicatorValueTimeout
433 syncTimeout: Utils.Constants.indicatorValueTimeout
qml/Shell.qml+11−35
@@ -124,7 +124,7 @@ StyledItem {
124 greeter.notifyAppFocusRequested(appId);
124 greeter.notifyAppFocusRequested(appId);
125
125
126 panel.indicators.hide();
126 panel.indicators.hide();
127− launcher.hide();
127+ launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
128 }
128 }
129
129
130 // For autopilot consumption
130 // For autopilot consumption
@@ -278,8 +278,6 @@ StyledItem {
278
278
279 dragAreaWidth: shell.edgeSize
279 dragAreaWidth: shell.edgeSize
280 background: wallpaperResolver.background
280 background: wallpaperResolver.background
281− leftEdgeDragProgress: !greeter || greeter.locked || !tutorial.launcherLongSwipeEnabled ? 0 :
282− Math.max(0, (launcher.dragDistance * (stage.width - launcher.panelWidth) / stage.width) - launcher.panelWidth)
283
281
284 applicationManager: ApplicationManager
282 applicationManager: ApplicationManager
285 topLevelSurfaceList: topLevelSurfaceList
283 topLevelSurfaceList: topLevelSurfaceList
@@ -301,14 +299,12 @@ StyledItem {
301
299
302 interactive: (!greeter || !greeter.shown)
300 interactive: (!greeter || !greeter.shown)
303 && panel.indicators.fullyClosed
301 && panel.indicators.fullyClosed
304− && launcher.progress == 0
305 && !notifications.useModal
302 && !notifications.useModal
306
303
307 onInteractiveChanged: { if (interactive) { focus = true; } }
304 onInteractiveChanged: { if (interactive) { focus = true; } }
308
305
309 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
306 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
310 suspended: greeter.shown
307 suspended: greeter.shown
311− keepDashRunning: launcher.shown || launcher.dashSwipe
312 altTabPressed: physicalKeysMapper.altTabPressed
308 altTabPressed: physicalKeysMapper.altTabPressed
313 oskEnabled: shell.oskEnabled
309 oskEnabled: shell.oskEnabled
314 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
310 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
@@ -323,7 +319,7 @@ StyledItem {
323 topMargin: panel.panelHeight
319 topMargin: panel.panelHeight
324 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
320 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
325 }
321 }
326− z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
322+ z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
327 }
323 }
328
324
329 Loader {
325 Loader {
@@ -345,7 +341,6 @@ StyledItem {
345 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
341 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
346 hides: [launcher, panel.indicators]
342 hides: [launcher, panel.indicators]
347 tabletMode: shell.usageScenario != "phone"
343 tabletMode: shell.usageScenario != "phone"
348− launcherOffset: launcher.progress
349 forcedUnlock: wizard.active || shell.mode === "full-shell"
344 forcedUnlock: wizard.active || shell.mode === "full-shell"
350 background: wallpaperResolver.cachedBackground
345 background: wallpaperResolver.cachedBackground
351 hasCustomBackground: wallpaperResolver.hasCustomBackground
346 hasCustomBackground: wallpaperResolver.hasCustomBackground
@@ -434,19 +429,6 @@ StyledItem {
434 }
429 }
435 }
430 }
436
431
437− function showDash() {
438− if (greeter.notifyShowingDashFromDrag()) {
439− launcher.fadeOut();
440− }
441−
442− if (!greeter.locked && tutorial.launcherLongSwipeEnabled
443− && (ApplicationManager.focusedApplicationId != "unity8-dash" || stage.spreadShown)) {
444− ApplicationManager.requestFocusApplication("unity8-dash")
445− launcher.fadeOut();
446− stage.closeSpread();
447− }
448− }
449−
450 Item {
432 Item {
451 id: overlay
433 id: overlay
452 z: 10
434 z: 10
@@ -497,15 +479,6 @@ StyledItem {
497 id: launcher
479 id: launcher
498 objectName: "launcher"
480 objectName: "launcher"
499
481
500− /*
501− * Since the Dash doesn't have the same controll over surfaces that the
502− * Shell does, it can't slowly move the scope out of the way, as the shell
503− * does with apps, and the dash is show instantly. This allows for some
504− * leeway and prevents accidental home swipes.
505− */
506− readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
507− readonly property bool dashSwipe: progress > offset
508−
509 anchors.top: parent.top
482 anchors.top: parent.top
510 anchors.topMargin: inverted ? 0 : panel.panelHeight
483 anchors.topMargin: inverted ? 0 : panel.panelHeight
511 anchors.bottom: parent.bottom
484 anchors.bottom: parent.bottom
@@ -521,14 +494,11 @@ StyledItem {
521 superTabPressed: physicalKeysMapper.superTabPressed
494 superTabPressed: physicalKeysMapper.superTabPressed
522 panelWidth: units.gu(settings.launcherWidth)
495 panelWidth: units.gu(settings.launcherWidth)
523 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
496 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
497+ blurSource: greeter.shown ? greeter : stages
498+ topPanelHeight: panel.panelHeight
499+ drawerEnabled: !greeter.shown
524
500
525 onShowDashHome: showHome()
501 onShowDashHome: showHome()
526− onDash: showDash()
527− onDashSwipeChanged: {
528− if (dashSwipe) {
529− dash.setCurrentScope(0, false, true)
530− }
531− }
532 onLauncherApplicationSelected: {
502 onLauncherApplicationSelected: {
533 greeter.notifyUserRequestedApp();
503 greeter.notifyUserRequestedApp();
534 shell.activateApplication(appId);
504 shell.activateApplication(appId);
@@ -544,6 +514,12 @@ StyledItem {
544 }
514 }
545 }
515 }
546
516
517+ GlobalShortcut {
518+ shortcut: Qt.MetaModifier | Qt.Key_A
519+ onTriggered: {
520+ launcher.openDrawer(true);
521+ }
522+ }
547 GlobalShortcut {
523 GlobalShortcut {
548 shortcut: Qt.AltModifier | Qt.Key_F1
524 shortcut: Qt.AltModifier | Qt.Key_F1
549 onTriggered: {
525 onTriggered: {
qml/Stage/MoveHandler.qml+2−2
@@ -90,8 +90,8 @@ QtObject {
90 if (target.anyMaximized) {
90 if (target.anyMaximized) {
91 // keep distanceX relative to the normal window width minus the window control buttons (+spacing)
91 // keep distanceX relative to the normal window width minus the window control buttons (+spacing)
92 // so that dragging it back doesn't make the window jump around to weird positions, away from the mouse pointer
92 // so that dragging it back doesn't make the window jump around to weird positions, away from the mouse pointer
93− priv.distanceX = MathUtils.clampAndProject(pos.x, 0, target.width, buttonsWidth, target.resizeArea.normalWidth);
93+ priv.distanceX = MathUtils.clampAndProject(pos.x, 0, target.width, buttonsWidth, target.normalWidth);
94− priv.distanceY = MathUtils.clampAndProject(pos.y, 0, target.height, 0, target.resizeArea.normalHeight);
94+ priv.distanceY = MathUtils.clampAndProject(pos.y, 0, target.height, 0, target.normalHeight);
95 } else {
95 } else {
96 priv.distanceX = pos.x;
96 priv.distanceX = pos.x;
97 priv.distanceY = pos.y;
97 priv.distanceY = pos.y;
qml/Stage/Spread/WindowedRightEdgeMaths.qml+19−4
@@ -25,21 +25,31 @@ QtObject {
25 // Input
25 // Input
26 property int itemIndex: 0
26 property int itemIndex: 0
27 property int normalZ: 0
27 property int normalZ: 0
28− property real progress: 0
29 property int startWidth: 0
28 property int startWidth: 0
30 property int startHeight: 0
29 property int startHeight: 0
31 property int startX: 0
30 property int startX: 0
32 property int targetX: 0
31 property int targetX: 0
33 property int startY: 0
32 property int startY: 0
34 property int targetY: 0
33 property int targetY: 0
35−// property real startAngle: 40
36 property real targetAngle: 0
34 property real targetAngle: 0
37 property int targetHeight: 0
35 property int targetHeight: 0
38 property real targetScale: 0
36 property real targetScale: 0
37+ property real swipeProgress: 0
38+ property real pushProgress: 0
39
39
40 // Config
40 // Config
41 property real breakPoint: 0.4
41 property real breakPoint: 0.4
42
42
43+ // internal
44+ readonly property real progress: {
45+ if (pushProgress > 0) {
46+ // we don't do the full animation when pushing, just a little bit
47+ return MathUtils.linearAnimation(0, 1, 0, breakPoint + .1, pushProgress)
48+ } else {
49+ return swipeProgress;
50+ }
51+ }
52+
43 // Output
53 // Output
44
54
45 readonly property real scaleToPreviewProgress: {
55 readonly property real scaleToPreviewProgress: {
@@ -68,12 +78,17 @@ QtObject {
68
78
69 readonly property int animatedZ: {
79 readonly property int animatedZ: {
70 if (progress < breakPoint + (1 - breakPoint) / 2) {
80 if (progress < breakPoint + (1 - breakPoint) / 2) {
81+ if (swipeProgress > 0) {
71− return itemIndex == 1 ? normalZ + 2 : normalZ
82+ return itemIndex == 1 ? normalZ + 2 : normalZ
83+ }
84+ if (pushProgress > 0) {
85+ return normalZ;
86+ }
72 }
87 }
73 return itemIndex
88 return itemIndex
74 }
89 }
75
90
76− readonly property real opacityMask: itemIndex == 1 ? MathUtils.linearAnimation(0, breakPoint, 0, 1, progress) : 1
91+ readonly property real opacityMask: (swipeProgress > 0 && itemIndex == 1) ? MathUtils.linearAnimation(0, breakPoint, 0, 1, progress) : 1
77
92
78 readonly property real animatedScale: progress < breakPoint ? 1 : MathUtils.linearAnimation(breakPoint, 1, 1, targetScale, progress)
93 readonly property real animatedScale: progress < breakPoint ? 1 : MathUtils.linearAnimation(breakPoint, 1, 1, targetScale, progress)
79
94
qml/Stage/Stage.qml+66−38
@@ -36,7 +36,6 @@ FocusScope {
36 property url background
36 property url background
37 property int dragAreaWidth
37 property int dragAreaWidth
38 property bool interactive
38 property bool interactive
39− property bool keepDashRunning: true
40 property real nativeHeight
39 property real nativeHeight
41 property real nativeWidth
40 property real nativeWidth
42 property QtObject orientations
41 property QtObject orientations
@@ -50,7 +49,6 @@ FocusScope {
50
49
51 // Configuration
50 // Configuration
52 property string mode: "staged"
51 property string mode: "staged"
53− property real leftEdgeDragProgress: 0
54
52
55 // Used by the tutorial code
53 // Used by the tutorial code
56 readonly property real rightEdgeDragProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0 // How far left the stage has been dragged
54 readonly property real rightEdgeDragProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0 // How far left the stage has been dragged
@@ -415,7 +413,7 @@ FocusScope {
415 // in staged mode, when it switches to Windowed mode it will suddenly
413 // in staged mode, when it switches to Windowed mode it will suddenly
416 // resume all those apps at once. We might want to avoid that.
414 // resume all those apps at once. We might want to avoid that.
417 value: root.mode === "windowed"
415 value: root.mode === "windowed"
418− || (isDash && root.keepDashRunning)
416+ || isDash
419 || (!root.suspended && model.application && priv.focusedAppDelegate &&
417 || (!root.suspended && model.application && priv.focusedAppDelegate &&
420 (priv.focusedAppDelegate.appId === model.application.appId ||
418 (priv.focusedAppDelegate.appId === model.application.appId ||
421 priv.mainStageAppId === model.application.appId ||
419 priv.mainStageAppId === model.application.appId ||
@@ -478,7 +476,7 @@ FocusScope {
478 visible: true
476 visible: true
479 blurRadius: 32
477 blurRadius: 32
480 brightness: .65
478 brightness: .65
481− opacity: MathUtils.linearAnimation(spreadItem.rightEdgeBreakPoint, 1, 0, 1, Math.max(rightEdgeDragArea.progress, edgeBarrier.progress))
479+ opacity: MathUtils.linearAnimation(spreadItem.rightEdgeBreakPoint, 1, 0, 1, Math.max(rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0, edgeBarrier.progress))
482 }
480 }
483 },
481 },
484 State {
482 State {
@@ -712,6 +710,29 @@ FocusScope {
712 property real restoredX
710 property real restoredX
713 property real restoredY
711 property real restoredY
714
712
713+ // Keeps track of the window geometry while in normal or restored state
714+ // Useful when returning from some maxmized state or when saving the geometry while maximized
715+ // FIXME: find a better solution
716+ property real normalX: 0
717+ property real normalY: 0
718+ property real normalWidth: 0
719+ property real normalHeight: 0
720+ function updateNormalGeometry() {
721+ if (appDelegate.state == "normal" || appDelegate.state == "restored") {
722+ normalX = appDelegate.requestedX;
723+ normalY = appDelegate.requestedY;
724+ normalWidth = appDelegate.width;
725+ normalHeight = appDelegate.height;
726+ }
727+ }
728+ Connections {
729+ target: appDelegate
730+ onXChanged: appDelegate.updateNormalGeometry();
731+ onYChanged: appDelegate.updateNormalGeometry();
732+ onWidthChanged: appDelegate.updateNormalGeometry();
733+ onHeightChanged: appDelegate.updateNormalGeometry();
734+ }
735+
715 Binding {
736 Binding {
716 target: appDelegate
737 target: appDelegate
717 property: "y"
738 property: "y"
@@ -852,6 +873,16 @@ FocusScope {
852 MirFocusController.focusedSurface = null;
873 MirFocusController.focusedSurface = null;
853 }
874 }
854 }
875 }
876+
877+ WindowStateSaver {
878+ id: windowStateSaver
879+ target: appDelegate
880+ screenWidth: appContainer.width
881+ screenHeight: appContainer.height
882+ leftMargin: root.leftMargin
883+ minimumY: PanelState.panelHeight
884+ }
885+
855 Component.onCompleted: {
886 Component.onCompleted: {
856 if (application && application.rotatesWindowContents) {
887 if (application && application.rotatesWindowContents) {
857 decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
888 decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
@@ -863,7 +894,7 @@ FocusScope {
863 windowedX = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedX + units.gu(3) : (normalZ - 1) * units.gu(3)
894 windowedX = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedX + units.gu(3) : (normalZ - 1) * units.gu(3)
864 windowedY = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedY + units.gu(3) : normalZ * units.gu(3)
895 windowedY = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedY + units.gu(3) : normalZ * units.gu(3)
865 // Now load any saved state. This needs to happen *after* the cascading!
896 // Now load any saved state. This needs to happen *after* the cascading!
866− resizeArea.loadWindowState();
897+ windowStateSaver.load();
867
898
868 // NB: We're differentiating if this delegate was created in response to a new entry in the model
899 // NB: We're differentiating if this delegate was created in response to a new entry in the model
869 // or if the Repeater is just populating itself with delegates to match the model it received.
900 // or if the Repeater is just populating itself with delegates to match the model it received.
@@ -880,6 +911,8 @@ FocusScope {
880 _constructing = false;
911 _constructing = false;
881 }
912 }
882 Component.onDestruction: {
913 Component.onDestruction: {
914+ windowStateSaver.save();
915+
883 if (!root.parent) {
916 if (!root.parent) {
884 // This stage is about to be destroyed. Don't mess up with the model at this point
917 // This stage is about to be destroyed. Don't mess up with the model at this point
885 return;
918 return;
@@ -1061,7 +1094,6 @@ FocusScope {
1061 sideStageX: sideStage.x
1094 sideStageX: sideStage.x
1062 itemIndex: appDelegate.itemIndex
1095 itemIndex: appDelegate.itemIndex
1063 nextInStack: priv.nextInStack
1096 nextInStack: priv.nextInStack
1064− leftEdgeDragProgress: root.leftEdgeDragProgress
1065 }
1097 }
1066
1098
1067 StagedRightEdgeMaths {
1099 StagedRightEdgeMaths {
@@ -1164,7 +1196,8 @@ FocusScope {
1164 when: root.mode == "windowed" && (root.state == "windowedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running || edgeBarrier.progress > 0)
1196 when: root.mode == "windowed" && (root.state == "windowedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running || edgeBarrier.progress > 0)
1165 PropertyChanges {
1197 PropertyChanges {
1166 target: windowedRightEdgeMaths
1198 target: windowedRightEdgeMaths
1199+ swipeProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0
1167− progress: Math.max(rightEdgeDragArea.progress, edgeBarrier.progress)
1200+ pushProgress: edgeBarrier.progress
1168 }
1201 }
1169 PropertyChanges {
1202 PropertyChanges {
1170 target: appDelegate
1203 target: appDelegate
@@ -1482,27 +1515,22 @@ FocusScope {
1482 id: resizeArea
1515 id: resizeArea
1483 objectName: "windowResizeArea"
1516 objectName: "windowResizeArea"
1484
1517
1518+ anchors.fill: appDelegate
1519+
1485 // workaround so that it chooses the correct resize borders when you drag from a corner ResizeGrip
1520 // workaround so that it chooses the correct resize borders when you drag from a corner ResizeGrip
1486 anchors.margins: touchControls.overlayShown ? borderThickness/2 : -borderThickness
1521 anchors.margins: touchControls.overlayShown ? borderThickness/2 : -borderThickness
1487
1522
1488 target: appDelegate
1523 target: appDelegate
1524+ minimumY: PanelState.panelHeight // disallow resizing up past Panel
1489 minWidth: units.gu(10)
1525 minWidth: units.gu(10)
1490 minHeight: units.gu(10)
1526 minHeight: units.gu(10)
1491 borderThickness: units.gu(2)
1527 borderThickness: units.gu(2)
1492− windowId: model.application.appId // FIXME: Change this to point to windowId once we have such a thing
1493− screenWidth: appContainer.width
1494− screenHeight: appContainer.height
1495− leftMargin: root.leftMargin
1496 enabled: false
1528 enabled: false
1497 visible: enabled
1529 visible: enabled
1498
1530
1499 onPressed: {
1531 onPressed: {
1500 appDelegate.focus = true;
1532 appDelegate.focus = true;
1501 }
1533 }
1502−
1503− Component.onDestruction: {
1504− saveWindowState();
1505− }
1506 }
1534 }
1507
1535
1508 DecoratedWindow {
1536 DecoratedWindow {
@@ -1652,10 +1680,10 @@ FocusScope {
1652 }
1680 }
1653 }
1681 }
1654
1682
1655− Image {
1683+ MouseArea {
1656− id: closeImage
1684+ id: closeMouseArea
1685+ objectName: "closeMouseArea"
1657 anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset }
1686 anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset }
1658− source: "graphics/window-close.svg"
1659 readonly property var mousePos: hoverMouseArea.mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1687 readonly property var mousePos: hoverMouseArea.mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1660 visible: !appDelegate.isDash && dragArea.distance == 0
1688 visible: !appDelegate.isDash && dragArea.distance == 0
1661 && index == spreadItem.highlightedIndex
1689 && index == spreadItem.highlightedIndex
@@ -1663,20 +1691,20 @@ FocusScope {
1663 && mousePos.y > -units.gu(4)
1691 && mousePos.y > -units.gu(4)
1664 && mousePos.x > -units.gu(4)
1692 && mousePos.x > -units.gu(4)
1665 && mousePos.x < (decoratedWindow.width * 2 / 3)
1693 && mousePos.x < (decoratedWindow.width * 2 / 3)
1666− height: units.gu(2)
1694+ height: units.gu(6)
1667 width: height
1695 width: height
1668− sourceSize.width: width
1696+
1669− sourceSize.height: height
1670−
1671− MouseArea {
1672− id: closeMouseArea
1673− objectName: "closeMouseArea"
1674− anchors.fill: closeImage
1675− anchors.margins: -units.gu(2)
1676− onClicked: {
1697+ onClicked: {
1677− priv.closingIndex = index;
1698+ priv.closingIndex = index;
1678− appDelegate.close();
1699+ appDelegate.close();
1679− }
1700+ }
1701+ Image {
1702+ id: closeImage
1703+ source: "graphics/window-close.svg"
1704+ anchors.fill: closeMouseArea
1705+ anchors.margins: units.gu(2)
1706+ sourceSize.width: width
1707+ sourceSize.height: height
1680 }
1708 }
1681 }
1709 }
1682 }
1710 }
@@ -1735,13 +1763,13 @@ FocusScope {
1735 }
1763 }
1736
1764
1737 // Find the hovered item and mark it active
1765 // Find the hovered item and mark it active
1738− var mapped = mapToItem(appContainer, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1766+ for (var i = appRepeater.count - 1; i >= 0; i--) {
1739− var itemUnder = appContainer.childAt(mapped.x, mapped.y)
1767+ var appDelegate = appRepeater.itemAt(i);
1740− if (itemUnder) {
1741− mapped = mapToItem(itemUnder, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1768+ var mapped = mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1742− var delegateChild = itemUnder.childAt(mapped.x, mapped.y)
1769+ var itemUnder = appDelegate.childAt(mapped.x, mapped.y);
1743− if (delegateChild && (delegateChild.objectName === "dragArea" || delegateChild.objectName === "windowInfoItem")) {
1770+ if (itemUnder && (itemUnder.objectName === "dragArea" || itemUnder.objectName === "windowInfoItem" || itemUnder.objectName == "closeMouseArea")) {
1744− spreadItem.highlightedIndex = appRepeater.indexOf(itemUnder)
1771+ spreadItem.highlightedIndex = i;
1772+ break;
1745 }
1773 }
1746 }
1774 }
1747
1775
qml/Stage/StageMaths.qml+2−7
@@ -12,7 +12,6 @@ QtObject {
12 property int sideStageWidth: 0
12 property int sideStageWidth: 0
13 property int sideStageX: sceneWidth
13 property int sideStageX: sceneWidth
14 property bool animateX: false
14 property bool animateX: false
15− property int leftEdgeDragProgress: 0
16
15
17 property int stage: ApplicationInfoInterface.MainStage
16 property int stage: ApplicationInfoInterface.MainStage
18 property var thisDelegate: null
17 property var thisDelegate: null
@@ -26,10 +25,6 @@ QtObject {
26 // of the last focused order.
25 // of the last focused order.
27 readonly property int itemZ: {
26 readonly property int itemZ: {
28 // only shuffle when we've got a main and side stage
27 // only shuffle when we've got a main and side stage
29− if (thisDelegate.isDash && thisDelegate != mainStageDelegate && leftEdgeDragProgress > 0) {
30− return -1; // Keep the dash behind all other apps for the left edge gesture
31− }
32−
33 if (!sideStageDelegate) return itemIndex;
28 if (!sideStageDelegate) return itemIndex;
34
29
35 // don't shuffle indexes greater than "actives or next"
30 // don't shuffle indexes greater than "actives or next"
@@ -64,12 +59,12 @@ QtObject {
64
59
65 property int itemX: {
60 property int itemX: {
66 if (mainStageDelegate == thisDelegate) {
61 if (mainStageDelegate == thisDelegate) {
67− return thisDelegate.isDash ? 0 : leftEdgeDragProgress;
62+ return 0
68 }
63 }
69 if (sideStageDelegate == thisDelegate) {
64 if (sideStageDelegate == thisDelegate) {
70 return sideStageX;
65 return sideStageX;
71 }
66 }
72− return thisDelegate.isDash && leftEdgeDragProgress > 0 ? 0 : sceneWidth;
67+ return sceneWidth;
73 }
68 }
74 Behavior on itemX { enabled: root.animateX; UbuntuNumberAnimation {} }
69 Behavior on itemX { enabled: root.animateX; UbuntuNumberAnimation {} }
75
70
qml/Stage/WindowResizeArea.qml+2−70
@@ -18,91 +18,23 @@ import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19import Utils 0.1
19import Utils 0.1
20import Unity.Application 0.1 // for Mir.cursorName
20import Unity.Application 0.1 // for Mir.cursorName
21−import "../Components/PanelState"
22
21
23MouseArea {
22MouseArea {
24 id: root
23 id: root
25
24
26− anchors.fill: target
27 anchors.margins: -borderThickness
25 anchors.margins: -borderThickness
28
26
29 hoverEnabled: target && !target.maximized // don't grab the resize under the panel
27 hoverEnabled: target && !target.maximized // don't grab the resize under the panel
30
28
31− property var windowStateStorage: WindowStateStorage
32 readonly property alias dragging: d.dragging
29 readonly property alias dragging: d.dragging
33− readonly property alias normalWidth: priv.normalWidth
34− readonly property alias normalHeight: priv.normalHeight
35
30
36 // The target item managed by this. Must be a parent or a sibling
31 // The target item managed by this. Must be a parent or a sibling
37 // The area will anchor to it and manage resize events
32 // The area will anchor to it and manage resize events
38 property Item target: null
33 property Item target: null
39− property string windowId: ""
40 property int borderThickness: 0
34 property int borderThickness: 0
35+ property real minimumY: -100000000 // By default, impose no limit
41 property int minWidth: 0
36 property int minWidth: 0
42 property int minHeight: 0
37 property int minHeight: 0
43− property int defaultWidth: units.gu(60)
44− property int defaultHeight: units.gu(50)
45− property int screenWidth: 0
46− property int screenHeight: 0
47− property int leftMargin: 0
48−
49− QtObject {
50− id: priv
51− objectName: "priv"
52−
53− property int normalX: 0
54− property int normalY: 0
55− property int normalWidth: 0
56− property int normalHeight: 0
57−
58− function updateNormalGeometry() {
59− if (root.target.state == "normal" || root.target.state == "restored") {
60− normalX = root.target.requestedX
61− normalY = root.target.requestedY
62− normalWidth = root.target.width
63− normalHeight = root.target.height
64− }
65− }
66− }
67−
68− Connections {
69− target: root.target
70− onXChanged: priv.updateNormalGeometry();
71− onYChanged: priv.updateNormalGeometry();
72− onWidthChanged: priv.updateNormalGeometry();
73− onHeightChanged: priv.updateNormalGeometry();
74− }
75−
76− function loadWindowState() {
77− var windowGeometry = windowStateStorage.getGeometry(root.windowId,
78− Qt.rect(target.windowedX, target.windowedY, defaultWidth, defaultHeight));
79−
80−
81− target.windowedWidth = Qt.binding(function() { return Math.min(Math.max(windowGeometry.width, d.minimumWidth), screenWidth - root.leftMargin); });
82− target.windowedHeight = Qt.binding(function() { return Math.min(Math.max(windowGeometry.height, d.minimumHeight),
83− root.screenHeight - (target.fullscreen ? 0 : PanelState.panelHeight)); });
84− target.windowedX = Qt.binding(function() { return Math.max(Math.min(windowGeometry.x, root.screenWidth - root.leftMargin - target.windowedWidth),
85− (target.fullscreen ? 0 : root.leftMargin)); });
86− target.windowedY = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, root.screenHeight - target.windowedHeight), PanelState.panelHeight); });
87−
88− var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
89− target.restore(false /* animated */, windowState);
90−
91− priv.updateNormalGeometry();
92−
93− // initialize the x/y to restore to
94− target.restoredX = priv.normalX;
95− target.restoredY = priv.normalY;
96− }
97−
98− function saveWindowState() {
99− var state = target.windowState;
100− if (state === WindowStateStorage.WindowStateRestored) {
101− state = WindowStateStorage.WindowStateNormal;
102− }
103− windowStateStorage.saveState(root.windowId, state & ~WindowStateStorage.WindowStateMinimized); // clear the minimized bit when saving
104− windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight));
105− }
106
38
107 QtObject {
39 QtObject {
108 id: d
40 id: d
@@ -311,7 +243,7 @@ MouseArea {
311 }
243 }
312
244
313 if (d.topBorder) {
245 if (d.topBorder) {
314− var newTargetY = Math.max(d.startY + deltaY, PanelState.panelHeight); // disallow resizing up past Panel
246+ var newTargetY = Math.max(d.startY + deltaY, root.minimumY);
315 var bottomBorderY = target.windowedY + target.height;
247 var bottomBorderY = target.windowedY + target.height;
316 if (bottomBorderY > newTargetY + d.minimumHeight) {
248 if (bottomBorderY > newTargetY + d.minimumHeight) {
317 if (bottomBorderY < newTargetY + d.maximumHeight) {
249 if (bottomBorderY < newTargetY + d.maximumHeight) {
qml/Stage/WindowStateSaver.qmlnew file+63
@@ -0,0 +1,63 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import Utils 0.1
20+
21+QtObject {
22+ id: root
23+
24+ // set from outside
25+ property var target
26+ property int screenWidth: 0
27+ property int screenHeight: 0
28+ property int leftMargin: 0
29+ property int minimumY: 0
30+
31+ function load() {
32+ var defaultWidth = units.gu(60);
33+ var defaultHeight = units.gu(50);
34+ var windowGeometry = WindowStateStorage.getGeometry(target.appId,
35+ Qt.rect(target.windowedX, target.windowedY, defaultWidth, defaultHeight));
36+
37+ target.windowedWidth = Qt.binding(function() { return Math.min(Math.max(windowGeometry.width, target.minimumWidth), screenWidth - root.leftMargin); });
38+ target.windowedHeight = Qt.binding(function() { return Math.min(Math.max(windowGeometry.height, target.minimumHeight),
39+ screenHeight - (target.fullscreen ? 0 : minimumY)); });
40+ target.windowedX = Qt.binding(function() { return Math.max(Math.min(windowGeometry.x, screenWidth - root.leftMargin - target.windowedWidth),
41+ (target.fullscreen ? 0 : root.leftMargin)); });
42+ target.windowedY = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, screenHeight - target.windowedHeight), minimumY); });
43+
44+ var windowState = WindowStateStorage.getState(target.appId, WindowStateStorage.WindowStateNormal)
45+ target.restore(false /* animated */, windowState);
46+
47+ target.updateNormalGeometry();
48+
49+ // initialize the x/y to restore to
50+ target.restoredX = target.normalX;
51+ target.restoredY = target.normalY;
52+ }
53+
54+ function save() {
55+ var state = target.windowState;
56+ if (state === WindowStateStorage.WindowStateRestored) {
57+ state = WindowStateStorage.WindowStateNormal;
58+ }
59+
60+ WindowStateStorage.saveState(target.appId, state & ~WindowStateStorage.WindowStateMinimized); // clear the minimized bit when saving
61+ WindowStateStorage.saveGeometry(target.appId, Qt.rect(target.normalX, target.normalY, target.normalWidth, target.normalHeight));
62+ }
63+}
qml/Tutorial/TutorialLeftLong.qml+1−1
@@ -25,7 +25,7 @@ TutorialPage {
25
25
26 // Unlike other tutorials, this one can't be skipped before we show it, so
26 // Unlike other tutorials, this one can't be skipped before we show it, so
27 // only set opacityOverride if we're already shown.
27 // only set opacityOverride if we're already shown.
28− opacityOverride: shown ? 1 - launcher.dragDistance / launcher.minimizeDistance : 1
28+ opacityOverride: shown ? 1 - launcher.dragDistance / (launcher.panelWidth * 3) : 1
29
29
30 mouseArea {
30 mouseArea {
31 anchors.leftMargin: launcher.dragAreaWidth
31 anchors.leftMargin: launcher.dragAreaWidth
src/ShellApplication.cpp+1
@@ -36,6 +36,7 @@ ShellApplication::ShellApplication(int & argc, char ** argv, bool isMirServer)
36{
36{
37
37
38 setApplicationName(QStringLiteral("unity8"));
38 setApplicationName(QStringLiteral("unity8"));
39+ setOrganizationName(QStringLiteral("Canonical"));
39
40
40 connect(this, &QGuiApplication::screenAdded, this, &ShellApplication::onScreenAdded);
41 connect(this, &QGuiApplication::screenAdded, this, &ShellApplication::onScreenAdded);
41
42
tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.cpp+1−1
@@ -22,6 +22,7 @@ namespace QLightDM
22
22
23SessionsModelPrivate::SessionsModelPrivate(SessionsModel* parent)
23SessionsModelPrivate::SessionsModelPrivate(SessionsModel* parent)
24 : testScenario("singleSession")
24 : testScenario("singleSession")
25+ , q_ptr(parent)
25 , m_availableSessions(
26 , m_availableSessions(
26 {
27 {
27 {"ubuntu", "", "Ubuntu", ""},
28 {"ubuntu", "", "Ubuntu", ""},
@@ -36,7 +37,6 @@ SessionsModelPrivate::SessionsModelPrivate(SessionsModel* parent)
36 {"xterm", "", "Recovery Console", ""},
37 {"xterm", "", "Recovery Console", ""},
37 {"", "", "Unknown?", ""}
38 {"", "", "Unknown?", ""}
38 })
39 })
39− , q_ptr(parent)
40{
40{
41 numSessions = numAvailableSessions();
41 numSessions = numAvailableSessions();
42 resetEntries();
42 resetEntries();
tests/mocks/LightDM/IntegratedLightDM/liblightdm/UsersModelPrivate.cpp+23−23
@@ -73,7 +73,7 @@ void UsersModelPrivate::resetEntries_single()
73{
73{
74 entries =
74 entries =
75 {
75 {
76− { "single", "Single User", 0, 0, false, false, "ubuntu", 0 },
76+ { "single", "Single User", 0, 0, false, false, "ubuntu", 0, 0 },
77 };
77 };
78}
78}
79
79
@@ -81,7 +81,7 @@ void UsersModelPrivate::resetEntries_singlePassphrase()
81{
81{
82 entries =
82 entries =
83 {
83 {
84− { "single", "Single User", 0, 0, false, false, "ubuntu", 0 },
84+ { "single", "Single User", 0, 0, false, false, "ubuntu", 0, 0 },
85 };
85 };
86}
86}
87
87
@@ -89,7 +89,7 @@ void UsersModelPrivate::resetEntries_singlePin()
89{
89{
90 entries =
90 entries =
91 {
91 {
92− { "has-pin", "Has PIN", 0, 0, false, false, "ubuntu", 0 },
92+ { "has-pin", "Has PIN", 0, 0, false, false, "ubuntu", 0, 0 },
93 };
93 };
94}
94}
95
95
@@ -97,27 +97,27 @@ void UsersModelPrivate::resetEntries_full()
97{
97{
98 entries =
98 entries =
99 {
99 {
100− { "has-password", "Has Password", 0, 0, false, false, "ubuntu", 0 },
100+ { "has-password", "Has Password", 0, 0, false, false, "ubuntu", 0, 0 },
101− { "has-pin", "Has PIN", 0, 0, false, false, "ubuntu", 0 },
101+ { "has-pin", "Has PIN", 0, 0, false, false, "ubuntu", 0, 0 },
102− { "different-prompt", "Different Prompt", 0, 0, false, false, "ubuntu", 0 },
102+ { "different-prompt", "Different Prompt", 0, 0, false, false, "ubuntu", 0, 0 },
103− { "no-password", "No Password", 0, 0, false, false, "ubuntu", 0 },
103+ { "no-password", "No Password", 0, 0, false, false, "ubuntu", 0, 0 },
104− { "auth-error", "Auth Error", 0, 0, false, false, "ubuntu", 0 },
104+ { "auth-error", "Auth Error", 0, 0, false, false, "ubuntu", 0, 0 },
105− { "two-factor", "Two Factor", 0, 0, false, false, "ubuntu", 0 },
105+ { "two-factor", "Two Factor", 0, 0, false, false, "ubuntu", 0, 0 },
106− { "info-prompt", "Info Prompt", 0, 0, false, false, "ubuntu", 0 },
106+ { "info-prompt", "Info Prompt", 0, 0, false, false, "ubuntu", 0, 0 },
107− { "html-info-prompt", "HTML Info Prompt", 0, 0, false, false, "ubuntu", 0 },
107+ { "html-info-prompt", "HTML Info Prompt", 0, 0, false, false, "ubuntu", 0, 0 },
108− { "long-info-prompt", "Long Info Prompt", 0, 0, false, false, "ubuntu", 0 },
108+ { "long-info-prompt", "Long Info Prompt", 0, 0, false, false, "ubuntu", 0, 0 },
109− { "wide-info-prompt", "Wide Info Prompt", 0, 0, false, false, "ubuntu", 0 },
109+ { "wide-info-prompt", "Wide Info Prompt", 0, 0, false, false, "ubuntu", 0, 0 },
110− { "multi-info-prompt", "Multi Info Prompt", 0, 0, false, false, "ubuntu", 0 },
110+ { "multi-info-prompt", "Multi Info Prompt", 0, 0, false, false, "ubuntu", 0, 0 },
111− { "long-name", "Long name (far far too long to fit, seriously this would never fit on the screen, you will never see this part of the name)", 0, 0, false, false, "ubuntu", 0 },
111+ { "long-name", "Long name (far far too long to fit, seriously this would never fit on the screen, you will never see this part of the name)", 0, 0, false, false, "ubuntu", 0, 0 },
112− { "color-background", "Color Background", "#E95420", 0, false, false, "ubuntu", 0 },
112+ { "color-background", "Color Background", "#E95420", 0, false, false, "ubuntu", 0, 0 },
113 // white and black are a bit redundant, but useful for manually testing if UI is still readable
113 // white and black are a bit redundant, but useful for manually testing if UI is still readable
114− { "white-background", "White Background", "#ffffff", 0, false, false, "ubuntu", 0 },
114+ { "white-background", "White Background", "#ffffff", 0, false, false, "ubuntu", 0, 0 },
115− { "black-background", "Black Background", "#000000", 0, false, false, "ubuntu", 0 },
115+ { "black-background", "Black Background", "#000000", 0, false, false, "ubuntu", 0, 0 },
116− { "no-background", "No Background", "", 0, false, false, "ubuntu", 0 },
116+ { "no-background", "No Background", "", 0, false, false, "ubuntu", 0, 0 },
117− { "unicode", "가나다라마", 0, 0, false, false, "ubuntu", 0 },
117+ { "unicode", "가나다라마", 0, 0, false, false, "ubuntu", 0, 0 },
118− { "no-response", "No Response", 0, 0, false, false, "ubuntu", 0 },
118+ { "no-response", "No Response", 0, 0, false, false, "ubuntu", 0, 0 },
119− { "empty-name", "", 0, 0, false, false, "ubuntu", 0 },
119+ { "empty-name", "", 0, 0, false, false, "ubuntu", 0, 0 },
120− { "active", "Active Account", 0, 0, true, false, "ubuntu", 0 },
120+ { "active", "Active Account", 0, 0, true, false, "ubuntu", 0, 0 },
121 };
121 };
122}
122}
123
123
tests/mocks/QtMultimedia/videooutput.h+1−1
@@ -31,7 +31,7 @@ public:
31 QObject *source() const { return m_source.data(); }
31 QObject *source() const { return m_source.data(); }
32 void setSource(QObject *source);
32 void setSource(QObject *source);
33
33
34− void itemChange(ItemChange change, const ItemChangeData & value);
34+ void itemChange(ItemChange change, const ItemChangeData & value) override;
35
35
36Q_SIGNALS:
36Q_SIGNALS:
37 void sourceChanged();
37 void sourceChanged();
tests/mocks/Unity/Launcher/CMakeLists.txt+5−1
@@ -1,7 +1,8 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=10)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=11)
2
2
3include_directories(
3include_directories(
4 ${CMAKE_CURRENT_SOURCE_DIR}
4 ${CMAKE_CURRENT_SOURCE_DIR}
5+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/
5)
6)
6
7
7set(MockLauncherModel_SOURCES
8set(MockLauncherModel_SOURCES
@@ -9,10 +10,13 @@ set(MockLauncherModel_SOURCES
9 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
10 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
10 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
11 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
11 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
12 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
13+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/AppDrawerModelInterface.h
12 plugin.cpp
14 plugin.cpp
13 MockLauncherModel.cpp
15 MockLauncherModel.cpp
14 MockLauncherItem.cpp
16 MockLauncherItem.cpp
15 MockQuickListModel.cpp
17 MockQuickListModel.cpp
18+ MockAppDrawerModel.cpp
19+ ${CMAKE_SOURCE_DIR}/plugins/Utils/applicationsfiltermodel.cpp
16)
20)
17
21
18add_library(MockLauncherPlugin MODULE ${MockLauncherModel_SOURCES})
22add_library(MockLauncherPlugin MODULE ${MockLauncherModel_SOURCES})
tests/mocks/Unity/Launcher/MockAppDrawerModel.cppnew file+75
@@ -0,0 +1,75 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "MockAppDrawerModel.h"
18+
19+#include <QDebug>
20+#include <QDateTime>
21+
22+MockAppDrawerModel::MockAppDrawerModel(QObject *parent):
23+ AppDrawerModelInterface(parent)
24+{
25+ MockLauncherItem *item = new MockLauncherItem("dialer-app", "/usr/share/applications/dialer-app.desktop", "Dialer", "dialer-app", this);
26+ m_list.append(item);
27+ item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
28+ m_list.append(item);
29+ item = new MockLauncherItem("camera-app2", "/usr/share/applications/camera-app2.desktop", "Camera2", "camera", this);
30+ m_list.append(item);
31+ item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
32+ m_list.append(item);
33+ item = new MockLauncherItem("music-app", "/usr/share/applications/music-app.desktop", "Music", "soundcloud", this);
34+ m_list.append(item);
35+ item = new MockLauncherItem("facebook-webapp", "/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook", this);
36+ m_list.append(item);
37+ item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
38+ m_list.append(item);
39+ item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
40+ m_list.append(item);
41+ item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
42+ m_list.append(item);
43+ item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
44+ m_list.append(item);
45+ item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
46+ m_list.append(item);
47+ item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
48+ m_list.append(item);
49+ item = new MockLauncherItem("libreoffice", "/usr/share/applications/libreoffice.desktop","Libre Office", "libreoffice", this);
50+ m_list.append(item);
51+ qDebug() << "mock model created";
52+
53+ qsrand(QDateTime::currentMSecsSinceEpoch() / 1000);
54+}
55+
56+int MockAppDrawerModel::rowCount(const QModelIndex &parent) const
57+{
58+ return m_list.count();
59+}
60+
61+QVariant MockAppDrawerModel::data(const QModelIndex &index, int role) const
62+{
63+ switch (role) {
64+ case RoleAppId:
65+ return m_list.at(index.row())->appId();
66+ case RoleName:
67+ return m_list.at(index.row())->name();
68+ case RoleIcon:
69+ return m_list.at(index.row())->icon();
70+ case RoleUsage:
71+ return qrand();
72+ }
73+
74+ return QVariant();
75+}
tests/mocks/Unity/Launcher/MockAppDrawerModel.hnew file+32
@@ -0,0 +1,32 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <unity/shell/launcher/AppDrawerModelInterface.h>
18+
19+#include "MockLauncherItem.h"
20+
21+class MockAppDrawerModel: public AppDrawerModelInterface
22+{
23+ Q_OBJECT
24+public:
25+ MockAppDrawerModel(QObject* parent = nullptr);
26+
27+ int rowCount(const QModelIndex &parent) const override;
28+ QVariant data(const QModelIndex &index, int role) const override;
29+
30+private:
31+ QList<MockLauncherItem*> m_list;
32+};
tests/mocks/Unity/Launcher/MockLauncherItem.cpp+13
@@ -67,6 +67,11 @@ QString MockLauncherItem::icon() const
67 return m_icon;
67 return m_icon;
68}
68}
69
69
70+QStringList MockLauncherItem::keywords() const
71+{
72+ return m_keywords;
73+}
74+
70bool MockLauncherItem::pinned() const
75bool MockLauncherItem::pinned() const
71{
76{
72 return m_pinned;
77 return m_pinned;
@@ -200,3 +205,11 @@ unity::shell::launcher::QuickListModelInterface *MockLauncherItem::quickList() c
200{
205{
201 return m_quickList;
206 return m_quickList;
202}
207}
208+
209+void MockLauncherItem::setKeywords(const QStringList &keywords)
210+{
211+ if (m_keywords != keywords) {
212+ m_keywords = keywords;
213+ Q_EMIT keywordsChanged(keywords);
214+ }
215+}
tests/mocks/Unity/Launcher/MockLauncherItem.h+6
@@ -22,6 +22,8 @@
22
22
23#include <unity/shell/launcher/LauncherItemInterface.h>
23#include <unity/shell/launcher/LauncherItemInterface.h>
24
24
25+#include <QStringList>
26+
25class MockQuickListModel;
27class MockQuickListModel;
26
28
27using namespace unity::shell::launcher;
29using namespace unity::shell::launcher;
@@ -37,6 +39,7 @@ public:
37 QString desktopFile() const;
39 QString desktopFile() const;
38 QString name() const override;
40 QString name() const override;
39 QString icon() const override;
41 QString icon() const override;
42+ QStringList keywords() const override;
40
43
41 bool pinned() const override;
44 bool pinned() const override;
42 bool running() const override;
45 bool running() const override;
@@ -51,6 +54,7 @@ public:
51 unity::shell::launcher::QuickListModelInterface *quickList() const override;
54 unity::shell::launcher::QuickListModelInterface *quickList() const override;
52
55
53private:
56private:
57+ void setKeywords(const QStringList &keywords);
54 void setPinned(bool pinned);
58 void setPinned(bool pinned);
55 void setRunning(bool running);
59 void setRunning(bool running);
56 void setRecent(bool recent);
60 void setRecent(bool recent);
@@ -65,6 +69,7 @@ private:
65 QString m_desktopFile;
69 QString m_desktopFile;
66 QString m_name;
70 QString m_name;
67 QString m_icon;
71 QString m_icon;
72+ QStringList m_keywords;
68 bool m_pinned;
73 bool m_pinned;
69 bool m_running;
74 bool m_running;
70 bool m_recent;
75 bool m_recent;
@@ -77,6 +82,7 @@ private:
77 MockQuickListModel *m_quickList;
82 MockQuickListModel *m_quickList;
78
83
79 friend class MockLauncherModel;
84 friend class MockLauncherModel;
85+ friend class MockAppDrawerModel;
80};
86};
81
87
82#endif // MOCKLAUNCHERITEM_H
88#endif // MOCKLAUNCHERITEM_H
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+3
@@ -36,6 +36,9 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
36 item = new MockLauncherItem("camera-app2", "/usr/share/applications/camera-app2.desktop", "Camera2", "camera", this);
36 item = new MockLauncherItem("camera-app2", "/usr/share/applications/camera-app2.desktop", "Camera2", "camera", this);
37 item->setPinned(true);
37 item->setPinned(true);
38 m_list.append(item);
38 m_list.append(item);
39+ item = new MockLauncherItem("camera-app3", "/usr/share/applications/camera-app2.desktop", "Camera2", "camera", this);
40+ item->setPinned(true);
41+ m_list.append(item);
39 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
42 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
40 item->setProgress(50);
43 item->setProgress(50);
41 item->setCountVisible(true);
44 item->setCountVisible(true);
tests/mocks/Unity/Launcher/plugin.cpp+4
@@ -21,9 +21,11 @@
21#include "MockLauncherModel.h"
21#include "MockLauncherModel.h"
22#include "MockLauncherItem.h"
22#include "MockLauncherItem.h"
23#include "MockQuickListModel.h"
23#include "MockQuickListModel.h"
24+#include "MockAppDrawerModel.h"
24
25
25#include <unity/shell/launcher/LauncherModelInterface.h>
26#include <unity/shell/launcher/LauncherModelInterface.h>
26#include <unity/shell/launcher/LauncherItemInterface.h>
27#include <unity/shell/launcher/LauncherItemInterface.h>
28+#include <unity/shell/launcher/AppDrawerModelInterface.h>
27
29
28#include <QtQml/qqml.h>
30#include <QtQml/qqml.h>
29
31
@@ -41,8 +43,10 @@ void TestLauncherPlugin::registerTypes(const char* uri)
41 qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
43 qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
42 qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
44 qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
43 qmlRegisterUncreatableType<QuickListModelInterface>(uri, 0, 1, "QuickListModelInterface", "Abstract Interface. Cannot be instantiated.");
45 qmlRegisterUncreatableType<QuickListModelInterface>(uri, 0, 1, "QuickListModelInterface", "Abstract Interface. Cannot be instantiated.");
46+ qmlRegisterUncreatableType<AppDrawerModelInterface>(uri, 0, 1, "AppDrawerModelInterface", "Abstract Interface. Cannot be instantiated.");
44
47
45 qmlRegisterSingletonType<MockLauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
48 qmlRegisterSingletonType<MockLauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
46 qmlRegisterUncreatableType<MockLauncherItem>(uri, 0, 1, "LauncherItem", "Can't create LauncherItems in QML. Get them from the LauncherModel");
49 qmlRegisterUncreatableType<MockLauncherItem>(uri, 0, 1, "LauncherItem", "Can't create LauncherItems in QML. Get them from the LauncherModel");
47 qmlRegisterUncreatableType<MockQuickListModel>(uri, 0, 1, "QuickListModel", "Can't create QuickLists in QML. Get them from the LauncherItems");
50 qmlRegisterUncreatableType<MockQuickListModel>(uri, 0, 1, "QuickListModel", "Can't create QuickLists in QML. Get them from the LauncherItems");
51+ qmlRegisterType<MockAppDrawerModel>(uri, 0, 1, "AppDrawerModel");
48}
52}
tests/mocks/Utils/CMakeLists.txt+4
@@ -24,11 +24,15 @@ set(QMLPLUGIN_SRC
24 ${CMAKE_SOURCE_DIR}/plugins/Utils/inputeventgenerator.cpp
24 ${CMAKE_SOURCE_DIR}/plugins/Utils/inputeventgenerator.cpp
25 ${CMAKE_SOURCE_DIR}/plugins/Utils/deviceconfigparser.cpp
25 ${CMAKE_SOURCE_DIR}/plugins/Utils/deviceconfigparser.cpp
26 ${CMAKE_SOURCE_DIR}/plugins/Utils/globalfunctions.cpp
26 ${CMAKE_SOURCE_DIR}/plugins/Utils/globalfunctions.cpp
27+ ${CMAKE_SOURCE_DIR}/plugins/Utils/appdrawerproxymodel.cpp
27 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
28 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
28 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
29 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
29 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
30 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
30 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
31 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
31 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
32 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
33+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/AppDrawerModelInterface.h
34+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
35+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
32 constants.cpp
36 constants.cpp
33 plugin.cpp
37 plugin.cpp
34 windowstatestorage.cpp
38 windowstatestorage.cpp
tests/mocks/Utils/plugin.cpp+2
@@ -40,6 +40,7 @@
40#include <inputeventgenerator.h>
40#include <inputeventgenerator.h>
41#include <deviceconfigparser.h>
41#include <deviceconfigparser.h>
42#include <globalfunctions.h>
42#include <globalfunctions.h>
43+#include <appdrawerproxymodel.h>
43
44
44static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
45static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
45{
46{
@@ -82,4 +83,5 @@ void FakeUtilsPlugin::registerTypes(const char *uri)
82 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
83 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
83 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
84 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
84 qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher");
85 qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher");
86+ qmlRegisterType<AppDrawerProxyModel>(uri, 0, 1, "AppDrawerProxyModel");
85}
87}
tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=10)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=11)
2
2
3include_directories(
3include_directories(
4 ${CMAKE_CURRENT_SOURCE_DIR}
4 ${CMAKE_CURRENT_SOURCE_DIR}
tests/plugins/Unity/Launcher/CMakeLists.txt+2−1
@@ -1,5 +1,5 @@
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=10)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=11)
3
3
4include_directories(
4include_directories(
5 ${CMAKE_CURRENT_SOURCE_DIR}
5 ${CMAKE_CURRENT_SOURCE_DIR}
@@ -30,6 +30,7 @@ add_executable(launchermodeltestExec
30 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistmodel.cpp
30 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistmodel.cpp
31 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/dbusinterface.cpp
31 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/dbusinterface.cpp
32 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistentry.cpp
32 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistentry.cpp
33+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/ualwrapper.cpp
33 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
34 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
34 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
35 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
35 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
36 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
tests/plugins/Utils/WindowInputMonitorTest.cpp+76−9
@@ -25,14 +25,15 @@ class FakeElapsedTimer : public AbstractElapsedTimer {
25public:
25public:
26 static qint64 msecsSinceEpoch;
26 static qint64 msecsSinceEpoch;
27
27
28− FakeElapsedTimer() { start(); }
28+ FakeElapsedTimer() {}
29
29
30− void start() override { m_msecsSinceReference = msecsSinceEpoch; }
30+ void start() override { m_msecsSinceReference = msecsSinceEpoch; m_valid = true; }
31 qint64 msecsSinceReference() const override { return m_msecsSinceReference; }
31 qint64 msecsSinceReference() const override { return m_msecsSinceReference; }
32− qint64 elapsed() const override { return msecsSinceEpoch - m_msecsSinceReference; }
32+ qint64 elapsed() const override { return m_valid ? msecsSinceEpoch - m_msecsSinceReference : qrand(); }
33
33
34private:
34private:
35 qint64 m_msecsSinceReference;
35 qint64 m_msecsSinceReference;
36+ bool m_valid{false};
36};
37};
37qint64 FakeElapsedTimer::msecsSinceEpoch = 0;
38qint64 FakeElapsedTimer::msecsSinceEpoch = 0;
38
39
@@ -48,11 +49,14 @@ public:
48 int interval() const override;
49 int interval() const override;
49 void setInterval(int msecs) override;
50 void setInterval(int msecs) override;
50 void start() override;
51 void start() override;
52+ void stop() override;
53+ bool isRunning() const override;
51 bool isSingleShot() const override;
54 bool isSingleShot() const override;
52 void setSingleShot(bool value) override;
55 void setSingleShot(bool value) override;
53private:
56private:
54 int m_interval;
57 int m_interval;
55 bool m_singleShot;
58 bool m_singleShot;
59+ bool m_isRunning;
56 qint64 m_nextTimeoutTime;
60 qint64 m_nextTimeoutTime;
57};
61};
58
62
@@ -85,6 +89,8 @@ private Q_SLOTS:
85 void tapWhileTouching();
89 void tapWhileTouching();
86 void multipleHomeKeys();
90 void multipleHomeKeys();
87
91
92+ void repeatedSuperPress();
93+
88private:
94private:
89 void passTime(qint64 timeSpanMs);
95 void passTime(qint64 timeSpanMs);
90
96
@@ -98,11 +104,10 @@ void WindowInputMonitorTest::init()
98
104
99void WindowInputMonitorTest::cleanup()
105void WindowInputMonitorTest::cleanup()
100{
106{
101− delete m_fakeTimerFactory;
107+ delete m_fakeTimerFactory;
102 m_fakeTimerFactory = nullptr;
108 m_fakeTimerFactory = nullptr;
103}
109}
104
110
105−
106void WindowInputMonitorTest::passTime(qint64 timeSpanMs)
111void WindowInputMonitorTest::passTime(qint64 timeSpanMs)
107{
112{
108 qint64 finalTime = FakeElapsedTimer::msecsSinceEpoch + timeSpanMs;
113 qint64 finalTime = FakeElapsedTimer::msecsSinceEpoch + timeSpanMs;
@@ -133,7 +138,7 @@ void WindowInputMonitorTest::touchTapTouch()
133 QFETCH(int, silenceAfterTap);
138 QFETCH(int, silenceAfterTap);
134 QFETCH(int, expectedActivatedCount);
139 QFETCH(int, expectedActivatedCount);
135 QFETCH(Qt::Key, key);
140 QFETCH(Qt::Key, key);
136− WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(), new FakeElapsedTimer);
141+ WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
137 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
142 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
138 QVERIFY(activatedSpy.isValid());
143 QVERIFY(activatedSpy.isValid());
139
144
@@ -194,7 +199,7 @@ void WindowInputMonitorTest::touchTapTouch()
194
199
195void WindowInputMonitorTest::tapWhileTouching()
200void WindowInputMonitorTest::tapWhileTouching()
196{
201{
197− WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(), new FakeElapsedTimer);
202+ WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
198 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
203 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
199 QVERIFY(activatedSpy.isValid());
204 QVERIFY(activatedSpy.isValid());
200
205
@@ -229,7 +234,7 @@ void WindowInputMonitorTest::tapWhileTouching()
229 */
234 */
230void WindowInputMonitorTest::multipleHomeKeys()
235void WindowInputMonitorTest::multipleHomeKeys()
231{
236{
232− WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(), new FakeElapsedTimer);
237+ WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
233 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
238 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
234 QVERIFY(activatedSpy.isValid());
239 QVERIFY(activatedSpy.isValid());
235
240
@@ -258,12 +263,64 @@ void WindowInputMonitorTest::multipleHomeKeys()
258 QCOMPARE(activatedSpy.count(), 1);
263 QCOMPARE(activatedSpy.count(), 1);
259}
264}
260
265
266+// regression test for lp:1607427
267+void WindowInputMonitorTest::repeatedSuperPress()
268+{
269+ WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
270+ QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
271+ QVERIFY(activatedSpy.isValid());
272+
273+ // 1st try
274+ passTime(1000);
275+ {
276+ QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Super_L, Qt::NoModifier);
277+ homeKeyWatcher.update(&keyEvent);
278+ }
279+ passTime(50);
280+ {
281+ QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_Super_L, Qt::NoModifier);
282+ homeKeyWatcher.update(&keyEvent);
283+ }
284+ passTime(1000);
285+ QCOMPARE(activatedSpy.count(), 1);
286+
287+ // a touch event in between
288+ {
289+ QTouchEvent touchEvent(QEvent::TouchBegin);
290+ homeKeyWatcher.update(&touchEvent);
291+ }
292+ {
293+ QTouchEvent touchEvent(QEvent::TouchUpdate);
294+ homeKeyWatcher.update(&touchEvent);
295+ }
296+ {
297+ QTouchEvent touchEvent(QEvent::TouchEnd);
298+ homeKeyWatcher.update(&touchEvent);
299+ }
300+
301+ passTime(1000);
302+ // 2nd try
303+ activatedSpy.clear();
304+ {
305+ QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Super_L, Qt::NoModifier);
306+ homeKeyWatcher.update(&keyEvent);
307+ }
308+ passTime(50);
309+ {
310+ QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_Super_L, Qt::NoModifier);
311+ homeKeyWatcher.update(&keyEvent);
312+ }
313+ passTime(1000);
314+ QCOMPARE(activatedSpy.count(), 1);
315+}
316+
261/////////////////////////////////// FakeTimer //////////////////////////////////
317/////////////////////////////////// FakeTimer //////////////////////////////////
262
318
263FakeTimer::FakeTimer(QObject *parent)
319FakeTimer::FakeTimer(QObject *parent)
264 : UnityUtil::AbstractTimer(parent)
320 : UnityUtil::AbstractTimer(parent)
265 , m_interval(0)
321 , m_interval(0)
266 , m_singleShot(true)
322 , m_singleShot(true)
323+ , m_isRunning(false)
267{
324{
268}
325}
269
326
@@ -285,10 +342,20 @@ void FakeTimer::update()
285
342
286void FakeTimer::start()
343void FakeTimer::start()
287{
344{
288− AbstractTimer::start();
345+ m_isRunning = true;
289 m_nextTimeoutTime = FakeElapsedTimer::msecsSinceEpoch + (qint64)interval();
346 m_nextTimeoutTime = FakeElapsedTimer::msecsSinceEpoch + (qint64)interval();
290}
347}
291
348
349+void FakeTimer::stop()
350+{
351+ m_isRunning = false;
352+}
353+
354+bool FakeTimer::isRunning() const
355+{
356+ return m_isRunning;
357+}
358+
292int FakeTimer::interval() const
359int FakeTimer::interval() const
293{
360{
294 return m_interval;
361 return m_interval;
tests/qmltests/CMakeLists.txt+1
@@ -78,6 +78,7 @@ add_unity8_qmltest(Greeter NarrowView)
78add_unity8_qmltest(Greeter WideView)
78add_unity8_qmltest(Greeter WideView)
79add_unity8_qmltest_data(Greeter TestView)
79add_unity8_qmltest_data(Greeter TestView)
80add_unity8_qmltest(Launcher Launcher)
80add_unity8_qmltest(Launcher Launcher)
81+add_unity8_qmltest(Launcher Drawer)
81add_unity8_qmltest(Notifications Notifications)
82add_unity8_qmltest(Notifications Notifications)
82add_unity8_qmltest(Notifications VisualSnapDecisionsQueue)
83add_unity8_qmltest(Notifications VisualSnapDecisionsQueue)
83add_unity8_qmltest(Notifications OptionToggle)
84add_unity8_qmltest(Notifications OptionToggle)
tests/qmltests/Components/tst_VirtualTouchPad.qml+55−1
@@ -21,14 +21,20 @@ import UInput 0.1
21import "../../../qml/Components"
21import "../../../qml/Components"
22
22
23
23
24−Item {
24+Rectangle {
25 id: root
25 id: root
26 width: units.gu(70)
26 width: units.gu(70)
27 height: units.gu(70)
27 height: units.gu(70)
28+ color: "black"
28
29
29 VirtualTouchPad {
30 VirtualTouchPad {
30 id: touchScreenPad
31 id: touchScreenPad
31 anchors.fill: parent
32 anchors.fill: parent
33+ property int internalGu: units.gu(1)
34+ settings: QtObject {
35+ property bool touchpadTutorialHasRun: true
36+ property bool oskEnabled: true
37+ }
32 }
38 }
33
39
34 SignalSpy {
40 SignalSpy {
@@ -139,5 +145,53 @@ Item {
139 event.release(1, startX2, startY + units.gu(11));
145 event.release(1, startX2, startY + units.gu(11));
140 event.commit();
146 event.commit();
141 }
147 }
148+
149+ function test_tutorial() {
150+ var oskButton = findChild(touchScreenPad, "oskButton");
151+ var leftButton = findChild(touchScreenPad, "leftButton");
152+ var rightButton = findChild(touchScreenPad, "rightButton");
153+ var touchPad = findChild(touchScreenPad, "virtualTouchPad")
154+ var tutorial = findInvisibleChild(touchScreenPad, "tutorialAnimation")
155+ var tutorialFinger1 = findChild(touchScreenPad, "tutorialFinger1");
156+ var tutorialFinger2 = findChild(touchScreenPad, "tutorialFinger2");
157+ var tutorialLabel = findChild(touchScreenPad, "tutorialLabel");
158+ var tutorialImage = findChild(touchScreenPad, "tutorialImage");
159+
160+ mouseEventSpy1.signalName = "mousePressed"
161+ mouseEventSpy2.signalName = "mouseReleased"
162+
163+ mouseEventSpy1.clear();
164+ mouseEventSpy2.clear();
165+
166+ // run the tutorial
167+ touchScreenPad.runTutorial();
168+ tryCompare(tutorial, "running", true)
169+
170+ // Wait for it to pause
171+ tryCompare(tutorial, "paused", true)
172+
173+ // Click somewhere to make it continue
174+ mouseClick(root, root.width / 2, root.height /2)
175+ tryCompare(tutorial, "paused", false)
176+
177+ // Verify that the touchpad does NOT accept input while the tutorial is running
178+ mouseClick(root, root.width / 2, root.height /2)
179+ compare(mouseEventSpy1.count, 0)
180+ compare(mouseEventSpy2.count, 0)
181+
182+ // Wait for it to finish
183+ tryCompare(tutorial, "running", false, 60000)
184+
185+ // Make sure after the tutorial, all the visible states are proper
186+ tryCompare(oskButton, "visible", true)
187+ tryCompare(leftButton, "visible", true)
188+ tryCompare(rightButton, "visible", true)
189+
190+ tryCompare(tutorialFinger1, "visible", false)
191+ tryCompare(tutorialFinger2, "visible", false)
192+ tryCompare(tutorialImage, "visible", false)
193+ tryCompare(tutorialLabel, "visible", false)
194+ }
195+
142 }
196 }
143}
197}
tests/qmltests/Dash/Previews/tst_Preview.qml+63
@@ -37,6 +37,51 @@ Rectangle {
37 id: mockPreviewModel
37 id: mockPreviewModel
38 }
38 }
39
39
40+ ListModel {
41+ id: tracksModel
42+ Component.onCompleted:
43+ {
44+ tracksModel.append( { "type" : "audio1",
45+ "widgetId" : "audiopw1",
46+ "properties" : { "tracks" : [
47+ { title: "Some track name", length: "30", source: "/not/existinga/path/testsound1" }
48+ ]
49+ }
50+ }
51+ );
52+ tracksModel.append(
53+ { "type" : "audio",
54+ "widgetId" : "audiopw",
55+ "properties" : { "tracks" : [
56+ { title: "Some track name", length: "30", source: "/not/existing/path/testsound1" },
57+ { title: "Some other track name", subtitle: "Subtitle", length: "83", source: "/not/existing/path/testsound2" },
58+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound3" },
59+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound4" },
60+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound5" },
61+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound6" },
62+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound7" },
63+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound8" },
64+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound9" },
65+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound10" },
66+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound11" },
67+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound12" },
68+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound13" },
69+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound14" },
70+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound15" },
71+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound16" },
72+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound17" }
73+ ]
74+ }
75+ } );
76+ }
77+ }
78+
79+ QtObject {
80+ id: audiosModel
81+ property int widgetColumnCount: 1
82+ property var columnModel: { tracksModel }
83+ }
84+
40 Preview {
85 Preview {
41 id: preview
86 id: preview
42 anchors.fill: parent
87 anchors.fill: parent
@@ -57,6 +102,7 @@ Rectangle {
57 function init() {
102 function init() {
58 var widget = findChild(preview, "previewListRow0");
103 var widget = findChild(preview, "previewListRow0");
59 widget.positionViewAtBeginning();
104 widget.positionViewAtBeginning();
105+ preview.previewModel = mockPreviewModel;
60 }
106 }
61
107
62 function test_triggered() {
108 function test_triggered() {
@@ -147,5 +193,22 @@ Rectangle {
147 skip("preview %1 or widget %2 have been destroyed, thus we can't safely continue this test".arg(preview_str).arg(widget_str))
193 skip("preview %1 or widget %2 have been destroyed, thus we can't safely continue this test".arg(preview_str).arg(widget_str))
148 }
194 }
149 }
195 }
196+
197+ function test_audios() {
198+ preview.previewModel = audiosModel;
199+ waitForRendering(preview);
200+
201+ // Scroll down
202+ var previewListRow0 = findChild(preview, "previewListRow0");
203+ flickToYEnd(previewListRow0);
204+
205+ var previewsContentY = previewListRow0.contentY;
206+
207+ var trackItem = findChild(preview, "trackItem16");
208+ mouseClick(findChild(trackItem, "playButton"));
209+
210+ expectFail("", "Clicking on a track should not change contentY.");
211+ tryCompareFunction(function () { return previewsContentY != previewListRow0.contentY; }, true);
212+ }
150 }
213 }
151}
214}
tests/qmltests/Dash/tst_Dash.qml+4−3
@@ -310,8 +310,9 @@ Item {
310 var stopX = units.gu(1)
310 var stopX = units.gu(1)
311 var stopY = startY;
311 var stopY = startY;
312 waitForRendering(dashContentList)
312 waitForRendering(dashContentList)
313− mouseFlick(dash, startX, startY, stopX, stopY);
313+ mouseFlick(dash, startX, startY, stopX, stopY, true, true, units.gu(200));
314+ compare(dashContentList.currentIndex, 1, "Could not flick to scope id 1");
314− mouseFlick(dash, startX, startY, stopX, stopY);
315+ mouseFlick(dash, startX, startY, stopX, stopY, units.gu(200));
315 compare(dashContentList.currentIndex, 2, "Could not flick to scope id 2");
316 compare(dashContentList.currentIndex, 2, "Could not flick to scope id 2");
316 var dashCommunicatorService = findInvisibleChild(dash, "dashCommunicatorService");
317 var dashCommunicatorService = findInvisibleChild(dash, "dashCommunicatorService");
317 dashCommunicatorService.mockSetCurrentScope(0, true, false);
318 dashCommunicatorService.mockSetCurrentScope(0, true, false);
@@ -411,7 +412,7 @@ Item {
411 compare(dashContentList.currentItem.scopeId, "clickscope");
412 compare(dashContentList.currentItem.scopeId, "clickscope");
412
413
413 // Move to second scope
414 // Move to second scope
414− mouseFlick(dash, dash.width / 2, units.gu(2), dash.width / 5, units.gu(2));
415+ mouseFlick(dash, dash.width / 2, units.gu(2), dash.width / 5, units.gu(2), true, true, units.gu(20));
415 tryCompare(dashContentList, "currentIndex", 1);
416 tryCompare(dashContentList, "currentIndex", 1);
416 compare(dashContentList.currentItem.scopeId, "MockScope1");
417 compare(dashContentList.currentItem.scopeId, "MockScope1");
417 }
418 }
tests/qmltests/Dash/tst_DashShell.qml−29
@@ -112,34 +112,5 @@ Item {
112 mouseClick(buttonShowDashHome);
112 mouseClick(buttonShowDashHome);
113 tryCompare(dashContentList, "currentIndex", 0);
113 tryCompare(dashContentList, "currentIndex", 0);
114 }
114 }
115−
116− function test_setShortLauncherMoveResetNonActiveDash() {
117− var dashContentList = findChild(dash, "dashContentList");
118− var startX = dash.width - units.gu(1);
119− var startY = dash.height / 2;
120− var stopX = units.gu(1)
121− var stopY = startY;
122− waitForRendering(dashContentList);
123− touchFlick(dash, startX, startY, stopX, stopY);
124− touchFlick(dash, startX, startY, stopX, stopY);
125− compare(dashContentList.currentIndex, 2, "Could not flick to scope id 2");
126−
127− // Pretend the dash is not active
128− dash.windowActive = false;
129−
130− // Flick the greeter away
131− var startX = shell.width - units.gu(1);
132− var startY = shell.height / 2;
133− var stopX = units.gu(1)
134− var stopY = startY;
135− touchFlick(shell, startX, startY, stopX, stopY);
136−
137− var greeter = findChild(shell, "greeter");
138− tryCompare(greeter, "shown", false);
139−
140− // Now do a small launcher movement
141− touchFlick(shell, stopX, startY, startX, stopY);
142− compare(dashContentList.currentIndex, 0, "Small launcher move did not reset dash on non active window");
143− }
144 }
115 }
145}
116}
tests/qmltests/Launcher/tst_Drawer.qmlnew file+264
@@ -0,0 +1,264 @@
1+/*
2+ * Copyright 2013-2016 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import QtQuick.Layouts 1.1
19+import Ubuntu.Components 1.3
20+import ".."
21+import "../../../qml/Launcher"
22+import Unity.Launcher 0.1
23+import Utils 0.1 // For EdgeBarrierSettings
24+import Unity.Test 0.1
25+
26+StyledItem {
27+ id: root
28+ theme.name: "Ubuntu.Components.Themes.SuruDark"
29+
30+ width: units.gu(140)
31+ height: units.gu(70)
32+ Rectangle {
33+ anchors.fill: parent
34+ color: UbuntuColors.graphite // something neither white nor black
35+ }
36+
37+ Launcher {
38+ id: launcher
39+ x: 0
40+ y: 0
41+ width: units.gu(40)
42+ height: root.height
43+
44+ lockedVisible: lockedVisibleCheckBox.checked
45+
46+ property string lastSelectedApplication
47+ onLauncherApplicationSelected: {
48+ lastSelectedApplication = appId
49+ }
50+
51+ Component.onCompleted: {
52+ launcher.focus = true
53+ edgeBarrierControls.target = testCase.findChild(this, "edgeBarrierController");
54+ }
55+ }
56+
57+ ColumnLayout {
58+ anchors { bottom: parent.bottom; right: parent.right; margins: units.gu(1) }
59+ spacing: units.gu(1)
60+ width: childrenRect.width
61+
62+ RowLayout {
63+ CheckBox {
64+ id: lockedVisibleCheckBox
65+ checked: false
66+ }
67+ Label {
68+ text: "Launcher always visible"
69+ }
70+ }
71+
72+ Slider {
73+ id: widthSlider
74+ Layout.fillWidth: true
75+ minimumValue: 6
76+ maximumValue: 12
77+ value: 10
78+ }
79+
80+ MouseTouchEmulationCheckbox {}
81+
82+ EdgeBarrierControls {
83+ id: edgeBarrierControls
84+ text: "Drag here to pull out launcher"
85+ onDragged: { launcher.pushEdge(amount); }
86+ }
87+ }
88+
89+ UnityTestCase {
90+ id: testCase
91+ when: windowShown
92+ name: "Drawer"
93+
94+ function dragDrawerIntoView() {
95+ var startX = launcher.dragAreaWidth/2;
96+ var startY = launcher.height/2;
97+ touchFlick(launcher,
98+ startX, startY,
99+ startX+units.gu(35), startY);
100+
101+ var drawer = findChild(launcher, "drawer");
102+ verify(!!drawer);
103+
104+ // wait until it gets fully extended
105+ // a tryCompare doesn't work since
106+ // compare(-0.000005917593600024418, 0);
107+ // is true and in this case we want exactly 0 or will have pain later on
108+ tryCompareFunction( function(){ return drawer.x === 0; }, true );
109+ tryCompare(launcher, "state", "drawer");
110+ tryCompare(launcher, "drawerShown", true);
111+ }
112+
113+ function revealByEdgePush() {
114+ // Place the mouse against the window/screen edge and push beyond the barrier threshold
115+ // If the mouse did not move between two revealByEdgePush(), we need it to move out the
116+ // area to make sure we're not just accumulating to the previous push
117+ mouseMove(root, root.width, root.height / 2);
118+ mouseMove(root, 1, root.height / 2);
119+
120+ launcher.pushEdge(EdgeBarrierSettings.pushThreshold * 1.1);
121+
122+ var drawer = findChild(launcher, "drawer");
123+ verify(!!drawer);
124+
125+ // wait until it gets fully extended
126+ tryCompare(drawer, "x", 0);
127+ tryCompare(launcher, "state", "drawer");
128+ tryCompare(launcher, "drawerShown", true);
129+ }
130+
131+ function init() {
132+ launcher.lastSelectedApplication = "";
133+ launcher.lockedVisible = false;
134+ launcher.hide();
135+ var drawer = findChild(launcher, "drawer");
136+ tryCompare(drawer, "x", -drawer.width);
137+ }
138+
139+ function test_revealByEdgeDrag() {
140+ dragDrawerIntoView();
141+ }
142+
143+ function test_revealByEdgePush_data() {
144+ return [
145+ { tag: "autohide launcher", autohide: true },
146+ { tag: "locked launcher", autohide: false }
147+ ]
148+ }
149+
150+ function test_revealByEdgePush(data) {
151+ launcher.lockedVisible = !data.autohide;
152+
153+ var panel = findChild(launcher, "launcherPanel");
154+ tryCompare(panel, "x", data.autohide ? -panel.width : 0);
155+ tryCompare(launcher, "state", data.autohide ? "" : "visible");
156+ waitForRendering(launcher)
157+
158+ revealByEdgePush();
159+ }
160+
161+ function test_hideByDraggingDrawer_data() {
162+ return [
163+ {tag: "autohide", autohide: true, endState: ""},
164+ {tag: "locked", autohide: false, endState: "visible"}
165+ ]
166+ }
167+
168+ function test_hideByDraggingDrawer(data) {
169+ launcher.lockedVisible = !data.autohide;
170+
171+ dragDrawerIntoView();
172+ waitForRendering(launcher);
173+
174+ var drawer = findChild(launcher, "drawer");
175+
176+ mouseFlick(root, drawer.width - units.gu(1), drawer.height / 2, units.gu(2), drawer.height / 2, true, true);
177+
178+ tryCompare(drawer.anchors, "rightMargin", 0);
179+ tryCompare(launcher, "state", data.endState);
180+ launcher.lockedVisible = false;
181+ }
182+
183+ function test_hideByClickingOutside() {
184+ dragDrawerIntoView();
185+
186+ var drawer = findChild(launcher, "drawer");
187+
188+ mouseClick(root, drawer.width + units.gu(1), root.height / 2);
189+
190+ tryCompare(launcher, "state", "");
191+ }
192+
193+ function test_launchAppFromDrawer() {
194+ dragDrawerIntoView();
195+
196+ var drawerList = findChild(launcher, "drawerItemList");
197+ var dialerApp = findChild(drawerList, "drawerItem_dialer-app");
198+ mouseClick(dialerApp, dialerApp.width / 2, dialerApp.height / 2);
199+
200+ tryCompare(launcher, "lastSelectedApplication", "dialer-app");
201+
202+ tryCompare(launcher, "state", "");
203+ }
204+
205+ function test_launcherGivesUpFocusAfterLaunchingFromDrawer() {
206+ dragDrawerIntoView();
207+
208+ tryCompare(launcher, "focus", true);
209+
210+ var drawerList = findChild(launcher, "drawerItemList");
211+ var dialerApp = findChild(drawerList, "drawerItem_dialer-app");
212+ mouseClick(dialerApp, dialerApp.width / 2, dialerApp.height / 2);
213+
214+ tryCompare(launcher, "focus", false);
215+ }
216+
217+ function test_drawerDisabled() {
218+ launcher.drawerEnabled = false;
219+
220+ var startX = launcher.dragAreaWidth/2;
221+ var startY = launcher.height/2;
222+ touchFlick(launcher,
223+ startX, startY,
224+ startX+units.gu(35), startY);
225+
226+ var drawer = findChild(launcher, "drawer");
227+ verify(!!drawer);
228+
229+ tryCompare(launcher, "state", "visible");
230+ tryCompare(launcher, "drawerShown", false);
231+
232+ launcher.drawerEnabled = true;
233+ }
234+
235+ function test_search() {
236+ compare(launcher.lastSelectedApplication, "");
237+
238+ launcher.openDrawer(true)
239+ typeString("cam");
240+ keyClick(Qt.Key_Enter);
241+
242+ compare(launcher.lastSelectedApplication, "camera-app");
243+ }
244+
245+ function test_dragDirectionOnLeftEdgeDrag_data() {
246+ return [
247+ { tag: "reveal", direction: "right", endState: "drawer" },
248+ { tag: "cancel", direction: "left", endState: "visible" },
249+ ]
250+ }
251+
252+ function test_dragDirectionOnLeftEdgeDrag(data) {
253+ var startX = launcher.dragAreaWidth/2;
254+ var startY = launcher.height/2;
255+ var stopX = startX + units.gu(35);
256+ var endX = stopX + (units.gu(4) * (data.direction === "left" ? -1 : 1))
257+
258+ touchFlick(launcher, startX, startY, stopX, startY, true, false);
259+ touchFlick(launcher, stopX, startY, endX, startY, false, true);
260+
261+ tryCompare(launcher, "state", data.endState)
262+ }
263+ }
264+}
tests/qmltests/Launcher/tst_Launcher.qml+4−4
@@ -322,7 +322,7 @@ Rectangle {
322 function revealByEdgePush() {
322 function revealByEdgePush() {
323 // Place the mouse against the window/screen edge and push beyond the barrier threshold
323 // Place the mouse against the window/screen edge and push beyond the barrier threshold
324 mouseMove(root, 1, root.height / 2);
324 mouseMove(root, 1, root.height / 2);
325− launcher.pushEdge(EdgeBarrierSettings.pushThreshold * 1.1);
325+ launcher.pushEdge(EdgeBarrierSettings.pushThreshold * .6);
326
326
327 var panel = findChild(launcher, "launcherPanel");
327 var panel = findChild(launcher, "launcherPanel");
328 verify(!!panel);
328 verify(!!panel);
@@ -824,7 +824,7 @@ Rectangle {
824
824
825 // Dragging a bit (> 1.5 gu)
825 // Dragging a bit (> 1.5 gu)
826 newMouseX -= units.gu(2)
826 newMouseX -= units.gu(2)
827− mouseFlick(launcher, currentMouseX, currentMouseY, newMouseX, newMouseY, false, false, 100)
827+ mouseFlick(launcher, currentMouseX, currentMouseY, newMouseX, newMouseY, false, false)
828 currentMouseX = newMouseX
828 currentMouseX = newMouseX
829
829
830 // Other items need to expand and become 0.6 opaque
830 // Other items need to expand and become 0.6 opaque
@@ -832,8 +832,8 @@ Rectangle {
832 tryCompare(item0, "itemOpacity", 0.6)
832 tryCompare(item0, "itemOpacity", 0.6)
833
833
834 // Dragging a bit more
834 // Dragging a bit more
835− newMouseY += initialItemHeight * 1.5
835+ newMouseY += initialItemHeight * 1.25
836− mouseFlick(launcher, currentMouseX, currentMouseY, newMouseX, newMouseY, false, false, 100)
836+ mouseFlick(launcher, currentMouseX, currentMouseY, newMouseX, newMouseY, false, false)
837 currentMouseY = newMouseY
837 currentMouseY = newMouseY
838
838
839 tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 1)
839 tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 1)
tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml+6−1
@@ -370,7 +370,8 @@ Item {
370 function test_create_switchMenu_data() {
370 function test_create_switchMenu_data() {
371 return [
371 return [
372 {label: "testLabel1", enabled: true, checked: false, icon: "file:///testIcon1" },
372 {label: "testLabel1", enabled: true, checked: false, icon: "file:///testIcon1" },
373− {label: "testLabel2", enabled: false, checked: true, icon: "file:///testIcon2" },
373+ {label: "testLabel2", enabled: false, checked: true, icon: "file:///testIcon2", subtitleAction: "action::subtitle1" },
374+ {label: "testLabel2", enabled: false, checked: true, icon: "file:///testIcon3", subtitleAction: "action::subtitle2" },
374 ];
375 ];
375 }
376 }
376
377
@@ -380,6 +381,9 @@ Item {
380 menuData.sensitive = data.enabled;
381 menuData.sensitive = data.enabled;
381 menuData.icon = data.icon;
382 menuData.icon = data.icon;
382 menuData.isToggled = data.checked;
383 menuData.isToggled = data.checked;
384+ menuData.ext = {
385+ 'xCanonicalSubtitleAction': data.subtitleAction,
386+ };
383
387
384 loader.data = menuData;
388 loader.data = menuData;
385 loader.sourceComponent = factory.load(menuData);
389 loader.sourceComponent = factory.load(menuData);
@@ -390,6 +394,7 @@ Item {
390 compare(loader.item.iconSource, data.icon, "Icon does not match data");
394 compare(loader.item.iconSource, data.icon, "Icon does not match data");
391 compare(loader.item.checked, data.checked, "Checked does not match data");
395 compare(loader.item.checked, data.checked, "Checked does not match data");
392 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
396 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
397+ compare(loader.item.subtitleAction.name, data.subtitleAction === undefined ? "" : data.subtitleAction, "Subtitle action does not match data");
393 }
398 }
394
399
395 function test_create_alarmMenu_data() {
400 function test_create_alarmMenu_data() {
tests/qmltests/Stage/tst_ApplicationWindow.qml+2−20
@@ -53,6 +53,8 @@ Rectangle {
53 surfaceOrientationAngle: 0
53 surfaceOrientationAngle: 0
54 interactive: true
54 interactive: true
55 focus: true
55 focus: true
56+ requestedWidth: width
57+ requestedHeight: height
56 Component.onDestruction: {
58 Component.onDestruction: {
57 applicationWindowLoader.itemDestroyed = true;
59 applicationWindowLoader.itemDestroyed = true;
58 }
60 }
@@ -150,26 +152,6 @@ Rectangle {
150 }
152 }
151 }
153 }
152 }
154 }
153−
154− Button {
155− anchors { left: parent.left; right: parent.right }
156− activeFocusOnPress: false
157− text: "Rotate device \u27F3"
158− onClicked: {
159− var orientation = applicationWindowLoader.item.orientation
160− if (orientation == Qt.PortraitOrientation) {
161− orientation = Qt.LandscapeOrientation;
162− } else if (orientation == Qt.LandscapeOrientation) {
163− orientation = Qt.InvertedPortraitOrientation;
164− } else if (orientation == Qt.InvertedPortraitOrientation) {
165− orientation = Qt.InvertedLandscapeOrientation;
166− } else {
167− orientation = Qt.PortraitOrientation;
168− }
169− applicationWindowLoader.item.orientation = orientation;
170− }
171− }
172−
173 }
155 }
174 }
156 }
175
157
tests/qmltests/Stage/tst_DesktopStage.qml+71
@@ -790,6 +790,77 @@ Item {
790 tryCompare(dialerAppDelegate, "state", "maximized");
790 tryCompare(dialerAppDelegate, "state", "maximized");
791 }
791 }
792
792
793+ function test_saveRestoreSize() {
794+ var originalWindowCount = topSurfaceList.count;
795+ var appDelegate = startApplication("dialer-app");
796+ compare(topSurfaceList.count, originalWindowCount + 1);
797+
798+ var initialWindowX = appDelegate.windowedX;
799+ var initialWindowY = appDelegate.windowedY;
800+ var initialWindowWidth = appDelegate.width
801+ var initialWindowHeight = appDelegate.height
802+
803+ var resizeDelta = units.gu(5)
804+ var startDragX = initialWindowX + initialWindowWidth + 1
805+ var startDragY = initialWindowY + initialWindowHeight + 1
806+ mouseFlick(root, startDragX, startDragY, startDragX + resizeDelta, startDragY + resizeDelta, true, true, units.gu(.5), 10);
807+
808+ tryCompare(appDelegate, "width", initialWindowWidth + resizeDelta);
809+ tryCompare(appDelegate, "height", initialWindowHeight + resizeDelta);
810+
811+ // Close the window and restart the application
812+ var closeButton = findChild(appDelegate, "closeWindowButton");
813+ appDelegate = null;
814+ verify(closeButton);
815+ mouseClick(closeButton);
816+ tryCompare(topSurfaceList, "count", originalWindowCount);
817+ wait(100); // plus some spare room
818+ appDelegate = startApplication("dialer-app");
819+
820+ // Make sure its size is again the same as before
821+ tryCompare(appDelegate, "width", initialWindowWidth + resizeDelta);
822+ tryCompare(appDelegate, "height", initialWindowHeight + resizeDelta);
823+ }
824+
825+ function test_saveRestoreMaximized() {
826+ var originalWindowCount = topSurfaceList.count;
827+ var appDelegate = startApplication("dialer-app");
828+ compare(topSurfaceList.count, originalWindowCount + 1);
829+
830+ var initialWindowX = appDelegate.windowedX;
831+ var initialWindowY = appDelegate.windowedY;
832+
833+ var moveDelta = units.gu(5);
834+
835+ appDelegate.windowedX = initialWindowX + moveDelta
836+ appDelegate.windowedY = initialWindowY + moveDelta
837+
838+ // Now change the state to maximized. The window should not keep updating the stored values
839+ maximizeAppDelegate(appDelegate);
840+
841+ // Close the window and restart the application
842+ var closeButton = findChild(appDelegate, "closeWindowButton");
843+ appDelegate = null;
844+ verify(closeButton);
845+ mouseClick(closeButton);
846+ closeButton = null;
847+ tryCompare(topSurfaceList, "count", originalWindowCount);
848+ wait(100); // plus some spare room
849+ appDelegate = startApplication("dialer-app");
850+
851+ // Make sure it's again where we left it in normal state before destroying
852+ tryCompare(appDelegate, "windowedX", initialWindowX + moveDelta)
853+ tryCompare(appDelegate, "windowedY", initialWindowY + moveDelta)
854+
855+ // Make sure maximize() has been called after restoring
856+ tryCompare(appDelegate, "state", "maximized")
857+
858+ // clean up
859+ // click on restore button (same one as maximize)
860+ var maximizeButton = findChild(appDelegate, "maximizeWindowButton");
861+ mouseClick(maximizeButton);
862+ }
863+
793 function test_grabbingCursorOnDecorationPress() {
864 function test_grabbingCursorOnDecorationPress() {
794 var appDelegate = startApplication("dialer-app");
865 var appDelegate = startApplication("dialer-app");
795 verify(appDelegate);
866 verify(appDelegate);
tests/qmltests/Stage/tst_PhoneStage.qml−26
@@ -334,32 +334,6 @@ Item {
334 tryCompare(firstApp, "focused", true);
334 tryCompare(firstApp, "focused", true);
335 }
335 }
336
336
337− function test_leftEdge_data() {
338− return [
339− { tag: "normal", inSpread: false, leftEdgeDragWidth: units.gu(20), shouldMoveApp: true },
340− { tag: "inSpread", inSpread: true, leftEdgeDragWidth: units.gu(5), shouldMoveApp: false }
341− ]
342− }
343−
344− function test_leftEdge(data) {
345− addApps(2);
346−
347− if (data.inSpread) {
348− performEdgeSwipeToShowAppSpread();
349− }
350−
351− var focusedDelegate = findChild(stage, "appDelegate_" + topLevelSurfaceList.idAt(0));
352− var currentX = focusedDelegate.x;
353−
354− stage.leftEdgeDragProgress = data.leftEdgeDragWidth;
355−
356− tryCompare(focusedDelegate, "x", data.shouldMoveApp ? data.leftEdgeDragWidth : currentX);
357−
358− stage.leftEdgeDragProgress = 0;
359−
360− tryCompare(focusedDelegate, "x", currentX);
361− }
362−
363 function test_focusedAppIsTheOnlyRunningApp() {
337 function test_focusedAppIsTheOnlyRunningApp() {
364 addApps(2);
338 addApps(2);
365
339
tests/qmltests/Stage/tst_WindowResizeArea.qml+1−66
@@ -88,22 +88,11 @@ Item {
88
88
89 WindowResizeArea {
89 WindowResizeArea {
90 id: windowResizeArea
90 id: windowResizeArea
91+ anchors.fill: parent
91 target: fakeWindow
92 target: fakeWindow
92 borderThickness: units.gu(2)
93 borderThickness: units.gu(2)
93 minWidth: units.gu(15)
94 minWidth: units.gu(15)
94 minHeight: units.gu(10)
95 minHeight: units.gu(10)
95− defaultWidth: units.gu(20)
96− defaultHeight: units.gu(20)
97− windowId: "test-window-id"
98− screenWidth: root.width
99− screenHeight: root.height
100−
101− Component.onCompleted: {
102− loadWindowState();
103− }
104− Component.onDestruction: {
105− saveWindowState();
106− }
107 }
96 }
108
97
109 Rectangle {
98 Rectangle {
@@ -312,30 +301,6 @@ Item {
312 compare(fakeWindow.requestedY, Math.min(initialWindowY + data.dy, initialWindowY + maxMoveY));
301 compare(fakeWindow.requestedY, Math.min(initialWindowY + data.dy, initialWindowY + maxMoveY));
313 }
302 }
314
303
315− function test_saveRestoreSize() {
316− var initialWindowX = fakeWindow.windowedX;
317− var initialWindowY = fakeWindow.windowedY;
318− var initialWindowWidth = fakeWindow.width
319− var initialWindowHeight = fakeWindow.height
320−
321− var resizeDelta = units.gu(5)
322− var startDragX = initialWindowX + initialWindowWidth + 1
323− var startDragY = initialWindowY + initialWindowHeight + 1
324− mouseFlick(root, startDragX, startDragY, startDragX + resizeDelta, startDragY + resizeDelta, true, true, units.gu(.5), 10);
325−
326− tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + resizeDelta, fakeWindow.resizeAreaMinWidth));
327− tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + resizeDelta, fakeWindow.resizeAreaMinHeight));
328−
329− // This will destroy the window and recreate it
330− windowLoader.active = false;
331− waitForRendering(root);
332− windowLoader.active = true;
333−
334− // Make sure its size is again the same as before
335− tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + resizeDelta, fakeWindow.resizeAreaMinWidth));
336− tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + resizeDelta, fakeWindow.resizeAreaMinHeight));
337− }
338−
339 // This tests if dragging smaller than minSize and then larger again, will keep the edge sticking
304 // This tests if dragging smaller than minSize and then larger again, will keep the edge sticking
340 // to the mouse, instead of immediately making the window grow again when switching direction
305 // to the mouse, instead of immediately making the window grow again when switching direction
341 function test_resizeSmallerAndLarger_data() {
306 function test_resizeSmallerAndLarger_data() {
@@ -361,36 +326,6 @@ Item {
361 tryCompare(fakeWindow, "height", initialWindowHeight);
326 tryCompare(fakeWindow, "height", initialWindowHeight);
362 }
327 }
363
328
364− function test_saveRestoreMaximized() {
365− var initialWindowX = fakeWindow.windowedX;
366− var initialWindowY = fakeWindow.windowedY;
367−
368− var moveDelta = units.gu(5);
369−
370− fakeWindow.windowedX = initialWindowX + moveDelta
371− fakeWindow.windowedY = initialWindowY + moveDelta
372−
373− // Now change the state to maximized. The window should not keep updating the stored values
374− fakeWindow.maximize()
375− fakeWindow.windowedX = 31415 // 0 is too risky to pass the test even when broken
376− fakeWindow.windowedY = 31415
377−
378− // This will destroy the window and recreate it
379− windowLoader.active = false;
380− waitForRendering(root);
381− windowLoader.active = true;
382−
383− // Make sure it's again where we left it in normal state before destroying
384− tryCompare(fakeWindow, "x", initialWindowX + moveDelta)
385− tryCompare(fakeWindow, "y", initialWindowX + moveDelta)
386−
387− // Make sure maximize() has been called after restoring
388− tryCompare(fakeWindow, "state", "maximized")
389−
390− // clean up
391− fakeWindow.restoreFromMaximized()
392− }
393−
394 function test_restoreMovesIntoBounds_data() {
329 function test_restoreMovesIntoBounds_data() {
395 return [
330 return [
396 {tag: "left off", x: -units.gu(5), y: units.gu(5), w: units.gu(10), h: units.gu(10)},
331 {tag: "left off", x: -units.gu(5), y: units.gu(5), w: units.gu(10), h: units.gu(10)},
tests/qmltests/tst_DisabledScreenNotice.qml+23−9
@@ -32,6 +32,12 @@ Item {
32 anchors.fill: parent
32 anchors.fill: parent
33 anchors.rightMargin: units.gu(40)
33 anchors.rightMargin: units.gu(40)
34
34
35+ // Mock some things here
36+ property int internalGu: units.gu(1)
37+ property var applicationArguments: QtObject {
38+ property string deviceName: "mako"
39+ }
40+
35 screen: QtObject {
41 screen: QtObject {
36 property int orientation: {
42 property int orientation: {
37 switch (orientationSelector.selectedIndex) {
43 switch (orientationSelector.selectedIndex) {
@@ -52,8 +58,9 @@ Item {
52 }
58 }
53
59
54 Column {
60 Column {
55− anchors.fill: parent
61+ anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
56− anchors.leftMargin: units.gu(40)
62+ width: units.gu(40)
63+ spacing: units.gu(1)
57
64
58 ItemSelector {
65 ItemSelector {
59 id: orientationSelector
66 id: orientationSelector
@@ -71,6 +78,20 @@ Item {
71 Layout.fillWidth: true
78 Layout.fillWidth: true
72 }
79 }
73 }
80 }
81+ Button {
82+ text: "Reset first run settings"
83+ onClicked: {
84+ var settings = testCase.findInvisibleChild(touchScreenPad, "virtualTouchPadSettings");
85+ settings.touchpadTutorialHasRun = false;
86+ }
87+ }
88+ Button {
89+ text: "Run tutorial now"
90+ onClicked: {
91+ var touchPad = testCase.findChild(touchScreenPad, "virtualTouchPad")
92+ touchPad.runTutorial();
93+ }
94+ }
74 }
95 }
75
96
76 UnityTestCase {
97 UnityTestCase {
@@ -78,13 +99,6 @@ Item {
78 name: "DisabledScreenNotice"
99 name: "DisabledScreenNotice"
79 when: windowShown
100 when: windowShown
80
101
81− function test_mouseAreaHidesOnFirstTap() {
82− var noticeArea = findChild(touchScreenPad, "infoNoticeArea")
83− compare(noticeArea.visible, true)
84− tap(root)
85− tryCompare(noticeArea, "visible", false)
86− }
87−
88 function test_rotation_data() {
102 function test_rotation_data() {
89 return [
103 return [
90 {tag: "portrait", selectedOrientation: 0, expectedAngle: 0},
104 {tag: "portrait", selectedOrientation: 0, expectedAngle: 0},
tests/qmltests/tst_OrientedShell.qml+1−13
@@ -999,7 +999,7 @@ Rectangle {
999 var rotationStates = findInvisibleChild(orientedShell, "rotationStates");
999 var rotationStates = findInvisibleChild(orientedShell, "rotationStates");
1000 waitUntilTransitionsEnd(rotationStates);
1000 waitUntilTransitionsEnd(rotationStates);
1001
1001
1002− performLeftEdgeSwipeToSwitchToDash();
1002+ ApplicationManager.requestFocusApplication("unity8-dash");
1003
1003
1004 // Should be back to portrait
1004 // Should be back to portrait
1005 tryCompare(shell, "transformRotationAngle", 0);
1005 tryCompare(shell, "transformRotationAngle", 0);
@@ -1391,18 +1391,6 @@ Rectangle {
1391 waitUntilTransitionsEnd(rotationStates);
1391 waitUntilTransitionsEnd(rotationStates);
1392 }
1392 }
1393
1393
1394− function performLeftEdgeSwipeToSwitchToDash() {
1395− var swipeLength = shell.width * 0.7;
1396−
1397− var touchStartX = 1;
1398− var touchStartY = shell.height / 2;
1399− touchFlick(shell,
1400− touchStartX, touchStartY,
1401− touchStartX + swipeLength, touchStartY);
1402−
1403− tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
1404− }
1405−
1406 function performEdgeSwipeToSwitchToPreviousApp() {
1394 function performEdgeSwipeToSwitchToPreviousApp() {
1407 // swipe just enough to ensure an app switch action.
1395 // swipe just enough to ensure an app switch action.
1408 // If we swipe too much we will trigger the spread mode
1396 // If we swipe too much we will trigger the spread mode
tests/qmltests/tst_Shell.qml+6−150
@@ -605,38 +605,6 @@ Rectangle {
605 compare(ApplicationManager.get(0).appId, "unity8-dash");
605 compare(ApplicationManager.get(0).appId, "unity8-dash");
606 }
606 }
607
607
608− function test_phoneLeftEdgeDrag_data() {
609− return [
610− {tag: "without launcher",
611− revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
612− launcherHides: true, greeterShown: false},
613−
614− {tag: "with launcher",
615− revealLauncher: true, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
616− launcherHides: true, greeterShown: false},
617−
618− {tag: "small swipe",
619− revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app",
620− launcherHides: false, greeterShown: false},
621−
622− {tag: "long swipe",
623− revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
624− launcherHides: true, greeterShown: false},
625−
626− {tag: "small swipe with greeter",
627− revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app",
628− launcherHides: false, greeterShown: true},
629−
630− {tag: "long swipe with greeter",
631− revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
632− launcherHides: true, greeterShown: true},
633−
634− {tag: "swipe over dash",
635− revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "unity8-dash",
636− launcherHides: false, greeterShown: false},
637− ];
638− }
639−
640 function test_snapDecisionDismissalReturnsFocus() {
608 function test_snapDecisionDismissalReturnsFocus() {
641 loadShell("phone");
609 loadShell("phone");
642 swipeAwayGreeter();
610 swipeAwayGreeter();
@@ -701,80 +669,6 @@ Rectangle {
701 mockNotificationsModel.append(n)
669 mockNotificationsModel.append(n)
702 }
670 }
703
671
704− function test_phoneLeftEdgeDrag(data) {
705− loadShell("phone");
706− swipeAwayGreeter();
707− dragLauncherIntoView();
708− var appSurfaceId = topLevelSurfaceList.nextId;
709− tapOnAppIconInLauncher();
710− waitUntilAppWindowIsFullyLoaded(appSurfaceId);
711−
712− var greeter = findChild(shell, "greeter");
713− if (data.greeterShown) {
714− showGreeter();
715− }
716−
717− if (data.revealLauncher) {
718− dragLauncherIntoView();
719− }
720−
721− swipeFromLeftEdge(data.swipeLength);
722− if (data.appHides) {
723− waitUntilDashIsFocused();
724− tryCompare(greeter, "shown", false);
725− } else {
726− tryCompare(greeter, "fullyShown", data.greeterShown);
727− }
728−
729− var launcher = findChild(shell, "launcherPanel");
730− tryCompare(launcher, "x", data.launcherHides ? -launcher.width : 0)
731−
732− //FIXME: This check fails. Don't understand the rationale behind it.
733− /*
734− // Make sure the helper for sliding out the launcher wasn't touched. We want to fade it out here.
735− var animateTimer = findInvisibleChild(shell, "animateTimer");
736− compare(animateTimer.nextState, "visible");
737− */
738− }
739−
740− function test_tabletLeftEdgeDrag_data() {
741− return [
742− {tag: "without password", user: "no-password", loggedIn: true},
743− {tag: "with password", user: "has-password", loggedIn: false},
744− ]
745− }
746−
747− function test_tabletLeftEdgeDrag(data) {
748− setLightDMMockMode("full");
749− loadShell("tablet");
750−
751− selectUser(data.user)
752−
753− swipeFromLeftEdge(shell.width * 0.75)
754− wait(500) // to give time to handle dash() signal from Launcher
755− confirmLoggedIn(data.loggedIn)
756− }
757−
758− function test_longLeftEdgeSwipeTakesToAppsAndResetSearchString() {
759− loadShell("phone");
760− swipeAwayGreeter();
761− dragLauncherIntoView();
762− dashCommunicatorSpy.clear();
763−
764− var appSurfaceId = topLevelSurfaceList.nextId;
765− tapOnAppIconInLauncher();
766− waitUntilAppWindowIsFullyLoaded(appSurfaceId);
767−
768− tryCompareFunction(function() { return ApplicationManager.focusedApplicationId !== "unity8-dash"; }, true);
769−
770− //Long left swipe
771− swipeFromLeftEdge(units.gu(30));
772−
773− tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
774−
775− compare(dashCommunicatorSpy.count, 1);
776− }
777−
778 function test_ClickUbuntuIconInLauncherTakesToAppsAndResetSearchString() {
672 function test_ClickUbuntuIconInLauncherTakesToAppsAndResetSearchString() {
779 loadShell("phone");
673 loadShell("phone");
780 swipeAwayGreeter();
674 swipeAwayGreeter();
@@ -913,39 +807,6 @@ Rectangle {
913 LightDM.Users.mockMode = mode;
807 LightDM.Users.mockMode = mode;
914 }
808 }
915
809
916− /*
917− Regression test for bug https://bugs.launchpad.net/touch-preview-images/+bug/1193419
918−
919− When the user minimizes an application (left-edge swipe) he should always end up in the
920− "Applications" scope view.
921−
922− Steps:
923− - reveal launcher and launch an app that covers the dash
924− - perform long left edge swipe to go minimize the app and go back to the dash.
925− - verify the setCurrentScope() D-Bus call to the dash has been called for the correct scope id.
926− */
927− function test_minimizingAppTakesToDashApps() {
928− loadShell("phone");
929− swipeAwayGreeter();
930− dragLauncherIntoView();
931−
932− // Launch an app from the launcher
933− var appSurfaceId = topLevelSurfaceList.nextId;
934− tapOnAppIconInLauncher();
935− waitUntilAppWindowIsFullyLoaded(appSurfaceId);
936−
937− verify(ApplicationManager.focusedApplicationId !== "unity8-dash")
938−
939− dashCommunicatorSpy.clear();
940− // Minimize the application we just launched
941− swipeFromLeftEdge(shell.width * 0.75);
942−
943− tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
944−
945− compare(dashCommunicatorSpy.count, 1);
946− compare(dashCommunicatorSpy.signalArguments[0][0], 0);
947− }
948−
949 function test_showInputMethod() {
810 function test_showInputMethod() {
950 loadShell("phone");
811 loadShell("phone");
951 swipeAwayGreeter();
812 swipeAwayGreeter();
@@ -1166,7 +1027,7 @@ Rectangle {
1166
1027
1167 // Place the mouse against the window/screen edge and push beyond the barrier threshold
1028 // Place the mouse against the window/screen edge and push beyond the barrier threshold
1168 mouseMove(shell, 0, shell.height / 2);
1029 mouseMove(shell, 0, shell.height / 2);
1169− launcher.pushEdge(EdgeBarrierSettings.pushThreshold * 1.1);
1030+ launcher.pushEdge(EdgeBarrierSettings.pushThreshold * .8);
1170
1031
1171 var panel = findChild(launcher, "launcherPanel");
1032 var panel = findChild(launcher, "launcherPanel");
1172 verify(panel);
1033 verify(panel);
@@ -1186,9 +1047,7 @@ Rectangle {
1186 waitUntilAppWindowIsFullyLoaded(appSurfaceId);
1047 waitUntilAppWindowIsFullyLoaded(appSurfaceId);
1187
1048
1188 // Minimize the application we just launched
1049 // Minimize the application we just launched
1189− swipeFromLeftEdge(shell.width * 0.75);
1050+ swipeFromLeftEdge(units.gu(15));
1190−
1191− waitUntilDashIsFocused();
1192
1051
1193 showGreeter();
1052 showGreeter();
1194
1053
@@ -1998,14 +1857,11 @@ Rectangle {
1998 var appRepeater = findChild(shell, "appRepeater");
1857 var appRepeater = findChild(shell, "appRepeater");
1999 var appDelegate = appRepeater.itemAt(0);
1858 var appDelegate = appRepeater.itemAt(0);
2000
1859
2001− var resizeArea = findChild(appDelegate, "windowResizeArea");
2002− var priv = findInvisibleChild(resizeArea, "priv");
2003−
2004 // Make sure windows are at 0,0 or greater and they have a size that's > 0
1860 // Make sure windows are at 0,0 or greater and they have a size that's > 0
2005− compare(priv.normalX >= 0, true)
1861+ compare(appDelegate.normalX >= 0, true)
2006− compare(priv.normalY >= 0, true)
1862+ compare(appDelegate.normalY >= 0, true)
2007− compare(priv.normalWidth > 0, true)
1863+ compare(appDelegate.normalWidth > 0, true)
2008− compare(priv.normalHeight > 0, true)
1864+ compare(appDelegate.normalHeight > 0, true)
2009 }
1865 }
2010
1866
2011 // bug http://pad.lv/1431566
1867 // bug http://pad.lv/1431566
tests/qmltests/tst_ShellWithPin.qml−66
@@ -547,71 +547,5 @@ Item {
547 var lockscreen = findChild(shell, "lockscreen");
547 var lockscreen = findChild(shell, "lockscreen");
548 verify(lockscreen.shown);
548 verify(lockscreen.shown);
549 }
549 }
550−
551− /*
552− Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1393447
553−
554− Do a left edge drag that is long enough to start displacing the greeter
555− but short engough so that the greeter comes back into place once the
556− finger is lifted.
557−
558− In this situation the launcher should remaing fully shown and hide itself
559− only after its idle timeout is triggered.
560− */
561− function test_shortLeftEdgeSwipeMakesLauncherStayVisible() {
562− showGreeter();
563−
564− var launcher = testCase.findChild(shell, "launcher")
565− var launcherPanel = testCase.findChild(launcher, "launcherPanel");
566−
567− var toX = shell.width * 0.45;
568− touchFlick(shell,
569− 1 /* fromX */, shell.height * 0.5 /* fromY */,
570− toX /* toX */, shell.height * 0.5 /* toY */,
571− true /* beginTouch */, false /* endTouch */,
572− 50, 100);
573−
574− // Launcher must be fully shown by now
575− tryCompare(launcherPanel, "x", 0);
576−
577− // Greeter should be displaced
578− var coverPage = findChild(shell, "coverPage");
579− tryCompareFunction(function() { return coverPage.mapToItem(shell, 0, 0).x > shell.width*0.2; }, true);
580−
581− touchRelease(shell, toX, shell.height * 0.5);
582−
583− // Upon release the greeter should have slid back into full view
584− tryCompareFunction(function() { return coverPage.mapToItem(shell, 0, 0).x === 0; }, true);
585−
586− // And the launcher should stay fully shown
587− for (var i = 0; i < 10; ++i) {
588− wait(50);
589− compare(launcherPanel.x, 0);
590− }
591− }
592−
593− function test_longLeftEdgeDrags() {
594− var coverPage = findChild(shell, "coverPage");
595− var lockscreen = findChild(shell, "lockscreen");
596− var launcher = findChild(shell, "launcherPanel");
597− var galleryApp = ApplicationManager.startApplication("gallery-app");
598− tryCompare(shell, "mainApp", galleryApp);
599−
600− // Show greeter
601− showGreeter();
602−
603− // Swipe cover page away
604− touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);
605− tryCompare(launcher, "x", -launcher.width);
606− tryCompare(coverPage, "showProgress", 0);
607− compare(lockscreen.shown, true);
608− tryCompare(shell, "mainApp", galleryApp);
609−
610− // Now attempt a swipe on lockscreen
611− touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);
612− tryCompare(launcher, "x", 0);
613− compare(lockscreen.shown, true);
614− tryCompare(shell, "mainApp", galleryApp);
615− }
616 }
550 }
617}
551}
tests/uqmlscene/CMakeLists.txt+5−1
@@ -9,10 +9,14 @@ pkg_check_modules(XCB REQUIRED xcb)
9include_directories(
9include_directories(
10 ${CMAKE_CURRENT_BINARY_DIR}
10 ${CMAKE_CURRENT_BINARY_DIR}
11 ${CMAKE_SOURCE_DIR}/src
11 ${CMAKE_SOURCE_DIR}/src
12− ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
13 ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures
12 ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures
14)
13)
15
14
15+include_directories(
16+ SYSTEM
17+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
18+)
19+
16if (NOT "${XCB_INCLUDE_DIRS}" STREQUAL "")
20if (NOT "${XCB_INCLUDE_DIRS}" STREQUAL "")
17 set_target_properties(uqmlscene PROPERTIES INCLUDE_DIRECTORIES ${XCB_INCLUDE_DIRS})
21 set_target_properties(uqmlscene PROPERTIES INCLUDE_DIRECTORIES ${XCB_INCLUDE_DIRS})
18endif()
22endif()
tests/utils/modules/Unity/Test/UnityTestCase.qml+4−4
@@ -163,9 +163,9 @@ TestCase {
163 if (i === iterations - 1) {
163 if (i === iterations - 1) {
164 // Avoid any rounding errors by making the last move be at precisely
164 // Avoid any rounding errors by making the last move be at precisely
165 // the point specified
165 // the point specified
166− mouseMove(item, toX, toY, iterations / speed)
166+ mouseMove(item, toX, toY, timeStep)
167 } else {
167 } else {
168− mouseMove(item, x + (i + 1) * diffX, y + (i + 1) * diffY, iterations / speed)
168+ mouseMove(item, x + (i + 1) * diffX, y + (i + 1) * diffY, timeStep)
169 }
169 }
170 }
170 }
171 if (releaseMouse) {
171 if (releaseMouse) {
@@ -346,12 +346,12 @@ TestCase {
346 if (i === iterations - 1) {
346 if (i === iterations - 1) {
347 // Avoid any rounding errors by making the last move be at precisely
347 // Avoid any rounding errors by making the last move be at precisely
348 // the point specified
348 // the point specified
349− wait(iterations / speed)
349+ wait(timeStep)
350 var event = touchEvent(item)
350 var event = touchEvent(item)
351 event.move(0 /* touchId */, rootTo.x, rootTo.y)
351 event.move(0 /* touchId */, rootTo.x, rootTo.y)
352 event.commit()
352 event.commit()
353 } else {
353 } else {
354− wait(iterations / speed)
354+ wait(timeStep)
355 var event = touchEvent(item)
355 var event = touchEvent(item)
356 event.move(0 /* touchId */, rootFrom.x + (i + 1) * diffX, rootFrom.y + (i + 1) * diffY)
356 event.move(0 /* touchId */, rootFrom.x + (i + 1) * diffX, rootFrom.y + (i + 1) * diffY)
357 event.commit()
357 event.commit()