on xenial help sign in
DesktopStage: Refactor focus handling
e18d93dDaniel d'Andrada
plugins/Utils/CMakeLists.txt+1
@@ -9,6 +9,7 @@ include_directories(
9)
9)
10
10
11set(QMLPLUGIN_SRC
11set(QMLPLUGIN_SRC
12+ activefocuslogger.cpp
12 constants.cpp
13 constants.cpp
13 HomeKeyWatcher.cpp
14 HomeKeyWatcher.cpp
14 inputwatcher.cpp
15 inputwatcher.cpp
tests/uqmlscene/ActiveFocusLogger.cpp → plugins/Utils/activefocuslogger.cpprenamed+21−8
@@ -14,21 +14,34 @@
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−#include "ActiveFocusLogger.h"
17+#include "activefocuslogger.h"
18
18
19−#include <QDebug>
19+#include <QQuickWindow>
20−#include <QQuickItem>
21
20
22−void ActiveFocusLogger::setWindow(QQuickWindow *window)
21+ActiveFocusLogger::ActiveFocusLogger(QQuickItem *parent)
22+ : QQuickItem(parent)
23{
23{
24+ connect(this, &QQuickItem::windowChanged,
25+ this, &ActiveFocusLogger::setupFilterOnWindow);
26+}
27+
28+void ActiveFocusLogger::setupFilterOnWindow(QQuickWindow *window)
29+{
30+ if (!m_window.isNull()) {
31+ disconnect(m_window.data(), nullptr, this, nullptr);
32+ m_window.clear();
33+ }
34+
35+ if (window) {
24− m_window = window;
36+ m_window = window;
25− QObject::connect(window, &QQuickWindow::activeFocusItemChanged,
37+ QObject::connect(window, &QQuickWindow::activeFocusItemChanged,
26− this, &ActiveFocusLogger::printActiveFocusInfo);
38+ this, &ActiveFocusLogger::printActiveFocusInfo);
39+ }
27}
40}
28
41
29void ActiveFocusLogger::printActiveFocusInfo()
42void ActiveFocusLogger::printActiveFocusInfo()
30{
43{
31− if (!m_window) {
44+ if (!m_window || !isEnabled()) {
32 return;
45 return;
33 }
46 }
34
47
tests/uqmlscene/ActiveFocusLogger.h → plugins/Utils/activefocuslogger.hrenamed+8−8
@@ -14,24 +14,24 @@
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−#ifndef ACTIVE_FOCUS_LOGGER_H
17+#ifndef UNITY_ACTIVEFOCUSLOGGER_H
18−#define ACTIVE_FOCUS_LOGGER_H
18+#define UNITY_ACTIVEFOCUSLOGGER_H
19
19
20−#include <QObject>
20+#include <QQuickItem>
21−#include <QQuickWindow>
22#include <QPointer>
21#include <QPointer>
23
22
24−class ActiveFocusLogger : public QObject {
23+class ActiveFocusLogger : public QQuickItem
24+{
25 Q_OBJECT
25 Q_OBJECT
26−
27public:
26public:
28− void setWindow(QQuickWindow *window);
27+ ActiveFocusLogger(QQuickItem *parent = 0);
29
28
30private Q_SLOTS:
29private Q_SLOTS:
30+ void setupFilterOnWindow(QQuickWindow *window);
31 void printActiveFocusInfo();
31 void printActiveFocusInfo();
32
32
33private:
33private:
34 QPointer<QQuickWindow> m_window;
34 QPointer<QQuickWindow> m_window;
35};
35};
36
36
37−#endif // ACTIVE_FOCUS_LOGGER_H
37+#endif // UNITY_ACTIVEFOCUSLOGGER_H
plugins/Utils/plugin.cpp+2
@@ -23,6 +23,7 @@
23#include "plugin.h"
23#include "plugin.h"
24
24
25// local
25// local
26+#include "activefocuslogger.h"
26#include "easingcurve.h"
27#include "easingcurve.h"
27#include "HomeKeyWatcher.h"
28#include "HomeKeyWatcher.h"
28#include "inputwatcher.h"
29#include "inputwatcher.h"
@@ -66,6 +67,7 @@ void UtilsPlugin::registerTypes(const char *uri)
66 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
67 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
67 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
68 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
68 qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
69 qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
70+ qmlRegisterType<ActiveFocusLogger>(uri, 0, 1, "ActiveFocusLogger");
69}
71}
70
72
71void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
73void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
qml/Stages/ApplicationWindow.qml−3
@@ -137,9 +137,6 @@ FocusScope {
137 onSurfaceChanged: {
137 onSurfaceChanged: {
138 if (sessionContainer.surface) {
138 if (sessionContainer.surface) {
139 surfaceInitTimer.start();
139 surfaceInitTimer.start();
140− if (ApplicationManager.focusedApplicationId == application.appId) {
141− root.forceActiveFocus();
142− }
143 } else {
140 } else {
144 d.surfaceInitialized = false;
141 d.surfaceInitialized = false;
145 }
142 }
qml/Stages/DesktopStage.qml+14−15
@@ -78,7 +78,6 @@ Rectangle {
78 var appDelegate = appRepeater.itemAt(appIndex);
78 var appDelegate = appRepeater.itemAt(appIndex);
79 appDelegate.minimized = false;
79 appDelegate.minimized = false;
80 appDelegate.focus = true;
80 appDelegate.focus = true;
81− ApplicationManager.focusApplication(appId);
82 }
81 }
83 }
82 }
84
83
@@ -91,12 +90,6 @@ Rectangle {
91 return index >= 0 && index < appRepeater.count ? appRepeater.itemAt(index) : null
90 return index >= 0 && index < appRepeater.count ? appRepeater.itemAt(index) : null
92 }
91 }
93
92
94− onFocusedAppDelegateChanged: {
95− if (focusedAppDelegate) {
96− focusedAppDelegate.focus = true;
97− }
98− }
99−
100 function indexOf(appId) {
93 function indexOf(appId) {
101 for (var i = 0; i < ApplicationManager.count; i++) {
94 for (var i = 0; i < ApplicationManager.count; i++) {
102 if (ApplicationManager.get(i).appId == appId) {
95 if (ApplicationManager.get(i).appId == appId) {
@@ -133,23 +126,28 @@ Rectangle {
133 id: appContainer
126 id: appContainer
134 objectName: "appContainer"
127 objectName: "appContainer"
135 anchors.fill: parent
128 anchors.fill: parent
129+ focus: true
136
130
137 Keys.onPressed: {
131 Keys.onPressed: {
138 switch (event.key) {
132 switch (event.key) {
139 case Qt.Key_Left:
133 case Qt.Key_Left:
140 case Qt.Key_Backtab:
134 case Qt.Key_Backtab:
141 selectPrevious(event.isAutoRepeat)
135 selectPrevious(event.isAutoRepeat)
136+ event.accepted = true;
142 break;
137 break;
143 case Qt.Key_Right:
138 case Qt.Key_Right:
144 case Qt.Key_Tab:
139 case Qt.Key_Tab:
145 selectNext(event.isAutoRepeat)
140 selectNext(event.isAutoRepeat)
141+ event.accepted = true;
146 break;
142 break;
147 case Qt.Key_Escape:
143 case Qt.Key_Escape:
148 appRepeater.highlightedIndex = -1
144 appRepeater.highlightedIndex = -1
145+ event.accepted = true;
149 case Qt.Key_Enter:
146 case Qt.Key_Enter:
150 case Qt.Key_Return:
147 case Qt.Key_Return:
151 case Qt.Key_Space:
148 case Qt.Key_Space:
152 root.state = ""
149 root.state = ""
150+ event.accepted = true;
153 }
151 }
154 }
152 }
155
153
@@ -180,7 +178,8 @@ Rectangle {
180
178
181 function focusSelected() {
179 function focusSelected() {
182 if (appRepeater.highlightedIndex != -1) {
180 if (appRepeater.highlightedIndex != -1) {
183− appRepeater.itemAt(appRepeater.highlightedIndex).focus = true;
181+ var application = ApplicationManager.get(appRepeater.highlightedIndex);
182+ ApplicationManager.requestFocusApplication(application.appId)
184 }
183 }
185 }
184 }
186
185
@@ -222,13 +221,14 @@ Rectangle {
222
221
223 onFocusChanged: {
222 onFocusChanged: {
224 if (focus && ApplicationManager.focusedApplicationId !== model.appId) {
223 if (focus && ApplicationManager.focusedApplicationId !== model.appId) {
225− ApplicationManager.requestFocusApplication(model.appId);
224+ ApplicationManager.focusApplication(model.appId);
226− decoratedWindow.forceActiveFocus();
227 }
225 }
228 }
226 }
227+
229 Component.onCompleted: {
228 Component.onCompleted: {
229+ // Focus the top-most or AppMan-focused application on start up.
230− if (ApplicationManager.focusedApplicationId == model.appId) {
230+ if (ApplicationManager.focusedApplicationId === model.appId && !focus) {
231− decoratedWindow.forceActiveFocus();
231+ focus = true;
232 }
232 }
233 }
233 }
234
234
@@ -356,7 +356,7 @@ Rectangle {
356 resizeHandleWidth: units.gu(2)
356 resizeHandleWidth: units.gu(2)
357 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
357 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
358
358
359− onPressed: appDelegate.focus = true;
359+ onPressed: { appDelegate.focus = true; }
360 }
360 }
361
361
362 DecoratedWindow {
362 DecoratedWindow {
@@ -368,7 +368,7 @@ Rectangle {
368 windowHeight: appDelegate.windowHeight
368 windowHeight: appDelegate.windowHeight
369 application: ApplicationManager.get(index)
369 application: ApplicationManager.get(index)
370 active: ApplicationManager.focusedApplicationId === model.appId
370 active: ApplicationManager.focusedApplicationId === model.appId
371− focus: false
371+ focus: true
372
372
373 onClose: ApplicationManager.stopApplication(model.appId)
373 onClose: ApplicationManager.stopApplication(model.appId)
374 onMaximize: appDelegate.maximize()
374 onMaximize: appDelegate.maximize()
@@ -642,7 +642,6 @@ Rectangle {
642 PropertyChanges { target: spreadFlickable; enabled: spreadFlickable.contentWidth > spreadFlickable.minContentWidth }
642 PropertyChanges { target: spreadFlickable; enabled: spreadFlickable.contentWidth > spreadFlickable.minContentWidth }
643 PropertyChanges { target: currentSelectedLabel; visible: true }
643 PropertyChanges { target: currentSelectedLabel; visible: true }
644 PropertyChanges { target: spreadBackground; visible: true }
644 PropertyChanges { target: spreadBackground; visible: true }
645− PropertyChanges { target: appContainer; focus: true }
646 PropertyChanges { target: hoverMouseArea; enabled: true }
645 PropertyChanges { target: hoverMouseArea; enabled: true }
647 }
646 }
648 ]
647 ]
tests/mocks/Unity/Application/MirSurfaceItem.cpp+15
@@ -161,6 +161,21 @@ void MirSurfaceItem::touchEvent(QTouchEvent * event)
161 }
161 }
162}
162}
163
163
164+void MirSurfaceItem::mousePressEvent(QMouseEvent * event)
165+{
166+ event->accept();
167+}
168+
169+void MirSurfaceItem::mouseMoveEvent(QMouseEvent * event)
170+{
171+ event->accept();
172+}
173+
174+void MirSurfaceItem::mouseReleaseEvent(QMouseEvent * event)
175+{
176+ event->accept();
177+}
178+
164void MirSurfaceItem::setSurface(MirSurfaceInterface* surface)
179void MirSurfaceItem::setSurface(MirSurfaceInterface* surface)
165{
180{
166 qDebug().nospace() << "MirSurfaceItem::setSurface() this=" << (void*)(this)
181 qDebug().nospace() << "MirSurfaceItem::setSurface() this=" << (void*)(this)
tests/mocks/Unity/Application/MirSurfaceItem.h+3
@@ -79,6 +79,9 @@ Q_SIGNALS:
79
79
80protected:
80protected:
81 void touchEvent(QTouchEvent * event) override;
81 void touchEvent(QTouchEvent * event) override;
82+ void mousePressEvent(QMouseEvent * event) override;
83+ void mouseMoveEvent(QMouseEvent * event) override;
84+ void mouseReleaseEvent(QMouseEvent * event) override;
82 void itemChange(ItemChange change, const ItemChangeData & value) override;
85 void itemChange(ItemChange change, const ItemChangeData & value) override;
83
86
84private Q_SLOTS:
87private Q_SLOTS:
tests/mocks/Utils/CMakeLists.txt+1
@@ -10,6 +10,7 @@ include_directories(
10)
10)
11
11
12set(QMLPLUGIN_SRC
12set(QMLPLUGIN_SRC
13+ ${CMAKE_SOURCE_DIR}/plugins/Utils/activefocuslogger.cpp
13 ${CMAKE_SOURCE_DIR}/plugins/Utils/qlimitproxymodelqml.cpp
14 ${CMAKE_SOURCE_DIR}/plugins/Utils/qlimitproxymodelqml.cpp
14 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
15 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
15 ${CMAKE_SOURCE_DIR}/plugins/Utils/relativetimeformatter.cpp
16 ${CMAKE_SOURCE_DIR}/plugins/Utils/relativetimeformatter.cpp
tests/mocks/Utils/plugin.cpp+2
@@ -35,6 +35,7 @@
35#include "constants.h"
35#include "constants.h"
36
36
37// plugin
37// plugin
38+#include <activefocuslogger.h>
38#include <inputwatcher.h>
39#include <inputwatcher.h>
39#include <qlimitproxymodelqml.h>
40#include <qlimitproxymodelqml.h>
40#include <unitysortfilterproxymodelqml.h>
41#include <unitysortfilterproxymodelqml.h>
@@ -74,6 +75,7 @@ void FakeUtilsPlugin::registerTypes(const char *uri)
74 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
75 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
75 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
76 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
76 qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
77 qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
78+ qmlRegisterType<ActiveFocusLogger>(uri, 0, 1, "ActiveFocusLogger");
77}
79}
78
80
79void FakeUtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
81void FakeUtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
tests/qmltests/Stages/tst_DesktopStage.qml+10−7
@@ -61,7 +61,6 @@ Item {
61 Component.onDestruction: {
61 Component.onDestruction: {
62 desktopStageLoader.itemDestroyed = true;
62 desktopStageLoader.itemDestroyed = true;
63 }
63 }
64− focus: true
65 }
64 }
66 }
65 }
67 }
66 }
@@ -142,8 +141,8 @@ Item {
142
141
143 function test_appFocusSwitch_data() {
142 function test_appFocusSwitch_data() {
144 return [
143 return [
145− {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 0, focusTo: 1 },
144+ {tag: "dash to dialer", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 0, focusTo: 1 },
146− {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 1, focusTo: 0 },
145+ {tag: "dialer to dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 1, focusTo: 0 },
147 ]
146 ]
148 }
147 }
149
148
@@ -162,8 +161,8 @@ Item {
162
161
163 function test_tappingOnWindowChangesFocusedApp_data() {
162 function test_tappingOnWindowChangesFocusedApp_data() {
164 return [
163 return [
165− {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 0, focusTo: 1 },
164+ {tag: "dash to dialer", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 0, focusTo: 1 },
166− {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 1, focusTo: 0 },
165+ {tag: "dialer to dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 1, focusTo: 0 },
167 ]
166 ]
168 }
167 }
169
168
@@ -172,16 +171,20 @@ Item {
172 for (i = 0; i < data.apps.length; i++) {
171 for (i = 0; i < data.apps.length; i++) {
173 startApplication(data.apps[i]);
172 startApplication(data.apps[i]);
174 }
173 }
174+ var fromAppId = data.apps[data.focusfrom];
175+ var toAppId = data.apps[data.focusTo]
175
176
176− var fromAppWindow = findChild(desktopStage, "appWindow_" + data.apps[data.focusfrom]);
177+ var fromAppWindow = findChild(desktopStage, "appWindow_" + fromAppId);
177 verify(fromAppWindow);
178 verify(fromAppWindow);
178 tap(fromAppWindow);
179 tap(fromAppWindow);
179 compare(fromAppWindow.application.session.surface.activeFocus, true);
180 compare(fromAppWindow.application.session.surface.activeFocus, true);
181+ compare(ApplicationManager.focusedApplicationId, fromAppId);
180
182
181− var toAppWindow = findChild(desktopStage, "appWindow_" + data.apps[data.focusTo]);
183+ var toAppWindow = findChild(desktopStage, "appWindow_" + toAppId);
182 verify(toAppWindow);
184 verify(toAppWindow);
183 tap(toAppWindow);
185 tap(toAppWindow);
184 compare(toAppWindow.application.session.surface.activeFocus, true);
186 compare(toAppWindow.application.session.surface.activeFocus, true);
187+ compare(ApplicationManager.focusedApplicationId, toAppId);
185 }
188 }
186
189
187 function test_tappingOnDecorationFocusesApplication_data() {
190 function test_tappingOnDecorationFocusesApplication_data() {
tests/uqmlscene/CMakeLists.txt−1
@@ -1,6 +1,5 @@
1add_executable(uqmlscene
1add_executable(uqmlscene
2 ${shellapplication_MOC_SRCS}
2 ${shellapplication_MOC_SRCS}
3− ActiveFocusLogger.cpp
4 main.cpp
3 main.cpp
5 )
4 )
6
5
tests/uqmlscene/main.cpp−13
@@ -63,12 +63,6 @@
63#include <QtCore/QTranslator>
63#include <QtCore/QTranslator>
64#include <QtCore/QLibraryInfo>
64#include <QtCore/QLibraryInfo>
65
65
66−#define UQMLSCENE_DEBUG_ACTIVE_FOCUS 0
67−
68−#if UQMLSCENE_DEBUG_ACTIVE_FOCUS
69− #include "ActiveFocusLogger.h"
70−#endif
71−
72#ifdef QML_RUNTIME_TESTING
66#ifdef QML_RUNTIME_TESTING
73class RenderStatistics
67class RenderStatistics
74{
68{
@@ -393,10 +387,6 @@ int main(int argc, char ** argv)
393{
387{
394 Options options;
388 Options options;
395
389
396− #if UQMLSCENE_DEBUG_ACTIVE_FOCUS
397− ActiveFocusLogger activeFocusLogger;
398− #endif
399−
400 QStringList imports;
390 QStringList imports;
401 QList<QPair<QString, QString> > bundles;
391 QList<QPair<QString, QString> > bundles;
402 for (int i = 1; i < argc; ++i) {
392 for (int i = 1; i < argc; ++i) {
@@ -515,9 +505,6 @@ int main(int argc, char ** argv)
515 QQuickItem *contentItem = qobject_cast<QQuickItem *>(topLevel);
505 QQuickItem *contentItem = qobject_cast<QQuickItem *>(topLevel);
516 if (contentItem) {
506 if (contentItem) {
517 qxView = new QQuickView(&engine, nullptr);
507 qxView = new QQuickView(&engine, nullptr);
518− #if UQMLSCENE_DEBUG_ACTIVE_FOCUS
519− activeFocusLogger.setWindow(qxView);
520− #endif
521 window = qxView;
508 window = qxView;
522 // Set window default properties; the qml can still override them
509 // Set window default properties; the qml can still override them
523 QString oname = contentItem->objectName();
510 QString oname = contentItem->objectName();