@@ -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 "wmpolicyinterface.h"
33+
34+// qtmir
35+#include <qtmir/mirserverapplication.h>
36+#include <qtmir/windowmodelnotifier.h>
3237
3338Q_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL, "toplevelwindowmodel", QtInfoMsg)
3439
@@ -55,7 +60,6 @@ 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
@@ -64,26 +68,27 @@ void TopLevelWindowModel::setApplicationManager(unityapi::ApplicationManagerInte
6468 if (m_applicationManager) {
6569 connect(m_applicationManager, &QAbstractItemModel::rowsInserted,
6670 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− });
71+ if (!m_workspace || !m_workspace->isActive())
72+ return;
73+
74+ for (int i = first; i <= last; ++i) {
75+ auto application = m_applicationManager->get(i);
76+ addApplication(application);
77+ }
78+ });
7279
7380 connect(m_applicationManager, &QAbstractItemModel::rowsAboutToBeRemoved,
7481 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− });
80−
81− for (int i = 0; i < m_applicationManager->rowCount(); ++i) {
82− auto application = m_applicationManager->get(i);
83− addApplication(application);
84− }
82+ for (int i = first; i <= last; ++i) {
83+ auto application = m_applicationManager->get(i);
84+ removeApplication(application);
85+ }
86+ });
8587 }
8688
89+ Q_EMIT applicationManagerChanged(m_applicationManager);
90+ refreshWindows();
91+
8792 endResetModel();
8893 m_modelState = IdleState;
8994}
@@ -96,6 +101,11 @@ void TopLevelWindowModel::setSurfaceManager(unityapi::SurfaceManagerInterface *s
96101
97102 DEBUG_MSG << "(" << surfaceManager << ")";
98103
104+ Q_ASSERT(m_modelState == IdleState);
105+ m_modelState = ResettingState;
106+
107+ beginResetModel();
108+
99109 if (m_surfaceManager) {
100110 disconnect(m_surfaceManager, 0, this, 0);
101111 }
@@ -107,9 +117,44 @@ void TopLevelWindowModel::setSurfaceManager(unityapi::SurfaceManagerInterface *s
107117 connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesRaised, this, &TopLevelWindowModel::onSurfacesRaised);
108118 connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsStarted, this, &TopLevelWindowModel::onModificationsStarted);
109119 connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsEnded, this, &TopLevelWindowModel::onModificationsEnded);
120+
121+ connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesAddedToWorkspace,
122+ this, &TopLevelWindowModel::onSurfacesAddedToWorkspace);
123+ connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesAboutToBeRemovedFromWorkspace,
124+ this, &TopLevelWindowModel::onSurfacesAboutToBeRemovedFromWorkspace);
110125 }
111126
127+ refreshWindows();
112128 Q_EMIT surfaceManagerChanged(m_surfaceManager);
129+
130+ endResetModel();
131+ m_modelState = IdleState;
132+}
133+
134+void TopLevelWindowModel::setWorkspace(Workspace *workspace)
135+{
136+ if (workspace == m_workspace) {
137+ return;
138+ }
139+
140+ DEBUG_MSG << "(" << workspace << ")";
141+
142+ Q_ASSERT(m_modelState == IdleState);
143+ m_modelState = ResettingState;
144+
145+ beginResetModel();
146+
147+ if (m_workspace) {
148+ disconnect(m_workspace, 0, this, 0);
149+ }
150+
151+ m_workspace = workspace;
152+
153+ refreshWindows();
154+ Q_EMIT workspaceChanged(workspace);
155+
156+ endResetModel();
157+ m_modelState = IdleState;
113158}
114159
115160void TopLevelWindowModel::addApplication(unityapi::ApplicationInfoInterface *application)
@@ -153,6 +198,7 @@ void TopLevelWindowModel::prependSurface(unityapi::MirSurfaceInterface *surface,
153198 ModelEntry &entry = m_windowModel[i];
154199 if (entry.application == application && entry.window->surface() == nullptr) {
155200 entry.window->setSurface(surface);
201+ m_allSurfaces.insert(surface);
156202 connectSurface(surface);
157203 INFO_MSG << " appId=" << application->appId() << " surface=" << surface
158204 << ", filling out placeholder. after: " << toString();
@@ -180,6 +226,7 @@ void TopLevelWindowModel::prependSurfaceHelper(unityapi::MirSurfaceInterface *su
180226
181227 m_windowModel.prepend(ModelEntry(window, application));
182228 if (surface) {
229+ m_allSurfaces.insert(surface);
183230 connectSurface(surface);
184231 }
185232
@@ -207,15 +254,14 @@ void TopLevelWindowModel::connectWindow(Window *window)
207254
208255 connect(window, &Window::focusedChanged, this, [this, window](bool focused) {
209256 if (window->surface()) {
210− // Condense changes to the focused window
211− // eg: Do focusedWindow=A to focusedWindow=B instead of
212− // focusedWindow=A to focusedWindow=null to focusedWindow=B
213257 if (focused) {
214− Q_ASSERT(m_newlyFocusedWindow == nullptr);
215− m_focusedWindowChanged = true;
216− m_newlyFocusedWindow = window;
258+ setFocusedWindow(window);
259+ m_focusedWindowCleared = false;
217260 } else if (m_focusedWindow == window) {
218− m_focusedWindowChanged = true;
261+ // Condense changes to the focused window
262+ // eg: Do focusedWindow=A to focusedWindow=B instead of
263+ // focusedWindow=A to focusedWindow=null to focusedWindow=B
264+ m_focusedWindowCleared = true;
219265 } else {
220266 // don't clear the focused window if you were not there in the first place
221267 // happens when a filled window gets replaced with an empty one (no surface) as the focused window.
@@ -313,6 +359,7 @@ void TopLevelWindowModel::onSurfaceDestroyed(unityapi::MirSurfaceInterface *surf
313359 auto window = m_windowModel[i].window;
314360 window->setSurface(nullptr);
315361 window->setFocused(false);
362+ m_allSurfaces.remove(surface);
316363 INFO_MSG << " Removed surface from entry. After: " << toString();
317364 }
318365}
@@ -328,39 +375,103 @@ Window *TopLevelWindowModel::createWindow(unityapi::MirSurfaceInterface *surface
328375 return qmlWindow;
329376}
330377
331−void TopLevelWindowModel::onSurfaceCreated(unityapi::MirSurfaceInterface *surface)
378+
379+void TopLevelWindowModel::onSurfaceCreated(unityapi::MirSurfaceInterface */*surface*/)
332380{
333− DEBUG_MSG << "(" << surface << ")";
381+}
334382
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− connectSurface(surface);
345− setInputMethodWindow(createWindow(surface));
383+void TopLevelWindowModel::onSurfacesAddedToWorkspace(const std::shared_ptr<miral::Workspace>& workspace,
384+ const QVector<unity::shell::application::MirSurfaceInterface*> surfaces)
385+{
386+ if (!m_workspace || !m_applicationManager) return;
387+ if (workspace != m_workspace->workspace()) return;
388+
389+ Q_FOREACH(auto surface, surfaces) {
390+ if (m_allSurfaces.contains(surface)) continue;
391+
392+ if (surface->parentSurface()) {
393+ // Wrap it in a Window so that we keep focusedWindow() up to date.
394+ Window *window = createWindow(surface);
395+ connect(surface, &QObject::destroyed, window, [=](){
396+ window->setSurface(nullptr);
397+ window->deleteLater();
398+ });
346399 } else {
347− auto *application = m_applicationManager->findApplicationWithSurface(surface);
348− if (application) {
349− prependSurface(surface, application);
400+ if (surface->type() == Mir::InputMethodType) {
401+ connectSurface(surface);
402+ setInputMethodWindow(createWindow(surface));
350403 } else {
351− // 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.
353− // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
354− Window *promptWindow = createWindow(surface);
355− connect(surface, &QObject::destroyed, promptWindow, [=](){
356− promptWindow->setSurface(nullptr);
357− promptWindow->deleteLater();
358− });
404+ auto *application = m_applicationManager->findApplicationWithSurface(surface);
405+ if (application) {
406+ prependSurface(surface, application);
407+ } else {
408+ // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
409+ // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
410+ // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
411+ Window *promptWindow = createWindow(surface);
412+ connect(surface, &QObject::destroyed, promptWindow, [=](){
413+ promptWindow->setSurface(nullptr);
414+ promptWindow->deleteLater();
415+ });
416+ }
359417 }
360418 }
361419 }
362420}
363421
422+void TopLevelWindowModel::onSurfacesAboutToBeRemovedFromWorkspace(const std::shared_ptr<miral::Workspace> &workspace,
423+ const QVector<unity::shell::application::MirSurfaceInterface *> surfaces)
424+{
425+ if (!m_workspace || workspace != m_workspace->workspace()) return;
426+
427+ int start = -1;
428+ int end = -1;
429+ for (auto iter = surfaces.constBegin(); iter != surfaces.constEnd();) {
430+ auto surface = *iter;
431+ iter++;
432+
433+ // Do removals in adjacent blocks.
434+ start = end = indexOf(surface);
435+ if (start == -1) {
436+ // probably a child surface
437+ m_allSurfaces.remove(surface);
438+ continue;
439+ }
440+ while(iter != surfaces.constEnd()) {
441+ int index = indexOf(*iter);
442+ if (index != end+1) {
443+ break;
444+ }
445+ end++;
446+ iter++;
447+ }
448+
449+ if (m_modelState == IdleState) {
450+ beginRemoveRows(QModelIndex(), start, end);
451+ m_modelState = RemovingState;
452+ } else {
453+ Q_ASSERT(m_modelState == ResettingState);
454+ // No point in signaling anything if we're resetting the whole model
455+ }
456+
457+ for (int index = start; index <= end; index++) {
458+ auto window = m_windowModel[start].window;
459+ window->setSurface(nullptr);
460+ window->setFocused(false);
461+
462+ m_windowModel.removeAt(start);
463+ m_allSurfaces.remove(surface);
464+ }
465+
466+ if (m_modelState == RemovingState) {
467+ endRemoveRows();
468+ Q_EMIT countChanged();
469+ Q_EMIT listChanged();
470+ m_modelState = IdleState;
471+ }
472+ }
473+}
474+
364475void TopLevelWindowModel::removeAt(int index)
365476{
366477 if (m_modelState == IdleState) {
@@ -372,11 +483,13 @@ void TopLevelWindowModel::removeAt(int index)
372483 }
373484
374485 auto window = m_windowModel[index].window;
486+ auto surface = window->surface();
375487
376488 window->setSurface(nullptr);
377489 window->setFocused(false);
378490
379491 m_windowModel.removeAt(index);
492+ m_allSurfaces.remove(surface);
380493
381494 if (m_modelState == RemovingState) {
382495 endRemoveRows();
@@ -388,6 +501,7 @@ void TopLevelWindowModel::removeAt(int index)
388501 disconnect(window, 0, this, 0);
389502 if (m_focusedWindow == window) {
390503 setFocusedWindow(nullptr);
504+ m_focusedWindowCleared = false;
391505 }
392506 delete window;
393507
@@ -656,12 +770,11 @@ void TopLevelWindowModel::onModificationsStarted()
656770
657771void TopLevelWindowModel::onModificationsEnded()
658772{
659− if (m_focusedWindowChanged) {
660− setFocusedWindow(m_newlyFocusedWindow);
773+ if (m_focusedWindowCleared) {
774+ setFocusedWindow(nullptr);
661775 }
662776 // reset
663− m_focusedWindowChanged = false;
664− m_newlyFocusedWindow = nullptr;
777+ m_focusedWindowCleared = false;
665778}
666779
667780void TopLevelWindowModel::activateTopMostWindowWithoutId(int forbiddenId)
@@ -675,3 +788,54 @@ void TopLevelWindowModel::activateTopMostWindowWithoutId(int forbiddenId)
675788 }
676789 }
677790}
791+
792+void TopLevelWindowModel::refreshWindows()
793+{
794+ DEBUG_MSG << "()";
795+ clear();
796+
797+ if (!m_workspace || !m_applicationManager || !m_surfaceManager) return;
798+
799+ m_surfaceManager->forEachSurfaceInWorkspace(m_workspace->workspace(), [this](unity::shell::application::MirSurfaceInterface* surface) {
800+ if (surface->parentSurface()) {
801+ // Wrap it in a Window so that we keep focusedWindow() up to date.
802+ Window *window = createWindow(surface);
803+ connect(surface, &QObject::destroyed, window, [=](){
804+ window->setSurface(nullptr);
805+ window->deleteLater();
806+ });
807+ } else {
808+ if (surface->type() == Mir::InputMethodType) {
809+ setInputMethodWindow(createWindow(surface));
810+ } else {
811+ auto *application = m_applicationManager->findApplicationWithSurface(surface);
812+ if (application) {
813+ prependSurface(surface, application);
814+ } else {
815+ // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
816+ // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
817+ // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
818+ Window *promptWindow = createWindow(surface);
819+ connect(surface, &QObject::destroyed, promptWindow, [=](){
820+ promptWindow->setSurface(nullptr);
821+ promptWindow->deleteLater();
822+ });
823+ }
824+ }
825+ }
826+ });
827+}
828+
829+void TopLevelWindowModel::clear()
830+{
831+ DEBUG_MSG << "()";
832+
833+ while(m_windowModel.count() > 0) {
834+ ModelEntry entry = m_windowModel.takeAt(0);
835+ disconnect(entry.window, 0, this, 0);
836+ delete entry.window;
837+ }
838+ m_allSurfaces.clear();
839+ setFocusedWindow(nullptr);
840+ m_focusedWindowCleared = false;
841+}