on xenial help sign in
Let TopLevelWindowModel deside what app to refocus, if any
c08b89cMarius Gripsgard
plugins/WindowManager/TopLevelWindowModel.cpp+33−9
@@ -650,16 +650,19 @@ void TopLevelWindowModel::setFocusedWindow(Window *window)
650 if (window != m_focusedWindow) {
650 if (window != m_focusedWindow) {
651 INFO_MSG << "(" << window << ")";
651 INFO_MSG << "(" << window << ")";
652
652
653− Window* previousWindow = m_focusedWindow;
653+ m_previousWindow = m_focusedWindow;
654
654
655 m_focusedWindow = window;
655 m_focusedWindow = window;
656 Q_EMIT focusedWindowChanged(m_focusedWindow);
656 Q_EMIT focusedWindowChanged(m_focusedWindow);
657
657
658− if (previousWindow && previousWindow->focused() && !previousWindow->surface()) {
658+ if (m_previousWindow && m_previousWindow->focused() && !m_previousWindow->surface()) {
659 // do it ourselves. miral doesn't know about this window
659 // do it ourselves. miral doesn't know about this window
660− previousWindow->setFocused(false);
660+ m_previousWindow->setFocused(false);
661 }
661 }
662 }
662 }
663+
664+ // Reset
665+ m_pendingActivation = false;
663}
666}
664
667
665unityapi::MirSurfaceInterface* TopLevelWindowModel::inputMethodSurface() const
668unityapi::MirSurfaceInterface* TopLevelWindowModel::inputMethodSurface() const
@@ -727,12 +730,6 @@ void TopLevelWindowModel::activateTopMostWindowWithoutId(int forbiddenId)
727 }
730 }
728}
731}
729
732
730−void TopLevelWindowModel::activateNullWindow()
731−{
732− if (!m_nullWindow->focused())
733− m_nullWindow->activate();
734−}
735−
736void TopLevelWindowModel::closeAllWindows()
733void TopLevelWindowModel::closeAllWindows()
737{
734{
738 m_closingAllApps = true;
735 m_closingAllApps = true;
@@ -740,3 +737,30 @@ void TopLevelWindowModel::closeAllWindows()
740 win.window->close();
737 win.window->close();
741 }
738 }
742}
739}
740+
741+void TopLevelWindowModel::rootFocus(bool focus)
742+{
743+ if (focus) {
744+ // Give focus back to previous focused window, only if null window is focused.
745+ // If null window is not focused, a different app had taken the focus and we
746+ // should repect that, or if a pendingActivation is going on.
747+ if (m_previousWindow && !m_previousWindow->focused() && !m_pendingActivation &&
748+ m_nullWindow == m_focusedWindow && m_previousWindow != m_nullWindow) {
749+ m_previousWindow->activate();
750+ }
751+ } else {
752+ if (!m_nullWindow->focused()) {
753+ m_nullWindow->activate();
754+ }
755+ }
756+}
757+
758+// Pending Activation will block refocus of previous focused window
759+// this is needed since surface activation with miral is async,
760+// and activation of placeholder is sync. This causes a race condition
761+// between placeholder and prev window. This results in prev window
762+// gets focused, as it will always be later than the placeholder.
763+void TopLevelWindowModel::pendingActivation()
764+{
765+ m_pendingActivation = true;
766+}
plugins/WindowManager/TopLevelWindowModel.h+7−1
@@ -167,13 +167,17 @@ public:
167 /**
167 /**
168 * @brief Raises and focuses a window with no surface
168 * @brief Raises and focuses a window with no surface
169 */
169 */
170− Q_INVOKABLE void activateNullWindow();
170+ Q_INVOKABLE void rootFocus(bool focus);
171
171
172 /**
172 /**
173 * @brief Closes all windows, emits closedAllWindows when done
173 * @brief Closes all windows, emits closedAllWindows when done
174 */
174 */
175 Q_INVOKABLE void closeAllWindows();
175 Q_INVOKABLE void closeAllWindows();
176
176
177+ /**
178+ * @brief Sets pending activation flag
179+ */
180+ Q_INVOKABLE void pendingActivation();
177Q_SIGNALS:
181Q_SIGNALS:
178 void countChanged();
182 void countChanged();
179 void inputMethodSurfaceChanged(unity::shell::application::MirSurfaceInterface* inputMethodSurface);
183 void inputMethodSurfaceChanged(unity::shell::application::MirSurfaceInterface* inputMethodSurface);
@@ -252,6 +256,8 @@ private:
252 Window* m_inputMethodWindow{nullptr};
256 Window* m_inputMethodWindow{nullptr};
253 Window* m_focusedWindow{nullptr};
257 Window* m_focusedWindow{nullptr};
254 Window* m_nullWindow;
258 Window* m_nullWindow;
259+ Window* m_previousWindow{nullptr};
260+ bool m_pendingActivation;
255
261
256 int m_nextId{1};
262 int m_nextId{1};
257 // Just something big enough that we don't risk running out of unused id numbers.
263 // Just something big enough that we don't risk running out of unused id numbers.
qml/Launcher/Launcher.qml+1−1
@@ -375,8 +375,8 @@ FocusScope {
375 allowSlidingAnimation: !dragArea.dragging && !launcherDragArea.drag.active && panel.animate
375 allowSlidingAnimation: !dragArea.dragging && !launcherDragArea.drag.active && panel.animate
376
376
377 onApplicationSelected: {
377 onApplicationSelected: {
378− root.hide();
379 root.launcherApplicationSelected(appId)
378 root.launcherApplicationSelected(appId)
379+ root.hide();
380 root.focus = false;
380 root.focus = false;
381 }
381 }
382
382
qml/Shell.qml+3−1
@@ -1,4 +1,4 @@
1−/*
1+/*
2 * Copyright (C) 2013-2016 Canonical, Ltd.
2 * Copyright (C) 2013-2016 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
@@ -180,6 +180,8 @@ StyledItem {
180 readonly property alias greeter: greeterLoader.item
180 readonly property alias greeter: greeterLoader.item
181
181
182 function activateApplication(appId) {
182 function activateApplication(appId) {
183+ topLevelSurfaceList.pendingActivation();
184+
183 // Either open the app in our own session, or -- if we're acting as a
185 // Either open the app in our own session, or -- if we're acting as a
184 // greeter -- ask the user's session to open it for us.
186 // greeter -- ask the user's session to open it for us.
185 if (shell.mode === "greeter") {
187 if (shell.mode === "greeter") {
qml/Stage/Stage.qml+7−10
@@ -93,17 +93,14 @@ FocusScope {
93 // Stage must have focus before activating windows, including null
93 // Stage must have focus before activating windows, including null
94 if (interactive) {
94 if (interactive) {
95 focus = true;
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 }
96 }
97+
98+ // This will:
99+ // - If interactive: Try to reactivate last focused application.
100+ // this will not happen if a pending activation is going on
101+ // - If not interactive: Activate nullWindow, this makes
102+ // sure none of the apps have focus when stage is not active
103+ topLevelSurfaceList.rootFocus(interactive);
107 }
104 }
108
105
109 onAltTabPressedChanged: {
106 onAltTabPressedChanged: {
tests/qmltests/Stage/tst_PhoneStage.qml+26
@@ -500,6 +500,32 @@ Item {
500 compare(webbrowserApp.surfaceList.count, 1);
500 compare(webbrowserApp.surfaceList.count, 1);
501 }
501 }
502
502
503+ function test_givePlaceholderFocusOnStartup() {
504+ compare(topLevelSurfaceList.applicationAt(0).appId, "unity8-dash");
505+ var dashSurfaceId = topLevelSurfaceList.idAt(0);
506+ waitUntilAppSurfaceShowsUp(dashSurfaceId);
507+ waitForRendering(stage);
508+
509+ // Give nullwindow focus
510+ topLevelSurfaceList.rootFocus(false);
511+ var webbrowserSurfaceId = topLevelSurfaceList.nextId;
512+ topLevelSurfaceList.pendingActivation();
513+ var webbrowserApp = ApplicationManager.startApplication("morph-browser");
514+ compare(topLevelSurfaceList.applicationAt(0).appId, "morph-browser");
515+ var webbrowserWindow = topLevelSurfaceList.windowAt(0);
516+
517+ // Give focus back to stage, but we should NOT refocus prev window (dash in this case)
518+ topLevelSurfaceList.rootFocus(true);
519+
520+ // We should have given focus to webbrowser at this point, not dashWindow
521+ tryCompare(topLevelSurfaceList, "focusedWindow", webbrowserWindow);
522+
523+ waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
524+
525+ // Should still have webbrowser as focused app
526+ tryCompare(topLevelSurfaceList, "focusedWindow", webbrowserWindow);
527+ }
528+
503 /*
529 /*
504 1 - user suspends an application (ie, focus a surface from a different application)
530 1 - user suspends an application (ie, focus a surface from a different application)
505 2 - That suspended application gets killed, causing its surface to go zombie
531 2 - That suspended application gets killed, causing its surface to go zombie