on xenial help sign in
Various fixes and refactoring
8bb9768Daniel d'Andrada
plugins/WindowManager/TopLevelSurfaceList.cpp+82−24
@@ -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>
23
23
24+#include <QMetaObject>
25+
24Q_LOGGING_CATEGORY(UNITY_TOPSURFACELIST, "unity.topsurfacelist")
26Q_LOGGING_CATEGORY(UNITY_TOPSURFACELIST, "unity.topsurfacelist")
25
27
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)
69
71
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);
73 }
75 }
74}
76}
75
77
76−void TopLevelSurfaceList::prependPlaceholder(ApplicationInfoInterface *application)
78+void TopLevelSurfaceList::appendPlaceholder(ApplicationInfoInterface *application)
77{
79{
78 DEBUG_MSG << "(" << application->appId() << ")";
80 DEBUG_MSG << "(" << application->appId() << ")";
81+
79− prependSurfaceHelper(nullptr, application);
82+ appendSurfaceHelper(nullptr, application);
80}
83}
81
84
82−void TopLevelSurfaceList::prependSurface(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
85+void TopLevelSurfaceList::appendSurface(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
83{
86{
84 Q_ASSERT(surface != nullptr);
87 Q_ASSERT(surface != nullptr);
85
88
@@ -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;
95 }
99 }
@@ -97,22 +101,34 @@ void TopLevelSurfaceList::prependSurface(MirSurfaceInterface *surface, Applicati
97
101
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);
101 }
105 }
102}
106}
103
107
104−void TopLevelSurfaceList::prependSurfaceHelper(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
108+void TopLevelSurfaceList::appendSurfaceHelper(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
105{
109{
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*/);
113+ } else {
114+ Q_ASSERT(m_modelState == ResettingState);
115+ // No point in signaling anything if we're resetting the whole model
116+ }
117+
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);
111 }
122 }
123+
124+ if (m_modelState == InsertingState) {
112− endInsertRows();
125+ endInsertRows();
126+ Q_EMIT countChanged();
127+ Q_EMIT listChanged();
128+ m_modelState = IdleState;
129+ }
130+
113 DEBUG_MSG << " after " << toString();
131 DEBUG_MSG << " after " << toString();
114− Q_EMIT countChanged();
115− Q_EMIT listChanged();
116}
132}
117
133
118void TopLevelSurfaceList::connectSurface(MirSurfaceInterface *surface)
134void TopLevelSurfaceList::connectSurface(MirSurfaceInterface *surface)
@@ -171,12 +187,24 @@ void TopLevelSurfaceList::onSurfaceDestroyed(MirSurfaceInterface *surface)
171
187
172void TopLevelSurfaceList::removeAt(int index)
188void TopLevelSurfaceList::removeAt(int index)
173{
189{
190+ if (m_modelState == IdleState) {
174− beginRemoveRows(QModelIndex(), index, index);
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+
175 m_surfaceList.removeAt(index);
198 m_surfaceList.removeAt(index);
199+
200+ if (m_modelState == RemovingState) {
176− endRemoveRows();
201+ endRemoveRows();
202+ Q_EMIT countChanged();
203+ Q_EMIT listChanged();
204+ m_modelState = IdleState;
205+ }
206+
177 DEBUG_MSG << " after " << toString();
207 DEBUG_MSG << " after " << toString();
178− Q_EMIT countChanged();
179− Q_EMIT listChanged();
180}
208}
181
209
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;
190}
218}
191
219
192−void TopLevelSurfaceList::moveSurface(int from, int to)
220+void TopLevelSurfaceList::move(int from, int to)
193{
221{
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 */
202
230
231+ Q_ASSERT(m_modelState == IdleState);
232+ m_modelState = MovingState;
233+
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();
238+
239+ m_modelState = IdleState;
240+
241+ DEBUG_MSG << " after " << toString();
208 }
242 }
209}
243}
210
244
@@ -245,12 +279,27 @@ int TopLevelSurfaceList::indexForId(int id) const
245 return -1;
279 return -1;
246}
280}
247
281
248−void TopLevelSurfaceList::move(int id, int toIndex)
282+void TopLevelSurfaceList::doRaiseId(int id)
249{
283{
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 */);
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));
254 }
303 }
255}
304}
256
305
@@ -306,10 +355,10 @@ void TopLevelSurfaceList::addApplication(ApplicationInfoInterface *application)
306
355
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);
310 } else {
359 } else {
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);
313 }
362 }
314 }
363 }
315 }
364 }
@@ -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)
319 {
368 {
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);
322 }
371 }
323 });
372 });
324}
373}
@@ -332,6 +381,9 @@ void TopLevelSurfaceList::removeApplication(ApplicationInfoInterface *applicatio
332
381
333 disconnect(surfaceList, 0, this, 0);
382 disconnect(surfaceList, 0, this, 0);
334
383
384+ Q_ASSERT(m_modelState == IdleState);
385+ m_modelState = RemovingState;
386+
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
345 }
397 }
346 }
398 }
347
399
400+ m_modelState = IdleState;
401+
348 DEBUG_MSG << " after " << toString();
402 DEBUG_MSG << " after " << toString();
349
403
350 m_applications.removeAll(application);
404 m_applications.removeAll(application);
@@ -363,6 +417,9 @@ void TopLevelSurfaceList::setApplicationsModel(QAbstractListModel* value)
363
417
364 DEBUG_MSG << "(" << value << ")";
418 DEBUG_MSG << "(" << value << ")";
365
419
420+ Q_ASSERT(m_modelState == IdleState);
421+ m_modelState = ResettingState;
422+
366 beginResetModel();
423 beginResetModel();
367
424
368 if (m_applicationsModel) {
425 if (m_applicationsModel) {
@@ -399,6 +456,7 @@ void TopLevelSurfaceList::setApplicationsModel(QAbstractListModel* value)
399 }
456 }
400
457
401 endResetModel();
458 endResetModel();
459+ m_modelState = IdleState;
402}
460}
403
461
404ApplicationInfoInterface *TopLevelSurfaceList::getApplicationFromModelAt(int index)
462ApplicationInfoInterface *TopLevelSurfaceList::getApplicationFromModelAt(int index)
plugins/WindowManager/TopLevelSurfaceList.h+19−6
@@ -124,13 +124,17 @@ public Q_SLOTS:
124
124
125 /**
125 /**
126 * @brief Returns the index where the row with the given id is located
126 * @brief Returns the index where the row with the given id is located
127+ *
128+ * Returns -1 if there's no row with the given id.
127 */
129 */
128 int indexForId(int id) const;
130 int indexForId(int id) const;
129
131
130 /**
132 /**
131− * @brief Moves the row with the given id to the specified index
133+ * @brief Raises the row with the given id to index 0
132 */
134 */
133− void move(int id, int toIndex);
135+ void raiseId(int id);
136+
137+ void doRaiseId(int id);
134
138
135Q_SIGNALS:
139Q_SIGNALS:
136 void countChanged();
140 void countChanged();
@@ -152,8 +156,8 @@ private:
152
156
153 int indexOf(unity::shell::application::MirSurfaceInterface *surface);
157 int indexOf(unity::shell::application::MirSurfaceInterface *surface);
154 void raise(unity::shell::application::MirSurfaceInterface *surface);
158 void raise(unity::shell::application::MirSurfaceInterface *surface);
155− void moveSurface(int from, int to);
159+ void move(int from, int to);
156− void prependSurfaceHelper(unity::shell::application::MirSurfaceInterface *surface,
160+ void appendSurfaceHelper(unity::shell::application::MirSurfaceInterface *surface,
157 unity::shell::application::ApplicationInfoInterface *application);
161 unity::shell::application::ApplicationInfoInterface *application);
158 void connectSurface(unity::shell::application::MirSurfaceInterface *surface);
162 void connectSurface(unity::shell::application::MirSurfaceInterface *surface);
159 int generateId();
163 int generateId();
@@ -173,7 +177,7 @@ private:
173
177
174 It's a model row containing a null surface and the given application.
178 It's a model row containing a null surface and the given application.
175 */
179 */
176− void prependPlaceholder(unity::shell::application::ApplicationInfoInterface *application);
180+ void appendPlaceholder(unity::shell::application::ApplicationInfoInterface *application);
177
181
178 /*
182 /*
179 Adds a model row with the given surface and application
183 Adds a model row with the given surface and application
@@ -181,7 +185,7 @@ private:
181 Alternatively, if a placeholder exists for the given application it's
185 Alternatively, if a placeholder exists for the given application it's
182 filled with the given surface instead.
186 filled with the given surface instead.
183 */
187 */
184− void prependSurface(unity::shell::application::MirSurfaceInterface *surface,
188+ void appendSurface(unity::shell::application::MirSurfaceInterface *surface,
185 unity::shell::application::ApplicationInfoInterface *application);
189 unity::shell::application::ApplicationInfoInterface *application);
186
190
187 struct ModelEntry {
191 struct ModelEntry {
@@ -202,6 +206,15 @@ private:
202
206
203 QAbstractListModel* m_applicationsModel{nullptr};
207 QAbstractListModel* m_applicationsModel{nullptr};
204 int m_applicationRole{-1};
208 int m_applicationRole{-1};
209+
210+ enum ModelState {
211+ IdleState,
212+ InsertingState,
213+ RemovingState,
214+ MovingState,
215+ ResettingState
216+ };
217+ ModelState m_modelState{IdleState};
205};
218};
206
219
207Q_DECLARE_METATYPE(TopLevelSurfaceList*)
220Q_DECLARE_METATYPE(TopLevelSurfaceList*)
qml/Shell.qml+4
@@ -265,6 +265,10 @@ Item {
265 return "Stages/DesktopStage.qml";
265 return "Stages/DesktopStage.qml";
266 }
266 }
267 }
267 }
268+ // TODO: Ensure the current stage is destroyed before the new one gets loaded.
269+ // Currently the new one will get loaded while the old is still hanging
270+ // around for a bit, which might lead to conflicts where both stages
271+ // change the model simultaneously.
268 onQmlComponentChanged: {
272 onQmlComponentChanged: {
269 if (item) item.stageAboutToBeUnloaded();
273 if (item) item.stageAboutToBeUnloaded();
270 source = qmlComponent;
274 source = qmlComponent;
qml/Stages/DesktopStage.qml+13−6
@@ -215,6 +215,7 @@ AbstractStage {
215 target: MirFocusController
215 target: MirFocusController
216 property: "focusedSurface"
216 property: "focusedSurface"
217 value: priv.focusedAppDelegate ? priv.focusedAppDelegate.surface : null
217 value: priv.focusedAppDelegate ? priv.focusedAppDelegate.surface : null
218+ when: !appRepeater.startingUp && root.parent
218 }
219 }
219
220
220 FocusScope {
221 FocusScope {
@@ -231,7 +232,7 @@ AbstractStage {
231 fillMode: Image.PreserveAspectCrop
232 fillMode: Image.PreserveAspectCrop
232 }
233 }
233
234
234− Repeater {
235+ TopLevelSurfaceRepeater {
235 id: appRepeater
236 id: appRepeater
236 model: topLevelSurfaceList
237 model: topLevelSurfaceList
237 objectName: "appRepeater"
238 objectName: "appRepeater"
@@ -294,19 +295,25 @@ AbstractStage {
294 }
295 }
295
296
296 onFocusChanged: {
297 onFocusChanged: {
297− if (focus) {
298+ if (focus && !appRepeater.startingUp) {
298 priv.focusedAppDelegate = appDelegate;
299 priv.focusedAppDelegate = appDelegate;
299− topLevelSurfaceList.move(model.id, 0);
300 }
300 }
301 }
301 }
302 Component.onCompleted: {
302 Component.onCompleted: {
303+ // NB: We're differentiating if this delegate was created in response to a new entry in the model
304+ // or if the Repeater is just populating itself with delegates to match the model it received.
305+ if (!appRepeater.startingUp) {
303− // a top level window is always the focused one when it first appears, unfocusing
306+ // a top level window is always the focused one when it first appears, unfocusing
304− // any preexisting one
307+ // any preexisting one
305− if (index == 0) {
306 focus = true;
308 focus = true;
307 }
309 }
308 }
310 }
309 Component.onDestruction: {
311 Component.onDestruction: {
312+ if (!root.parent) {
313+ // This stage is about to be destroyed. Don't mess up with the model at this point
314+ return;
315+ }
316+
310 if (focus) {
317 if (focus) {
311 // focus some other window
318 // focus some other window
312 for (var i = 0; i < appRepeater.count; i++) {
319 for (var i = 0; i < appRepeater.count; i++) {
qml/Stages/PhoneStage.qml+17−6
@@ -227,6 +227,7 @@ AbstractStage {
227 target: MirFocusController
227 target: MirFocusController
228 property: "focusedSurface"
228 property: "focusedSurface"
229 value: priv.focusedAppDelegate ? priv.focusedAppDelegate.surface : null
229 value: priv.focusedAppDelegate ? priv.focusedAppDelegate.surface : null
230+ when: root.parent && !spreadRepeater.startingUp
230 }
231 }
231
232
232 Flickable {
233 Flickable {
@@ -446,7 +447,7 @@ AbstractStage {
446 }
447 }
447 }
448 }
448
449
449− Repeater {
450+ TopLevelSurfaceRepeater {
450 id: spreadRepeater
451 id: spreadRepeater
451 objectName: "spreadRepeater"
452 objectName: "spreadRepeater"
452 model: topLevelSurfaceList
453 model: topLevelSurfaceList
@@ -497,17 +498,27 @@ AbstractStage {
497 readonly property bool isDash: model.application.appId == "unity8-dash"
498 readonly property bool isDash: model.application.appId == "unity8-dash"
498
499
499 Component.onCompleted: {
500 Component.onCompleted: {
501+ // NB: We're differentiating if this delegate was created in response to a new entry in the model
502+ // or if the Repeater is just populating itself with delegates to match the model it received.
503+ if (!spreadRepeater.startingUp) {
500− // a top level window is always the focused one when it first appears, unfocusing
504+ // a top level window is always the focused one when it first appears, unfocusing
501− // any preexisting one
505+ // any preexisting one
502− if (model.index == 0) {
506+ //
507+ // new items are appended and must be manually brought to front.
508+ // that's how it *must* be in order to get the animation for new
509+ // surfaces working
503 focus = true;
510 focus = true;
504 }
511 }
505 }
512 }
506
513
507 onFocusChanged: {
514 onFocusChanged: {
508− if (focus) {
515+ if (focus && !spreadRepeater.startingUp) {
509 priv.focusedAppDelegate = appDelegate;
516 priv.focusedAppDelegate = appDelegate;
517+ // If we're orphan (!parent) it means this stage is no longer the current one
518+ // and will be deleted shortly. So we should no longer have a say over the model
519+ if (root.parent) {
510− topLevelSurfaceList.move(model.id, 0);
520+ topLevelSurfaceList.raiseId(model.id);
521+ }
511 }
522 }
512 }
523 }
513 Connections {
524 Connections {
qml/Stages/TabletStage.qml+8−12
@@ -167,11 +167,6 @@ AbstractStage {
167 onListChanged: priv.updateMainAndSideStageIndexes()
167 onListChanged: priv.updateMainAndSideStageIndexes()
168 }
168 }
169
169
170− onTopLevelSurfaceListChanged: {
171− // Initialize their values
172− priv.updateMainAndSideStageIndexes();
173− }
174−
175 QtObject {
170 QtObject {
176 id: priv
171 id: priv
177 objectName: "stagesPriv"
172 objectName: "stagesPriv"
@@ -183,7 +178,7 @@ AbstractStage {
183 if (!root.topLevelSurfaceList)
178 if (!root.topLevelSurfaceList)
184 return;
179 return;
185
180
186− for (var i = 0; i < root.topLevelSurfaceList.count && (!choseMainStage || !choseSideStage); ++i) {
181+ for (var i = 0; i < spreadRepeater.count && (!choseMainStage || !choseSideStage); ++i) {
187 var spreadDelegate = spreadRepeater.itemAt(i);
182 var spreadDelegate = spreadRepeater.itemAt(i);
188 if (!spreadView.sideStageHidden && spreadDelegate.stage == ApplicationInfoInterface.SideStage
183 if (!spreadView.sideStageHidden && spreadDelegate.stage == ApplicationInfoInterface.SideStage
189 && !choseSideStage) {
184 && !choseSideStage) {
@@ -298,6 +293,7 @@ AbstractStage {
298 target: MirFocusController
293 target: MirFocusController
299 property: "focusedSurface"
294 property: "focusedSurface"
300 value: priv.focusedAppDelegate ? priv.focusedAppDelegate.surface : null
295 value: priv.focusedAppDelegate ? priv.focusedAppDelegate.surface : null
296+ when: root.parent && !spreadRepeater.startingUp
301 }
297 }
302
298
303 Flickable {
299 Flickable {
@@ -687,18 +683,20 @@ AbstractStage {
687 }
683 }
688 }
684 }
689
685
690− Repeater {
686+ TopLevelSurfaceRepeater {
691 id: spreadRepeater
687 id: spreadRepeater
692 objectName: "spreadRepeater"
688 objectName: "spreadRepeater"
693 model: root.topLevelSurfaceList
689 model: root.topLevelSurfaceList
694
690
695 onItemAdded: {
691 onItemAdded: {
692+ priv.updateMainAndSideStageIndexes();
696 if (spreadView.phase == 2) {
693 if (spreadView.phase == 2) {
697 spreadView.snapTo(index);
694 spreadView.snapTo(index);
698 }
695 }
699 }
696 }
700
697
701 onItemRemoved: {
698 onItemRemoved: {
699+ priv.updateMainAndSideStageIndexes();
702 // Unless we're closing the app ourselves in the spread,
700 // Unless we're closing the app ourselves in the spread,
703 // lets make sure the spread doesn't mess up by the changing app list.
701 // lets make sure the spread doesn't mess up by the changing app list.
704 if (spreadView.closingIndex == -1) {
702 if (spreadView.closingIndex == -1) {
@@ -746,9 +744,9 @@ AbstractStage {
746 readonly property bool isDash: model.application.appId == "unity8-dash"
744 readonly property bool isDash: model.application.appId == "unity8-dash"
747
745
748 onFocusChanged: {
746 onFocusChanged: {
749− if (focus) {
747+ if (focus && !spreadRepeater.startingUp) {
750 priv.focusedAppDelegate = spreadTile;
748 priv.focusedAppDelegate = spreadTile;
751− root.topLevelSurfaceList.move(model.id, 0);
749+ root.topLevelSurfaceList.raiseId(model.id);
752 }
750 }
753 if (focus && priv.sideStageEnabled && stage === ApplicationInfoInterface.SideStage) {
751 if (focus && priv.sideStageEnabled && stage === ApplicationInfoInterface.SideStage) {
754 sideStage.show();
752 sideStage.show();
@@ -824,9 +822,7 @@ AbstractStage {
824 Component.onCompleted: {
822 Component.onCompleted: {
825 // a top level window is always the focused one when it first appears, unfocusing
823 // a top level window is always the focused one when it first appears, unfocusing
826 // any preexisting one
824 // any preexisting one
827− if (model.index == 0) {
828− focus = true;
825+ focus = true;
829− }
830 refreshStage();
826 refreshStage();
831 _constructing = false;
827 _constructing = false;
832 }
828 }
qml/Stages/TopLevelSurfaceRepeater.qmlnew file+52
@@ -0,0 +1,52 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+
19+Repeater {
20+ id: root
21+ // FIXME: This is a hack around us not knowing whether the Repeater has finished creating its
22+ // delegates on start up.
23+ // This is a problem when the stage gets a TopLevelSurfaceList already populated with several
24+ // rows.
25+ property bool startingUp: true
26+ onStartingUpChanged: {
27+ if (!startingUp) {
28+ // the top-most surface must be the focused one.
29+ var topmostDelegate = itemAt(0);
30+ topmostDelegate.focus = true;
31+ }
32+ }
33+
34+ onItemAdded: {
35+ if (startingUp) {
36+ checkIfStillStartingUp();
37+ }
38+ }
39+
40+ function checkIfStillStartingUp() {
41+ var i = 0;
42+ var missingDelegate = false;
43+ for (i = 0; i < model.count && !missingDelegate; ++i) {
44+ if (!itemAt(i)) {
45+ missingDelegate = true;
46+ }
47+ }
48+ if (!missingDelegate) {
49+ startingUp = false;
50+ }
51+ }
52+}
tests/mocks/Unity/Application/MirSurface.cpp−3
@@ -263,9 +263,6 @@ void MirSurface::setActiveFocus(bool value)
263 return;
263 return;
264
264
265 m_activeFocus = value;
265 m_activeFocus = value;
266− if (value) {
267− MirFocusController::instance()->setFocusedSurface(this);
268− }
269
266
270 Q_EMIT activeFocusChanged(value);
267 Q_EMIT activeFocusChanged(value);
271}
268}
tests/qmltests/Stages/tst_DesktopStage.qml+1
@@ -20,6 +20,7 @@ import Ubuntu.Components 1.3
20import Ubuntu.Components.ListItems 1.3
20import Ubuntu.Components.ListItems 1.3
21import Unity.Application 0.1
21import Unity.Application 0.1
22import Unity.Test 0.1
22import Unity.Test 0.1
23+import WindowManager 0.1
23import Utils 0.1
24import Utils 0.1
24
25
25import ".." // For EdgeBarrierControls
26import ".." // For EdgeBarrierControls
tests/qmltests/Stages/tst_TabletStage.qml+2−5
@@ -21,6 +21,7 @@ import Ubuntu.Components.ListItems 1.3 as ListItem
21import Unity.Application 0.1
21import Unity.Application 0.1
22import Unity.Test 0.1
22import Unity.Test 0.1
23import Utils 0.1
23import Utils 0.1
24+import WindowManager 0.1
24
25
25import ".."
26import ".."
26import "../../../qml/Stages"
27import "../../../qml/Stages"
@@ -170,11 +171,7 @@ Rectangle {
170 // Shell instance gets destroyed.
171 // Shell instance gets destroyed.
171 tryCompare(tabletStageLoader, "itemDestroyed", true);
172 tryCompare(tabletStageLoader, "itemDestroyed", true);
172
173
173− // kill all (fake) running apps
174+ killApps();
174− webbrowserCheckBox.checked = false;
175− galleryCheckBox.checked = false;
176− dialerCheckBox.checked = false;
177− facebookCheckBox.checked = false;
178 WindowStateStorage.clear();
175 WindowStateStorage.clear();
179 }
176 }
180
177