@@ -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,101 @@ 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 (workspace != m_workspace->workspace()) return;
403+
404+ Q_FOREACH(auto surface, surfaces) {
405+ if (m_allSurfaces.contains(surface)) continue;
406+
407+ if (surface->parentSurface()) {
408+ // Wrap it in a Window so that we keep focusedWindow() up to date.
409+ Window *window = createWindow(surface);
410+ connect(surface, &QObject::destroyed, window, [=](){
411+ window->setSurface(nullptr);
412+ window->deleteLater();
413+ });
345414 } else {
346− auto *application = m_applicationManager->findApplicationWithSurface(surface);
347− if (application) {
348− prependSurface(surface, application);
415+ if (surface->type() == Mir::InputMethodType) {
416+ setInputMethodWindow(createWindow(surface));
349417 } 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− });
418+ auto *application = m_applicationManager->findApplicationWithSurface(surface);
419+ if (application) {
420+ prependSurface(surface, application);
421+ } else {
422+ // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
423+ // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
424+ // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
425+ Window *promptWindow = createWindow(surface);
426+ connect(surface, &QObject::destroyed, promptWindow, [=](){
427+ promptWindow->setSurface(nullptr);
428+ promptWindow->deleteLater();
429+ });
430+ }
358431 }
359432 }
360433 }
361434}
362435
436+void TopLevelWindowModel::onSurfacesAboutToBeRemovedFromWorkspace(const std::shared_ptr<miral::Workspace> &workspace,
437+ const QVector<unity::shell::application::MirSurfaceInterface *> surfaces)
438+{
439+ if (workspace != m_workspace->workspace()) return;
440+
441+ int start = -1;
442+ int end = -1;
443+ for (auto iter = surfaces.constBegin(); iter != surfaces.constEnd();) {
444+ auto surface = *iter;
445+ iter++;
446+
447+ // Do removals in adjacent blocks.
448+ start = end = indexOf(surface);
449+ if (start == -1) {
450+ // probably a child surface
451+ m_allSurfaces.remove(surface);
452+ continue;
453+ }
454+ while(iter != surfaces.constEnd()) {
455+ int index = indexOf(*iter);
456+ if (index != end+1) {
457+ break;
458+ }
459+ end++;
460+ iter++;
461+ }
462+
463+ if (m_modelState == IdleState) {
464+ beginRemoveRows(QModelIndex(), start, end);
465+ m_modelState = RemovingState;
466+ } else {
467+ Q_ASSERT(m_modelState == ResettingState);
468+ // No point in signaling anything if we're resetting the whole model
469+ }
470+
471+ for (int index = start; index <= end; index++) {
472+ auto window = m_windowModel[start].window;
473+ window->setSurface(nullptr);
474+ window->setFocused(false);
475+
476+ m_windowModel.removeAt(start);
477+ m_allSurfaces.remove(window->surface());
478+ }
479+
480+ if (m_modelState == RemovingState) {
481+ endRemoveRows();
482+ Q_EMIT countChanged();
483+ Q_EMIT listChanged();
484+ m_modelState = IdleState;
485+ }
486+ }
487+}
488+
363489void TopLevelWindowModel::removeAt(int index)
364490{
365491 if (m_modelState == IdleState) {
@@ -376,6 +502,7 @@ void TopLevelWindowModel::removeAt(int index)
376502 window->setFocused(false);
377503
378504 m_windowModel.removeAt(index);
505+ m_allSurfaces.remove(window->surface());
379506
380507 if (m_modelState == RemovingState) {
381508 endRemoveRows();
@@ -444,6 +571,26 @@ QVariant TopLevelWindowModel::data(const QModelIndex& index, int role) const
444571 }
445572}
446573
574+void TopLevelWindowModel::connectApplicationManager()
575+{
576+ connect(m_applicationManager, &QAbstractItemModel::rowsInserted,
577+ this, [this](const QModelIndex &/*parent*/, int first, int last) {
578+ for (int i = first; i <= last; ++i) {
579+ auto application = m_applicationManager->get(i);
580+ addApplication(application);
581+ }
582+ });
583+
584+ connect(m_applicationManager, &QAbstractItemModel::rowsAboutToBeRemoved,
585+ this, [this](const QModelIndex &/*parent*/, int first, int last) {
586+ for (int i = first; i <= last; ++i) {
587+ auto application = m_applicationManager->get(i);
588+ removeApplication(application);
589+ }
590+ });
591+}
592+
593+
447594int TopLevelWindowModel::findIndexOf(const unityapi::MirSurfaceInterface *surface) const
448595{
449596 for (int i=0; i<m_windowModel.count(); i++) {
@@ -674,3 +821,43 @@ void TopLevelWindowModel::activateTopMostWindowWithoutId(int forbiddenId)
674821 }
675822 }
676823}
824+
825+void TopLevelWindowModel::refreshWindows()
826+{
827+ DEBUG_MSG << "()";
828+
829+ m_windowModel.clear();
830+ m_allSurfaces.clear();
831+
832+ WindowManagementPolicy::instance()->forEachWindowInWorkspace(m_workspace->workspace(), [this](const miral::Window &window) {
833+ auto surface = m_surfaceManager->surfaceFor(window);
834+ if (surface) {
835+ if (surface->parentSurface()) {
836+ // Wrap it in a Window so that we keep focusedWindow() up to date.
837+ Window *window = createWindow(surface);
838+ connect(surface, &QObject::destroyed, window, [=](){
839+ window->setSurface(nullptr);
840+ window->deleteLater();
841+ });
842+ } else {
843+ if (surface->type() == Mir::InputMethodType) {
844+ setInputMethodWindow(createWindow(surface));
845+ } else {
846+ auto *application = m_applicationManager->findApplicationWithSurface(surface);
847+ if (application) {
848+ prependSurface(surface, application);
849+ } else {
850+ // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
851+ // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
852+ // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
853+ Window *promptWindow = createWindow(surface);
854+ connect(surface, &QObject::destroyed, promptWindow, [=](){
855+ promptWindow->setSurface(nullptr);
856+ promptWindow->deleteLater();
857+ });
858+ }
859+ }
860+ }
861+ }
862+ });
863+}