on xenial help sign in
introduce GlobalShortcut component
6657af0Lukáš Tinkl
plugins/CMakeLists.txt+1
@@ -12,6 +12,7 @@ macro(add_unity8_plugin PLUGIN VERSION PATH)
12endmacro()
12endmacro()
13
13
14add_subdirectory(AccountsService)
14add_subdirectory(AccountsService)
15+add_subdirectory(GlobalShortcut)
15add_subdirectory(Greeter)
16add_subdirectory(Greeter)
16add_subdirectory(LightDM)
17add_subdirectory(LightDM)
17add_subdirectory(Lights)
18add_subdirectory(Lights)
plugins/GlobalShortcut/CMakeLists.txtnew file+10
@@ -0,0 +1,10 @@
1+set(globalshortcutplugin_SRCS
2+ globalshortcut.cpp
3+ globalshortcutregistry.cpp
4+ plugin.cpp)
5+
6+add_library(GlobalShortcut-qml SHARED ${globalshortcutplugin_SRCS})
7+
8+qt5_use_modules(GlobalShortcut-qml Quick Gui)
9+
10+add_unity8_plugin(GlobalShortcut 1.0 GlobalShortcut TARGETS GlobalShortcut-qml)
plugins/GlobalShortcut/globalshortcut.cppnew file+80
@@ -0,0 +1,80 @@
1+/*
2+ * Copyright (C) 2015 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 "globalshortcut.h"
18+#include "globalshortcutregistry.h"
19+
20+#include <QDebug>
21+#include <QQuickItem>
22+
23+Q_GLOBAL_STATIC(GlobalShortcutRegistry, registry)
24+
25+GlobalShortcut::GlobalShortcut(QQuickItem *parent)
26+ : QQuickItem(parent)
27+{
28+}
29+
30+QVariant GlobalShortcut::shortcut() const
31+{
32+ return m_shortcut;
33+}
34+
35+void GlobalShortcut::setShortcut(const QVariant &shortcut)
36+{
37+ if (m_shortcut == shortcut)
38+ return;
39+
40+ m_shortcut = shortcut;
41+ registry->addShortcut(shortcut, this);
42+ Q_EMIT shortcutChanged(shortcut);
43+}
44+
45+bool GlobalShortcut::isActive() const
46+{
47+ return m_active;
48+}
49+
50+void GlobalShortcut::setActive(bool active)
51+{
52+ if (m_active == active)
53+ return;
54+
55+ m_active = active;
56+ Q_EMIT activeChanged(active);
57+}
58+
59+void GlobalShortcut::componentComplete()
60+{
61+ connect(this, &QQuickItem::windowChanged, this, &GlobalShortcut::setupFilterOnWindow);
62+}
63+
64+void GlobalShortcut::keyPressEvent(QKeyEvent * event)
65+{
66+ Q_UNUSED(event)
67+ if (m_active) {
68+ Q_EMIT triggered(m_shortcut.toString());
69+ }
70+}
71+
72+void GlobalShortcut::setupFilterOnWindow(QQuickWindow *window)
73+{
74+ if (!window) {
75+ qWarning() << Q_FUNC_INFO << "Failed to setup filter on window";
76+ return;
77+ }
78+
79+ registry->setupFilterOnWindow((qulonglong) window->winId());
80+}
plugins/GlobalShortcut/globalshortcut.hnew file+76
@@ -0,0 +1,76 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef GLOBALSHORTCUT_H
18+#define GLOBALSHORTCUT_H
19+
20+#include <QVariant>
21+#include <QQuickWindow>
22+#include <QQuickItem>
23+
24+/**
25+ * @brief The GlobalShortcut class
26+ *
27+ * QML component for registering a shortcut; the shortcut itself can be
28+ * specified either as a string ("Ctrl+Alt+L") or as an enum value
29+ * (Qt.ControlModifier|Qt.AltModifier|Qt.Key_L).
30+ *
31+ * When the shortcut is detected, the signal triggered() gets emitted.
32+ */
33+class GlobalShortcut: public QQuickItem
34+{
35+ Q_OBJECT
36+ /**
37+ * The shortcut itself
38+ */
39+ Q_PROPERTY(QVariant shortcut READ shortcut WRITE setShortcut NOTIFY shortcutChanged)
40+ /**
41+ * Whether the shortcut is active (true by default)
42+ */
43+ Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged)
44+
45+public:
46+ GlobalShortcut(QQuickItem *parent = nullptr);
47+ ~GlobalShortcut() = default;
48+
49+ QVariant shortcut() const;
50+ void setShortcut(const QVariant &shortcut);
51+
52+ bool isActive() const;
53+ void setActive(bool active);
54+
55+protected:
56+ void componentComplete() override;
57+ void keyPressEvent(QKeyEvent * event) override;
58+
59+Q_SIGNALS:
60+ void shortcutChanged(const QVariant &shortcut);
61+ /**
62+ * Emitted when a global keypress of @p shortcut is detected
63+ */
64+ void triggered(const QString &shortcut);
65+ void activeChanged(bool active);
66+
67+private Q_SLOTS:
68+ void setupFilterOnWindow(QQuickWindow* window);
69+
70+private:
71+ QVariant m_shortcut;
72+ bool m_active = true;
73+};
74+
75+
76+#endif // GLOBALSHORTCUT_H
plugins/GlobalShortcut/globalshortcutregistry.cppnew file+95
@@ -0,0 +1,95 @@
1+/*
2+ * Copyright (C) 2015 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+#include <QGuiApplication>
19+#include <QKeyEvent>
20+#include <QKeySequence>
21+
22+#include "globalshortcutregistry.h"
23+
24+static qulonglong s_windowId = 0;
25+
26+GlobalShortcutRegistry::GlobalShortcutRegistry(QObject *parent)
27+ : QObject(parent)
28+{
29+}
30+
31+GlobalShortcutList GlobalShortcutRegistry::shortcuts() const
32+{
33+ return m_shortcuts;
34+}
35+
36+bool GlobalShortcutRegistry::hasShortcut(const QVariant &seq) const
37+{
38+ return m_shortcuts.keys().contains(seq);
39+}
40+
41+void GlobalShortcutRegistry::addShortcut(const QVariant &seq, GlobalShortcut *sc)
42+{
43+ if (!m_shortcuts.keys().contains(seq)) { // create a new entry
44+ m_shortcuts.insert(seq, {sc});
45+ } else { // append to an existing one
46+ auto shortcuts = m_shortcuts[seq];
47+ shortcuts.append(sc);
48+ m_shortcuts.insert(seq, shortcuts);
49+ }
50+}
51+
52+bool GlobalShortcutRegistry::eventFilter(QObject *obj, QEvent *event)
53+{
54+ Q_ASSERT(m_filteredWindow);
55+ Q_ASSERT(obj == static_cast<QObject*>(m_filteredWindow.data()));
56+
57+ if (event->type() == QEvent::KeyPress) {
58+ QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
59+
60+ QKeySequence seq = QKeySequence(keyEvent->key() + keyEvent->modifiers());
61+ if (m_shortcuts.keys().contains(seq)) {
62+ const auto shortcuts = m_shortcuts.value(seq);
63+ Q_FOREACH(auto shortcut, shortcuts) {
64+ qApp->sendEvent(shortcut, keyEvent);
65+ }
66+ event->accept();
67+ }
68+
69+ return event->isAccepted();
70+ }
71+
72+ return QObject::eventFilter(obj, event);
73+}
74+
75+void GlobalShortcutRegistry::setupFilterOnWindow(qulonglong wid)
76+{
77+ if (wid == s_windowId) {
78+ return;
79+ }
80+
81+ if (m_filteredWindow) {
82+ m_filteredWindow->removeEventFilter(this);
83+ m_filteredWindow.clear();
84+ s_windowId = 0;
85+ }
86+
87+ for(QWindow *window: qApp->allWindows()) {
88+ if (window && window->winId() == wid) {
89+ m_filteredWindow = window;
90+ window->installEventFilter(this);
91+ s_windowId = wid;
92+ break;
93+ }
94+ }
95+}
plugins/GlobalShortcut/globalshortcutregistry.hnew file+67
@@ -0,0 +1,67 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef GLOBALSHORTCUT_REGISTRY_H
18+#define GLOBALSHORTCUT_REGISTRY_H
19+
20+#include <QObject>
21+#include <QVariantList>
22+#include <QPointer>
23+#include <QWindow>
24+
25+#include "globalshortcut.h"
26+
27+typedef QMap<QVariant, QVector<GlobalShortcut *>> GlobalShortcutList;
28+
29+/**
30+ * @brief The GlobalShortcutRegistry class
31+ *
32+ * Serves as a central point for shortcut registration.
33+ */
34+class Q_DECL_EXPORT GlobalShortcutRegistry: public QObject
35+{
36+ Q_OBJECT
37+public:
38+ GlobalShortcutRegistry(QObject * parent = nullptr);
39+ ~GlobalShortcutRegistry() = default;
40+
41+ /**
42+ * @return the list of shortcuts currently registered
43+ */
44+ GlobalShortcutList shortcuts() const;
45+ /**
46+ * @return whether shortcut @p seq is currently registered
47+ */
48+ bool hasShortcut(const QVariant &seq) const;
49+ /**
50+ * Adds a shortcut @p seq to the registry
51+ */
52+ void addShortcut(const QVariant &seq, GlobalShortcut * sc);
53+
54+ /**
55+ * Sets up key events filtering on window @p wid
56+ */
57+ void setupFilterOnWindow(qulonglong wid);
58+
59+protected:
60+ bool eventFilter(QObject *obj, QEvent *event) override;
61+
62+private:
63+ GlobalShortcutList m_shortcuts;
64+ QPointer<QWindow> m_filteredWindow = nullptr;
65+};
66+
67+#endif
plugins/GlobalShortcut/plugin.cppnew file+27
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright (C) 2015 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 "plugin.h"
18+#include "globalshortcut.h"
19+
20+#include <QtQml>
21+
22+void GlobalShortcutPlugin::registerTypes(const char *uri)
23+{
24+ Q_ASSERT(uri == QLatin1String("GlobalShortcut"));
25+
26+ qmlRegisterType<GlobalShortcut>(uri, 1, 0, "GlobalShortcut");
27+}
plugins/GlobalShortcut/plugin.hnew file+32
@@ -0,0 +1,32 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef GLOBALSHORTCUTPLUGIN_H
18+#define GLOBALSHORTCUTPLUGIN_H
19+
20+#include <QQmlExtensionPlugin>
21+
22+class GlobalShortcutPlugin: public QQmlExtensionPlugin
23+{
24+ Q_OBJECT
25+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
26+
27+public:
28+ void registerTypes(const char *uri) override;
29+};
30+
31+
32+#endif // GLOBALSHORTCUTPLUGIN_H
plugins/GlobalShortcut/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module GlobalShortcut
2+plugin GlobalShortcut-qml
plugins/Unity/Session/Session.qmltypes+34
@@ -14,28 +14,62 @@ Module {
14 isCreatable: false
14 isCreatable: false
15 isSingleton: true
15 isSingleton: true
16 exportMetaObjectRevisions: [0]
16 exportMetaObjectRevisions: [0]
17+ Signal {
18+ name: "LogoutRequested"
19+ Parameter { name: "have_inhibitors"; type: "bool" }
20+ }
17 Signal {
21 Signal {
18 name: "logoutRequested"
22 name: "logoutRequested"
19 Parameter { name: "have_inhibitors"; type: "bool" }
23 Parameter { name: "have_inhibitors"; type: "bool" }
20 }
24 }
25+ Signal {
26+ name: "RebootRequested"
27+ Parameter { name: "have_inhibitors"; type: "bool" }
28+ }
21 Signal {
29 Signal {
22 name: "rebootRequested"
30 name: "rebootRequested"
23 Parameter { name: "have_inhibitors"; type: "bool" }
31 Parameter { name: "have_inhibitors"; type: "bool" }
24 }
32 }
33+ Signal {
34+ name: "ShutdownRequested"
35+ Parameter { name: "have_inhibitors"; type: "bool" }
36+ }
25 Signal {
37 Signal {
26 name: "shutdownRequested"
38 name: "shutdownRequested"
27 Parameter { name: "have_inhibitors"; type: "bool" }
39 Parameter { name: "have_inhibitors"; type: "bool" }
28 }
40 }
41+ Signal { name: "LogoutReady" }
29 Signal { name: "logoutReady" }
42 Signal { name: "logoutReady" }
43+ Signal { name: "LockRequested" }
44+ Signal { name: "lockRequested" }
45+ Signal { name: "Locked" }
46+ Signal { name: "Unlocked" }
30 Method { name: "Logout" }
47 Method { name: "Logout" }
31 Method { name: "Reboot" }
48 Method { name: "Reboot" }
32 Method { name: "Shutdown" }
49 Method { name: "Shutdown" }
50+ Method { name: "Suspend" }
51+ Method { name: "Hibernate" }
52+ Method { name: "HybridSleep" }
33 Method { name: "RequestLogout" }
53 Method { name: "RequestLogout" }
34 Method { name: "RequestReboot" }
54 Method { name: "RequestReboot" }
35 Method { name: "RequestShutdown" }
55 Method { name: "RequestShutdown" }
56+ Method { name: "EndSession" }
57+ Method { name: "CanHibernate"; type: "bool" }
58+ Method { name: "CanSuspend"; type: "bool" }
59+ Method { name: "CanHybridSleep"; type: "bool" }
60+ Method { name: "CanReboot"; type: "bool" }
61+ Method { name: "CanShutdown"; type: "bool" }
62+ Method { name: "CanLock"; type: "bool" }
63+ Method { name: "UserName"; type: "string" }
64+ Method { name: "RealName"; type: "string" }
65+ Method { name: "HostName"; type: "string" }
66+ Method { name: "PromptLock" }
67+ Method { name: "Lock" }
68+ Method { name: "IsLocked"; type: "bool" }
36 Method { name: "logout" }
69 Method { name: "logout" }
37 Method { name: "reboot" }
70 Method { name: "reboot" }
38 Method { name: "shutdown" }
71 Method { name: "shutdown" }
72+ Method { name: "endSession" }
39 }
73 }
40 Component {
74 Component {
41 name: "OrientationLock"
75 name: "OrientationLock"
plugins/Unity/Session/dbusunitysessionservice.cpp+38−2
@@ -28,6 +28,7 @@
28#include <QDBusReply>
28#include <QDBusReply>
29#include <QElapsedTimer>
29#include <QElapsedTimer>
30#include <QDateTime>
30#include <QDateTime>
31+#include <QDBusUnixFileDescriptor>
31
32
32#define LOGIN1_SERVICE QStringLiteral("org.freedesktop.login1")
33#define LOGIN1_SERVICE QStringLiteral("org.freedesktop.login1")
33#define LOGIN1_PATH QStringLiteral("/org/freedesktop/login1")
34#define LOGIN1_PATH QStringLiteral("/org/freedesktop/login1")
@@ -44,6 +45,7 @@ public:
44 QString logindSessionPath;
45 QString logindSessionPath;
45 bool isSessionActive = true;
46 bool isSessionActive = true;
46 QElapsedTimer screensaverActiveTimer;
47 QElapsedTimer screensaverActiveTimer;
48+ QDBusUnixFileDescriptor m_systemdInhibitFd;
47
49
48 DBusUnitySessionServicePrivate(): QObject() {
50 DBusUnitySessionServicePrivate(): QObject() {
49 init();
51 init();
@@ -66,11 +68,40 @@ public:
66 // start watching the Active property
68 // start watching the Active property
67 QDBusConnection::systemBus().connect(LOGIN1_SERVICE, logindSessionPath, "org.freedesktop.DBus.Properties", "PropertiesChanged",
69 QDBusConnection::systemBus().connect(LOGIN1_SERVICE, logindSessionPath, "org.freedesktop.DBus.Properties", "PropertiesChanged",
68 this, SLOT(onPropertiesChanged(QString,QVariantMap,QStringList)));
70 this, SLOT(onPropertiesChanged(QString,QVariantMap,QStringList)));
71+
72+ setupSystemdInhibition();
73+
74+ // re-enable the inhibition upon resume from sleep
75+ QDBusConnection::systemBus().connect(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, "PrepareForSleep",
76+ this, SLOT(onResuming(bool)));
69 } else {
77 } else {
70 qWarning() << "Failed to get logind session path" << reply.error().message();
78 qWarning() << "Failed to get logind session path" << reply.error().message();
71 }
79 }
72 }
80 }
73
81
82+ void setupSystemdInhibition()
83+ {
84+ if (m_systemdInhibitFd.isValid())
85+ return;
86+
87+ // inhibit systemd handling of power/sleep/lid buttons
88+ // http://www.freedesktop.org/wiki/Software/systemd/inhibit
89+
90+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, "Inhibit");
91+ msg << "handle-power-key:handle-suspend-key:handle-hibernate-key:handle-lid-switch"; // what
92+ msg << "Unity"; // who
93+ msg << "Unity8 handles power events"; // why
94+ msg << "block"; // mode
95+ QDBusReply<QDBusUnixFileDescriptor> desc = QDBusConnection::systemBus().asyncCall(msg);
96+
97+ if (desc.isValid()) {
98+ m_systemdInhibitFd = desc.value();
99+ } else {
100+ qWarning() << "failed to inhibit systemd powersave handling";
101+ }
102+ }
103+
104+
74 bool checkLogin1Call(const QString &method) const
105 bool checkLogin1Call(const QString &method) const
75 {
106 {
76 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, method);
107 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, method);
@@ -105,7 +136,6 @@ public:
105 QDBusReply<QVariant> reply = QDBusConnection::systemBus().asyncCall(msg);
136 QDBusReply<QVariant> reply = QDBusConnection::systemBus().asyncCall(msg);
106 if (reply.isValid()) {
137 if (reply.isValid()) {
107 isSessionActive = reply.value().toBool();
138 isSessionActive = reply.value().toBool();
108− qDebug() << "Session" << logindSessionPath << "is active:" << isSessionActive;
109 } else {
139 } else {
110 qWarning() << "Failed to get Active property" << reply.error().message();
140 qWarning() << "Failed to get Active property" << reply.error().message();
111 }
141 }
@@ -173,6 +203,13 @@ private Q_SLOTS:
173 }
203 }
174 }
204 }
175
205
206+ void onResuming(bool active)
207+ {
208+ if (!active) {
209+ setupSystemdInhibition();
210+ }
211+ }
212+
176Q_SIGNALS:
213Q_SIGNALS:
177 void screensaverActiveChanged(bool active);
214 void screensaverActiveChanged(bool active);
178};
215};
@@ -287,7 +324,6 @@ void DBusUnitySessionService::Lock()
287 sessionPath,
324 sessionPath,
288 "org.freedesktop.DisplayManager.Session",
325 "org.freedesktop.DisplayManager.Session",
289 "Lock");
326 "Lock");
290− qDebug() << "Locking session" << msg.path();
291 QDBusReply<void> reply = QDBusConnection::systemBus().asyncCall(msg);
327 QDBusReply<void> reply = QDBusConnection::systemBus().asyncCall(msg);
292 if (!reply.isValid()) {
328 if (!reply.isValid()) {
293 qWarning() << "Lock call failed" << reply.error().message();
329 qWarning() << "Lock call failed" << reply.error().message();
plugins/Utils/Utils.qmltypes+1−1
@@ -145,7 +145,7 @@ Module {
145 type: "int"
145 type: "int"
146 Parameter { name: "row"; type: "int" }
146 Parameter { name: "row"; type: "int" }
147 }
147 }
148− }
148+ }
149 Component {
149 Component {
150 name: "WindowKeysFilter"
150 name: "WindowKeysFilter"
151 defaultProperty: "data"
151 defaultProperty: "data"
plugins/Utils/easingcurve.h+2−2
@@ -28,8 +28,8 @@
28 * This class exposes the QEasingCurve C++ API to QML.
28 * This class exposes the QEasingCurve C++ API to QML.
29 * This is useful for user interactive animations. While the QML Animation types
29 * This is useful for user interactive animations. While the QML Animation types
30 * all require a "from", "to" and "duration", this one is based on "period" and
30 * all require a "from", "to" and "duration", this one is based on "period" and
31− * "progress". So you can control the position of the aimation by changing the
31+ * "progress". So you can control the position of the animation by changing the
32− * progress, also going back and forward in the animation. Depending on the type
32+ * progress, also going back and forward in the aimation. Depending on the type
33 * of the easing curve, value will return the transformed progress.
33 * of the easing curve, value will return the transformed progress.
34 */
34 */
35
35
po/unity8.pot+83−73
@@ -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: 2015-07-17 10:10+0200\n"
11+"POT-Creation-Date: 2015-08-06 14:55+0200\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"
@@ -22,12 +22,16 @@ msgstr ""
22msgid "Password: "
22msgid "Password: "
23msgstr ""
23msgstr ""
24
24
25−#: plugins/Unity/Launcher/launcheritem.cpp:45
25+#: plugins/Unity/Launcher/launcheritem.cpp:47
26−#: plugins/Unity/Launcher/launcheritem.cpp:100
26+#: plugins/Unity/Launcher/launcheritem.cpp:106
27msgid "Pin shortcut"
27msgid "Pin shortcut"
28msgstr ""
28msgstr ""
29
29
30−#: plugins/Unity/Launcher/launcheritem.cpp:100
30+#: plugins/Unity/Launcher/launcheritem.cpp:52
31+msgid "Quit"
32+msgstr ""
33+
34+#: plugins/Unity/Launcher/launcheritem.cpp:106
31msgid "Unpin shortcut"
35msgid "Unpin shortcut"
32msgstr ""
36msgstr ""
33
37
@@ -130,83 +134,83 @@ msgid_plural "Please wait %1 minutes and then try again…"
130msgstr[0] ""
134msgstr[0] ""
131msgstr[1] ""
135msgstr[1] ""
132
136
133−#: qml/Components/Dialogs.qml:69
137+#: qml/Components/Dialogs.qml:112
134msgctxt "Title: Lock/Log out dialog"
138msgctxt "Title: Lock/Log out dialog"
135msgid "Log out"
139msgid "Log out"
136msgstr ""
140msgstr ""
137
141
138−#: qml/Components/Dialogs.qml:70
142+#: qml/Components/Dialogs.qml:113
139msgid "Are you sure you want to log out?"
143msgid "Are you sure you want to log out?"
140msgstr ""
144msgstr ""
141
145
142−#: qml/Components/Dialogs.qml:72
146+#: qml/Components/Dialogs.qml:115
143msgctxt "Button: Lock the system"
147msgctxt "Button: Lock the system"
144msgid "Lock"
148msgid "Lock"
145msgstr ""
149msgstr ""
146
150
147−#: qml/Components/Dialogs.qml:79
151+#: qml/Components/Dialogs.qml:122
148msgctxt "Button: Log out from the system"
152msgctxt "Button: Log out from the system"
149msgid "Log Out"
153msgid "Log Out"
150msgstr ""
154msgstr ""
151
155
152−#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
156+#: qml/Components/Dialogs.qml:129 qml/Components/Dialogs.qml:162
153−#: qml/Components/Dialogs.qml:173
157+#: qml/Components/Dialogs.qml:221
154msgid "Cancel"
158msgid "Cancel"
155msgstr ""
159msgstr ""
156
160
157−#: qml/Components/Dialogs.qml:98
161+#: qml/Components/Dialogs.qml:141
158msgctxt "Title: Reboot/Shut down dialog"
162msgctxt "Title: Reboot/Shut down dialog"
159msgid "Shut down"
163msgid "Shut down"
160msgstr ""
164msgstr ""
161
165
162−#: qml/Components/Dialogs.qml:99
166+#: qml/Components/Dialogs.qml:142
163msgid "Are you sure you want to shut down?"
167msgid "Are you sure you want to shut down?"
164msgstr ""
168msgstr ""
165
169
166−#: qml/Components/Dialogs.qml:101
170+#: qml/Components/Dialogs.qml:144
167msgctxt "Button: Reboot the system"
171msgctxt "Button: Reboot the system"
168msgid "Reboot"
172msgid "Reboot"
169msgstr ""
173msgstr ""
170
174
171−#: qml/Components/Dialogs.qml:109
175+#: qml/Components/Dialogs.qml:153
172msgctxt "Button: Shut down the system"
176msgctxt "Button: Shut down the system"
173msgid "Shut down"
177msgid "Shut down"
174msgstr ""
178msgstr ""
175
179
176−#: qml/Components/Dialogs.qml:129
180+#: qml/Components/Dialogs.qml:175
177msgctxt "Title: Reboot dialog"
181msgctxt "Title: Reboot dialog"
178msgid "Reboot"
182msgid "Reboot"
179msgstr ""
183msgstr ""
180
184
181−#: qml/Components/Dialogs.qml:130
185+#: qml/Components/Dialogs.qml:176
182msgid "Are you sure you want to reboot?"
186msgid "Are you sure you want to reboot?"
183msgstr ""
187msgstr ""
184
188
185−#: qml/Components/Dialogs.qml:132
189+#: qml/Components/Dialogs.qml:178
186msgid "No"
190msgid "No"
187msgstr ""
191msgstr ""
188
192
189−#: qml/Components/Dialogs.qml:138
193+#: qml/Components/Dialogs.qml:185
190msgid "Yes"
194msgid "Yes"
191msgstr ""
195msgstr ""
192
196
193−#: qml/Components/Dialogs.qml:152
197+#: qml/Components/Dialogs.qml:200
194msgctxt "Title: Power off/Restart dialog"
198msgctxt "Title: Power off/Restart dialog"
195msgid "Power"
199msgid "Power"
196msgstr ""
200msgstr ""
197
201
198−#: qml/Components/Dialogs.qml:153
202+#: qml/Components/Dialogs.qml:201
199msgid ""
203msgid ""
200"Are you sure you would like\n"
204"Are you sure you would like\n"
201"to power off?"
205"to power off?"
202msgstr ""
206msgstr ""
203
207
204−#: qml/Components/Dialogs.qml:155
208+#: qml/Components/Dialogs.qml:203
205msgctxt "Button: Power off the system"
209msgctxt "Button: Power off the system"
206msgid "Power off"
210msgid "Power off"
207msgstr ""
211msgstr ""
208
212
209−#: qml/Components/Dialogs.qml:164
213+#: qml/Components/Dialogs.qml:212
210msgctxt "Button: Restart the system"
214msgctxt "Button: Restart the system"
211msgid "Restart"
215msgid "Restart"
212msgstr ""
216msgstr ""
@@ -223,15 +227,15 @@ msgstr ""
223msgid "OK"
227msgid "OK"
224msgstr ""
228msgstr ""
225
229
226−#: qml/Dash/GenericScopeView.qml:539 qml/Dash/GenericScopeView.qml:697
230+#: qml/Dash/GenericScopeView.qml:544 qml/Dash/GenericScopeView.qml:702
227msgid "See less"
231msgid "See less"
228msgstr ""
232msgstr ""
229
233
230−#: qml/Dash/GenericScopeView.qml:539
234+#: qml/Dash/GenericScopeView.qml:544
231msgid "See all"
235msgid "See all"
232msgstr ""
236msgstr ""
233
237
234−#: qml/Dash/GenericScopeView.qml:601
238+#: qml/Dash/GenericScopeView.qml:606
235msgctxt "Label: Hint for dash search line edit"
239msgctxt "Label: Hint for dash search line edit"
236msgid "Search"
240msgid "Search"
237msgstr ""
241msgstr ""
@@ -267,6 +271,12 @@ msgstr ""
267msgid "Less..."
271msgid "Less..."
268msgstr ""
272msgstr ""
269
273
274+#: qml/Dash/Previews/PreviewCommentInput.qml:61
275+#: qml/Dash/Previews/PreviewRatingInput.qml:169
276+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:205
277+msgid "Send"
278+msgstr ""
279+
270#: qml/Dash/Previews/PreviewRatingInput.qml:84
280#: qml/Dash/Previews/PreviewRatingInput.qml:84
271msgid "Rate this"
281msgid "Rate this"
272msgstr ""
282msgstr ""
@@ -275,10 +285,6 @@ msgstr ""
275msgid "Add a review"
285msgid "Add a review"
276msgstr ""
286msgstr ""
277
287
278−#: qml/Dash/Previews/PreviewRatingInput.qml:169
279−msgid "Send"
280−msgstr ""
281−
282#: qml/Dash/PullToRefreshScopeStyle.qml:55
288#: qml/Dash/PullToRefreshScopeStyle.qml:55
283msgid "Pull to refresh…"
289msgid "Pull to refresh…"
284msgstr ""
290msgstr ""
@@ -382,62 +388,62 @@ msgstr ""
382msgid "Conference"
388msgid "Conference"
383msgstr ""
389msgstr ""
384
390
385−#: qml/Panel/Indicators/MenuItemFactory.qml:639
391+#: qml/Panel/Indicators/MenuItemFactory.qml:674
386msgid "Nothing is playing"
392msgid "Nothing is playing"
387msgstr ""
393msgstr ""
388
394
389−#: qml/Panel/Indicators/MenuItemFactory.qml:768
395+#: qml/Panel/Indicators/MenuItemFactory.qml:803
390#, qt-format
396#, qt-format
391msgid "%1 hour"
397msgid "%1 hour"
392msgid_plural "%1 hours"
398msgid_plural "%1 hours"
393msgstr[0] ""
399msgstr[0] ""
394msgstr[1] ""
400msgstr[1] ""
395
401
396−#: qml/Panel/Indicators/MenuItemFactory.qml:772
402+#: qml/Panel/Indicators/MenuItemFactory.qml:807
397#, qt-format
403#, qt-format
398msgid "%1 minute"
404msgid "%1 minute"
399msgid_plural "%1 minutes"
405msgid_plural "%1 minutes"
400msgstr[0] ""
406msgstr[0] ""
401msgstr[1] ""
407msgstr[1] ""
402
408
403−#: qml/Panel/Indicators/MenuItemFactory.qml:777
409+#: qml/Panel/Indicators/MenuItemFactory.qml:812
404#, qt-format
410#, qt-format
405msgid "%1 second"
411msgid "%1 second"
406msgid_plural "%1 seconds"
412msgid_plural "%1 seconds"
407msgstr[0] ""
413msgstr[0] ""
408msgstr[1] ""
414msgstr[1] ""
409
415
410−#: qml/Panel/Indicators/MenuItemFactory.qml:780
416+#: qml/Panel/Indicators/MenuItemFactory.qml:815
411msgid "0 seconds"
417msgid "0 seconds"
412msgstr ""
418msgstr ""
413
419
414#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
420#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
415−#: qml/Panel/Indicators/MenuItemFactory.qml:782
421+#: qml/Panel/Indicators/MenuItemFactory.qml:817
416#, qt-format
422#, qt-format
417msgid "%1 remaining"
423msgid "%1 remaining"
418msgstr ""
424msgstr ""
419
425
420−#: qml/Panel/Indicators/MenuItemFactory.qml:788
426+#: qml/Panel/Indicators/MenuItemFactory.qml:823
421msgid "In queue…"
427msgid "In queue…"
422msgstr ""
428msgstr ""
423
429
424−#: qml/Panel/Indicators/MenuItemFactory.qml:792
430+#: qml/Panel/Indicators/MenuItemFactory.qml:827
425msgid "Downloading"
431msgid "Downloading"
426msgstr ""
432msgstr ""
427
433
428−#: qml/Panel/Indicators/MenuItemFactory.qml:794
434+#: qml/Panel/Indicators/MenuItemFactory.qml:829
429msgid "Paused, tap to resume"
435msgid "Paused, tap to resume"
430msgstr ""
436msgstr ""
431
437
432−#: qml/Panel/Indicators/MenuItemFactory.qml:796
438+#: qml/Panel/Indicators/MenuItemFactory.qml:831
433msgid "Canceled"
439msgid "Canceled"
434msgstr ""
440msgstr ""
435
441
436−#: qml/Panel/Indicators/MenuItemFactory.qml:798
442+#: qml/Panel/Indicators/MenuItemFactory.qml:833
437msgid "Finished"
443msgid "Finished"
438msgstr ""
444msgstr ""
439
445
440−#: qml/Panel/Indicators/MenuItemFactory.qml:800
446+#: qml/Panel/Indicators/MenuItemFactory.qml:835
441msgid "Failed, tap to retry"
447msgid "Failed, tap to retry"
442msgstr ""
448msgstr ""
443
449
@@ -446,6 +452,10 @@ msgctxt "Label: Hint in message indicator line edit"
446msgid "Reply"
452msgid "Reply"
447msgstr ""
453msgstr ""
448
454
455+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:204
456+msgid "Call back"
457+msgstr ""
458+
449#: qml/Panel/Indicators/ModemInfoItem.qml:105
459#: qml/Panel/Indicators/ModemInfoItem.qml:105
450msgid "Unlock SIM"
460msgid "Unlock SIM"
451msgstr ""
461msgstr ""
@@ -516,6 +526,38 @@ msgctxt "Button: Go back one page in the Wizard"
516msgid "Back"
526msgid "Back"
517msgstr ""
527msgstr ""
518
528
529+#: qml/Wizard/Pages/here-terms.qml:27
530+msgid "Terms & Conditions"
531+msgstr ""
532+
533+#: qml/Wizard/Pages/passwd-confirm.qml:49
534+msgid "Confirm passphrase"
535+msgstr ""
536+
537+#: qml/Wizard/Pages/passwd-confirm.qml:50
538+msgid "Confirm passcode"
539+msgstr ""
540+
541+#: qml/Wizard/Pages/passwd-confirm.qml:53
542+#: qml/Wizard/Pages/passwd-confirm.qml:54
543+msgid "Please try again."
544+msgstr ""
545+
546+#: qml/Wizard/Pages/passwd-confirm.qml:82 qml/Wizard/Pages/passwd-set.qml:90
547+#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
548+#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
549+#: qml/Wizard/Pages/60-reporting.qml:50
550+msgid "Continue"
551+msgstr ""
552+
553+#: qml/Wizard/Pages/passwd-set.qml:61
554+msgid "Choose your passcode"
555+msgstr ""
556+
557+#: qml/Wizard/Pages/passwd-set.qml:68
558+msgid "Passphrase must be 4 characters long"
559+msgstr ""
560+
519#: qml/Wizard/Pages/10-welcome.qml:27
561#: qml/Wizard/Pages/10-welcome.qml:27
520msgid "Hi!"
562msgid "Hi!"
521msgstr ""
563msgstr ""
@@ -528,13 +570,6 @@ msgstr ""
528msgid "Let’s get started."
570msgid "Let’s get started."
529msgstr ""
571msgstr ""
530
572
531−#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
532−#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
533−#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
534−#: qml/Wizard/Pages/passwd-set.qml:90
535−msgid "Continue"
536−msgstr ""
537−
538#: qml/Wizard/Pages/20-sim.qml:25
573#: qml/Wizard/Pages/20-sim.qml:25
539msgid "Add a SIM card and restart your device"
574msgid "Add a SIM card and restart your device"
540msgstr ""
575msgstr ""
@@ -660,31 +695,6 @@ msgstr ""
660msgid "Finish"
695msgid "Finish"
661msgstr ""
696msgstr ""
662
697
663−#: qml/Wizard/Pages/here-terms.qml:27
664−msgid "Terms & Conditions"
665−msgstr ""
666−
667−#: qml/Wizard/Pages/passwd-confirm.qml:49
668−msgid "Confirm passphrase"
669−msgstr ""
670−
671−#: qml/Wizard/Pages/passwd-confirm.qml:50
672−msgid "Confirm passcode"
673−msgstr ""
674−
675−#: qml/Wizard/Pages/passwd-confirm.qml:53
676−#: qml/Wizard/Pages/passwd-confirm.qml:54
677−msgid "Please try again."
678−msgstr ""
679−
680−#: qml/Wizard/Pages/passwd-set.qml:61
681−msgid "Choose your passcode"
682−msgstr ""
683−
684−#: qml/Wizard/Pages/passwd-set.qml:68
685−msgid "Passphrase must be 4 characters long"
686−msgstr ""
687−
688#. Translators: This is the arrow for "Back" buttons
698#. Translators: This is the arrow for "Back" buttons
689#: qml/Wizard/StackButton.qml:39
699#: qml/Wizard/StackButton.qml:39
690#, qt-format
700#, qt-format
qml/Components/Dialogs.qml+61−6
@@ -18,8 +18,9 @@ import QtQuick 2.0
18
18
19import Unity.Application 0.1
19import Unity.Application 0.1
20import Unity.Session 0.1
20import Unity.Session 0.1
21−import Ubuntu.Components 1.1
21+import Ubuntu.Components 1.3
22import LightDM 0.1 as LightDM
22import LightDM 0.1 as LightDM
23+import GlobalShortcut 1.0
23
24
24Item {
25Item {
25 id: root
26 id: root
@@ -35,6 +36,7 @@ Item {
35 ApplicationManager.stopApplication(app.appId);
36 ApplicationManager.stopApplication(app.appId);
36 }
37 }
37 }
38 }
39+ property string usageScenario
38
40
39 signal powerOffClicked();
41 signal powerOffClicked();
40
42
@@ -42,19 +44,61 @@ Item {
42 d.showPowerDialog();
44 d.showPowerDialog();
43 }
45 }
44
46
47+ GlobalShortcut { // reboot/shutdown dialog
48+ shortcut: Qt.Key_PowerDown
49+ active: root.usageScenario === "desktop"
50+ onTriggered: root.unitySessionService.RequestShutdown()
51+ }
52+
53+ GlobalShortcut { // reboot/shutdown dialog
54+ shortcut: Qt.Key_PowerOff
55+ active: root.usageScenario === "desktop"
56+ onTriggered: root.unitySessionService.RequestShutdown()
57+ }
58+
59+ GlobalShortcut { // sleep
60+ shortcut: Qt.Key_Sleep
61+ onTriggered: root.unitySessionService.Suspend()
62+ }
63+
64+ GlobalShortcut { // hibernate
65+ shortcut: Qt.Key_Hibernate
66+ onTriggered: root.unitySessionService.Hibernate()
67+ }
68+
69+ GlobalShortcut { // logout/lock dialog
70+ shortcut: Qt.Key_LogOff
71+ onTriggered: root.unitySessionService.RequestLogout()
72+ }
73+
74+ GlobalShortcut { // logout/lock dialog
75+ shortcut: Qt.ControlModifier|Qt.AltModifier|Qt.Key_Delete
76+ onTriggered: root.unitySessionService.RequestLogout()
77+ }
78+
79+ GlobalShortcut { // lock screen
80+ shortcut: Qt.Key_ScreenSaver
81+ onTriggered: LightDM.Greeter.showGreeter()
82+ }
83+
84+ GlobalShortcut { // lock screen
85+ shortcut: Qt.ControlModifier|Qt.AltModifier|Qt.Key_L
86+ onTriggered: LightDM.Greeter.showGreeter()
87+ }
88+
45 QtObject {
89 QtObject {
46 id: d // private stuff
90 id: d // private stuff
47 objectName: "dialogsPrivate"
91 objectName: "dialogsPrivate"
48
92
49−
50 function showPowerDialog() {
93 function showPowerDialog() {
51 if (!dialogLoader.active) {
94 if (!dialogLoader.active) {
52 dialogLoader.sourceComponent = powerDialogComponent;
95 dialogLoader.sourceComponent = powerDialogComponent;
96+ dialogLoader.focus = true;
53 dialogLoader.active = true;
97 dialogLoader.active = true;
54− dialogLoader.item.forceActiveFocus();
55 }
98 }
56 }
99 }
57 }
100 }
101+
58 Loader {
102 Loader {
59 id: dialogLoader
103 id: dialogLoader
60 objectName: "dialogLoader"
104 objectName: "dialogLoader"
@@ -104,6 +148,7 @@ Item {
104 unitySessionService.reboot();
148 unitySessionService.reboot();
105 shutdownDialog.hide();
149 shutdownDialog.hide();
106 }
150 }
151+ color: UbuntuColors.lightGrey
107 }
152 }
108 Button {
153 Button {
109 text: i18n.ctr("Button: Shut down the system", "Shut down")
154 text: i18n.ctr("Button: Shut down the system", "Shut down")
@@ -112,12 +157,14 @@ Item {
112 unitySessionService.shutdown();
157 unitySessionService.shutdown();
113 shutdownDialog.hide();
158 shutdownDialog.hide();
114 }
159 }
160+ color: UbuntuColors.red
115 }
161 }
116 Button {
162 Button {
117 text: i18n.tr("Cancel")
163 text: i18n.tr("Cancel")
118 onClicked: {
164 onClicked: {
119 shutdownDialog.hide();
165 shutdownDialog.hide();
120 }
166 }
167+ color: UbuntuColors.lightGrey
121 }
168 }
122 }
169 }
123 }
170 }
@@ -133,6 +180,7 @@ Item {
133 onClicked: {
180 onClicked: {
134 rebootDialog.hide();
181 rebootDialog.hide();
135 }
182 }
183+ color: UbuntuColors.lightGrey
136 }
184 }
137 Button {
185 Button {
138 text: i18n.tr("Yes")
186 text: i18n.tr("Yes")
@@ -141,6 +189,7 @@ Item {
141 unitySessionService.reboot();
189 unitySessionService.reboot();
142 rebootDialog.hide();
190 rebootDialog.hide();
143 }
191 }
192+ color: UbuntuColors.red
144 }
193 }
145 }
194 }
146 }
195 }
@@ -167,14 +216,14 @@ Item {
167 unitySessionService.reboot();
216 unitySessionService.reboot();
168 powerDialog.hide();
217 powerDialog.hide();
169 }
218 }
170− color: UbuntuColors.green
219+ color: UbuntuColors.lightGrey
171 }
220 }
172 Button {
221 Button {
173 text: i18n.tr("Cancel")
222 text: i18n.tr("Cancel")
174 onClicked: {
223 onClicked: {
175 powerDialog.hide();
224 powerDialog.hide();
176 }
225 }
177− color: UbuntuColors.coolGrey
226+ color: UbuntuColors.lightGrey
178 }
227 }
179 }
228 }
180 }
229 }
@@ -186,6 +235,7 @@ Item {
186 // Display a dialog to ask the user to confirm.
235 // Display a dialog to ask the user to confirm.
187 if (!dialogLoader.active) {
236 if (!dialogLoader.active) {
188 dialogLoader.sourceComponent = logoutDialogComponent;
237 dialogLoader.sourceComponent = logoutDialogComponent;
238+ dialogLoader.focus = true;
189 dialogLoader.active = true;
239 dialogLoader.active = true;
190 }
240 }
191 }
241 }
@@ -194,6 +244,7 @@ Item {
194 // Display a dialog to ask the user to confirm.
244 // Display a dialog to ask the user to confirm.
195 if (!dialogLoader.active) {
245 if (!dialogLoader.active) {
196 dialogLoader.sourceComponent = shutdownDialogComponent;
246 dialogLoader.sourceComponent = shutdownDialogComponent;
247+ dialogLoader.focus = true;
197 dialogLoader.active = true;
248 dialogLoader.active = true;
198 }
249 }
199 }
250 }
@@ -201,7 +252,11 @@ Item {
201 onRebootRequested: {
252 onRebootRequested: {
202 // Display a dialog to ask the user to confirm.
253 // Display a dialog to ask the user to confirm.
203 if (!dialogLoader.active) {
254 if (!dialogLoader.active) {
204− dialogLoader.sourceComponent = rebootDialogComponent;
255+ // display a combined reboot/shutdown dialog, sadly the session indicator calls rather the "Reboot()" method
256+ // than shutdown when clicking on the "Shutdown..." menu item
257+ // FIXME: when/if session indicator is fixed, put the rebootDialogComponent here
258+ dialogLoader.sourceComponent = shutdownDialogComponent;
259+ dialogLoader.focus = true;
205 dialogLoader.active = true;
260 dialogLoader.active = true;
206 }
261 }
207 }
262 }
qml/Components/PhysicalKeysMapper.qml+7−8
@@ -30,7 +30,6 @@ import Powerd 0.1
30 * Power dialog
30 * Power dialog
31 * Volume Decreases/Increases
31 * Volume Decreases/Increases
32 * Screenshots
32 * Screenshots
33−
34*/
33*/
35
34
36Item {
35Item {
@@ -80,15 +79,15 @@ Item {
80 if (Powerd.status === Powerd.On) {
79 if (Powerd.status === Powerd.On) {
81 powerKeyLongPressTimer.restart();
80 powerKeyLongPressTimer.restart();
82 }
81 }
83− event.accepted = true;
84− } else if ((event.key == Qt.Key_MediaTogglePlayPause || event.key == Qt.Key_MediaPlay)
82+ } else if ((event.key == Qt.Key_MediaTogglePlayPause || event.key == Qt.Key_MediaPlay) && !event.isAutoRepeat) {
85− && !event.isAutoRepeat) {
86 event.accepted = callManager.handleMediaKey(false);
83 event.accepted = callManager.handleMediaKey(false);
87 } else if (event.key == Qt.Key_VolumeDown) {
84 } else if (event.key == Qt.Key_VolumeDown) {
88 if (event.isAutoRepeat && !d.ignoreVolumeEvents) root.volumeDownTriggered();
85 if (event.isAutoRepeat && !d.ignoreVolumeEvents) root.volumeDownTriggered();
89 else if (!event.isAutoRepeat) {
86 else if (!event.isAutoRepeat) {
90 if (d.volumeUpKeyPressed) {
87 if (d.volumeUpKeyPressed) {
88+ if (Powerd.status === Powerd.On) {
91− if (Powerd.status === Powerd.On) root.screenshotTriggered();
89+ root.screenshotTriggered();
90+ }
92 d.ignoreVolumeEvents = true;
91 d.ignoreVolumeEvents = true;
93 }
92 }
94 d.volumeDownKeyPressed = true;
93 d.volumeDownKeyPressed = true;
@@ -97,7 +96,9 @@ Item {
97 if (event.isAutoRepeat && !d.ignoreVolumeEvents) root.volumeUpTriggered();
96 if (event.isAutoRepeat && !d.ignoreVolumeEvents) root.volumeUpTriggered();
98 else if (!event.isAutoRepeat) {
97 else if (!event.isAutoRepeat) {
99 if (d.volumeDownKeyPressed) {
98 if (d.volumeDownKeyPressed) {
99+ if (Powerd.status === Powerd.On) {
100− if (Powerd.status === Powerd.On) root.screenshotTriggered();
100+ root.screenshotTriggered();
101+ }
101 d.ignoreVolumeEvents = true;
102 d.ignoreVolumeEvents = true;
102 }
103 }
103 d.volumeUpKeyPressed = true;
104 d.volumeUpKeyPressed = true;
@@ -109,8 +110,6 @@ Item {
109 d.altTabPressed = true;
110 d.altTabPressed = true;
110 event.accepted = true;
111 event.accepted = true;
111 }
112 }
112− } else if (event.key == Qt.Key_Print) {
113− root.screenshotTriggered();
114 }
113 }
115 }
114 }
116
115
qml/Components/ScreenGrabber.qml+7
@@ -17,6 +17,7 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import QtMultimedia 5.0
18import QtMultimedia 5.0
19import ScreenGrabber 0.1
19import ScreenGrabber 0.1
20+import GlobalShortcut 1.0
20
21
21Rectangle {
22Rectangle {
22 id: root
23 id: root
@@ -30,6 +31,12 @@ Rectangle {
30 objectName: "screenGrabber"
31 objectName: "screenGrabber"
31 }
32 }
32
33
34+ GlobalShortcut {
35+ id: screenshotShortcut
36+ shortcut: Qt.Key_Print
37+ onTriggered: capture()
38+ }
39+
33 Audio {
40 Audio {
34 id: shutterSound
41 id: shutterSound
35 audioRole: MediaPlayer.alert
42 audioRole: MediaPlayer.alert
qml/Components/ShellDialog.qml+7−1
@@ -33,5 +33,11 @@ Dialog {
33
33
34 onVisibleChanged: { if (!visible) { dialogLoader.active = false; } }
34 onVisibleChanged: { if (!visible) { dialogLoader.active = false; } }
35
35
36+ Keys.onEscapePressed: hide()
37+
38+ focus: true
39+
36− Component.onCompleted: show()
40+ Component.onCompleted: {
41+ show();
42+ }
37}
43}
qml/Components/VolumeControl.qml+20−6
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -14,8 +14,9 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.4
18−import QMenuModel 0.1
18+import QMenuModel 0.1 as QMenuModel
19+import GlobalShortcut 1.0
19
20
20Item {
21Item {
21 id: root
22 id: root
@@ -26,13 +27,20 @@ Item {
26 readonly property int stepUp: 1
27 readonly property int stepUp: 1
27 readonly property int stepDown: -1
28 readonly property int stepDown: -1
28
29
30+ GlobalShortcut {
31+ id: muteShortcut
32+ shortcut: Qt.Key_VolumeMute
33+ Keys.onPressed: toggleMute()
34+ }
35+
29− QDBusActionGroup {
36+ QMenuModel.QDBusActionGroup {
30 id: actionGroup
37 id: actionGroup
31− busType: 1
38+ busType: QMenuModel.DBus.SessionBus
32 busName: "com.canonical.indicator.sound"
39 busName: "com.canonical.indicator.sound"
33 objectPath: "/com/canonical/indicator/sound"
40 objectPath: "/com/canonical/indicator/sound"
34
41
35 property variant actionObject: action("volume")
42 property variant actionObject: action("volume")
43+ property variant muteActionObject: action("mute")
36 }
44 }
37
45
38 function volumeUp() {
46 function volumeUp() {
@@ -43,5 +51,11 @@ Item {
43 actionGroup.actionObject.activate(stepDown);
51 actionGroup.actionObject.activate(stepDown);
44 }
52 }
45
53
54+ function toggleMute() {
55+ actionGroup.muteActionObject.activate();
56+ }
57+
46− Component.onCompleted: actionGroup.start()
58+ Component.onCompleted: {
59+ actionGroup.start();
60+ }
47}
61}
qml/OrientedShell.qml+1−5
@@ -103,7 +103,7 @@ Rectangle {
103 // TODO: Choose the closest to the current one
103 // TODO: Choose the closest to the current one
104 if (supportedOrientations & Qt.PortraitOrientation) {
104 if (supportedOrientations & Qt.PortraitOrientation) {
105 return Screen.angleBetween(nativeOrientation, Qt.PortraitOrientation);
105 return Screen.angleBetween(nativeOrientation, Qt.PortraitOrientation);
106− } else if (supportedOrientations & Qt.LandcscapeOrientation) {
106+ } else if (supportedOrientations & Qt.LandscapeOrientation) {
107 return Screen.angleBetween(nativeOrientation, Qt.LandscapeOrientation);
107 return Screen.angleBetween(nativeOrientation, Qt.LandscapeOrientation);
108 } else if (supportedOrientations & Qt.InvertedPortraitOrientation) {
108 } else if (supportedOrientations & Qt.InvertedPortraitOrientation) {
109 return Screen.angleBetween(nativeOrientation, Qt.InvertedPortraitOrientation);
109 return Screen.angleBetween(nativeOrientation, Qt.InvertedPortraitOrientation);
@@ -120,19 +120,15 @@ Rectangle {
120 switch (angle) {
120 switch (angle) {
121 case 0:
121 case 0:
122 return nativeOrientation;
122 return nativeOrientation;
123− break;
124 case 90:
123 case 90:
125 return nativeOrientation === Qt.PortraitOrientation ? Qt.InvertedLandscapeOrientation
124 return nativeOrientation === Qt.PortraitOrientation ? Qt.InvertedLandscapeOrientation
126 : Qt.PortraitOrientation;
125 : Qt.PortraitOrientation;
127− break;
128 case 180:
126 case 180:
129 return nativeOrientation === Qt.PortraitOrientation ? Qt.InvertedPortraitOrientation
127 return nativeOrientation === Qt.PortraitOrientation ? Qt.InvertedPortraitOrientation
130 : Qt.InvertedLandscapeOrientation;
128 : Qt.InvertedLandscapeOrientation;
131− break;
132 case 270:
129 case 270:
133 return nativeOrientation === Qt.PortraitOrientation ? Qt.LandscapeOrientation
130 return nativeOrientation === Qt.PortraitOrientation ? Qt.LandscapeOrientation
134 : Qt.InvertedPortraitOrientation;
131 : Qt.InvertedPortraitOrientation;
135− break;
136 default:
132 default:
137 console.warn("angleToOrientation: Invalid orientation angle: " + angle);
133 console.warn("angleToOrientation: Invalid orientation angle: " + angle);
138 return primaryOrientation;
134 return primaryOrientation;
qml/Panel/Indicators/MessageMenuItemFactory.qml+2−2
@@ -201,8 +201,8 @@ Loader {
201 title: menuData && menuData.label || ""
201 title: menuData && menuData.label || ""
202 time: timeFormatter.timeString
202 time: timeFormatter.timeString
203 body: getExtendedProperty(extendedData, "xCanonicalText", "")
203 body: getExtendedProperty(extendedData, "xCanonicalText", "")
204− actionButtonText: getExtendedProperty(activateActionDescription, "label", "Call back")
204+ actionButtonText: getExtendedProperty(activateActionDescription, "label", i18n.tr("Call back"))
205− replyButtonText: getExtendedProperty(replyActionDescription, "label", "Send")
205+ replyButtonText: getExtendedProperty(replyActionDescription, "label", i18n.tr("Send"))
206 // icons
206 // icons
207 avatar: getExtendedProperty(extendedData, "icon", "image://theme/contact")
207 avatar: getExtendedProperty(extendedData, "icon", "image://theme/contact")
208 icon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "image://theme/missed-call")
208 icon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "image://theme/missed-call")
qml/Shell.qml+13−4
@@ -25,6 +25,7 @@ import Ubuntu.Gestures 0.1
25import Ubuntu.Telephony 0.1 as Telephony
25import Ubuntu.Telephony 0.1 as Telephony
26import Unity.Connectivity 0.1
26import Unity.Connectivity 0.1
27import Unity.Launcher 0.1
27import Unity.Launcher 0.1
28+import GlobalShortcut 1.0 // has to be before Utils, because of WindowKeysFilter
28import Utils 0.1
29import Utils 0.1
29import LightDM 0.1 as LightDM
30import LightDM 0.1 as LightDM
30import Powerd 0.1
31import Powerd 0.1
@@ -42,6 +43,7 @@ import Unity.Session 0.1
42import Unity.DashCommunicator 0.1
43import Unity.DashCommunicator 0.1
43import Unity.Indicators 0.1 as Indicators
44import Unity.Indicators 0.1 as Indicators
44
45
46+
45Item {
47Item {
46 id: shell
48 id: shell
47
49
@@ -146,7 +148,7 @@ Item {
146 // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
148 // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
147 Image {
149 Image {
148 id: gsImageTester
150 id: gsImageTester
149− source: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
151+ source: backgroundSettings.pictureUri && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
150 height: 0
152 height: 0
151 width: 0
153 width: 0
152 sourceSize.height: 0
154 sourceSize.height: 0
@@ -180,7 +182,7 @@ Item {
180 id: physicalKeysMapper
182 id: physicalKeysMapper
181 objectName: "physicalKeysMapper"
183 objectName: "physicalKeysMapper"
182
184
183− onPowerKeyLongPressed: dialogs.showPowerDialog()
185+ onPowerKeyLongPressed: dialogs.showPowerDialog();
184 onVolumeDownTriggered: volumeControl.volumeDown();
186 onVolumeDownTriggered: volumeControl.volumeDown();
185 onVolumeUpTriggered: volumeControl.volumeUp();
187 onVolumeUpTriggered: volumeControl.volumeUp();
186 onScreenshotTriggered: screenGrabber.capture();
188 onScreenshotTriggered: screenGrabber.capture();
@@ -191,7 +193,13 @@ Item {
191 z: dialogs.z + 10
193 z: dialogs.z + 10
192 }
194 }
193
195
196+ GlobalShortcut {
197+ // dummy shortcut to force creation of GlobalShortcutRegistry before WindowKeyFilter
198+ }
199+
194 WindowKeysFilter {
200 WindowKeysFilter {
201+ // this serves merely for forwarding the remaining key events
202+ // to the PhysicalKeysMapper, not as a shortcut itself
195 Keys.onPressed: physicalKeysMapper.onKeyPressed(event);
203 Keys.onPressed: physicalKeysMapper.onKeyPressed(event);
196 Keys.onReleased: physicalKeysMapper.onKeyReleased(event);
204 Keys.onReleased: physicalKeysMapper.onKeyReleased(event);
197 }
205 }
@@ -432,7 +440,7 @@ Item {
432 sourceComponent: shell.mode != "shell" ? integratedGreeter :
440 sourceComponent: shell.mode != "shell" ? integratedGreeter :
433 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
441 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
434 onLoaded: {
442 onLoaded: {
435− item.objectName = "greeter"
443+ item.objectName = "greeter"
436 }
444 }
437 }
445 }
438
446
@@ -679,6 +687,7 @@ Item {
679 objectName: "dialogs"
687 objectName: "dialogs"
680 anchors.fill: parent
688 anchors.fill: parent
681 z: overlay.z + 10
689 z: overlay.z + 10
690+ usageScenario: shell.usageScenario
682 onPowerOffClicked: {
691 onPowerOffClicked: {
683 shutdownFadeOutRectangle.enabled = true;
692 shutdownFadeOutRectangle.enabled = true;
684 shutdownFadeOutRectangle.visible = true;
693 shutdownFadeOutRectangle.visible = true;
@@ -705,7 +714,7 @@ Item {
705 to: 1.0
714 to: 1.0
706 onStopped: {
715 onStopped: {
707 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
716 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
708− DBusUnitySessionService.Shutdown();
717+ DBusUnitySessionService.shutdown();
709 }
718 }
710 }
719 }
711 }
720 }
tests/mocks/Utils/windowstatestorage.cpp−2
@@ -16,8 +16,6 @@
16
16
17#include "windowstatestorage.h"
17#include "windowstatestorage.h"
18
18
19−#include <QRect>
20−
21WindowStateStorage::WindowStateStorage(QObject *parent):
19WindowStateStorage::WindowStateStorage(QObject *parent):
22 QObject(parent)
20 QObject(parent)
23{
21{
tests/mocks/Utils/windowstatestorage.h+1
@@ -16,6 +16,7 @@
16
16
17#include <QObject>
17#include <QObject>
18#include <QVariantMap>
18#include <QVariantMap>
19+#include <QRect>
19
20
20class WindowStateStorage: public QObject
21class WindowStateStorage: public QObject
21{
22{
tests/plugins/CMakeLists.txt+1
@@ -1,4 +1,5 @@
1add_subdirectory(AccountsService)
1add_subdirectory(AccountsService)
2+add_subdirectory(GlobalShortcut)
2add_subdirectory(Greeter)
3add_subdirectory(Greeter)
3add_subdirectory(LightDM)
4add_subdirectory(LightDM)
4add_subdirectory(Dash)
5add_subdirectory(Dash)
tests/plugins/GlobalShortcut/CMakeLists.txtnew file+14
@@ -0,0 +1,14 @@
1+include_directories(
2+ ${CMAKE_SOURCE_DIR}/plugins/GlobalShortcut
3+ ${CMAKE_CURRENT_BINARY_DIR}
4+ )
5+
6+add_definitions(
7+ -DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}"
8+ )
9+add_definitions(-DBUILT_PLUGINS_DIR="${CMAKE_BINARY_DIR}/plugins")
10+
11+add_executable(GlobalShortcutTestExec GlobalShortcutTest.cpp ${CMAKE_SOURCE_DIR}/plugins/GlobalShortcut/globalshortcut.cpp)
12+qt5_use_modules(GlobalShortcutTestExec Test Quick)
13+target_link_libraries(GlobalShortcutTestExec ${Qt5Quick_LIBRARIES} GlobalShortcut-qml)
14+add_unity8_uitest(GlobalShortcut GlobalShortcutTestExec DEPENDS GlobalShortcut-qml)
tests/plugins/GlobalShortcut/GlobalShortcutTest.cppnew file+77
@@ -0,0 +1,77 @@
1+/*
2+ * Copyright (C) 2015 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 "globalshortcut.h"
18+
19+#include <QtTest>
20+#include <QtQuick/QQuickView>
21+#include <QtQuick/QQuickItem>
22+#include <QQmlEngine>
23+
24+class GlobalShortcutTest: public QObject
25+{
26+ Q_OBJECT
27+
28+private Q_SLOTS:
29+
30+ void initTestCase()
31+ {
32+ m_view = new QQuickView();
33+ m_view->engine()->addImportPath(BUILT_PLUGINS_DIR);
34+ m_view->setSource(QUrl::fromLocalFile(CURRENT_SOURCE_DIR "/shortcut.qml"));
35+ m_shortcut = dynamic_cast<GlobalShortcut*>(m_view->rootObject()->property("shortcut").value<QObject*>());
36+ QVERIFY(m_shortcut);
37+ m_inactiveShortcut = dynamic_cast<GlobalShortcut*>(m_view->rootObject()->property("inactiveShortcut").value<QObject*>());
38+ QVERIFY(m_inactiveShortcut);
39+ m_view->show();
40+ QTest::qWaitForWindowExposed(m_view);
41+ }
42+
43+ void cleanupTestCase()
44+ {
45+ m_view.clear();
46+ }
47+
48+ void testGlobalShortcut()
49+ {
50+ QSignalSpy shortcutSpy(m_shortcut, &GlobalShortcut::triggered);
51+ // test pressing "Mute Volume"
52+ QTest::keyClick(m_view, Qt::Key_VolumeMute);
53+ QTRY_COMPARE(shortcutSpy.count(), 1);
54+ const QVariantList args = shortcutSpy.takeFirst();
55+ // verify we got the signal back and a non-empty shortcut
56+ QCOMPARE(args.count(), 1);
57+ QVERIFY(!args.first().toString().isEmpty());
58+ }
59+
60+ void testInactiveGlobalShortcut()
61+ {
62+ QSignalSpy shortcutSpy(m_inactiveShortcut, &GlobalShortcut::triggered);
63+ // test pressing Ctrl+Alt+L
64+ QTest::keyClick(m_view, Qt::Key_L, Qt::ControlModifier|Qt::AltModifier);
65+ // verify we didn't get any signal back, shortcut is not active
66+ QTRY_COMPARE(shortcutSpy.count(), 0);
67+ }
68+
69+private:
70+ QPointer<QQuickView> m_view;
71+ GlobalShortcut *m_shortcut = nullptr;
72+ GlobalShortcut *m_inactiveShortcut = nullptr;
73+};
74+
75+QTEST_MAIN(GlobalShortcutTest)
76+
77+#include "GlobalShortcutTest.moc"
tests/plugins/GlobalShortcut/shortcut.qmlnew file+39
@@ -0,0 +1,39 @@
1+/*
2+ * Copyright 2015 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 GlobalShortcut 1.0
19+
20+Rectangle {
21+ property var shortcut: globalShortcut
22+ property var inactiveShortcut: inactiveGlobalShortcut
23+
24+ width: 100
25+ height: 100
26+
27+ GlobalShortcut {
28+ id: globalShortcut
29+ objectName: "globalShortcut"
30+ shortcut: Qt.Key_VolumeMute
31+ }
32+
33+ GlobalShortcut {
34+ id: inactiveGlobalShortcut
35+ objectName: "inactiveGlobalShortcut"
36+ shortcut: Qt.ControlModifier|Qt.AltModifier|Qt.Key_L
37+ active: false
38+ }
39+}
tests/qmltests/Components/tst_Dialogs.qml+13−8
@@ -32,6 +32,7 @@ Rectangle {
32 signal logoutRequested
32 signal logoutRequested
33 signal shutdownRequested
33 signal shutdownRequested
34 signal rebootRequested
34 signal rebootRequested
35+ signal logoutReady
35 function logout() {}
36 function logout() {}
36 function shutdown() {}
37 function shutdown() {}
37 function reboot() {}
38 function reboot() {}
@@ -79,15 +80,19 @@ Rectangle {
79 Column {
80 Column {
80 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
81 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
81 spacing: units.gu(1)
82 spacing: units.gu(1)
82− Button { text: "Power dialog"; onClicked: { testCase.test_showPowerDialog(); } }
83+ Button { text: "Power dialog"; onClicked: { testCase.test_showPowerDialog(); }
84+ activeFocusOnPress: false }
83− Button { text: "Logout Requested"; onClicked: { fakeUnitySession.logoutRequested(); } }
85+ Button { text: "Logout Requested"; onClicked: { fakeUnitySession.logoutRequested(); }
86+ activeFocusOnPress: false }
84− Button { text: "Shutdown Requested"; onClicked: { fakeUnitySession.shutdownRequested(); } }
87+ Button { text: "Shutdown Requested"; onClicked: { fakeUnitySession.shutdownRequested(); }
88+ activeFocusOnPress: false }
85− Button { text: "Reboot Requested"; onClicked: { fakeUnitySession.rebootRequested(); } }
89+ Button { text: "Reboot Requested"; onClicked: { fakeUnitySession.rebootRequested(); }
90+ activeFocusOnPress: false }
86 Label { text: "Rotation:" }
91 Label { text: "Rotation:" }
87− Button { text: "0"; onClicked: {fakeShell.rotation = 0;} }
92+ Button { text: "0"; onClicked: {fakeShell.rotation = 0;} activeFocusOnPress: false }
88− Button { text: "90"; onClicked: {fakeShell.rotation = 90;} }
93+ Button { text: "90"; onClicked: {fakeShell.rotation = 90;} activeFocusOnPress: false }
89− Button { text: "180"; onClicked: {fakeShell.rotation = 180;} }
94+ Button { text: "180"; onClicked: {fakeShell.rotation = 180;} activeFocusOnPress: false }
90− Button { text: "270"; onClicked: {fakeShell.rotation = 270;} }
95+ Button { text: "270"; onClicked: {fakeShell.rotation = 270;} activeFocusOnPress: false }
91 }
96 }
92 }
97 }
93
98
tests/qmltests/Components/tst_PhysicalKeysMapper.qml+1−44
@@ -37,18 +37,6 @@ TestCase {
37 signalName: "powerKeyLongPressed"
37 signalName: "powerKeyLongPressed"
38 }
38 }
39
39
40− SignalSpy {
41− id: volumeDownSpy
42− target: physicalKeysMapper
43− signalName: "volumeDownTriggered"
44− }
45−
46− SignalSpy {
47− id: volumeUpSpy
48− target: physicalKeysMapper
49− signalName: "volumeUpTriggered"
50− }
51−
52 SignalSpy {
40 SignalSpy {
53 id: screenshotSpy
41 id: screenshotSpy
54 target: physicalKeysMapper
42 target: physicalKeysMapper
@@ -63,8 +51,6 @@ TestCase {
63 function cleanup() {
51 function cleanup() {
64 loader.active = false;
52 loader.active = false;
65 powerSpy.clear();
53 powerSpy.clear();
66− volumeDownSpy.clear();
67− volumeUpSpy.clear();
68 screenshotSpy.clear();
54 screenshotSpy.clear();
69 }
55 }
70
56
@@ -89,11 +75,6 @@ TestCase {
89 compare(powerSpy.count, 1);
75 compare(powerSpy.count, 1);
90 }
76 }
91
77
92− function test_screenshotPrtScr() {
93− physicalKeysMapper.onKeyPressed({ key: Qt.Key_Print })
94− tryCompare(screenshotSpy, "count", 1) // verify the screenshotTriggered signal has been called, exactly once
95− }
96−
97 function test_screenshotButtons_data() {
78 function test_screenshotButtons_data() {
98 return [
79 return [
99 { tag: "UpFirst", first: Qt.Key_VolumeUp, second: Qt.Key_VolumeDown },
80 { tag: "UpFirst", first: Qt.Key_VolumeUp, second: Qt.Key_VolumeDown },
@@ -107,30 +88,6 @@ TestCase {
107 screenshotSpy.wait();
88 screenshotSpy.wait();
108 physicalKeysMapper.onKeyReleased({ key: data.first });
89 physicalKeysMapper.onKeyReleased({ key: data.first });
109 physicalKeysMapper.onKeyReleased({ key: data.second });
90 physicalKeysMapper.onKeyReleased({ key: data.second });
110−
91+ compare(screenshotSpy.count, 1);
111− wait(100);
112− compare(volumeUpSpy.count, 0);
113− compare(volumeDownSpy.count, 0);
114− }
115−
116− function test_volumeButton_data() {
117− return [
118− { tag: "Down", key: Qt.Key_VolumeDown, spy: volumeDownSpy },
119− { tag: "Up", key: Qt.Key_VolumeUp, spy: volumeUpSpy },
120− ];
121− }
122−
123− function test_volumeButton(data) {
124− physicalKeysMapper.onKeyPressed({ key: data.key });
125−
126− wait(100);
127− compare(data.spy.count, 0);
128−
129− physicalKeysMapper.onKeyReleased({ key: data.key });
130− data.spy.wait();
131−
132− physicalKeysMapper.onKeyPressed({ key: data.key });
133− physicalKeysMapper.onKeyPressed({ key: data.key, isAutoRepeat: true });
134− data.spy.wait();
135 }
92 }
136}
93}