Merge lp:~dandrader/unity8/hiddenWindows into lp:unity8

Proposed by Daniel d'Andrada
Status: Merged
Approved by: Lukáš Tinkl
Approved revision: 2870
Merged at revision: 2881
Proposed branch: lp:~dandrader/unity8/hiddenWindows
Merge into: lp:unity8
Prerequisite: lp:~dandrader/unity8/allowClientResize
Diff against target: 812 lines (+487/-39)
12 files modified
plugins/WindowManager/CMakeLists.txt (+2/-0)
plugins/WindowManager/TopLevelWindowModel.cpp (+64/-20)
plugins/WindowManager/TopLevelWindowModel.h (+6/-2)
plugins/WindowManager/Window.cpp (+6/-0)
plugins/WindowManager/Window.h (+4/-1)
plugins/WindowManager/WindowManagerGlobal.h (+23/-0)
tests/mocks/Unity/Application/MirSurfaceListModel.cpp (+7/-7)
tests/mocks/Unity/Application/MirSurfaceListModel.h (+7/-9)
tests/plugins/CMakeLists.txt (+1/-0)
tests/plugins/WindowManager/CMakeLists.txt (+34/-0)
tests/plugins/WindowManager/UnityApplicationMocks.h (+203/-0)
tests/plugins/WindowManager/tst_TopLevelWindowModel.cpp (+130/-0)
To merge this branch: bzr merge lp:~dandrader/unity8/hiddenWindows
Reviewer Review Type Date Requested Status
Unity8 CI Bot continuous-integration Approve
Albert Astals Cid (community) Approve
Review via email: mp+320058@code.launchpad.net

This proposal supersedes a proposal from 2017-03-15.

Commit message

TopLevelWindowModel: don't put hidden windows in the model

From the UI point of view, it's as if they didn't exist.

Description of the change

Prereq-archive: ppa:ci-train-ppa-service/2555

See bug description for steps on how to manually test this.

* Are there any related MPs required for this MP to build/function as expected? Please list.
No.

* Did you perform an exploratory manual test run of your code change and any related functionality?
Yes.

* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
Not applicable.

* If you changed the UI, has there been a design review?
Not applicable.

To post a comment you must log in.
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:2868
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/3390/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4469
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/2683
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=zesty,testname=qmluitests.sh/2683
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4497
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4324
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4324/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4324
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4324/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4324
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4324/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4324
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4324/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4324
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4324/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4324
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4324/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/3390/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal

PASSED: Continuous integration, rev:2869
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/3393/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4472
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/2685
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=zesty,testname=qmluitests.sh/2685
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4500
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4327
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4327/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4327
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4327/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4327
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4327/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4327
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4327/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4327
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4327/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4327
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4327/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/3393/rebuild

review: Approve (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote : Posted in a previous version of this proposal

 * Did you perform an exploratory manual test run of the code change and any related functionality?
Yes

 * Did CI run pass? If not, please explain why.
Yes

review: Approve
Revision history for this message
Albert Astals Cid (aacid) :
review: Approve
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

PASSED: Continuous integration, rev:2870
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/3481/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4593
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/2774
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=zesty,testname=qmluitests.sh/2774
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4621
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4449
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4449/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4449
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4449/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4449
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4449/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4449
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4449/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4449
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4449/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4449
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4449/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/3481/rebuild

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'plugins/WindowManager/CMakeLists.txt'
2--- plugins/WindowManager/CMakeLists.txt 2016-11-30 19:24:02 +0000
3+++ plugins/WindowManager/CMakeLists.txt 2017-03-16 15:31:06 +0000
4@@ -9,6 +9,8 @@
5 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/SurfaceManagerInterface.h
6 )
7
8+add_definitions(-DWINDOWMANAGERQML_LIBRARY)
9+
10 add_library(windowmanager-qml SHARED ${WINDOWMANAGER_SRC})
11
12 qt5_use_modules(windowmanager-qml Qml Quick Gui)
13
14=== modified file 'plugins/WindowManager/TopLevelWindowModel.cpp'
15--- plugins/WindowManager/TopLevelWindowModel.cpp 2017-03-07 13:42:02 +0000
16+++ plugins/WindowManager/TopLevelWindowModel.cpp 2017-03-16 15:31:06 +0000
17@@ -1,5 +1,5 @@
18 /*
19- * Copyright (C) 2016 Canonical, Ltd.
20+ * Copyright (C) 2016-2017 Canonical, Ltd.
21 *
22 * This program is free software: you can redistribute it and/or modify it under
23 * the terms of the GNU Lesser General Public License version 3, as published by
24@@ -130,7 +130,7 @@
25 int i = 0;
26 while (i < m_windowModel.count()) {
27 if (m_windowModel.at(i).application == application) {
28- removeAt(i);
29+ deleteAt(i);
30 } else {
31 ++i;
32 }
33@@ -148,12 +148,13 @@
34 {
35 Q_ASSERT(surface != nullptr);
36
37+ connectSurface(surface);
38+
39 bool filledPlaceholder = false;
40 for (int i = 0; i < m_windowModel.count() && !filledPlaceholder; ++i) {
41 ModelEntry &entry = m_windowModel[i];
42 if (entry.application == application && entry.window->surface() == nullptr) {
43 entry.window->setSurface(surface);
44- connectSurface(surface);
45 INFO_MSG << " appId=" << application->appId() << " surface=" << surface
46 << ", filling out placeholder. after: " << toString();
47 filledPlaceholder = true;
48@@ -168,6 +169,34 @@
49
50 void TopLevelWindowModel::prependSurfaceHelper(unityapi::MirSurfaceInterface *surface, unityapi::ApplicationInfoInterface *application)
51 {
52+
53+ Window *window = createWindow(surface);
54+
55+ connect(window, &Window::stateChanged, this, [=](Mir::State newState) {
56+ if (newState == Mir::HiddenState) {
57+ // Comply, removing it from our model. Just as if it didn't exist anymore.
58+ removeAt(indexForId(window->id()));
59+ } else {
60+ if (indexForId(window->id()) == -1) {
61+ // was probably hidden before. put it back on the list
62+ auto *application = m_applicationManager->findApplicationWithSurface(window->surface());
63+ Q_ASSERT(application);
64+ prependWindow(window, application);
65+ }
66+ }
67+ });
68+
69+ prependWindow(window, application);
70+
71+ if (!surface) {
72+ activateEmptyWindow(window);
73+ }
74+
75+ INFO_MSG << " after " << toString();
76+}
77+
78+void TopLevelWindowModel::prependWindow(Window *window, unityapi::ApplicationInfoInterface *application)
79+{
80 if (m_modelState == IdleState) {
81 m_modelState = InsertingState;
82 beginInsertRows(QModelIndex(), 0 /*first*/, 0 /*last*/);
83@@ -176,12 +205,7 @@
84 // No point in signaling anything if we're resetting the whole model
85 }
86
87- Window *window = createWindow(surface);
88-
89 m_windowModel.prepend(ModelEntry(window, application));
90- if (surface) {
91- connectSurface(surface);
92- }
93
94 if (m_modelState == InsertingState) {
95 endInsertRows();
96@@ -189,12 +213,6 @@
97 Q_EMIT listChanged();
98 m_modelState = IdleState;
99 }
100-
101- if (!surface) {
102- activateEmptyWindow(window);
103- }
104-
105- INFO_MSG << " after " << toString();
106 }
107
108 void TopLevelWindowModel::connectWindow(Window *window)
109@@ -243,6 +261,13 @@
110 connect(window, &Window::emptyWindowActivated, this, [this, window]() {
111 activateEmptyWindow(window);
112 });
113+
114+ connect(window, &Window::liveChanged, this, [this, window](bool isAlive) {
115+ if (!isAlive && window->state() == Mir::HiddenState) {
116+ // Hidden windows are not in the model. So just delete it right away.
117+ delete window;
118+ }
119+ });
120 }
121
122 void TopLevelWindowModel::activateEmptyWindow(Window *window)
123@@ -308,7 +333,7 @@
124 }
125
126 if (m_windowModel[i].removeOnceSurfaceDestroyed) {
127- removeAt(i);
128+ deleteAt(i);
129 } else {
130 auto window = m_windowModel[i].window;
131 window->setSurface(nullptr);
132@@ -346,7 +371,16 @@
133 } else {
134 auto *application = m_applicationManager->findApplicationWithSurface(surface);
135 if (application) {
136- prependSurface(surface, application);
137+ if (surface->state() == Mir::HiddenState) {
138+ // Ignore it until it's finally shown
139+ connect(surface, &unityapi::MirSurfaceInterface::stateChanged, this, [=](Mir::State newState) {
140+ Q_ASSERT(newState != Mir::HiddenState);
141+ disconnect(surface, &unityapi::MirSurfaceInterface::stateChanged, this, 0);
142+ prependSurface(surface, application);
143+ });
144+ } else {
145+ prependSurface(surface, application);
146+ }
147 } else {
148 // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
149 // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
150@@ -361,6 +395,17 @@
151 }
152 }
153
154+void TopLevelWindowModel::deleteAt(int index)
155+{
156+ auto window = m_windowModel[index].window;
157+
158+ removeAt(index);
159+
160+ window->setSurface(nullptr);
161+
162+ delete window;
163+}
164+
165 void TopLevelWindowModel::removeAt(int index)
166 {
167 if (m_modelState == IdleState) {
168@@ -373,8 +418,9 @@
169
170 auto window = m_windowModel[index].window;
171
172- window->setSurface(nullptr);
173- window->setFocused(false);
174+ if (!window->surface()) {
175+ window->setFocused(false);
176+ }
177
178 m_windowModel.removeAt(index);
179
180@@ -385,11 +431,9 @@
181 m_modelState = IdleState;
182 }
183
184- disconnect(window, 0, this, 0);
185 if (m_focusedWindow == window) {
186 setFocusedWindow(nullptr);
187 }
188- delete window;
189
190 INFO_MSG << " after " << toString();
191 }
192
193=== modified file 'plugins/WindowManager/TopLevelWindowModel.h'
194--- plugins/WindowManager/TopLevelWindowModel.h 2017-01-26 11:10:01 +0000
195+++ plugins/WindowManager/TopLevelWindowModel.h 2017-03-16 15:31:06 +0000
196@@ -1,5 +1,5 @@
197 /*
198- * Copyright (C) 2016 Canonical, Ltd.
199+ * Copyright (C) 2016-2017 Canonical, Ltd.
200 *
201 * This program is free software: you can redistribute it and/or modify it under
202 * the terms of the GNU Lesser General Public License version 3, as published by
203@@ -20,6 +20,8 @@
204 #include <QAbstractListModel>
205 #include <QLoggingCategory>
206
207+#include "WindowManagerGlobal.h"
208+
209 Q_DECLARE_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL)
210
211 class Window;
212@@ -47,7 +49,7 @@
213 * As applications can have multiple surfaces and you can also have entries without surfaces at all,
214 * the only way to unambiguously refer to an entry in this model is through its id.
215 */
216-class TopLevelWindowModel : public QAbstractListModel
217+class WINDOWMANAGERQML_EXPORT TopLevelWindowModel : public QAbstractListModel
218 {
219 Q_OBJECT
220
221@@ -197,6 +199,7 @@
222 void setFocusedWindow(Window *window);
223 void removeInputMethodWindow();
224 int findIndexOf(const unity::shell::application::MirSurfaceInterface *surface) const;
225+ void deleteAt(int index);
226 void removeAt(int index);
227
228 void addApplication(unity::shell::application::ApplicationInfoInterface *application);
229@@ -207,6 +210,7 @@
230 unity::shell::application::ApplicationInfoInterface *application);
231 void prependSurfaceHelper(unity::shell::application::MirSurfaceInterface *surface,
232 unity::shell::application::ApplicationInfoInterface *application);
233+ void prependWindow(Window *window, unity::shell::application::ApplicationInfoInterface *application);
234
235 void connectWindow(Window *window);
236 void connectSurface(unity::shell::application::MirSurfaceInterface *surface);
237
238=== modified file 'plugins/WindowManager/Window.cpp'
239--- plugins/WindowManager/Window.cpp 2017-03-16 15:31:06 +0000
240+++ plugins/WindowManager/Window.cpp 2017-03-16 15:31:06 +0000
241@@ -178,6 +178,12 @@
242 }
243 });
244
245+ connect(surface, &unityapi::MirSurfaceInterface::liveChanged, this, &Window::liveChanged);
246+
247+ connect(surface, &QObject::destroyed, this, [this]() {
248+ setSurface(nullptr);
249+ });
250+
251 // bring it up to speed
252 if (m_positionRequested) {
253 m_surface->setRequestedPosition(m_requestedPosition);
254
255=== modified file 'plugins/WindowManager/Window.h'
256--- plugins/WindowManager/Window.h 2017-03-16 15:31:06 +0000
257+++ plugins/WindowManager/Window.h 2017-03-16 15:31:06 +0000
258@@ -24,6 +24,8 @@
259 // Unity API
260 #include <unity/shell/application/Mir.h>
261
262+#include "WindowManagerGlobal.h"
263+
264 namespace unity {
265 namespace shell {
266 namespace application {
267@@ -42,7 +44,7 @@
268 was killed to free up memory, as it should still remain in the window list since the user
269 did not explicitly close it).
270 */
271-class Window : public QObject
272+class WINDOWMANAGERQML_EXPORT Window : public QObject
273 {
274 Q_OBJECT
275
276@@ -144,6 +146,7 @@
277 void confinesMousePointerChanged(bool value);
278 void surfaceChanged(unity::shell::application::MirSurfaceInterface *surface);
279 void allowClientResizeChanged(bool value);
280+ void liveChanged(bool value);
281
282 /**
283 * @brief Emitted when focus for this window is requested by an external party
284
285=== added file 'plugins/WindowManager/WindowManagerGlobal.h'
286--- plugins/WindowManager/WindowManagerGlobal.h 1970-01-01 00:00:00 +0000
287+++ plugins/WindowManager/WindowManagerGlobal.h 2017-03-16 15:31:06 +0000
288@@ -0,0 +1,23 @@
289+/*
290+ * Copyright (C) 2017 Canonical, Ltd.
291+ *
292+ * This program is free software; you can redistribute it and/or modify
293+ * it under the terms of the GNU General Public License as published by
294+ * the Free Software Foundation; version 3.
295+ *
296+ * This program is distributed in the hope that it will be useful,
297+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
298+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
299+ * GNU General Public License for more details.
300+ *
301+ * You should have received a copy of the GNU General Public License
302+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
303+ */
304+
305+#include <QtCore/QtGlobal>
306+
307+#if defined(WINDOWMANAGERQML_LIBRARY)
308+# define WINDOWMANAGERQML_EXPORT Q_DECL_EXPORT
309+#else
310+# define WINDOWMANAGERQML_EXPORT Q_DECL_IMPORT
311+#endif
312
313=== modified file 'tests/mocks/Unity/Application/MirSurfaceListModel.cpp'
314--- tests/mocks/Unity/Application/MirSurfaceListModel.cpp 2016-11-30 19:24:02 +0000
315+++ tests/mocks/Unity/Application/MirSurfaceListModel.cpp 2017-03-16 15:31:06 +0000
316@@ -1,5 +1,5 @@
317 /*
318- * Copyright (C) 2016 Canonical, Ltd.
319+ * Copyright (C) 2016-2017 Canonical, Ltd.
320 *
321 * This program is free software; you can redistribute it and/or modify
322 * it under the terms of the GNU General Public License as published by
323@@ -17,7 +17,7 @@
324 #include "MirSurfaceListModel.h"
325 #include "ApplicationInfo.h"
326
327-#include "MirSurface.h"
328+#include <unity/shell/application/MirSurfaceInterface.h>
329
330 #define MIRSURFACELISTMODEL_DEBUG 0
331
332@@ -46,14 +46,14 @@
333 return QVariant();
334
335 if (role == SurfaceRole) {
336- MirSurface *surface = m_surfaceList.at(index.row());
337+ MirSurfaceInterface *surface = m_surfaceList.at(index.row());
338 return QVariant::fromValue(static_cast<unity::shell::application::MirSurfaceInterface*>(surface));
339 } else {
340 return QVariant();
341 }
342 }
343
344-void MirSurfaceListModel::raise(MirSurface *surface)
345+void MirSurfaceListModel::raise(MirSurfaceInterface *surface)
346 {
347 DEBUG_MSG("(" << surface << ")");
348 int i = m_surfaceList.indexOf(surface);
349@@ -62,7 +62,7 @@
350 }
351 }
352
353-void MirSurfaceListModel::addSurface(MirSurface *surface)
354+void MirSurfaceListModel::addSurface(MirSurfaceInterface *surface)
355 {
356 DEBUG_MSG("(" << surface << ")");
357 beginInsertRows(QModelIndex(), 0, 0);
358@@ -73,7 +73,7 @@
359 Q_EMIT firstChanged();
360 }
361
362-void MirSurfaceListModel::connectSurface(MirSurface *surface)
363+void MirSurfaceListModel::connectSurface(MirSurfaceInterface *surface)
364 {
365 connect(surface, &QObject::destroyed, this, [this, surface](){ this->removeSurface(surface); });
366 connect(surface, &MirSurfaceInterface::focusedChanged, this, [this, surface](bool surfaceFocused){
367@@ -83,7 +83,7 @@
368 });
369 }
370
371-void MirSurfaceListModel::removeSurface(MirSurface *surface)
372+void MirSurfaceListModel::removeSurface(MirSurfaceInterface *surface)
373 {
374 int i = m_surfaceList.indexOf(surface);
375 if (i != -1) {
376
377=== modified file 'tests/mocks/Unity/Application/MirSurfaceListModel.h'
378--- tests/mocks/Unity/Application/MirSurfaceListModel.h 2017-01-26 11:10:01 +0000
379+++ tests/mocks/Unity/Application/MirSurfaceListModel.h 2017-03-16 15:31:06 +0000
380@@ -1,5 +1,5 @@
381 /*
382- * Copyright (C) 2016 Canonical, Ltd.
383+ * Copyright (C) 2016-2017 Canonical, Ltd.
384 *
385 * This program is free software; you can redistribute it and/or modify
386 * it under the terms of the GNU General Public License as published by
387@@ -23,8 +23,6 @@
388 #include <QAbstractListModel>
389 #include <QList>
390
391-class MirSurface;
392-
393 class MirSurfaceListModel : public unity::shell::application::MirSurfaceListInterface
394 {
395 Q_OBJECT
396@@ -38,17 +36,17 @@
397 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
398 QVariant data(const QModelIndex& index, int role) const override;
399
400- void addSurface(MirSurface *surface);
401- void removeSurface(MirSurface *surface);
402+ void addSurface(unity::shell::application::MirSurfaceInterface *surface);
403+ void removeSurface(unity::shell::application::MirSurfaceInterface *surface);
404
405- bool contains(MirSurface *surface) const { return m_surfaceList.contains(surface); }
406+ bool contains(unity::shell::application::MirSurfaceInterface *surface) const { return m_surfaceList.contains(surface); }
407
408 private:
409- void raise(MirSurface *surface);
410+ void raise(unity::shell::application::MirSurfaceInterface *surface);
411 void moveSurface(int from, int to);
412- void connectSurface(MirSurface *surface);
413+ void connectSurface(unity::shell::application::MirSurfaceInterface *surface);
414
415- QList<MirSurface*> m_surfaceList;
416+ QList<unity::shell::application::MirSurfaceInterface*> m_surfaceList;
417 };
418
419 Q_DECLARE_METATYPE(MirSurfaceListModel*)
420
421=== modified file 'tests/plugins/CMakeLists.txt'
422--- tests/plugins/CMakeLists.txt 2016-08-30 14:06:47 +0000
423+++ tests/plugins/CMakeLists.txt 2017-03-16 15:31:06 +0000
424@@ -9,4 +9,5 @@
425 add_subdirectory(Ubuntu)
426 add_subdirectory(Unity)
427 add_subdirectory(Utils)
428+add_subdirectory(WindowManager)
429 add_subdirectory(Wizard)
430
431=== added directory 'tests/plugins/WindowManager'
432=== added file 'tests/plugins/WindowManager/CMakeLists.txt'
433--- tests/plugins/WindowManager/CMakeLists.txt 1970-01-01 00:00:00 +0000
434+++ tests/plugins/WindowManager/CMakeLists.txt 2017-03-16 15:31:06 +0000
435@@ -0,0 +1,34 @@
436+include_directories(
437+ ${CMAKE_SOURCE_DIR}/plugins/WindowManager
438+ ${CMAKE_CURRENT_BINARY_DIR}
439+ ${CMAKE_SOURCE_DIR}/tests/mocks
440+ )
441+
442+add_executable(TopLevelWindowModelTestExec
443+ tst_TopLevelWindowModel.cpp
444+ UnityApplicationMocks.h
445+
446+ ${CMAKE_SOURCE_DIR}/tests/mocks/Unity/Application/MirSurfaceListModel.cpp
447+
448+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
449+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
450+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
451+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
452+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
453+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/SurfaceManagerInterface.h
454+ )
455+qt5_use_modules(TopLevelWindowModelTestExec Test Core Gui Qml Quick)
456+
457+target_link_libraries(TopLevelWindowModelTestExec windowmanager-qml)
458+
459+install(TARGETS TopLevelWindowModelTestExec
460+ DESTINATION "${SHELL_PRIVATE_LIBDIR}/tests/plugins/WindowManager"
461+)
462+
463+# To find libwindowmanager-qml.so
464+set_target_properties(TopLevelWindowModelTestExec PROPERTIES
465+ INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}")
466+
467+add_unity8_unittest(TopLevelWindowModel TopLevelWindowModelTestExec
468+ ENVIRONMENT LD_LIBRARY_PATH=${UNITY_PLUGINPATH}/WindowManager
469+)
470
471=== added file 'tests/plugins/WindowManager/UnityApplicationMocks.h'
472--- tests/plugins/WindowManager/UnityApplicationMocks.h 1970-01-01 00:00:00 +0000
473+++ tests/plugins/WindowManager/UnityApplicationMocks.h 2017-03-16 15:31:06 +0000
474@@ -0,0 +1,203 @@
475+/*
476+ * Copyright (C) 2017 Canonical, Ltd.
477+ *
478+ * This program is free software; you can redistribute it and/or modify
479+ * it under the terms of the GNU General Public License as published by
480+ * the Free Software Foundation; version 3.
481+ *
482+ * This program is distributed in the hope that it will be useful,
483+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
484+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
485+ * GNU General Public License for more details.
486+ *
487+ * You should have received a copy of the GNU General Public License
488+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
489+ */
490+
491+#ifndef UNITYAPPLICATIONMOCKS_H
492+#define UNITYAPPLICATIONMOCKS_H
493+
494+#include <unity/shell/application/ApplicationInfoInterface.h>
495+#include <unity/shell/application/ApplicationManagerInterface.h>
496+#include <unity/shell/application/MirSurfaceInterface.h>
497+#include <unity/shell/application/SurfaceManagerInterface.h>
498+
499+// from tests/mocks
500+#include <Unity/Application/MirSurfaceListModel.h>
501+
502+using namespace unity::shell::application;
503+
504+class MirSurface : public MirSurfaceInterface
505+{
506+ Q_OBJECT
507+public:
508+ Mir::Type type() const override { return m_type; }
509+ QString name() const override { return QString("foo"); }
510+ QString persistentId() const override { return QString("a-b-c-my-id"); }
511+ QString appId() const override { return QString(); }
512+ QPoint position() const override { return QPoint(); }
513+ QSize size() const override { return QSize(); }
514+ void resize(int, int) override {}
515+ void resize(const QSize &) override {}
516+ Mir::State state() const override { return m_state; }
517+ bool live() const override { return m_live; }
518+ bool visible() const override { return true; }
519+ Mir::OrientationAngle orientationAngle() const override { return Mir::Angle0; }
520+ void setOrientationAngle(Mir::OrientationAngle) override {}
521+
522+ int minimumWidth() const override { return 0; }
523+ int minimumHeight() const override { return 0; }
524+ int maximumWidth() const override { return 0; }
525+ int maximumHeight() const override { return 0; }
526+ int widthIncrement() const override { return 0; }
527+ int heightIncrement() const override { return 0; }
528+
529+ void setKeymap(const QString &) override {}
530+ QString keymap() const override { return QString(); }
531+ Mir::ShellChrome shellChrome() const override { return Mir::NormalChrome; }
532+ bool focused() const override { return true; }
533+ QRect inputBounds() const override { return QRect(); }
534+ bool confinesMousePointer() const override { return false; }
535+ bool allowClientResize() const override { return true; }
536+ void setAllowClientResize(bool) override {}
537+ QPoint requestedPosition() const override { return QPoint(); }
538+ void setRequestedPosition(const QPoint &) override {}
539+ MirSurfaceInterface* parentSurface() const override { return nullptr; }
540+ unity::shell::application::MirSurfaceListInterface* childSurfaceList() const override { return nullptr; }
541+ void close() override {}
542+ void activate() override {}
543+
544+public Q_SLOTS:
545+ void requestState(Mir::State value) override
546+ {
547+ if (m_state != value) {
548+ m_state = value;
549+ Q_EMIT stateChanged(m_state);
550+ }
551+ }
552+
553+public:
554+ Mir::Type m_type { Mir::NormalType };
555+ Mir::State m_state { Mir::RestoredState };
556+ bool m_live { true };
557+};
558+
559+class SurfaceManager : public SurfaceManagerInterface
560+{
561+ Q_OBJECT
562+
563+public:
564+ void raise(MirSurfaceInterface *) override {}
565+ void activate(MirSurfaceInterface *) override {}
566+};
567+
568+class Application : public ApplicationInfoInterface
569+{
570+ Q_OBJECT
571+public:
572+ Application(QString appId) : ApplicationInfoInterface(appId, nullptr), m_appId(std::move(appId)) {}
573+
574+ void close() override {}
575+ QString appId() const override { return m_appId;}
576+ QString name() const override { return "foo"; }
577+ QString comment() const override { return "bar"; }
578+ QUrl icon() const override { return QUrl(); }
579+ State state() const override { return m_state; }
580+ RequestedState requestedState() const override { return m_requestedState; }
581+ void setRequestedState(RequestedState value) override
582+ {
583+ if (value != m_requestedState) {
584+ m_requestedState = value;
585+ Q_EMIT requestedStateChanged(value);
586+ }
587+ }
588+ bool focused() const override { return false; }
589+ QString splashTitle() const override { return QString(); }
590+ QUrl splashImage() const override { return QUrl(); }
591+ bool splashShowHeader() const override { return false; }
592+ QColor splashColor() const override { return QColor(); }
593+ QColor splashColorHeader() const override { return QColor(); }
594+ QColor splashColorFooter() const override { return QColor(); }
595+ Qt::ScreenOrientations supportedOrientations() const override { return Qt::LandscapeOrientation; }
596+ bool rotatesWindowContents() const override { return false; }
597+ bool isTouchApp() const override { return false; }
598+ bool exemptFromLifecycle() const override { return false; }
599+ void setExemptFromLifecycle(bool) override {}
600+ QSize initialSurfaceSize() const override { return QSize(); }
601+ void setInitialSurfaceSize(const QSize &) override {}
602+ MirSurfaceListInterface* surfaceList() const override { return &m_surfaceList; }
603+ MirSurfaceListInterface* promptSurfaceList() const override { return nullptr; }
604+ int surfaceCount() const override { return 0; }
605+
606+ QString m_appId;
607+ State m_state;
608+ RequestedState m_requestedState;
609+ mutable MirSurfaceListModel m_surfaceList;
610+};
611+
612+class ApplicationManager : public ApplicationManagerInterface
613+{
614+ Q_OBJECT
615+
616+public:
617+
618+ int rowCount(const QModelIndex &) const override
619+ {
620+ return m_applications.count();
621+ }
622+
623+ QVariant data(const QModelIndex &/*index*/, int /*role*/) const override
624+ {
625+ return QVariant();
626+ }
627+
628+ QString focusedApplicationId() const override {return QString();}
629+
630+ ApplicationInfoInterface *get(int index) const override
631+ {
632+ return m_applications[index];
633+ }
634+
635+ ApplicationInfoInterface *findApplication(const QString &appId) const override
636+ {
637+ Q_UNUSED(appId);
638+ return nullptr;
639+ }
640+
641+ ApplicationInfoInterface *findApplicationWithSurface(MirSurfaceInterface* surface) const override
642+ {
643+ for (int i = 0; i < m_applications.count(); ++i) {
644+ if (m_applications[i]->m_surfaceList.contains(surface)) {
645+ return m_applications[i];
646+ }
647+ }
648+ return nullptr;
649+ }
650+
651+ bool requestFocusApplication(const QString &appId) override
652+ {
653+ Q_UNUSED(appId);
654+ return false;
655+ }
656+
657+ ApplicationInfoInterface *startApplication(const QString &appId, const QStringList &/*arguments*/) override
658+ {
659+ Application *application = new Application(appId);
660+ prepend(application);
661+ return application;
662+ }
663+
664+ bool stopApplication(const QString &) override { return true; }
665+
666+private:
667+ void prepend(Application *application)
668+ {
669+ beginInsertRows(QModelIndex(), 0 /*first*/, 0 /*last*/);
670+ m_applications.append(application);
671+ endInsertRows();
672+ }
673+
674+ QList<Application*> m_applications;
675+};
676+
677+#endif // UNITYAPPLICATIONMOCKS_H
678
679=== added file 'tests/plugins/WindowManager/tst_TopLevelWindowModel.cpp'
680--- tests/plugins/WindowManager/tst_TopLevelWindowModel.cpp 1970-01-01 00:00:00 +0000
681+++ tests/plugins/WindowManager/tst_TopLevelWindowModel.cpp 2017-03-16 15:31:06 +0000
682@@ -0,0 +1,130 @@
683+/*
684+ * Copyright (C) 2017 Canonical, Ltd.
685+ *
686+ * This program is free software; you can redistribute it and/or modify
687+ * it under the terms of the GNU General Public License as published by
688+ * the Free Software Foundation; version 3.
689+ *
690+ * This program is distributed in the hope that it will be useful,
691+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
692+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
693+ * GNU General Public License for more details.
694+ *
695+ * You should have received a copy of the GNU General Public License
696+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
697+ */
698+
699+#include <QtTest/QtTest>
700+
701+// WindowManager plugin
702+#include <TopLevelWindowModel.h>
703+#include <Window.h>
704+
705+#include "UnityApplicationMocks.h"
706+
707+class tst_TopLevelWindowModel : public QObject
708+{
709+ Q_OBJECT
710+
711+private Q_SLOTS:
712+ void init(); // called right before each and every test function is executed
713+ void cleanup(); // called right after each and every test function is executed
714+
715+ void singleSurfaceStartsHidden();
716+ void secondSurfaceIsHidden();
717+
718+private:
719+ ApplicationManager *applicationManager{nullptr};
720+ SurfaceManager *surfaceManager{nullptr};
721+ TopLevelWindowModel *topLevelWindowModel{nullptr};
722+};
723+
724+void tst_TopLevelWindowModel::init()
725+{
726+ applicationManager = new ApplicationManager;
727+ surfaceManager = new SurfaceManager;
728+
729+ topLevelWindowModel = new TopLevelWindowModel;
730+ topLevelWindowModel->setApplicationManager(applicationManager);
731+ topLevelWindowModel->setSurfaceManager(surfaceManager);
732+}
733+
734+void tst_TopLevelWindowModel::cleanup()
735+{
736+ delete topLevelWindowModel;
737+ topLevelWindowModel = nullptr;
738+
739+ delete surfaceManager;
740+ surfaceManager = nullptr;
741+
742+ delete applicationManager;
743+ applicationManager = nullptr;
744+}
745+
746+void tst_TopLevelWindowModel::singleSurfaceStartsHidden()
747+{
748+ QCOMPARE(topLevelWindowModel->rowCount(), 0);
749+
750+ auto application = static_cast<Application*>(applicationManager->startApplication(QString("hello-world"), QStringList()));
751+
752+ QCOMPARE(topLevelWindowModel->rowCount(), 1);
753+ QCOMPARE((void*)topLevelWindowModel->windowAt(0)->surface(), (void*)nullptr);
754+
755+ auto surface = new MirSurface;
756+ surface->m_state = Mir::HiddenState;
757+ application->m_surfaceList.addSurface(surface);
758+ Q_EMIT surfaceManager->surfaceCreated(surface);
759+
760+ QCOMPARE(topLevelWindowModel->rowCount(), 1);
761+ // not showing the surface as it's still hidden
762+ QCOMPARE((void*)topLevelWindowModel->windowAt(0)->surface(), (void*)nullptr);
763+
764+ surface->requestState(Mir::RestoredState);
765+
766+ QCOMPARE(topLevelWindowModel->rowCount(), 1);
767+ // Now that the surface is no longer hidden, TopLevelWindowModel should expose it.
768+ QCOMPARE((void*)topLevelWindowModel->windowAt(0)->surface(), (void*)surface);
769+}
770+
771+void tst_TopLevelWindowModel::secondSurfaceIsHidden()
772+{
773+ QCOMPARE(topLevelWindowModel->rowCount(), 0);
774+
775+ auto application = static_cast<Application*>(applicationManager->startApplication(QString("hello-world"), QStringList()));
776+
777+ QCOMPARE(topLevelWindowModel->rowCount(), 1);
778+ QCOMPARE((void*)topLevelWindowModel->windowAt(0)->surface(), (void*)nullptr);
779+
780+ auto firstSurface = new MirSurface;
781+ application->m_surfaceList.addSurface(firstSurface);
782+ Q_EMIT surfaceManager->surfaceCreated(firstSurface);
783+
784+ QCOMPARE(topLevelWindowModel->rowCount(), 1);
785+ QCOMPARE((void*)topLevelWindowModel->windowAt(0)->surface(), (void*)firstSurface);
786+
787+ auto secondSurface = new MirSurface;
788+ secondSurface->m_state = Mir::HiddenState;
789+ application->m_surfaceList.addSurface(secondSurface);
790+ Q_EMIT surfaceManager->surfaceCreated(secondSurface);
791+
792+ // still only the first surface is exposed by TopLevelWindowModel
793+ QCOMPARE(topLevelWindowModel->rowCount(), 1);
794+ QCOMPARE((void*)topLevelWindowModel->windowAt(0)->surface(), (void*)firstSurface);
795+
796+ secondSurface->requestState(Mir::RestoredState);
797+
798+ // now the second surface finally shows up
799+ QCOMPARE(topLevelWindowModel->rowCount(), 2);
800+ QCOMPARE((void*)topLevelWindowModel->windowAt(0)->surface(), (void*)secondSurface);
801+ QCOMPARE((void*)topLevelWindowModel->windowAt(1)->surface(), (void*)firstSurface);
802+
803+ secondSurface->requestState(Mir::HiddenState);
804+
805+ // and it's gone again
806+ QCOMPARE(topLevelWindowModel->rowCount(), 1);
807+ QCOMPARE((void*)topLevelWindowModel->windowAt(0)->surface(), (void*)firstSurface);
808+}
809+
810+QTEST_MAIN(tst_TopLevelWindowModel)
811+
812+#include "tst_TopLevelWindowModel.moc"

Subscribers

People subscribed via source and target branches