on xenial help sign in
TopLevelWindowModel: don't put hidden windows in the model
72172b5Daniel d'Andrada
plugins/WindowManager/CMakeLists.txt+2
@@ -9,6 +9,8 @@ set(WINDOWMANAGER_SRC
9 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/SurfaceManagerInterface.h
9 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/SurfaceManagerInterface.h
10 )
10 )
11
11
12+add_definitions(-DWINDOWMANAGERQML_LIBRARY)
13+
12add_library(windowmanager-qml SHARED ${WINDOWMANAGER_SRC})
14add_library(windowmanager-qml SHARED ${WINDOWMANAGER_SRC})
13
15
14qt5_use_modules(windowmanager-qml Qml Quick Gui)
16qt5_use_modules(windowmanager-qml Qml Quick Gui)
plugins/WindowManager/TopLevelWindowModel.cpp+64−20
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2016 Canonical, Ltd.
2+ * Copyright (C) 2016-2017 Canonical, Ltd.
3 *
3 *
4 * This program is free software: you can redistribute it and/or modify it under
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
5 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -130,7 +130,7 @@ void TopLevelWindowModel::removeApplication(unityapi::ApplicationInfoInterface *
130 int i = 0;
130 int i = 0;
131 while (i < m_windowModel.count()) {
131 while (i < m_windowModel.count()) {
132 if (m_windowModel.at(i).application == application) {
132 if (m_windowModel.at(i).application == application) {
133− removeAt(i);
133+ deleteAt(i);
134 } else {
134 } else {
135 ++i;
135 ++i;
136 }
136 }
@@ -148,12 +148,13 @@ void TopLevelWindowModel::prependSurface(unityapi::MirSurfaceInterface *surface,
148{
148{
149 Q_ASSERT(surface != nullptr);
149 Q_ASSERT(surface != nullptr);
150
150
151+ connectSurface(surface);
152+
151 bool filledPlaceholder = false;
153 bool filledPlaceholder = false;
152 for (int i = 0; i < m_windowModel.count() && !filledPlaceholder; ++i) {
154 for (int i = 0; i < m_windowModel.count() && !filledPlaceholder; ++i) {
153 ModelEntry &entry = m_windowModel[i];
155 ModelEntry &entry = m_windowModel[i];
154 if (entry.application == application && entry.window->surface() == nullptr) {
156 if (entry.application == application && entry.window->surface() == nullptr) {
155 entry.window->setSurface(surface);
157 entry.window->setSurface(surface);
156− connectSurface(surface);
157 INFO_MSG << " appId=" << application->appId() << " surface=" << surface
158 INFO_MSG << " appId=" << application->appId() << " surface=" << surface
158 << ", filling out placeholder. after: " << toString();
159 << ", filling out placeholder. after: " << toString();
159 filledPlaceholder = true;
160 filledPlaceholder = true;
@@ -167,6 +168,34 @@ void TopLevelWindowModel::prependSurface(unityapi::MirSurfaceInterface *surface,
167}
168}
168
169
169void TopLevelWindowModel::prependSurfaceHelper(unityapi::MirSurfaceInterface *surface, unityapi::ApplicationInfoInterface *application)
170void TopLevelWindowModel::prependSurfaceHelper(unityapi::MirSurfaceInterface *surface, unityapi::ApplicationInfoInterface *application)
171+{
172+
173+ Window *window = createWindow(surface);
174+
175+ connect(window, &Window::stateChanged, this, [=](Mir::State newState) {
176+ if (newState == Mir::HiddenState) {
177+ // Comply, removing it from our model. Just as if it didn't exist anymore.
178+ removeAt(indexForId(window->id()));
179+ } else {
180+ if (indexForId(window->id()) == -1) {
181+ // was probably hidden before. put it back on the list
182+ auto *application = m_applicationManager->findApplicationWithSurface(window->surface());
183+ Q_ASSERT(application);
184+ prependWindow(window, application);
185+ }
186+ }
187+ });
188+
189+ prependWindow(window, application);
190+
191+ if (!surface) {
192+ activateEmptyWindow(window);
193+ }
194+
195+ INFO_MSG << " after " << toString();
196+}
197+
198+void TopLevelWindowModel::prependWindow(Window *window, unityapi::ApplicationInfoInterface *application)
170{
199{
171 if (m_modelState == IdleState) {
200 if (m_modelState == IdleState) {
172 m_modelState = InsertingState;
201 m_modelState = InsertingState;
@@ -176,12 +205,7 @@ void TopLevelWindowModel::prependSurfaceHelper(unityapi::MirSurfaceInterface *su
176 // No point in signaling anything if we're resetting the whole model
205 // No point in signaling anything if we're resetting the whole model
177 }
206 }
178
207
179− Window *window = createWindow(surface);
180−
181 m_windowModel.prepend(ModelEntry(window, application));
208 m_windowModel.prepend(ModelEntry(window, application));
182− if (surface) {
183− connectSurface(surface);
184− }
185
209
186 if (m_modelState == InsertingState) {
210 if (m_modelState == InsertingState) {
187 endInsertRows();
211 endInsertRows();
@@ -189,12 +213,6 @@ void TopLevelWindowModel::prependSurfaceHelper(unityapi::MirSurfaceInterface *su
189 Q_EMIT listChanged();
213 Q_EMIT listChanged();
190 m_modelState = IdleState;
214 m_modelState = IdleState;
191 }
215 }
192−
193− if (!surface) {
194− activateEmptyWindow(window);
195− }
196−
197− INFO_MSG << " after " << toString();
198}
216}
199
217
200void TopLevelWindowModel::connectWindow(Window *window)
218void TopLevelWindowModel::connectWindow(Window *window)
@@ -243,6 +261,13 @@ void TopLevelWindowModel::connectWindow(Window *window)
243 connect(window, &Window::emptyWindowActivated, this, [this, window]() {
261 connect(window, &Window::emptyWindowActivated, this, [this, window]() {
244 activateEmptyWindow(window);
262 activateEmptyWindow(window);
245 });
263 });
264+
265+ connect(window, &Window::liveChanged, this, [this, window](bool isAlive) {
266+ if (!isAlive && window->state() == Mir::HiddenState) {
267+ // Hidden windows are not in the model. So just delete it right away.
268+ delete window;
269+ }
270+ });
246}
271}
247
272
248void TopLevelWindowModel::activateEmptyWindow(Window *window)
273void TopLevelWindowModel::activateEmptyWindow(Window *window)
@@ -308,7 +333,7 @@ void TopLevelWindowModel::onSurfaceDestroyed(unityapi::MirSurfaceInterface *surf
308 }
333 }
309
334
310 if (m_windowModel[i].removeOnceSurfaceDestroyed) {
335 if (m_windowModel[i].removeOnceSurfaceDestroyed) {
311− removeAt(i);
336+ deleteAt(i);
312 } else {
337 } else {
313 auto window = m_windowModel[i].window;
338 auto window = m_windowModel[i].window;
314 window->setSurface(nullptr);
339 window->setSurface(nullptr);
@@ -346,7 +371,16 @@ void TopLevelWindowModel::onSurfaceCreated(unityapi::MirSurfaceInterface *surfac
346 } else {
371 } else {
347 auto *application = m_applicationManager->findApplicationWithSurface(surface);
372 auto *application = m_applicationManager->findApplicationWithSurface(surface);
348 if (application) {
373 if (application) {
374+ if (surface->state() == Mir::HiddenState) {
375+ // Ignore it until it's finally shown
376+ connect(surface, &unityapi::MirSurfaceInterface::stateChanged, this, [=](Mir::State newState) {
377+ Q_ASSERT(newState != Mir::HiddenState);
378+ disconnect(surface, &unityapi::MirSurfaceInterface::stateChanged, this, 0);
349− prependSurface(surface, application);
379+ prependSurface(surface, application);
380+ });
381+ } else {
382+ prependSurface(surface, application);
383+ }
350 } else {
384 } else {
351 // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
385 // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
352 // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
386 // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
@@ -361,6 +395,17 @@ void TopLevelWindowModel::onSurfaceCreated(unityapi::MirSurfaceInterface *surfac
361 }
395 }
362}
396}
363
397
398+void TopLevelWindowModel::deleteAt(int index)
399+{
400+ auto window = m_windowModel[index].window;
401+
402+ removeAt(index);
403+
404+ window->setSurface(nullptr);
405+
406+ delete window;
407+}
408+
364void TopLevelWindowModel::removeAt(int index)
409void TopLevelWindowModel::removeAt(int index)
365{
410{
366 if (m_modelState == IdleState) {
411 if (m_modelState == IdleState) {
@@ -373,8 +418,9 @@ void TopLevelWindowModel::removeAt(int index)
373
418
374 auto window = m_windowModel[index].window;
419 auto window = m_windowModel[index].window;
375
420
376− window->setSurface(nullptr);
421+ if (!window->surface()) {
377− window->setFocused(false);
422+ window->setFocused(false);
423+ }
378
424
379 m_windowModel.removeAt(index);
425 m_windowModel.removeAt(index);
380
426
@@ -385,11 +431,9 @@ void TopLevelWindowModel::removeAt(int index)
385 m_modelState = IdleState;
431 m_modelState = IdleState;
386 }
432 }
387
433
388− disconnect(window, 0, this, 0);
389 if (m_focusedWindow == window) {
434 if (m_focusedWindow == window) {
390 setFocusedWindow(nullptr);
435 setFocusedWindow(nullptr);
391 }
436 }
392− delete window;
393
437
394 INFO_MSG << " after " << toString();
438 INFO_MSG << " after " << toString();
395}
439}
plugins/WindowManager/TopLevelWindowModel.h+6−2
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2016 Canonical, Ltd.
2+ * Copyright (C) 2016-2017 Canonical, Ltd.
3 *
3 *
4 * This program is free software: you can redistribute it and/or modify it under
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
5 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -20,6 +20,8 @@
20#include <QAbstractListModel>
20#include <QAbstractListModel>
21#include <QLoggingCategory>
21#include <QLoggingCategory>
22
22
23+#include "WindowManagerGlobal.h"
24+
23Q_DECLARE_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL)
25Q_DECLARE_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL)
24
26
25class Window;
27class Window;
@@ -47,7 +49,7 @@ namespace unity {
47 * As applications can have multiple surfaces and you can also have entries without surfaces at all,
49 * As applications can have multiple surfaces and you can also have entries without surfaces at all,
48 * the only way to unambiguously refer to an entry in this model is through its id.
50 * the only way to unambiguously refer to an entry in this model is through its id.
49 */
51 */
50−class TopLevelWindowModel : public QAbstractListModel
52+class WINDOWMANAGERQML_EXPORT TopLevelWindowModel : public QAbstractListModel
51{
53{
52 Q_OBJECT
54 Q_OBJECT
53
55
@@ -197,6 +199,7 @@ private:
197 void setFocusedWindow(Window *window);
199 void setFocusedWindow(Window *window);
198 void removeInputMethodWindow();
200 void removeInputMethodWindow();
199 int findIndexOf(const unity::shell::application::MirSurfaceInterface *surface) const;
201 int findIndexOf(const unity::shell::application::MirSurfaceInterface *surface) const;
202+ void deleteAt(int index);
200 void removeAt(int index);
203 void removeAt(int index);
201
204
202 void addApplication(unity::shell::application::ApplicationInfoInterface *application);
205 void addApplication(unity::shell::application::ApplicationInfoInterface *application);
@@ -207,6 +210,7 @@ private:
207 unity::shell::application::ApplicationInfoInterface *application);
210 unity::shell::application::ApplicationInfoInterface *application);
208 void prependSurfaceHelper(unity::shell::application::MirSurfaceInterface *surface,
211 void prependSurfaceHelper(unity::shell::application::MirSurfaceInterface *surface,
209 unity::shell::application::ApplicationInfoInterface *application);
212 unity::shell::application::ApplicationInfoInterface *application);
213+ void prependWindow(Window *window, unity::shell::application::ApplicationInfoInterface *application);
210
214
211 void connectWindow(Window *window);
215 void connectWindow(Window *window);
212 void connectSurface(unity::shell::application::MirSurfaceInterface *surface);
216 void connectSurface(unity::shell::application::MirSurfaceInterface *surface);
plugins/WindowManager/Window.cpp+6
@@ -178,6 +178,12 @@ void Window::setSurface(unityapi::MirSurfaceInterface *surface)
178 }
178 }
179 });
179 });
180
180
181+ connect(surface, &unityapi::MirSurfaceInterface::liveChanged, this, &Window::liveChanged);
182+
183+ connect(surface, &QObject::destroyed, this, [this]() {
184+ setSurface(nullptr);
185+ });
186+
181 // bring it up to speed
187 // bring it up to speed
182 if (m_positionRequested) {
188 if (m_positionRequested) {
183 m_surface->setRequestedPosition(m_requestedPosition);
189 m_surface->setRequestedPosition(m_requestedPosition);
plugins/WindowManager/Window.h+4−1
@@ -24,6 +24,8 @@
24// Unity API
24// Unity API
25#include <unity/shell/application/Mir.h>
25#include <unity/shell/application/Mir.h>
26
26
27+#include "WindowManagerGlobal.h"
28+
27namespace unity {
29namespace unity {
28 namespace shell {
30 namespace shell {
29 namespace application {
31 namespace application {
@@ -42,7 +44,7 @@ Q_DECLARE_LOGGING_CATEGORY(UNITY_WINDOW)
42 was killed to free up memory, as it should still remain in the window list since the user
44 was killed to free up memory, as it should still remain in the window list since the user
43 did not explicitly close it).
45 did not explicitly close it).
44 */
46 */
45−class Window : public QObject
47+class WINDOWMANAGERQML_EXPORT Window : public QObject
46{
48{
47 Q_OBJECT
49 Q_OBJECT
48
50
@@ -144,6 +146,7 @@ Q_SIGNALS:
144 void confinesMousePointerChanged(bool value);
146 void confinesMousePointerChanged(bool value);
145 void surfaceChanged(unity::shell::application::MirSurfaceInterface *surface);
147 void surfaceChanged(unity::shell::application::MirSurfaceInterface *surface);
146 void allowClientResizeChanged(bool value);
148 void allowClientResizeChanged(bool value);
149+ void liveChanged(bool value);
147
150
148 /**
151 /**
149 * @brief Emitted when focus for this window is requested by an external party
152 * @brief Emitted when focus for this window is requested by an external party
plugins/WindowManager/WindowManagerGlobal.hnew file+23
@@ -0,0 +1,23 @@
1+/*
2+ * Copyright (C) 2017 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <QtCore/QtGlobal>
18+
19+#if defined(WINDOWMANAGERQML_LIBRARY)
20+# define WINDOWMANAGERQML_EXPORT Q_DECL_EXPORT
21+#else
22+# define WINDOWMANAGERQML_EXPORT Q_DECL_IMPORT
23+#endif
tests/mocks/Unity/Application/MirSurfaceListModel.cpp+7−7
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2016 Canonical, Ltd.
2+ * Copyright (C) 2016-2017 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
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -17,7 +17,7 @@
17#include "MirSurfaceListModel.h"
17#include "MirSurfaceListModel.h"
18#include "ApplicationInfo.h"
18#include "ApplicationInfo.h"
19
19
20−#include "MirSurface.h"
20+#include <unity/shell/application/MirSurfaceInterface.h>
21
21
22#define MIRSURFACELISTMODEL_DEBUG 0
22#define MIRSURFACELISTMODEL_DEBUG 0
23
23
@@ -46,14 +46,14 @@ QVariant MirSurfaceListModel::data(const QModelIndex& index, int role) const
46 return QVariant();
46 return QVariant();
47
47
48 if (role == SurfaceRole) {
48 if (role == SurfaceRole) {
49− MirSurface *surface = m_surfaceList.at(index.row());
49+ MirSurfaceInterface *surface = m_surfaceList.at(index.row());
50 return QVariant::fromValue(static_cast<unity::shell::application::MirSurfaceInterface*>(surface));
50 return QVariant::fromValue(static_cast<unity::shell::application::MirSurfaceInterface*>(surface));
51 } else {
51 } else {
52 return QVariant();
52 return QVariant();
53 }
53 }
54}
54}
55
55
56−void MirSurfaceListModel::raise(MirSurface *surface)
56+void MirSurfaceListModel::raise(MirSurfaceInterface *surface)
57{
57{
58 DEBUG_MSG("(" << surface << ")");
58 DEBUG_MSG("(" << surface << ")");
59 int i = m_surfaceList.indexOf(surface);
59 int i = m_surfaceList.indexOf(surface);
@@ -62,7 +62,7 @@ void MirSurfaceListModel::raise(MirSurface *surface)
62 }
62 }
63}
63}
64
64
65−void MirSurfaceListModel::addSurface(MirSurface *surface)
65+void MirSurfaceListModel::addSurface(MirSurfaceInterface *surface)
66{
66{
67 DEBUG_MSG("(" << surface << ")");
67 DEBUG_MSG("(" << surface << ")");
68 beginInsertRows(QModelIndex(), 0, 0);
68 beginInsertRows(QModelIndex(), 0, 0);
@@ -73,7 +73,7 @@ void MirSurfaceListModel::addSurface(MirSurface *surface)
73 Q_EMIT firstChanged();
73 Q_EMIT firstChanged();
74}
74}
75
75
76−void MirSurfaceListModel::connectSurface(MirSurface *surface)
76+void MirSurfaceListModel::connectSurface(MirSurfaceInterface *surface)
77{
77{
78 connect(surface, &QObject::destroyed, this, [this, surface](){ this->removeSurface(surface); });
78 connect(surface, &QObject::destroyed, this, [this, surface](){ this->removeSurface(surface); });
79 connect(surface, &MirSurfaceInterface::focusedChanged, this, [this, surface](bool surfaceFocused){
79 connect(surface, &MirSurfaceInterface::focusedChanged, this, [this, surface](bool surfaceFocused){
@@ -83,7 +83,7 @@ void MirSurfaceListModel::connectSurface(MirSurface *surface)
83 });
83 });
84}
84}
85
85
86−void MirSurfaceListModel::removeSurface(MirSurface *surface)
86+void MirSurfaceListModel::removeSurface(MirSurfaceInterface *surface)
87{
87{
88 int i = m_surfaceList.indexOf(surface);
88 int i = m_surfaceList.indexOf(surface);
89 if (i != -1) {
89 if (i != -1) {
tests/mocks/Unity/Application/MirSurfaceListModel.h+7−9
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2016 Canonical, Ltd.
2+ * Copyright (C) 2016-2017 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
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -23,8 +23,6 @@
23#include <QAbstractListModel>
23#include <QAbstractListModel>
24#include <QList>
24#include <QList>
25
25
26−class MirSurface;
27−
28class MirSurfaceListModel : public unity::shell::application::MirSurfaceListInterface
26class MirSurfaceListModel : public unity::shell::application::MirSurfaceListInterface
29{
27{
30 Q_OBJECT
28 Q_OBJECT
@@ -38,17 +36,17 @@ public:
38 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
36 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
39 QVariant data(const QModelIndex& index, int role) const override;
37 QVariant data(const QModelIndex& index, int role) const override;
40
38
41− void addSurface(MirSurface *surface);
39+ void addSurface(unity::shell::application::MirSurfaceInterface *surface);
42− void removeSurface(MirSurface *surface);
40+ void removeSurface(unity::shell::application::MirSurfaceInterface *surface);
43
41
44− bool contains(MirSurface *surface) const { return m_surfaceList.contains(surface); }
42+ bool contains(unity::shell::application::MirSurfaceInterface *surface) const { return m_surfaceList.contains(surface); }
45
43
46private:
44private:
47− void raise(MirSurface *surface);
45+ void raise(unity::shell::application::MirSurfaceInterface *surface);
48 void moveSurface(int from, int to);
46 void moveSurface(int from, int to);
49− void connectSurface(MirSurface *surface);
47+ void connectSurface(unity::shell::application::MirSurfaceInterface *surface);
50
48
51− QList<MirSurface*> m_surfaceList;
49+ QList<unity::shell::application::MirSurfaceInterface*> m_surfaceList;
52};
50};
53
51
54Q_DECLARE_METATYPE(MirSurfaceListModel*)
52Q_DECLARE_METATYPE(MirSurfaceListModel*)
tests/plugins/CMakeLists.txt+1
@@ -9,4 +9,5 @@ add_subdirectory(SessionBroadcast)
9add_subdirectory(Ubuntu)
9add_subdirectory(Ubuntu)
10add_subdirectory(Unity)
10add_subdirectory(Unity)
11add_subdirectory(Utils)
11add_subdirectory(Utils)
12+add_subdirectory(WindowManager)
12add_subdirectory(Wizard)
13add_subdirectory(Wizard)
tests/plugins/WindowManager/CMakeLists.txtnew file+34
@@ -0,0 +1,34 @@
1+include_directories(
2+ ${CMAKE_SOURCE_DIR}/plugins/WindowManager
3+ ${CMAKE_CURRENT_BINARY_DIR}
4+ ${CMAKE_SOURCE_DIR}/tests/mocks
5+ )
6+
7+add_executable(TopLevelWindowModelTestExec
8+ tst_TopLevelWindowModel.cpp
9+ UnityApplicationMocks.h
10+
11+ ${CMAKE_SOURCE_DIR}/tests/mocks/Unity/Application/MirSurfaceListModel.cpp
12+
13+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
14+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
15+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
16+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
17+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
18+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/SurfaceManagerInterface.h
19+ )
20+qt5_use_modules(TopLevelWindowModelTestExec Test Core Gui Qml Quick)
21+
22+target_link_libraries(TopLevelWindowModelTestExec windowmanager-qml)
23+
24+install(TARGETS TopLevelWindowModelTestExec
25+ DESTINATION "${SHELL_PRIVATE_LIBDIR}/tests/plugins/WindowManager"
26+)
27+
28+# To find libwindowmanager-qml.so
29+set_target_properties(TopLevelWindowModelTestExec PROPERTIES
30+ INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}")
31+
32+add_unity8_unittest(TopLevelWindowModel TopLevelWindowModelTestExec
33+ ENVIRONMENT LD_LIBRARY_PATH=${UNITY_PLUGINPATH}/WindowManager
34+)
tests/plugins/WindowManager/UnityApplicationMocks.hnew file+203
@@ -0,0 +1,203 @@
1+/*
2+ * Copyright (C) 2017 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef UNITYAPPLICATIONMOCKS_H
18+#define UNITYAPPLICATIONMOCKS_H
19+
20+#include <unity/shell/application/ApplicationInfoInterface.h>
21+#include <unity/shell/application/ApplicationManagerInterface.h>
22+#include <unity/shell/application/MirSurfaceInterface.h>
23+#include <unity/shell/application/SurfaceManagerInterface.h>
24+
25+// from tests/mocks
26+#include <Unity/Application/MirSurfaceListModel.h>
27+
28+using namespace unity::shell::application;
29+
30+class MirSurface : public MirSurfaceInterface
31+{
32+ Q_OBJECT
33+public:
34+ Mir::Type type() const override { return m_type; }
35+ QString name() const override { return QString("foo"); }
36+ QString persistentId() const override { return QString("a-b-c-my-id"); }
37+ QString appId() const override { return QString(); }
38+ QPoint position() const override { return QPoint(); }
39+ QSize size() const override { return QSize(); }
40+ void resize(int, int) override {}
41+ void resize(const QSize &) override {}
42+ Mir::State state() const override { return m_state; }
43+ bool live() const override { return m_live; }
44+ bool visible() const override { return true; }
45+ Mir::OrientationAngle orientationAngle() const override { return Mir::Angle0; }
46+ void setOrientationAngle(Mir::OrientationAngle) override {}
47+
48+ int minimumWidth() const override { return 0; }
49+ int minimumHeight() const override { return 0; }
50+ int maximumWidth() const override { return 0; }
51+ int maximumHeight() const override { return 0; }
52+ int widthIncrement() const override { return 0; }
53+ int heightIncrement() const override { return 0; }
54+
55+ void setKeymap(const QString &) override {}
56+ QString keymap() const override { return QString(); }
57+ Mir::ShellChrome shellChrome() const override { return Mir::NormalChrome; }
58+ bool focused() const override { return true; }
59+ QRect inputBounds() const override { return QRect(); }
60+ bool confinesMousePointer() const override { return false; }
61+ bool allowClientResize() const override { return true; }
62+ void setAllowClientResize(bool) override {}
63+ QPoint requestedPosition() const override { return QPoint(); }
64+ void setRequestedPosition(const QPoint &) override {}
65+ MirSurfaceInterface* parentSurface() const override { return nullptr; }
66+ unity::shell::application::MirSurfaceListInterface* childSurfaceList() const override { return nullptr; }
67+ void close() override {}
68+ void activate() override {}
69+
70+public Q_SLOTS:
71+ void requestState(Mir::State value) override
72+ {
73+ if (m_state != value) {
74+ m_state = value;
75+ Q_EMIT stateChanged(m_state);
76+ }
77+ }
78+
79+public:
80+ Mir::Type m_type { Mir::NormalType };
81+ Mir::State m_state { Mir::RestoredState };
82+ bool m_live { true };
83+};
84+
85+class SurfaceManager : public SurfaceManagerInterface
86+{
87+ Q_OBJECT
88+
89+public:
90+ void raise(MirSurfaceInterface *) override {}
91+ void activate(MirSurfaceInterface *) override {}
92+};
93+
94+class Application : public ApplicationInfoInterface
95+{
96+ Q_OBJECT
97+public:
98+ Application(QString appId) : ApplicationInfoInterface(appId, nullptr), m_appId(std::move(appId)) {}
99+
100+ void close() override {}
101+ QString appId() const override { return m_appId;}
102+ QString name() const override { return "foo"; }
103+ QString comment() const override { return "bar"; }
104+ QUrl icon() const override { return QUrl(); }
105+ State state() const override { return m_state; }
106+ RequestedState requestedState() const override { return m_requestedState; }
107+ void setRequestedState(RequestedState value) override
108+ {
109+ if (value != m_requestedState) {
110+ m_requestedState = value;
111+ Q_EMIT requestedStateChanged(value);
112+ }
113+ }
114+ bool focused() const override { return false; }
115+ QString splashTitle() const override { return QString(); }
116+ QUrl splashImage() const override { return QUrl(); }
117+ bool splashShowHeader() const override { return false; }
118+ QColor splashColor() const override { return QColor(); }
119+ QColor splashColorHeader() const override { return QColor(); }
120+ QColor splashColorFooter() const override { return QColor(); }
121+ Qt::ScreenOrientations supportedOrientations() const override { return Qt::LandscapeOrientation; }
122+ bool rotatesWindowContents() const override { return false; }
123+ bool isTouchApp() const override { return false; }
124+ bool exemptFromLifecycle() const override { return false; }
125+ void setExemptFromLifecycle(bool) override {}
126+ QSize initialSurfaceSize() const override { return QSize(); }
127+ void setInitialSurfaceSize(const QSize &) override {}
128+ MirSurfaceListInterface* surfaceList() const override { return &m_surfaceList; }
129+ MirSurfaceListInterface* promptSurfaceList() const override { return nullptr; }
130+ int surfaceCount() const override { return 0; }
131+
132+ QString m_appId;
133+ State m_state;
134+ RequestedState m_requestedState;
135+ mutable MirSurfaceListModel m_surfaceList;
136+};
137+
138+class ApplicationManager : public ApplicationManagerInterface
139+{
140+ Q_OBJECT
141+
142+public:
143+
144+ int rowCount(const QModelIndex &) const override
145+ {
146+ return m_applications.count();
147+ }
148+
149+ QVariant data(const QModelIndex &/*index*/, int /*role*/) const override
150+ {
151+ return QVariant();
152+ }
153+
154+ QString focusedApplicationId() const override {return QString();}
155+
156+ ApplicationInfoInterface *get(int index) const override
157+ {
158+ return m_applications[index];
159+ }
160+
161+ ApplicationInfoInterface *findApplication(const QString &appId) const override
162+ {
163+ Q_UNUSED(appId);
164+ return nullptr;
165+ }
166+
167+ ApplicationInfoInterface *findApplicationWithSurface(MirSurfaceInterface* surface) const override
168+ {
169+ for (int i = 0; i < m_applications.count(); ++i) {
170+ if (m_applications[i]->m_surfaceList.contains(surface)) {
171+ return m_applications[i];
172+ }
173+ }
174+ return nullptr;
175+ }
176+
177+ bool requestFocusApplication(const QString &appId) override
178+ {
179+ Q_UNUSED(appId);
180+ return false;
181+ }
182+
183+ ApplicationInfoInterface *startApplication(const QString &appId, const QStringList &/*arguments*/) override
184+ {
185+ Application *application = new Application(appId);
186+ prepend(application);
187+ return application;
188+ }
189+
190+ bool stopApplication(const QString &) override { return true; }
191+
192+private:
193+ void prepend(Application *application)
194+ {
195+ beginInsertRows(QModelIndex(), 0 /*first*/, 0 /*last*/);
196+ m_applications.append(application);
197+ endInsertRows();
198+ }
199+
200+ QList<Application*> m_applications;
201+};
202+
203+#endif // UNITYAPPLICATIONMOCKS_H
tests/plugins/WindowManager/tst_TopLevelWindowModel.cppnew file+130
@@ -0,0 +1,130 @@
1+/*
2+ * Copyright (C) 2017 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <QtTest/QtTest>
18+
19+// WindowManager plugin
20+#include <TopLevelWindowModel.h>
21+#include <Window.h>
22+
23+#include "UnityApplicationMocks.h"
24+
25+class tst_TopLevelWindowModel : public QObject
26+{
27+ Q_OBJECT
28+
29+private Q_SLOTS:
30+ void init(); // called right before each and every test function is executed
31+ void cleanup(); // called right after each and every test function is executed
32+
33+ void singleSurfaceStartsHidden();
34+ void secondSurfaceIsHidden();
35+
36+private:
37+ ApplicationManager *applicationManager{nullptr};
38+ SurfaceManager *surfaceManager{nullptr};
39+ TopLevelWindowModel *topLevelWindowModel{nullptr};
40+};
41+
42+void tst_TopLevelWindowModel::init()
43+{
44+ applicationManager = new ApplicationManager;
45+ surfaceManager = new SurfaceManager;
46+
47+ topLevelWindowModel = new TopLevelWindowModel;
48+ topLevelWindowModel->setApplicationManager(applicationManager);
49+ topLevelWindowModel->setSurfaceManager(surfaceManager);
50+}
51+
52+void tst_TopLevelWindowModel::cleanup()
53+{
54+ delete topLevelWindowModel;
55+ topLevelWindowModel = nullptr;
56+
57+ delete surfaceManager;
58+ surfaceManager = nullptr;
59+
60+ delete applicationManager;
61+ applicationManager = nullptr;
62+}
63+
64+void tst_TopLevelWindowModel::singleSurfaceStartsHidden()
65+{
66+ QCOMPARE(topLevelWindowModel->rowCount(), 0);
67+
68+ auto application = static_cast<Application*>(applicationManager->startApplication(QString("hello-world"), QStringList()));
69+
70+ QCOMPARE(topLevelWindowModel->rowCount(), 1);
71+ QCOMPARE((void*)topLevelWindowModel->windowAt(0)->surface(), (void*)nullptr);
72+
73+ auto surface = new MirSurface;
74+ surface->m_state = Mir::HiddenState;
75+ application->m_surfaceList.addSurface(surface);
76+ Q_EMIT surfaceManager->surfaceCreated(surface);
77+
78+ QCOMPARE(topLevelWindowModel->rowCount(), 1);
79+ // not showing the surface as it's still hidden
80+ QCOMPARE((void*)topLevelWindowModel->windowAt(0)->surface(), (void*)nullptr);
81+
82+ surface->requestState(Mir::RestoredState);
83+
84+ QCOMPARE(topLevelWindowModel->rowCount(), 1);
85+ // Now that the surface is no longer hidden, TopLevelWindowModel should expose it.
86+ QCOMPARE((void*)topLevelWindowModel->windowAt(0)->surface(), (void*)surface);
87+}
88+
89+void tst_TopLevelWindowModel::secondSurfaceIsHidden()
90+{
91+ QCOMPARE(topLevelWindowModel->rowCount(), 0);
92+
93+ auto application = static_cast<Application*>(applicationManager->startApplication(QString("hello-world"), QStringList()));
94+
95+ QCOMPARE(topLevelWindowModel->rowCount(), 1);
96+ QCOMPARE((void*)topLevelWindowModel->windowAt(0)->surface(), (void*)nullptr);
97+
98+ auto firstSurface = new MirSurface;
99+ application->m_surfaceList.addSurface(firstSurface);
100+ Q_EMIT surfaceManager->surfaceCreated(firstSurface);
101+
102+ QCOMPARE(topLevelWindowModel->rowCount(), 1);
103+ QCOMPARE((void*)topLevelWindowModel->windowAt(0)->surface(), (void*)firstSurface);
104+
105+ auto secondSurface = new MirSurface;
106+ secondSurface->m_state = Mir::HiddenState;
107+ application->m_surfaceList.addSurface(secondSurface);
108+ Q_EMIT surfaceManager->surfaceCreated(secondSurface);
109+
110+ // still only the first surface is exposed by TopLevelWindowModel
111+ QCOMPARE(topLevelWindowModel->rowCount(), 1);
112+ QCOMPARE((void*)topLevelWindowModel->windowAt(0)->surface(), (void*)firstSurface);
113+
114+ secondSurface->requestState(Mir::RestoredState);
115+
116+ // now the second surface finally shows up
117+ QCOMPARE(topLevelWindowModel->rowCount(), 2);
118+ QCOMPARE((void*)topLevelWindowModel->windowAt(0)->surface(), (void*)secondSurface);
119+ QCOMPARE((void*)topLevelWindowModel->windowAt(1)->surface(), (void*)firstSurface);
120+
121+ secondSurface->requestState(Mir::HiddenState);
122+
123+ // and it's gone again
124+ QCOMPARE(topLevelWindowModel->rowCount(), 1);
125+ QCOMPARE((void*)topLevelWindowModel->windowAt(0)->surface(), (void*)firstSurface);
126+}
127+
128+QTEST_MAIN(tst_TopLevelWindowModel)
129+
130+#include "tst_TopLevelWindowModel.moc"