@@ -15,6 +15,7 @@
1515 */
1616
1717#include "TopLevelWindowModel.h"
18+#include "WindowManagerObjects.h"
1819
1920// unity-api
2021#include <unity/shell/application/ApplicationInfoInterface.h>
@@ -24,11 +25,11 @@
2425#include <unity/shell/application/SurfaceManagerInterface.h>
2526
2627// Qt
27−#include <QGuiApplication>
2828#include <QDebug>
2929
3030// local
3131#include "Window.h"
32+#include "Workspace.h"
3233
3334Q_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL, "toplevelwindowmodel", QtInfoMsg)
3435
@@ -37,7 +38,19 @@ Q_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL, "toplevelwindowmodel", QtInfoMsg)
3738
3839namespace unityapi = unity::shell::application;
3940
40−TopLevelWindowModel::TopLevelWindowModel()
41+TopLevelWindowModel::TopLevelWindowModel(Workspace* workspace)
42+ : m_workspace(workspace)
43+{
44+ connect(WindowManagerObjects::instance(), &WindowManagerObjects::applicationManagerChanged,
45+ this, &TopLevelWindowModel::setApplicationManager);
46+ connect(WindowManagerObjects::instance(), &WindowManagerObjects::surfaceManagerChanged,
47+ this, &TopLevelWindowModel::setSurfaceManager);
48+
49+ setApplicationManager(WindowManagerObjects::instance()->applicationManager());
50+ setSurfaceManager(WindowManagerObjects::instance()->surfaceManager());
51+}
52+
53+TopLevelWindowModel::~TopLevelWindowModel()
4154{
4255}
4356
@@ -55,7 +68,6 @@ void TopLevelWindowModel::setApplicationManager(unityapi::ApplicationManagerInte
5568 beginResetModel();
5669
5770 if (m_applicationManager) {
58− m_windowModel.clear();
5971 disconnect(m_applicationManager, 0, this, 0);
6072 }
6173
@@ -64,26 +76,26 @@ void TopLevelWindowModel::setApplicationManager(unityapi::ApplicationManagerInte
6476 if (m_applicationManager) {
6577 connect(m_applicationManager, &QAbstractItemModel::rowsInserted,
6678 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− });
79+ if (!m_workspace || !m_workspace->isActive())
80+ return;
81+
82+ for (int i = first; i <= last; ++i) {
83+ auto application = m_applicationManager->get(i);
84+ addApplication(application);
85+ }
86+ });
7287
7388 connect(m_applicationManager, &QAbstractItemModel::rowsAboutToBeRemoved,
7489 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− }
90+ for (int i = first; i <= last; ++i) {
91+ auto application = m_applicationManager->get(i);
92+ removeApplication(application);
93+ }
94+ });
8595 }
8696
97+ refreshWindows();
98+
8799 endResetModel();
88100 m_modelState = IdleState;
89101}
@@ -96,6 +108,11 @@ void TopLevelWindowModel::setSurfaceManager(unityapi::SurfaceManagerInterface *s
96108
97109 DEBUG_MSG << "(" << surfaceManager << ")";
98110
111+ Q_ASSERT(m_modelState == IdleState);
112+ m_modelState = ResettingState;
113+
114+ beginResetModel();
115+
99116 if (m_surfaceManager) {
100117 disconnect(m_surfaceManager, 0, this, 0);
101118 }
@@ -107,9 +124,17 @@ void TopLevelWindowModel::setSurfaceManager(unityapi::SurfaceManagerInterface *s
107124 connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesRaised, this, &TopLevelWindowModel::onSurfacesRaised);
108125 connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsStarted, this, &TopLevelWindowModel::onModificationsStarted);
109126 connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsEnded, this, &TopLevelWindowModel::onModificationsEnded);
127+
128+ connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesAddedToWorkspace,
129+ this, &TopLevelWindowModel::onSurfacesAddedToWorkspace);
130+ connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesAboutToBeRemovedFromWorkspace,
131+ this, &TopLevelWindowModel::onSurfacesAboutToBeRemovedFromWorkspace);
110132 }
111133
112− Q_EMIT surfaceManagerChanged(m_surfaceManager);
134+ refreshWindows();
135+
136+ endResetModel();
137+ m_modelState = IdleState;
113138}
114139
115140void TopLevelWindowModel::addApplication(unityapi::ApplicationInfoInterface *application)
@@ -153,6 +178,7 @@ void TopLevelWindowModel::prependSurface(unityapi::MirSurfaceInterface *surface,
153178 ModelEntry &entry = m_windowModel[i];
154179 if (entry.application == application && entry.window->surface() == nullptr) {
155180 entry.window->setSurface(surface);
181+ m_allSurfaces.insert(surface);
156182 connectSurface(surface);
157183 INFO_MSG << " appId=" << application->appId() << " surface=" << surface
158184 << ", filling out placeholder. after: " << toString();
@@ -180,6 +206,7 @@ void TopLevelWindowModel::prependSurfaceHelper(unityapi::MirSurfaceInterface *su
180206
181207 m_windowModel.prepend(ModelEntry(window, application));
182208 if (surface) {
209+ m_allSurfaces.insert(surface);
183210 connectSurface(surface);
184211 }
185212
@@ -207,15 +234,14 @@ void TopLevelWindowModel::connectWindow(Window *window)
207234
208235 connect(window, &Window::focusedChanged, this, [this, window](bool focused) {
209236 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
213237 if (focused) {
214− Q_ASSERT(m_newlyFocusedWindow == nullptr);
215− m_focusedWindowChanged = true;
216− m_newlyFocusedWindow = window;
238+ setFocusedWindow(window);
239+ m_focusedWindowCleared = false;
217240 } else if (m_focusedWindow == window) {
218− m_focusedWindowChanged = true;
241+ // Condense changes to the focused window
242+ // eg: Do focusedWindow=A to focusedWindow=B instead of
243+ // focusedWindow=A to focusedWindow=null to focusedWindow=B
244+ m_focusedWindowCleared = true;
219245 } else {
220246 // don't clear the focused window if you were not there in the first place
221247 // happens when a filled window gets replaced with an empty one (no surface) as the focused window.
@@ -313,6 +339,7 @@ void TopLevelWindowModel::onSurfaceDestroyed(unityapi::MirSurfaceInterface *surf
313339 auto window = m_windowModel[i].window;
314340 window->setSurface(nullptr);
315341 window->setFocused(false);
342+ m_allSurfaces.remove(surface);
316343 INFO_MSG << " Removed surface from entry. After: " << toString();
317344 }
318345}
@@ -328,39 +355,103 @@ Window *TopLevelWindowModel::createWindow(unityapi::MirSurfaceInterface *surface
328355 return qmlWindow;
329356}
330357
331−void TopLevelWindowModel::onSurfaceCreated(unityapi::MirSurfaceInterface *surface)
358+
359+void TopLevelWindowModel::onSurfaceCreated(unityapi::MirSurfaceInterface */*surface*/)
332360{
333− DEBUG_MSG << "(" << surface << ")";
361+}
334362
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));
363+void TopLevelWindowModel::onSurfacesAddedToWorkspace(const std::shared_ptr<miral::Workspace>& workspace,
364+ const QVector<unity::shell::application::MirSurfaceInterface*> surfaces)
365+{
366+ if (!m_workspace || !m_applicationManager) return;
367+ if (workspace != m_workspace->workspace()) return;
368+
369+ Q_FOREACH(auto surface, surfaces) {
370+ if (m_allSurfaces.contains(surface)) continue;
371+
372+ if (surface->parentSurface()) {
373+ // Wrap it in a Window so that we keep focusedWindow() up to date.
374+ Window *window = createWindow(surface);
375+ connect(surface, &QObject::destroyed, window, [=](){
376+ window->setSurface(nullptr);
377+ window->deleteLater();
378+ });
346379 } else {
347− auto *application = m_applicationManager->findApplicationWithSurface(surface);
348− if (application) {
349− prependSurface(surface, application);
380+ if (surface->type() == Mir::InputMethodType) {
381+ connectSurface(surface);
382+ setInputMethodWindow(createWindow(surface));
350383 } 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− });
384+ auto *application = m_applicationManager->findApplicationWithSurface(surface);
385+ if (application) {
386+ prependSurface(surface, application);
387+ } else {
388+ // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
389+ // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
390+ // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
391+ Window *promptWindow = createWindow(surface);
392+ connect(surface, &QObject::destroyed, promptWindow, [=](){
393+ promptWindow->setSurface(nullptr);
394+ promptWindow->deleteLater();
395+ });
396+ }
359397 }
360398 }
361399 }
362400}
363401
402+void TopLevelWindowModel::onSurfacesAboutToBeRemovedFromWorkspace(const std::shared_ptr<miral::Workspace> &workspace,
403+ const QVector<unity::shell::application::MirSurfaceInterface *> surfaces)
404+{
405+ if (!m_workspace || workspace != m_workspace->workspace()) return;
406+
407+ int start = -1;
408+ int end = -1;
409+ for (auto iter = surfaces.constBegin(); iter != surfaces.constEnd();) {
410+ auto surface = *iter;
411+ iter++;
412+
413+ // Do removals in adjacent blocks.
414+ start = end = indexOf(surface);
415+ if (start == -1) {
416+ // probably a child surface
417+ m_allSurfaces.remove(surface);
418+ continue;
419+ }
420+ while(iter != surfaces.constEnd()) {
421+ int index = indexOf(*iter);
422+ if (index != end+1) {
423+ break;
424+ }
425+ end++;
426+ iter++;
427+ }
428+
429+ if (m_modelState == IdleState) {
430+ beginRemoveRows(QModelIndex(), start, end);
431+ m_modelState = RemovingState;
432+ } else {
433+ Q_ASSERT(m_modelState == ResettingState);
434+ // No point in signaling anything if we're resetting the whole model
435+ }
436+
437+ for (int index = start; index <= end; index++) {
438+ auto window = m_windowModel[start].window;
439+ window->setSurface(nullptr);
440+ window->setFocused(false);
441+
442+ m_windowModel.removeAt(start);
443+ m_allSurfaces.remove(surface);
444+ }
445+
446+ if (m_modelState == RemovingState) {
447+ endRemoveRows();
448+ Q_EMIT countChanged();
449+ Q_EMIT listChanged();
450+ m_modelState = IdleState;
451+ }
452+ }
453+}
454+
364455void TopLevelWindowModel::removeAt(int index)
365456{
366457 if (m_modelState == IdleState) {
@@ -372,11 +463,13 @@ void TopLevelWindowModel::removeAt(int index)
372463 }
373464
374465 auto window = m_windowModel[index].window;
466+ auto surface = window->surface();
375467
376468 window->setSurface(nullptr);
377469 window->setFocused(false);
378470
379471 m_windowModel.removeAt(index);
472+ m_allSurfaces.remove(surface);
380473
381474 if (m_modelState == RemovingState) {
382475 endRemoveRows();
@@ -388,6 +481,7 @@ void TopLevelWindowModel::removeAt(int index)
388481 disconnect(window, 0, this, 0);
389482 if (m_focusedWindow == window) {
390483 setFocusedWindow(nullptr);
484+ m_focusedWindowCleared = false;
391485 }
392486 delete window;
393487
@@ -656,12 +750,11 @@ void TopLevelWindowModel::onModificationsStarted()
656750
657751void TopLevelWindowModel::onModificationsEnded()
658752{
659− if (m_focusedWindowChanged) {
660− setFocusedWindow(m_newlyFocusedWindow);
753+ if (m_focusedWindowCleared) {
754+ setFocusedWindow(nullptr);
661755 }
662756 // reset
663− m_focusedWindowChanged = false;
664− m_newlyFocusedWindow = nullptr;
757+ m_focusedWindowCleared = false;
665758}
666759
667760void TopLevelWindowModel::activateTopMostWindowWithoutId(int forbiddenId)
@@ -675,3 +768,54 @@ void TopLevelWindowModel::activateTopMostWindowWithoutId(int forbiddenId)
675768 }
676769 }
677770}
771+
772+void TopLevelWindowModel::refreshWindows()
773+{
774+ DEBUG_MSG << "()";
775+ clear();
776+
777+ if (!m_workspace || !m_applicationManager || !m_surfaceManager) return;
778+
779+ m_surfaceManager->forEachSurfaceInWorkspace(m_workspace->workspace(), [this](unity::shell::application::MirSurfaceInterface* surface) {
780+ if (surface->parentSurface()) {
781+ // Wrap it in a Window so that we keep focusedWindow() up to date.
782+ Window *window = createWindow(surface);
783+ connect(surface, &QObject::destroyed, window, [=](){
784+ window->setSurface(nullptr);
785+ window->deleteLater();
786+ });
787+ } else {
788+ if (surface->type() == Mir::InputMethodType) {
789+ setInputMethodWindow(createWindow(surface));
790+ } else {
791+ auto *application = m_applicationManager->findApplicationWithSurface(surface);
792+ if (application) {
793+ prependSurface(surface, application);
794+ } else {
795+ // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
796+ // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
797+ // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
798+ Window *promptWindow = createWindow(surface);
799+ connect(surface, &QObject::destroyed, promptWindow, [=](){
800+ promptWindow->setSurface(nullptr);
801+ promptWindow->deleteLater();
802+ });
803+ }
804+ }
805+ }
806+ });
807+}
808+
809+void TopLevelWindowModel::clear()
810+{
811+ DEBUG_MSG << "()";
812+
813+ while(m_windowModel.count() > 0) {
814+ ModelEntry entry = m_windowModel.takeAt(0);
815+ disconnect(entry.window, 0, this, 0);
816+ delete entry.window;
817+ }
818+ m_allSurfaces.clear();
819+ setFocusedWindow(nullptr);
820+ m_focusedWindowCleared = false;
821+}