on xenial help sign in
Make sure no apps have focus if stage is not active/focused

Make sure no apps have focus if stage is not active/focused, to achive this a null window is used to steal focus from the other apps. The reason why a null window is used is that we now use miral to handle all focusing of apps, and since thats only aware of clients it wont know if unity8 itself wants the focus. So to workaound this we make a null window have the focus while unitu8 have focus. Fixes: https://github.com/ubports/unity8/issues/130 Fixes: https://github.com/ubports/ubuntu-touch/issues/1123

commitee21555138f5343c7d296700a025defa2d911915 authorMarius Gripsgard <marius@ubports.com> dateThu 27 Jun 2019, 01:52 (7 years ago) parent305605c
6 files changed+52−3
plugins/WindowManager/TopLevelWindowModel.cpp+7 plugins/WindowManager/TopLevelWindowModel.h+6 qml/Launcher/Launcher.qml+7−1 qml/Shell.qml+2−2 qml/Stage/Stage.qml+16 tests/qmltests/tst_Shell.qml+14
plugins/WindowManager/TopLevelWindowModel.cpp+7
@@ -38,6 +38,7 @@ Q_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL, "toplevelwindowmodel", QtInfoMsg)
3838namespace unityapi = unity::shell::application;
3939
4040TopLevelWindowModel::TopLevelWindowModel()
41+ : m_nullWindow(createWindow(nullptr))
4142{
4243}
4344
@@ -719,3 +720,9 @@ void TopLevelWindowModel::activateTopMostWindowWithoutId(int forbiddenId)
719720 }
720721 }
721722}
723+
724+void TopLevelWindowModel::activateNullWindow()
725+{
726+ m_nullWindow->activate();
727+ m_surfaceManager->activate(nullptr);
728+}
plugins/WindowManager/TopLevelWindowModel.h+6
@@ -164,6 +164,11 @@ public:
164164 */
165165 Q_INVOKABLE void raiseId(int id);
166166
167+ /**
168+ * @brief Raises and focuses a window with no surface
169+ */
170+ Q_INVOKABLE void activateNullWindow();
171+
167172Q_SIGNALS:
168173 void countChanged();
169174 void inputMethodSurfaceChanged(unity::shell::application::MirSurfaceInterface* inputMethodSurface);
@@ -239,6 +244,7 @@ private:
239244 QVector<ModelEntry> m_windowModel;
240245 Window* m_inputMethodWindow{nullptr};
241246 Window* m_focusedWindow{nullptr};
247+ Window* m_nullWindow;
242248
243249 int m_nextId{1};
244250 // Just something big enough that we don't risk running out of unused id numbers.
qml/Launcher/Launcher.qml+7−1
@@ -42,6 +42,7 @@ FocusScope {
4242
4343 property bool superPressed: false
4444 property bool superTabPressed: false
45+ property bool takesFocus: false;
4546
4647 readonly property bool dragging: dragArea.dragging
4748 readonly property real dragDistance: dragArea.dragging ? dragArea.touchPosition.x : 0
@@ -65,6 +66,8 @@ FocusScope {
6566 }
6667 }
6768
69+ onFocusChanged: {if (!focus) { root.takesFocus = false; }}
70+
6871 onSuperPressedChanged: {
6972 if (state == "drawer")
7073 return;
@@ -84,6 +87,7 @@ FocusScope {
8487 if (superTabPressed) {
8588 switchToNextState("visible")
8689 panel.highlightIndex = -1;
90+ root.takesFocus = true;
8791 root.focus = true;
8892 superPressTimer.stop();
8993 superLongPressTimer.stop();
@@ -128,6 +132,7 @@ FocusScope {
128132 } else {
129133 switchToNextState("")
130134 }
135+ root.focus = false;
131136 }
132137
133138 function fadeOut() {
@@ -164,6 +169,7 @@ FocusScope {
164169 function openForKeyboardNavigation() {
165170 panel.highlightIndex = -1; // The BFB
166171 drawer.focus = false;
172+ root.takesFocus = true;
167173 root.focus = true;
168174 switchToNextState("visible")
169175 }
@@ -176,6 +182,7 @@ FocusScope {
176182 panel.shortcutHintsShown = false;
177183 superPressTimer.stop();
178184 superLongPressTimer.stop();
185+ root.takesFocus = true;
179186 root.focus = true;
180187 if (focusInputField) {
181188 drawer.focusInput();
@@ -232,7 +239,6 @@ FocusScope {
232239 root.hide();
233240 panel.highlightIndex = -2
234241 event.accepted = true;
235− root.focus = false;
236242 }
237243 }
238244
qml/Shell.qml+2−2
@@ -173,6 +173,7 @@ StyledItem {
173173 } else {
174174 startApp(appId);
175175 }
176+ stage.focus = true;
176177 }
177178
178179 function activateURL(url) {
@@ -309,8 +310,7 @@ StyledItem {
309310 interactive: (!greeter || !greeter.shown)
310311 && panel.indicators.fullyClosed
311312 && !notifications.useModal
312−
313− onInteractiveChanged: { if (interactive) { focus = true; } }
313+ && !launcher.takesFocus
314314
315315 suspended: greeter.shown
316316 altTabPressed: physicalKeysMapper.altTabPressed
qml/Stage/Stage.qml+16
@@ -89,6 +89,22 @@ FocusScope {
8989 Qt.InvertedLandscapeOrientation;
9090 }
9191
92+ onInteractiveChanged: {
93+ // Stage must have focus before activating windows, including null
94+ if (interactive) {
95+ focus = true;
96+
97+ // Activate focused app once stage is active again
98+ // this makes sure the focused app have focus
99+ if (priv.focusedAppDelegate) {
100+ priv.focusedAppDelegate.window.activate();
101+ }
102+ } else {
103+ // Activate null window once stage is not active this makes
104+ // sure none of the apps have focus when stage is not active
105+ topLevelSurfaceList.activateNullWindow();
106+ }
107+ }
92108
93109 onAltTabPressedChanged: {
94110 root.focus = true;
tests/qmltests/tst_Shell.qml+14
@@ -3289,6 +3289,20 @@ Rectangle {
32893289 tryCompare(appDelegate2.surface, "activeFocus", true);
32903290 }
32913291
3292+ function test_launchFromDrawerPutsFocusOnStage() {
3293+ loadShell("desktop");
3294+ shell.usageScenario = "desktop";
3295+ waitForRendering(shell);
3296+ swipeAwayGreeter();
3297+
3298+ keyClick(Qt.Key_A, Qt.MetaModifier);
3299+ typeString("browser");
3300+ keyClick(Qt.Key_Enter);
3301+
3302+ var appRepeater = findChild(shell, "appRepeater");
3303+ tryCompare(appRepeater.itemAt(0), "activeFocus", true);
3304+ }
3305+
32923306 function test_maximizedWindowAndMenuInPanel() {
32933307 loadShell("desktop");
32943308 shell.usageScenario = "desktop";