@@ -21,6 +21,8 @@
2121#include <unity/shell/application/MirSurfaceInterface.h>
2222#include <unity/shell/application/MirSurfaceListInterface.h>
2323
24+#include <QMetaObject>
25+
2426Q_LOGGING_CATEGORY(UNITY_TOPSURFACELIST, "unity.topsurfacelist")
2527
2628#define DEBUG_MSG qCDebug(UNITY_TOPSURFACELIST).nospace().noquote() << __func__
@@ -69,17 +71,18 @@ void TopLevelSurfaceList::raise(MirSurfaceInterface *surface)
6971
7072 int i = indexOf(surface);
7173 if (i != -1) {
72− moveSurface(i, 0);
74+ raiseId(m_surfaceList.at(i).id);
7375 }
7476}
7577
76−void TopLevelSurfaceList::prependPlaceholder(ApplicationInfoInterface *application)
78+void TopLevelSurfaceList::appendPlaceholder(ApplicationInfoInterface *application)
7779{
7880 DEBUG_MSG << "(" << application->appId() << ")";
79− prependSurfaceHelper(nullptr, application);
81+
82+ appendSurfaceHelper(nullptr, application);
8083}
8184
82−void TopLevelSurfaceList::prependSurface(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
85+void TopLevelSurfaceList::appendSurface(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
8386{
8487 Q_ASSERT(surface != nullptr);
8588
@@ -89,7 +92,8 @@ void TopLevelSurfaceList::prependSurface(MirSurfaceInterface *surface, Applicati
8992 if (entry.application == application && entry.surface == nullptr) {
9093 entry.surface = surface;
9194 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();
9397 Q_EMIT dataChanged(index(i) /* topLeft */, index(i) /* bottomRight */, QVector<int>() << SurfaceRole);
9498 filledPlaceholder = true;
9599 }
@@ -97,22 +101,34 @@ void TopLevelSurfaceList::prependSurface(MirSurfaceInterface *surface, Applicati
97101
98102 if (!filledPlaceholder) {
99103 DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface << ", adding new row";
100− prependSurfaceHelper(surface, application);
104+ appendSurfaceHelper(surface, application);
101105 }
102106}
103107
104−void TopLevelSurfaceList::prependSurfaceHelper(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
108+void TopLevelSurfaceList::appendSurfaceHelper(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
105109{
106− beginInsertRows(QModelIndex(), 0 /*first*/, 0 /*last*/);
110+ if (m_modelState == IdleState) {
111+ m_modelState = InsertingState;
112+ beginInsertRows(QModelIndex(), m_surfaceList.size() /*first*/, m_surfaceList.size() /*last*/);
113+ } else {
114+ Q_ASSERT(m_modelState == ResettingState);
115+ // No point in signaling anything if we're resetting the whole model
116+ }
117+
107118 int id = generateId();
108− m_surfaceList.prepend(ModelEntry(surface, application, id));
119+ m_surfaceList.append(ModelEntry(surface, application, id));
109120 if (surface) {
110121 connectSurface(surface);
111122 }
112− endInsertRows();
123+
124+ if (m_modelState == InsertingState) {
125+ endInsertRows();
126+ Q_EMIT countChanged();
127+ Q_EMIT listChanged();
128+ m_modelState = IdleState;
129+ }
130+
113131 DEBUG_MSG << " after " << toString();
114− Q_EMIT countChanged();
115− Q_EMIT listChanged();
116132}
117133
118134void TopLevelSurfaceList::connectSurface(MirSurfaceInterface *surface)
@@ -171,12 +187,24 @@ void TopLevelSurfaceList::onSurfaceDestroyed(MirSurfaceInterface *surface)
171187
172188void TopLevelSurfaceList::removeAt(int index)
173189{
174− beginRemoveRows(QModelIndex(), index, index);
190+ if (m_modelState == IdleState) {
191+ beginRemoveRows(QModelIndex(), index, index);
192+ m_modelState = RemovingState;
193+ } else {
194+ Q_ASSERT(m_modelState == ResettingState);
195+ // No point in signaling anything if we're resetting the whole model
196+ }
197+
175198 m_surfaceList.removeAt(index);
176− endRemoveRows();
199+
200+ if (m_modelState == RemovingState) {
201+ endRemoveRows();
202+ Q_EMIT countChanged();
203+ Q_EMIT listChanged();
204+ m_modelState = IdleState;
205+ }
206+
177207 DEBUG_MSG << " after " << toString();
178− Q_EMIT countChanged();
179− Q_EMIT listChanged();
180208}
181209
182210int TopLevelSurfaceList::indexOf(MirSurfaceInterface *surface)
@@ -189,7 +217,7 @@ int TopLevelSurfaceList::indexOf(MirSurfaceInterface *surface)
189217 return -1;
190218}
191219
192−void TopLevelSurfaceList::moveSurface(int from, int to)
220+void TopLevelSurfaceList::move(int from, int to)
193221{
194222 if (from == to) return;
195223 DEBUG_MSG << " from=" << from << " to=" << to;
@@ -200,11 +228,17 @@ void TopLevelSurfaceList::moveSurface(int from, int to)
200228 by one, as explained in the documentation:
201229 http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
202230
231+ Q_ASSERT(m_modelState == IdleState);
232+ m_modelState = MovingState;
233+
203234 beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
204235 m_surfaceList.move(from, to);
205− DEBUG_MSG << " after " << toString();
206236 endMoveRows();
207237 Q_EMIT listChanged();
238+
239+ m_modelState = IdleState;
240+
241+ DEBUG_MSG << " after " << toString();
208242 }
209243}
210244
@@ -245,12 +279,27 @@ int TopLevelSurfaceList::indexForId(int id) const
245279 return -1;
246280}
247281
248−void TopLevelSurfaceList::move(int id, int toIndex)
282+void TopLevelSurfaceList::doRaiseId(int id)
249283{
250− DEBUG_MSG << " id=" << id << " toIndex=" << toIndex;
251284 int fromIndex = indexForId(id);
252285 if (fromIndex != -1) {
253− moveSurface(fromIndex, toIndex);
286+ move(fromIndex, 0 /* toIndex */);
287+ }
288+}
289+
290+void TopLevelSurfaceList::raiseId(int id)
291+{
292+ if (m_modelState == IdleState) {
293+ DEBUG_MSG << "(id=" << id << ") - do it now.";
294+ doRaiseId(id);
295+ } else {
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.
299+ //
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));
254303 }
255304}
256305
@@ -306,10 +355,10 @@ void TopLevelSurfaceList::addApplication(ApplicationInfoInterface *application)
306355
307356 if (application->state() != ApplicationInfoInterface::Stopped) {
308357 if (surfaceList->count() == 0) {
309− prependPlaceholder(application);
358+ appendPlaceholder(application);
310359 } else {
311360 for (int i = 0; i < surfaceList->count(); ++i) {
312− prependSurface(surfaceList->get(i), application);
361+ appendSurface(surfaceList->get(i), application);
313362 }
314363 }
315364 }
@@ -318,7 +367,7 @@ void TopLevelSurfaceList::addApplication(ApplicationInfoInterface *application)
318367 [this, application, surfaceList](const QModelIndex & /*parent*/, int first, int last)
319368 {
320369 for (int i = last; i >= first; --i) {
321− this->prependSurface(surfaceList->get(i), application);
370+ this->appendSurface(surfaceList->get(i), application);
322371 }
323372 });
324373}
@@ -332,6 +381,9 @@ void TopLevelSurfaceList::removeApplication(ApplicationInfoInterface *applicatio
332381
333382 disconnect(surfaceList, 0, this, 0);
334383
384+ Q_ASSERT(m_modelState == IdleState);
385+ m_modelState = RemovingState;
386+
335387 int i = 0;
336388 while (i < m_surfaceList.count()) {
337389 if (m_surfaceList.at(i).application == application) {
@@ -345,6 +397,8 @@ void TopLevelSurfaceList::removeApplication(ApplicationInfoInterface *applicatio
345397 }
346398 }
347399
400+ m_modelState = IdleState;
401+
348402 DEBUG_MSG << " after " << toString();
349403
350404 m_applications.removeAll(application);
@@ -363,6 +417,9 @@ void TopLevelSurfaceList::setApplicationsModel(QAbstractListModel* value)
363417
364418 DEBUG_MSG << "(" << value << ")";
365419
420+ Q_ASSERT(m_modelState == IdleState);
421+ m_modelState = ResettingState;
422+
366423 beginResetModel();
367424
368425 if (m_applicationsModel) {
@@ -399,6 +456,7 @@ void TopLevelSurfaceList::setApplicationsModel(QAbstractListModel* value)
399456 }
400457
401458 endResetModel();
459+ m_modelState = IdleState;
402460}
403461
404462ApplicationInfoInterface *TopLevelSurfaceList::getApplicationFromModelAt(int index)