@@ -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 }
@@ -103,13 +120,20 @@ void TopLevelWindowModel::setSurfaceManager(unityapi::SurfaceManagerInterface *s
103120 m_surfaceManager = surfaceManager;
104121
105122 if (m_surfaceManager) {
106− connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfaceCreated, this, &TopLevelWindowModel::onSurfaceCreated);
107123 connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesRaised, this, &TopLevelWindowModel::onSurfacesRaised);
108124 connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsStarted, this, &TopLevelWindowModel::onModificationsStarted);
109125 connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsEnded, this, &TopLevelWindowModel::onModificationsEnded);
126+
127+ connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesAddedToWorkspace,
128+ this, &TopLevelWindowModel::onSurfacesAddedToWorkspace);
129+ connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesAboutToBeRemovedFromWorkspace,
130+ this, &TopLevelWindowModel::onSurfacesAboutToBeRemovedFromWorkspace);
110131 }
111132
112− Q_EMIT surfaceManagerChanged(m_surfaceManager);
133+ refreshWindows();
134+
135+ endResetModel();
136+ m_modelState = IdleState;
113137}
114138
115139void TopLevelWindowModel::addApplication(unityapi::ApplicationInfoInterface *application)
@@ -149,6 +173,7 @@ void TopLevelWindowModel::prependSurface(unityapi::MirSurfaceInterface *surface,
149173 Q_ASSERT(surface != nullptr);
150174
151175 connectSurface(surface);
176+ m_allSurfaces.insert(surface);
152177
153178 bool filledPlaceholder = false;
154179 for (int i = 0; i < m_windowModel.count() && !filledPlaceholder; ++i) {
@@ -225,15 +250,14 @@ void TopLevelWindowModel::connectWindow(Window *window)
225250
226251 connect(window, &Window::focusedChanged, this, [this, window](bool focused) {
227252 if (window->surface()) {
228− // Condense changes to the focused window
229− // eg: Do focusedWindow=A to focusedWindow=B instead of
230− // focusedWindow=A to focusedWindow=null to focusedWindow=B
231253 if (focused) {
232− Q_ASSERT(m_newlyFocusedWindow == nullptr);
233− m_focusedWindowChanged = true;
234− m_newlyFocusedWindow = window;
254+ setFocusedWindow(window);
255+ m_focusedWindowCleared = false;
235256 } else if (m_focusedWindow == window) {
236− m_focusedWindowChanged = true;
257+ // Condense changes to the focused window
258+ // eg: Do focusedWindow=A to focusedWindow=B instead of
259+ // focusedWindow=A to focusedWindow=null to focusedWindow=B
260+ m_focusedWindowCleared = true;
237261 } else {
238262 // don't clear the focused window if you were not there in the first place
239263 // happens when a filled window gets replaced with an empty one (no surface) as the focused window.
@@ -338,6 +362,7 @@ void TopLevelWindowModel::onSurfaceDestroyed(unityapi::MirSurfaceInterface *surf
338362 auto window = m_windowModel[i].window;
339363 window->setSurface(nullptr);
340364 window->setFocused(false);
365+ m_allSurfaces.remove(surface);
341366 INFO_MSG << " Removed surface from entry. After: " << toString();
342367 }
343368}
@@ -353,48 +378,107 @@ Window *TopLevelWindowModel::createWindow(unityapi::MirSurfaceInterface *surface
353378 return qmlWindow;
354379}
355380
356−void TopLevelWindowModel::onSurfaceCreated(unityapi::MirSurfaceInterface *surface)
381+void TopLevelWindowModel::onSurfacesAddedToWorkspace(const std::shared_ptr<miral::Workspace>& workspace,
382+ const QVector<unity::shell::application::MirSurfaceInterface*> surfaces)
357383{
358− DEBUG_MSG << "(" << surface << ")";
384+ if (!m_workspace || !m_applicationManager) return;
385+ if (workspace != m_workspace->workspace()) return;
359386
360− if (surface->parentSurface()) {
361− // Wrap it in a Window so that we keep focusedWindow() up to date.
362− Window *window = createWindow(surface);
363− connect(surface, &QObject::destroyed, window, [=](){
364− window->setSurface(nullptr);
365− window->deleteLater();
366− });
367− } else {
368− if (surface->type() == Mir::InputMethodType) {
369− connectSurface(surface);
370− setInputMethodWindow(createWindow(surface));
387+ Q_FOREACH(auto surface, surfaces) {
388+ if (m_allSurfaces.contains(surface)) continue;
389+
390+ if (surface->parentSurface()) {
391+ // Wrap it in a Window so that we keep focusedWindow() up to date.
392+ Window *window = createWindow(surface);
393+ connect(surface, &QObject::destroyed, window, [=](){
394+ window->setSurface(nullptr);
395+ window->deleteLater();
396+ });
371397 } else {
372− auto *application = m_applicationManager->findApplicationWithSurface(surface);
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);
398+ if (surface->type() == Mir::InputMethodType) {
399+ connectSurface(surface);
400+ setInputMethodWindow(createWindow(surface));
401+ } else {
402+ auto *application = m_applicationManager->findApplicationWithSurface(surface);
403+ if (application) {
404+ if (surface->state() == Mir::HiddenState) {
405+ // Ignore it until it's finally shown
406+ connect(surface, &unityapi::MirSurfaceInterface::stateChanged, this, [=](Mir::State newState) {
407+ Q_ASSERT(newState != Mir::HiddenState);
408+ disconnect(surface, &unityapi::MirSurfaceInterface::stateChanged, this, 0);
409+ prependSurface(surface, application);
410+ });
411+ } else {
379412 prependSurface(surface, application);
380− });
413+ }
381414 } else {
382− prependSurface(surface, application);
415+ // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
416+ // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
417+ // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
418+ Window *promptWindow = createWindow(surface);
419+ connect(surface, &QObject::destroyed, promptWindow, [=](){
420+ promptWindow->setSurface(nullptr);
421+ promptWindow->deleteLater();
422+ });
383423 }
384− } else {
385− // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
386− // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
387− // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
388− Window *promptWindow = createWindow(surface);
389− connect(surface, &QObject::destroyed, promptWindow, [=](){
390− promptWindow->setSurface(nullptr);
391− promptWindow->deleteLater();
392− });
393424 }
394425 }
395426 }
396427}
397428
429+void TopLevelWindowModel::onSurfacesAboutToBeRemovedFromWorkspace(const std::shared_ptr<miral::Workspace> &workspace,
430+ const QVector<unity::shell::application::MirSurfaceInterface *> surfaces)
431+{
432+ if (!m_workspace || workspace != m_workspace->workspace()) return;
433+
434+ int start = -1;
435+ int end = -1;
436+ for (auto iter = surfaces.constBegin(); iter != surfaces.constEnd();) {
437+ auto surface = *iter;
438+ iter++;
439+
440+ // Do removals in adjacent blocks.
441+ start = end = indexOf(surface);
442+ if (start == -1) {
443+ // probably a child surface
444+ m_allSurfaces.remove(surface);
445+ continue;
446+ }
447+ while(iter != surfaces.constEnd()) {
448+ int index = indexOf(*iter);
449+ if (index != end+1) {
450+ break;
451+ }
452+ end++;
453+ iter++;
454+ }
455+
456+ if (m_modelState == IdleState) {
457+ beginRemoveRows(QModelIndex(), start, end);
458+ m_modelState = RemovingState;
459+ } else {
460+ Q_ASSERT(m_modelState == ResettingState);
461+ // No point in signaling anything if we're resetting the whole model
462+ }
463+
464+ for (int index = start; index <= end; index++) {
465+ auto window = m_windowModel[start].window;
466+ window->setSurface(nullptr);
467+ window->setFocused(false);
468+
469+ m_windowModel.removeAt(start);
470+ m_allSurfaces.remove(surface);
471+ }
472+
473+ if (m_modelState == RemovingState) {
474+ endRemoveRows();
475+ Q_EMIT countChanged();
476+ Q_EMIT listChanged();
477+ m_modelState = IdleState;
478+ }
479+ }
480+}
481+
398482void TopLevelWindowModel::deleteAt(int index)
399483{
400484 auto window = m_windowModel[index].window;
@@ -417,12 +501,14 @@ void TopLevelWindowModel::removeAt(int index)
417501 }
418502
419503 auto window = m_windowModel[index].window;
504+ auto surface = window->surface();
420505
421506 if (!window->surface()) {
422507 window->setFocused(false);
423508 }
424509
425510 m_windowModel.removeAt(index);
511+ m_allSurfaces.remove(surface);
426512
427513 if (m_modelState == RemovingState) {
428514 endRemoveRows();
@@ -433,6 +519,7 @@ void TopLevelWindowModel::removeAt(int index)
433519
434520 if (m_focusedWindow == window) {
435521 setFocusedWindow(nullptr);
522+ m_focusedWindowCleared = false;
436523 }
437524
438525 INFO_MSG << " after " << toString();
@@ -700,12 +787,11 @@ void TopLevelWindowModel::onModificationsStarted()
700787
701788void TopLevelWindowModel::onModificationsEnded()
702789{
703− if (m_focusedWindowChanged) {
704− setFocusedWindow(m_newlyFocusedWindow);
790+ if (m_focusedWindowCleared) {
791+ setFocusedWindow(nullptr);
705792 }
706793 // reset
707− m_focusedWindowChanged = false;
708− m_newlyFocusedWindow = nullptr;
794+ m_focusedWindowCleared = false;
709795}
710796
711797void TopLevelWindowModel::activateTopMostWindowWithoutId(int forbiddenId)
@@ -719,3 +805,54 @@ void TopLevelWindowModel::activateTopMostWindowWithoutId(int forbiddenId)
719805 }
720806 }
721807}
808+
809+void TopLevelWindowModel::refreshWindows()
810+{
811+ DEBUG_MSG << "()";
812+ clear();
813+
814+ if (!m_workspace || !m_applicationManager || !m_surfaceManager) return;
815+
816+ m_surfaceManager->forEachSurfaceInWorkspace(m_workspace->workspace(), [this](unity::shell::application::MirSurfaceInterface* surface) {
817+ if (surface->parentSurface()) {
818+ // Wrap it in a Window so that we keep focusedWindow() up to date.
819+ Window *window = createWindow(surface);
820+ connect(surface, &QObject::destroyed, window, [=](){
821+ window->setSurface(nullptr);
822+ window->deleteLater();
823+ });
824+ } else {
825+ if (surface->type() == Mir::InputMethodType) {
826+ setInputMethodWindow(createWindow(surface));
827+ } else {
828+ auto *application = m_applicationManager->findApplicationWithSurface(surface);
829+ if (application) {
830+ prependSurface(surface, application);
831+ } else {
832+ // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
833+ // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
834+ // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
835+ Window *promptWindow = createWindow(surface);
836+ connect(surface, &QObject::destroyed, promptWindow, [=](){
837+ promptWindow->setSurface(nullptr);
838+ promptWindow->deleteLater();
839+ });
840+ }
841+ }
842+ }
843+ });
844+}
845+
846+void TopLevelWindowModel::clear()
847+{
848+ DEBUG_MSG << "()";
849+
850+ while(m_windowModel.count() > 0) {
851+ ModelEntry entry = m_windowModel.takeAt(0);
852+ disconnect(entry.window, 0, this, 0);
853+ delete entry.window;
854+ }
855+ m_allSurfaces.clear();
856+ setFocusedWindow(nullptr);
857+ m_focusedWindowCleared = false;
858+}