on xenial help sign in
Fix keys handling and the focus mayhem
commit9bbb23e4f98a8682bebf39a6ecb62d0d2eabd255 authorDaniel d'Andrada <daniel.dandrada@canonical.com> dateThu 17 Jul 2014, 14:10 (12 years ago) parentc8e34c7
8 files changed+130−26
plugins/Utils/CMakeLists.txt+1 plugins/Utils/plugin.cpp+2 plugins/Utils/windowkeysfilter.cpp+57 plugins/Utils/windowkeysfilter.h+51 qml/Shell.qml+8−7 qml/Stages/PhoneStage.qml−7 qml/Stages/SpreadDelegate.qml+11−6 qml/Stages/TabletStage.qml−6
plugins/Utils/CMakeLists.txt+1
@@ -15,6 +15,7 @@ set(QMLPLUGIN_SRC
1515 qsortfilterproxymodelqml.cpp
1616 timeformatter.cpp
1717 unitymenumodelpaths.cpp
18+ windowkeysfilter.cpp
1819 easingcurve.cpp
1920 plugin.cpp
2021 )
plugins/Utils/plugin.cpp+2
@@ -30,6 +30,7 @@
3030#include "qsortfilterproxymodelqml.h"
3131#include "timeformatter.h"
3232#include "unitymenumodelpaths.h"
33+#include "windowkeysfilter.h"
3334#include "easingcurve.h"
3435
3536void UtilsPlugin::registerTypes(const char *uri)
@@ -40,6 +41,7 @@ void UtilsPlugin::registerTypes(const char *uri)
4041 qmlRegisterType<QSortFilterProxyModelQML>(uri, 0, 1, "SortFilterProxyModel");
4142 qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
4243 qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");
44+ qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
4345 qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
4446 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
4547}
plugins/Utils/windowkeysfilter.cppnew file+57
@@ -0,0 +1,57 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Daniel d'Andrada <daniel.dandrada@canonical.com>
17+ */
18+
19+#include "windowkeysfilter.h"
20+
21+#include <QQuickWindow>
22+
23+WindowKeysFilter::WindowKeysFilter(QQuickItem *parent)
24+ : QQuickItem(parent)
25+{
26+ connect(this, &QQuickItem::windowChanged,
27+ this, &WindowKeysFilter::setupFilterOnWindow);
28+}
29+
30+bool WindowKeysFilter::eventFilter(QObject *watched, QEvent *event)
31+{
32+ Q_ASSERT(!m_filteredWindow.isNull());
33+ Q_ASSERT(watched == static_cast<QObject*>(m_filteredWindow.data()));
34+
35+ if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
36+ // Let QML see this event and decide if it does not want it
37+ event->accept();
38+ QCoreApplication::sendEvent(this, event);
39+ return event->isAccepted();
40+ } else {
41+ // Not interested
42+ return false;
43+ }
44+}
45+
46+void WindowKeysFilter::setupFilterOnWindow(QQuickWindow *window)
47+{
48+ if (!m_filteredWindow.isNull()) {
49+ m_filteredWindow->removeEventFilter(this);
50+ m_filteredWindow.clear();
51+ }
52+
53+ if (window) {
54+ window->installEventFilter(this);
55+ m_filteredWindow = window;
56+ }
57+}
plugins/Utils/windowkeysfilter.hnew file+51
@@ -0,0 +1,51 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Daniel d'Andrada <daniel.dandrada@canonical.com>
17+ */
18+
19+#ifndef UNITY_WINDOWKEYSFILTER_H
20+#define UNITY_WINDOWKEYSFILTER_H
21+
22+#include <QQuickItem>
23+#include <QPointer>
24+
25+/*
26+ Receives all key events that arrives in the QQuickWindow where this item is placed.
27+
28+ Rejected key events will be allowed to be processed normally by the QQuickWindow whereas
29+ accepted ones will be filtered out. Events are accepted by default, so make sure you reject
30+ the keys you're not interested in.
31+
32+ If more than one WindowKeysFilter exist in the same QML scene (and thus in the same QQuickWindow)
33+ they will be called in the order of creation, which can be tricky to assess. So the best practice
34+ is to have at most one WindowKeysFilter per QML scene.
35+ */
36+class WindowKeysFilter : public QQuickItem
37+{
38+ Q_OBJECT
39+public:
40+ WindowKeysFilter(QQuickItem *parent = 0);
41+
42+ bool eventFilter(QObject *watched, QEvent *event);
43+
44+private Q_SLOTS:
45+ void setupFilterOnWindow(QQuickWindow *window);
46+
47+private:
48+ QPointer<QQuickWindow> m_filteredWindow;
49+};
50+
51+#endif // UNITY_WINDOWKEYSFILTER_H
qml/Shell.qml+8−7
@@ -21,6 +21,7 @@ import Unity.Application 0.1
2121import Ubuntu.Components 0.1
2222import Ubuntu.Gestures 0.1
2323import Unity.Launcher 0.1
24+import Utils 0.1
2425import LightDM 0.1 as LightDM
2526import Powerd 0.1
2627import SessionBroadcast 0.1
@@ -34,7 +35,7 @@ import "Stages"
3435import Unity.Notifications 1.0 as NotificationBackend
3536import Unity.Session 0.1
3637
37−FocusScope {
38+Item {
3839 id: shell
3940
4041 // this is only here to select the width / height of the window if not running fullscreen
@@ -85,8 +86,11 @@ FocusScope {
8586 id: volumeControl
8687 }
8788
88− Keys.onVolumeUpPressed: volumeControl.volumeUp()
89− Keys.onVolumeDownPressed: volumeControl.volumeDown()
89+ WindowKeysFilter {
90+ // Handle but do not filter out volume keys
91+ Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }
92+ Keys.onVolumeDownPressed: { volumeControl.volumeDown(); event.accepted = false; }
93+ }
9094
9195 Item {
9296 id: underlay
@@ -282,7 +286,7 @@ FocusScope {
282286 Binding {
283287 target: applicationsDisplayLoader.item
284288 property: "interactive"
285− value: stages.roughlyFullyShown
289+ value: stages.roughlyFullyShown && !greeter.shown && !lockscreen.shown
286290 }
287291 }
288292 }
@@ -573,9 +577,6 @@ FocusScope {
573577 }
574578 }
575579
576− focus: true
577− onFocusChanged: if (!focus) forceActiveFocus();
578−
579580 Binding {
580581 target: i18n
581582 property: "domain"
qml/Stages/PhoneStage.qml−7
@@ -59,7 +59,6 @@ Item {
5959 var application = priv.focusedApplication;
6060 root.fullscreen = application.fullscreen;
6161 var appIndex = priv.indexOf(application.appId);
62− spreadRepeater.itemAt(appIndex).forceActiveFocus();
6362 }
6463 }
6564
@@ -81,12 +80,6 @@ Item {
8180 spreadView.contentX = -spreadView.shift;
8281 }
8382 }
84−
85− onSuspendedChanged: {
86− if (root.interactive && !ApplicationManager.suspended && ApplicationManager.count > 0) {
87− spreadRepeater.itemAt(0).forceActiveFocus();
88− }
89− }
9083 }
9184
9285 QtObject {
qml/Stages/SpreadDelegate.qml+11−6
@@ -35,12 +35,7 @@ Item {
3535 signal clicked()
3636 signal closed()
3737
38− onActiveFocusChanged: {
39− if (activeFocus)
40− surfaceContainer.forceActiveFocus();
41− }
42−
43− FocusScope {
38+ Item {
4439 id: surfaceContainer
4540 anchors.fill: parent
4641 readonly property var surface: model.surface
@@ -93,6 +88,16 @@ Item {
9388 Connections {
9489 target: surface
9590 onStateChanged: surfaceContainer.checkFullscreen(surface);
91+ // FIXME: I would rather not need to do this, but currently it doesn't get
92+ // active focus without it and I don't know why.
93+ onFocusChanged: forceSurfaceActiveFocusIfReady();
94+ onParentChanged: forceSurfaceActiveFocusIfReady();
95+ onEnabledChanged: forceSurfaceActiveFocusIfReady();
96+ function forceSurfaceActiveFocusIfReady() {
97+ if (surface.focus && surface.parent === surfaceContainer && surface.enabled) {
98+ surface.forceActiveFocus();
99+ }
100+ }
96101 }
97102
98103 BorderImage {
qml/Stages/TabletStage.qml−6
@@ -120,12 +120,6 @@ Item {
120120 spreadView.contentX = 0;
121121 }
122122 }
123−
124− onSuspendedChanged: {
125− if (root.interactive && !ApplicationManager.suspended && ApplicationManager.count > 0) {
126− spreadRepeater.itemAt(0).forceActiveFocus();
127− }
128− }
129123 }
130124
131125 Flickable {