Merge lp:~dandrader/unity8/hiddenWindows into lp:unity8
- hiddenWindows
- Merge into trunk
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 |
Related bugs: |
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
TopLevelWindowM
From the UI point of view, it's as if they didn't exist.
Description of the change
Prereq-archive: ppa:ci-
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.
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:2869
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
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
Albert Astals Cid (aacid) : | # |
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2870
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2870
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:2870
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Preview Diff
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" |
FAILED: Continuous integration, rev:2868 /unity8- jenkins. ubuntu. com/job/ lp-unity8- ci/3390/ /unity8- jenkins. ubuntu. com/job/ build/4469 /unity8- jenkins. ubuntu. com/job/ test-0- autopkgtest/ label=amd64, release= xenial+ overlay, testname= qmluitests. sh/2683 /unity8- jenkins. ubuntu. com/job/ test-0- autopkgtest/ label=amd64, release= zesty,testname= qmluitests. sh/2683 /unity8- jenkins. ubuntu. com/job/ build-0- fetch/4497 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 4324 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 4324/artifact/ output/ *zip*/output. zip /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= zesty/4324 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= zesty/4324/ artifact/ output/ *zip*/output. zip /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 4324 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 4324/artifact/ output/ *zip*/output. zip /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= zesty/4324 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= zesty/4324/ artifact/ output/ *zip*/output. zip /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 4324 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 4324/artifact/ output/ *zip*/output. zip /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= zesty/4324 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= zesty/4324/ artifact/ output/ *zip*/output. zip
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild: /unity8- jenkins. ubuntu. com/job/ lp-unity8- ci/3390/ rebuild
https:/