@@ -24,11 +24,16 @@
2424#include <unity/shell/application/SurfaceManagerInterface.h>
2525
2626// Qt
27−#include <QGuiApplication>
2827#include <QDebug>
2928
3029// local
3130#include "Window.h"
31+#include "Workspace.h"
32+#include "windowmanagementpolicy.h"
33+
34+// qtmir
35+#include <qtmir/mirserverapplication.h>
36+#include <qtmir/windowmodelnotifier.h>
3237
3338Q_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL, "toplevelwindowmodel", QtInfoMsg)
3439
@@ -55,35 +60,24 @@ void TopLevelWindowModel::setApplicationManager(unityapi::ApplicationManagerInte
5560 beginResetModel();
5661
5762 if (m_applicationManager) {
58− m_windowModel.clear();
5963 disconnect(m_applicationManager, 0, this, 0);
6064 }
6165
6266 m_applicationManager = value;
6367
64− if (m_applicationManager) {
65− connect(m_applicationManager, &QAbstractItemModel::rowsInserted,
66− this, [this](const QModelIndex &/*parent*/, int first, int last) {
67− for (int i = first; i <= last; ++i) {
68− auto application = m_applicationManager->get(i);
69− addApplication(application);
70− }
71− });
68+ if (m_applicationManager && m_workspace) {
7269
73− connect(m_applicationManager, &QAbstractItemModel::rowsAboutToBeRemoved,
74− this, [this](const QModelIndex &/*parent*/, int first, int last) {
75− for (int i = first; i <= last; ++i) {
76− auto application = m_applicationManager->get(i);
77− removeApplication(application);
78− }
79− });
70+ if (m_workspace->isActive()) {
71+ connectApplicationManager();
72+ }
8073
81− for (int i = 0; i < m_applicationManager->rowCount(); ++i) {
82− auto application = m_applicationManager->get(i);
83− addApplication(application);
74+ if (m_surfaceManager) {
75+ refreshWindows();
8476 }
8577 }
8678
79+ Q_EMIT applicationManagerChanged(m_applicationManager);
80+
8781 endResetModel();
8882 m_modelState = IdleState;
8983}
@@ -96,6 +90,11 @@ void TopLevelWindowModel::setSurfaceManager(unityapi::SurfaceManagerInterface *s
9690
9791 DEBUG_MSG << "(" << surfaceManager << ")";
9892
93+ Q_ASSERT(m_modelState == IdleState);
94+ m_modelState = ResettingState;
95+
96+ beginResetModel();
97+
9998 if (m_surfaceManager) {
10099 disconnect(m_surfaceManager, 0, this, 0);
101100 }
@@ -107,9 +106,71 @@ void TopLevelWindowModel::setSurfaceManager(unityapi::SurfaceManagerInterface *s
107106 connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesRaised, this, &TopLevelWindowModel::onSurfacesRaised);
108107 connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsStarted, this, &TopLevelWindowModel::onModificationsStarted);
109108 connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsEnded, this, &TopLevelWindowModel::onModificationsEnded);
109+
110+ connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesAddedToWorkspace,
111+ this, &TopLevelWindowModel::onSurfacesAddedToWorkspace);
112+ connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesAboutToBeRemovedFromWorkspace,
113+ this, &TopLevelWindowModel::onSurfacesAboutToBeRemovedFromWorkspace);
114+
115+ if (m_workspace && m_applicationManager) {
116+ refreshWindows();
117+ }
110118 }
111119
112120 Q_EMIT surfaceManagerChanged(m_surfaceManager);
121+
122+ endResetModel();
123+ m_modelState = IdleState;
124+}
125+
126+void TopLevelWindowModel::setWorkspace(Workspace *workspace)
127+{
128+ if (workspace == m_workspace) {
129+ return;
130+ }
131+
132+ DEBUG_MSG << "(" << workspace << ")";
133+
134+ Q_ASSERT(m_modelState == IdleState);
135+ m_modelState = ResettingState;
136+
137+ beginResetModel();
138+
139+ if (m_workspace) {
140+ m_windowModel.clear();
141+ m_allSurfaces.clear();
142+ if (m_applicationManager) {
143+ disconnect(m_applicationManager, 0, this, 0);
144+ }
145+ }
146+
147+ disconnect(m_applicationManager, 0 ,this, 0);
148+
149+ m_workspace = workspace;
150+
151+ if (m_workspace) {
152+ connect(m_workspace, &Workspace::activeChanged, this, [this](bool active) {
153+ if (m_applicationManager) {
154+ if (!active) disconnect(m_applicationManager, 0, this, 0);
155+ else {
156+ connectApplicationManager();
157+ }
158+ }
159+ });
160+
161+ if (m_applicationManager && m_workspace->isActive()) {
162+ connectApplicationManager();
163+ }
164+
165+ if (m_surfaceManager && m_applicationManager) {
166+ refreshWindows();
167+ }
168+ }
169+
170+ Q_EMIT workspaceChanged(workspace);
171+
172+ endResetModel();
173+ m_modelState = IdleState;
113174}
114175
115176void TopLevelWindowModel::addApplication(unityapi::ApplicationInfoInterface *application)
@@ -179,6 +240,7 @@ void TopLevelWindowModel::prependSurfaceHelper(unityapi::MirSurfaceInterface *su
179240 Window *window = createWindow(surface);
180241
181242 m_windowModel.prepend(ModelEntry(window, application));
243+ m_allSurfaces.insert(surface);
182244 if (surface) {
183245 connectSurface(surface);
184246 }
@@ -313,6 +375,7 @@ void TopLevelWindowModel::onSurfaceDestroyed(unityapi::MirSurfaceInterface *surf
313375 auto window = m_windowModel[i].window;
314376 window->setSurface(nullptr);
315377 window->setFocused(false);
378+ m_allSurfaces.remove(surface);
316379 INFO_MSG << " Removed surface from entry. After: " << toString();
317380 }
318381}
@@ -328,38 +391,102 @@ Window *TopLevelWindowModel::createWindow(unityapi::MirSurfaceInterface *surface
328391 return qmlWindow;
329392}
330393
331−void TopLevelWindowModel::onSurfaceCreated(unityapi::MirSurfaceInterface *surface)
394+
395+void TopLevelWindowModel::onSurfaceCreated(unityapi::MirSurfaceInterface */*surface*/)
332396{
333− DEBUG_MSG << "(" << surface << ")";
397+}
334398
335− if (surface->parentSurface()) {
336− // Wrap it in a Window so that we keep focusedWindow() up to date.
337− Window *window = createWindow(surface);
338− connect(surface, &QObject::destroyed, window, [=](){
339− window->setSurface(nullptr);
340− window->deleteLater();
341− });
342− } else {
343− if (surface->type() == Mir::InputMethodType) {
344− setInputMethodWindow(createWindow(surface));
399+void TopLevelWindowModel::onSurfacesAddedToWorkspace(const std::shared_ptr<miral::Workspace>& workspace,
400+ const QVector<unity::shell::application::MirSurfaceInterface*> surfaces)
401+{
402+ if (!m_workspace || !m_applicationManager) return;
403+ if (workspace != m_workspace->workspace()) return;
404+
405+ Q_FOREACH(auto surface, surfaces) {
406+ if (m_allSurfaces.contains(surface)) continue;
407+
408+ if (surface->parentSurface()) {
409+ // Wrap it in a Window so that we keep focusedWindow() up to date.
410+ Window *window = createWindow(surface);
411+ connect(surface, &QObject::destroyed, window, [=](){
412+ window->setSurface(nullptr);
413+ window->deleteLater();
414+ });
345415 } else {
346− auto *application = m_applicationManager->findApplicationWithSurface(surface);
347− if (application) {
348− prependSurface(surface, application);
416+ if (surface->type() == Mir::InputMethodType) {
417+ setInputMethodWindow(createWindow(surface));
349418 } else {
350− // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
351− // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
352− // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
353− Window *promptWindow = createWindow(surface);
354− connect(surface, &QObject::destroyed, promptWindow, [=](){
355− promptWindow->setSurface(nullptr);
356− promptWindow->deleteLater();
357− });
419+ auto *application = m_applicationManager->findApplicationWithSurface(surface);
420+ if (application) {
421+ prependSurface(surface, application);
422+ } else {
423+ // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
424+ // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
425+ // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
426+ Window *promptWindow = createWindow(surface);
427+ connect(surface, &QObject::destroyed, promptWindow, [=](){
428+ promptWindow->setSurface(nullptr);
429+ promptWindow->deleteLater();
430+ });
431+ }
358432 }
359433 }
360434 }
361435}
362436
437+void TopLevelWindowModel::onSurfacesAboutToBeRemovedFromWorkspace(const std::shared_ptr<miral::Workspace> &workspace,
438+ const QVector<unity::shell::application::MirSurfaceInterface *> surfaces)
439+{
440+ if (workspace != m_workspace->workspace()) return;
441+
442+ int start = -1;
443+ int end = -1;
444+ for (auto iter = surfaces.constBegin(); iter != surfaces.constEnd();) {
445+ auto surface = *iter;
446+ iter++;
447+
448+ // Do removals in adjacent blocks.
449+ start = end = indexOf(surface);
450+ if (start == -1) {
451+ // probably a child surface
452+ m_allSurfaces.remove(surface);
453+ continue;
454+ }
455+ while(iter != surfaces.constEnd()) {
456+ int index = indexOf(*iter);
457+ if (index != end+1) {
458+ break;
459+ }
460+ end++;
461+ iter++;
462+ }
463+
464+ if (m_modelState == IdleState) {
465+ beginRemoveRows(QModelIndex(), start, end);
466+ m_modelState = RemovingState;
467+ } else {
468+ Q_ASSERT(m_modelState == ResettingState);
469+ // No point in signaling anything if we're resetting the whole model
470+ }
471+
472+ for (int index = start; index <= end; index++) {
473+ auto window = m_windowModel[start].window;
474+ window->setSurface(nullptr);
475+ window->setFocused(false);
476+
477+ m_windowModel.removeAt(start);
478+ m_allSurfaces.remove(window->surface());
479+ }
480+
481+ if (m_modelState == RemovingState) {
482+ endRemoveRows();
483+ Q_EMIT countChanged();
484+ Q_EMIT listChanged();
485+ m_modelState = IdleState;
486+ }
487+ }
488+}
489+
363490void TopLevelWindowModel::removeAt(int index)
364491{
365492 if (m_modelState == IdleState) {
@@ -376,6 +503,7 @@ void TopLevelWindowModel::removeAt(int index)
376503 window->setFocused(false);
377504
378505 m_windowModel.removeAt(index);
506+ m_allSurfaces.remove(window->surface());
379507
380508 if (m_modelState == RemovingState) {
381509 endRemoveRows();
@@ -444,6 +572,26 @@ QVariant TopLevelWindowModel::data(const QModelIndex& index, int role) const
444572 }
445573}
446574
575+void TopLevelWindowModel::connectApplicationManager()
576+{
577+ connect(m_applicationManager, &QAbstractItemModel::rowsInserted,
578+ this, [this](const QModelIndex &/*parent*/, int first, int last) {
579+ for (int i = first; i <= last; ++i) {
580+ auto application = m_applicationManager->get(i);
581+ addApplication(application);
582+ }
583+ });
584+
585+ connect(m_applicationManager, &QAbstractItemModel::rowsAboutToBeRemoved,
586+ this, [this](const QModelIndex &/*parent*/, int first, int last) {
587+ for (int i = first; i <= last; ++i) {
588+ auto application = m_applicationManager->get(i);
589+ removeApplication(application);
590+ }
591+ });
592+}
593+
594+
447595int TopLevelWindowModel::findIndexOf(const unityapi::MirSurfaceInterface *surface) const
448596{
449597 for (int i=0; i<m_windowModel.count(); i++) {
@@ -674,3 +822,44 @@ void TopLevelWindowModel::activateTopMostWindowWithoutId(int forbiddenId)
674822 }
675823 }
676824}
825+
826+void TopLevelWindowModel::refreshWindows()
827+{
828+ DEBUG_MSG << "()";
829+
830+ m_windowModel.clear();
831+ m_allSurfaces.clear();
832+
833+ if (!m_workspace || !m_applicationManager) return;
834+ WindowManagementPolicy::instance()->forEachWindowInWorkspace(m_workspace->workspace(), [this](const miral::Window &window) {
835+ auto surface = m_surfaceManager->surfaceFor(window);
836+ if (surface) {
837+ if (surface->parentSurface()) {
838+ // Wrap it in a Window so that we keep focusedWindow() up to date.
839+ Window *window = createWindow(surface);
840+ connect(surface, &QObject::destroyed, window, [=](){
841+ window->setSurface(nullptr);
842+ window->deleteLater();
843+ });
844+ } else {
845+ if (surface->type() == Mir::InputMethodType) {
846+ setInputMethodWindow(createWindow(surface));
847+ } else {
848+ auto *application = m_applicationManager->findApplicationWithSurface(surface);
849+ if (application) {
850+ prependSurface(surface, application);
851+ } else {
852+ // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
853+ // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
854+ // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
855+ Window *promptWindow = createWindow(surface);
856+ connect(surface, &QObject::destroyed, promptWindow, [=](){
857+ promptWindow->setSurface(nullptr);
858+ promptWindow->deleteLater();
859+ });
860+ }
861+ }
862+ }
863+ }
864+ });
865+}