@@ -21,6 +21,8 @@
21#include <unity/shell/application/MirSurfaceInterface.h>
21#include <unity/shell/application/MirSurfaceInterface.h>
22#include <unity/shell/application/MirSurfaceListInterface.h>
22#include <unity/shell/application/MirSurfaceListInterface.h>
24+#include <QMetaObject>
24Q_LOGGING_CATEGORY(UNITY_TOPSURFACELIST, "unity.topsurfacelist")
26Q_LOGGING_CATEGORY(UNITY_TOPSURFACELIST, "unity.topsurfacelist")
26#define DEBUG_MSG qCDebug(UNITY_TOPSURFACELIST).nospace().noquote() << __func__
28#define DEBUG_MSG qCDebug(UNITY_TOPSURFACELIST).nospace().noquote() << __func__
@@ -69,17 +71,18 @@ void TopLevelSurfaceList::raise(MirSurfaceInterface *surface)
70 int i = indexOf(surface);
72 int i = indexOf(surface);
71 if (i != -1) {
73 if (i != -1) {
72− moveSurface(i, 0);
74+ raiseId(m_surfaceList.at(i).id);
76−void TopLevelSurfaceList::prependPlaceholder(ApplicationInfoInterface *application)
78+void TopLevelSurfaceList::appendPlaceholder(ApplicationInfoInterface *application)
78 DEBUG_MSG << "(" << application->appId() << ")";
80 DEBUG_MSG << "(" << application->appId() << ")";
79− prependSurfaceHelper(nullptr, application);
82+ appendSurfaceHelper(nullptr, application);
82−void TopLevelSurfaceList::prependSurface(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
85+void TopLevelSurfaceList::appendSurface(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
84 Q_ASSERT(surface != nullptr);
87 Q_ASSERT(surface != nullptr);
@@ -89,7 +92,8 @@ void TopLevelSurfaceList::prependSurface(MirSurfaceInterface *surface, Applicati
89 if (entry.application == application && entry.surface == nullptr) {
92 if (entry.application == application && entry.surface == nullptr) {
90 entry.surface = surface;
93 entry.surface = surface;
91 connectSurface(surface);
94 connectSurface(surface);
92− DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface << ", filling out placeholder. after: " << toString();
95+ DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface
96+ << ", filling out placeholder. after: " << toString();
93 Q_EMIT dataChanged(index(i) /* topLeft */, index(i) /* bottomRight */, QVector<int>() << SurfaceRole);
97 Q_EMIT dataChanged(index(i) /* topLeft */, index(i) /* bottomRight */, QVector<int>() << SurfaceRole);
94 filledPlaceholder = true;
98 filledPlaceholder = true;
@@ -97,22 +101,34 @@ void TopLevelSurfaceList::prependSurface(MirSurfaceInterface *surface, Applicati
98 if (!filledPlaceholder) {
102 if (!filledPlaceholder) {
99 DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface << ", adding new row";
103 DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface << ", adding new row";
100− prependSurfaceHelper(surface, application);
104+ appendSurfaceHelper(surface, application);
104−void TopLevelSurfaceList::prependSurfaceHelper(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
108+void TopLevelSurfaceList::appendSurfaceHelper(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
110+ if (m_modelState == IdleState) {
111+ m_modelState = InsertingState;
106− beginInsertRows(QModelIndex(), 0 /*first*/, 0 /*last*/);
112+ beginInsertRows(QModelIndex(), m_surfaceList.size() /*first*/, m_surfaceList.size() /*last*/);
114+ Q_ASSERT(m_modelState == ResettingState);
115+ // No point in signaling anything if we're resetting the whole model
107 int id = generateId();
118 int id = generateId();
108− m_surfaceList.prepend(ModelEntry(surface, application, id));
119+ m_surfaceList.append(ModelEntry(surface, application, id));
109 if (surface) {
120 if (surface) {
110 connectSurface(surface);
121 connectSurface(surface);
124+ if (m_modelState == InsertingState) {
112− endInsertRows();
125+ endInsertRows();
126+ Q_EMIT countChanged();
127+ Q_EMIT listChanged();
128+ m_modelState = IdleState;
113 DEBUG_MSG << " after " << toString();
131 DEBUG_MSG << " after " << toString();
114− Q_EMIT countChanged();
115− Q_EMIT listChanged();
118void TopLevelSurfaceList::connectSurface(MirSurfaceInterface *surface)
134void TopLevelSurfaceList::connectSurface(MirSurfaceInterface *surface)
@@ -171,12 +187,24 @@ void TopLevelSurfaceList::onSurfaceDestroyed(MirSurfaceInterface *surface)
172void TopLevelSurfaceList::removeAt(int index)
188void TopLevelSurfaceList::removeAt(int index)
190+ if (m_modelState == IdleState) {
174− beginRemoveRows(QModelIndex(), index, index);
191+ beginRemoveRows(QModelIndex(), index, index);
192+ m_modelState = RemovingState;
194+ Q_ASSERT(m_modelState == ResettingState);
195+ // No point in signaling anything if we're resetting the whole model
175 m_surfaceList.removeAt(index);
198 m_surfaceList.removeAt(index);
200+ if (m_modelState == RemovingState) {
176− endRemoveRows();
201+ endRemoveRows();
202+ Q_EMIT countChanged();
203+ Q_EMIT listChanged();
204+ m_modelState = IdleState;
177 DEBUG_MSG << " after " << toString();
207 DEBUG_MSG << " after " << toString();
178− Q_EMIT countChanged();
179− Q_EMIT listChanged();
182int TopLevelSurfaceList::indexOf(MirSurfaceInterface *surface)
210int TopLevelSurfaceList::indexOf(MirSurfaceInterface *surface)
@@ -189,7 +217,7 @@ int TopLevelSurfaceList::indexOf(MirSurfaceInterface *surface)
189 return -1;
217 return -1;
192−void TopLevelSurfaceList::moveSurface(int from, int to)
220+void TopLevelSurfaceList::move(int from, int to)
194 if (from == to) return;
222 if (from == to) return;
195 DEBUG_MSG << " from=" << from << " to=" << to;
223 DEBUG_MSG << " from=" << from << " to=" << to;
@@ -200,11 +228,17 @@ void TopLevelSurfaceList::moveSurface(int from, int to)
200 by one, as explained in the documentation:
228 by one, as explained in the documentation:
201 http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
229 http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
231+ Q_ASSERT(m_modelState == IdleState);
232+ m_modelState = MovingState;
203 beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
234 beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
204 m_surfaceList.move(from, to);
235 m_surfaceList.move(from, to);
205− DEBUG_MSG << " after " << toString();
206 endMoveRows();
236 endMoveRows();
207 Q_EMIT listChanged();
237 Q_EMIT listChanged();
239+ m_modelState = IdleState;
241+ DEBUG_MSG << " after " << toString();
@@ -245,12 +279,27 @@ int TopLevelSurfaceList::indexForId(int id) const
245 return -1;
279 return -1;
248−void TopLevelSurfaceList::move(int id, int toIndex)
282+void TopLevelSurfaceList::doRaiseId(int id)
250− DEBUG_MSG << " id=" << id << " toIndex=" << toIndex;
251 int fromIndex = indexForId(id);
284 int fromIndex = indexForId(id);
252 if (fromIndex != -1) {
285 if (fromIndex != -1) {
253− moveSurface(fromIndex, toIndex);
286+ move(fromIndex, 0 /* toIndex */);
290+void TopLevelSurfaceList::raiseId(int id)
292+ if (m_modelState == IdleState) {
293+ DEBUG_MSG << "(id=" << id << ") - do it now.";
296+ DEBUG_MSG << "(id=" << id << ") - Model busy (modelState=" << m_modelState << "). Try again in the next event loop.";
297+ // The model has just signalled some change. If we have a Repeater responding to this update, it will get nuts
298+ // if we perform yet another model change straight away.
300+ // A bad sympton of this problem is a Repeater.itemAt(index) call returning null event though Repeater.count says
301+ // the index is definitely within bounds.
302+ QMetaObject::invokeMethod(this, "raiseId", Qt::QueuedConnection, Q_ARG(int, id));
@@ -306,10 +355,10 @@ void TopLevelSurfaceList::addApplication(ApplicationInfoInterface *application)
307 if (application->state() != ApplicationInfoInterface::Stopped) {
356 if (application->state() != ApplicationInfoInterface::Stopped) {
308 if (surfaceList->count() == 0) {
357 if (surfaceList->count() == 0) {
309− prependPlaceholder(application);
358+ appendPlaceholder(application);
311 for (int i = 0; i < surfaceList->count(); ++i) {
360 for (int i = 0; i < surfaceList->count(); ++i) {
312− prependSurface(surfaceList->get(i), application);
361+ appendSurface(surfaceList->get(i), application);
@@ -318,7 +367,7 @@ void TopLevelSurfaceList::addApplication(ApplicationInfoInterface *application)
318 [this, application, surfaceList](const QModelIndex & /*parent*/, int first, int last)
367 [this, application, surfaceList](const QModelIndex & /*parent*/, int first, int last)
320 for (int i = last; i >= first; --i) {
369 for (int i = last; i >= first; --i) {
321− this->prependSurface(surfaceList->get(i), application);
370+ this->appendSurface(surfaceList->get(i), application);
@@ -332,6 +381,9 @@ void TopLevelSurfaceList::removeApplication(ApplicationInfoInterface *applicatio
333 disconnect(surfaceList, 0, this, 0);
382 disconnect(surfaceList, 0, this, 0);
384+ Q_ASSERT(m_modelState == IdleState);
385+ m_modelState = RemovingState;
335 int i = 0;
387 int i = 0;
336 while (i < m_surfaceList.count()) {
388 while (i < m_surfaceList.count()) {
337 if (m_surfaceList.at(i).application == application) {
389 if (m_surfaceList.at(i).application == application) {
@@ -345,6 +397,8 @@ void TopLevelSurfaceList::removeApplication(ApplicationInfoInterface *applicatio
400+ m_modelState = IdleState;
348 DEBUG_MSG << " after " << toString();
402 DEBUG_MSG << " after " << toString();
350 m_applications.removeAll(application);
404 m_applications.removeAll(application);
@@ -363,6 +417,9 @@ void TopLevelSurfaceList::setApplicationsModel(QAbstractListModel* value)
364 DEBUG_MSG << "(" << value << ")";
418 DEBUG_MSG << "(" << value << ")";
420+ Q_ASSERT(m_modelState == IdleState);
421+ m_modelState = ResettingState;
366 beginResetModel();
423 beginResetModel();
368 if (m_applicationsModel) {
425 if (m_applicationsModel) {
@@ -399,6 +456,7 @@ void TopLevelSurfaceList::setApplicationsModel(QAbstractListModel* value)
401 endResetModel();
458 endResetModel();
459+ m_modelState = IdleState;
404ApplicationInfoInterface *TopLevelSurfaceList::getApplicationFromModelAt(int index)
462ApplicationInfoInterface *TopLevelSurfaceList::getApplicationFromModelAt(int index)