Merge lp:~mzanetti/unity8/miral-qt54 into lp:unity8

Proposed by Michael Zanetti
Status: Work in progress
Proposed branch: lp:~mzanetti/unity8/miral-qt54
Merge into: lp:unity8
Diff against target: 4245 lines (+1588/-1275)
40 files modified
CMakeLists.txt (+1/-1)
debian/control (+3/-3)
debian/unity8-private.install (+0/-1)
plugins/CMakeLists.txt (+0/-1)
plugins/WindowManager/CMakeLists.txt (+0/-14)
plugins/WindowManager/TopLevelSurfaceList.cpp (+0/-481)
plugins/WindowManager/TopLevelSurfaceList.h (+0/-223)
plugins/WindowManager/WindowManagerPlugin.cpp (+0/-28)
plugins/WindowManager/WindowManagerPlugin.h (+0/-32)
plugins/WindowManager/qmldir (+0/-2)
qml/Components/InputMethod.qml (+5/-4)
qml/Components/KeymapSwitcher.qml (+4/-1)
qml/Shell.qml (+7/-6)
qml/Stage/DecoratedWindow.qml (+8/-2)
qml/Stage/FakeMaximizeDelegate.qml (+7/-7)
qml/Stage/Stage.qml (+130/-117)
qml/Stage/StagedFullscreenPolicy.qml (+4/-4)
qml/Stage/TopLevelSurfaceRepeater.qml (+0/-67)
qml/Stage/WindowedFullscreenPolicy.qml (+1/-1)
tests/mocks/Unity/Application/ApplicationInfo.cpp (+18/-4)
tests/mocks/Unity/Application/ApplicationManager.cpp (+78/-41)
tests/mocks/Unity/Application/ApplicationManager.h (+28/-1)
tests/mocks/Unity/Application/CMakeLists.txt (+4/-1)
tests/mocks/Unity/Application/MirSurface.cpp (+70/-93)
tests/mocks/Unity/Application/MirSurface.h (+24/-23)
tests/mocks/Unity/Application/MirSurfaceItem.cpp (+4/-4)
tests/mocks/Unity/Application/MirSurfaceItem.h (+1/-2)
tests/mocks/Unity/Application/MirSurfaceListModel.cpp (+20/-2)
tests/mocks/Unity/Application/MirSurfaceListModel.h (+2/-1)
tests/mocks/Unity/Application/SurfaceManager.cpp (+9/-8)
tests/mocks/Unity/Application/SurfaceManager.h (+4/-3)
tests/mocks/Unity/Application/TopLevelWindowModel.cpp (+610/-0)
tests/mocks/Unity/Application/TopLevelWindowModel.h (+146/-0)
tests/mocks/Unity/Application/Window.cpp (+229/-0)
tests/mocks/Unity/Application/Window.h (+69/-0)
tests/mocks/Unity/Application/plugin.cpp (+7/-11)
tests/qmltests/Stage/tst_DesktopStage.qml (+41/-36)
tests/qmltests/Stage/tst_PhoneStage.qml (+5/-7)
tests/qmltests/Stage/tst_TabletStage.qml (+3/-5)
tests/qmltests/tst_Shell.qml (+46/-38)
To merge this branch: bzr merge lp:~mzanetti/unity8/miral-qt54
Reviewer Review Type Date Requested Status
Unity Team Pending
Review via email: mp+310439@code.launchpad.net

Commit message

just so I can test this on something with a touchscreen

To post a comment you must log in.
lp:~mzanetti/unity8/miral-qt54 updated
2677. By Michael Zanetti

fix typo

Unmerged revisions

2677. By Michael Zanetti

fix typo

2676. By Michael Zanetti

make it build with Qt 5.4

2675. By Daniel d'Andrada

Let the model deal with some window management decisions

eg: which window to focus, whether to change surface state

unit8 requests and reacts to changes in the model instead of applying them

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2016-10-24 11:34:08 +0000
3+++ CMakeLists.txt 2016-11-09 15:21:35 +0000
4@@ -57,7 +57,7 @@
5 find_package(Qt5Concurrent 5.4 REQUIRED)
6 find_package(Qt5Sql 5.4 REQUIRED)
7
8-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=22)
9+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=23)
10 pkg_check_modules(GEONAMES REQUIRED geonames>=0.2)
11 pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
12 pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
13
14=== modified file 'debian/control'
15--- debian/control 2016-10-27 12:25:58 +0000
16+++ debian/control 2016-11-09 15:21:35 +0000
17@@ -38,7 +38,7 @@
18 libubuntugestures5-private-dev (>= 1.3.2030),
19 libudev-dev,
20 libudm-common-dev,
21- libunity-api-dev (>= 7.119),
22+ libunity-api-dev (>= 7.120),
23 libusermetricsoutput1-dev,
24 # Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
25 libx11-dev[!arm64 !armhf],
26@@ -158,7 +158,7 @@
27 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
28 ubuntu-thumbnailer-impl-0,
29 ubuntu-wallpapers,
30- unity-application-impl-22,
31+ unity-application-impl-23,
32 unity-notifications-impl-3,
33 unity-plugin-scopes | unity-scopes-impl,
34 unity-scopes-impl-12,
35@@ -204,7 +204,7 @@
36 Depends: ${misc:Depends},
37 ${shlibs:Depends},
38 Provides: unity-application-impl,
39- unity-application-impl-22,
40+ unity-application-impl-23,
41 Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1)
42 Description: Fake environment for running Unity 8 shell
43 Provides fake implementations of some QML modules used by Unity 8 shell
44
45=== modified file 'debian/unity8-private.install'
46--- debian/unity8-private.install 2016-08-30 14:06:47 +0000
47+++ debian/unity8-private.install 2016-11-09 15:21:35 +0000
48@@ -14,7 +14,6 @@
49 usr/lib/*/unity8/qml/Unity
50 usr/lib/*/unity8/qml/UInput
51 usr/lib/*/unity8/qml/Utils
52-usr/lib/*/unity8/qml/WindowManager
53 usr/lib/*/unity8/qml/Wizard
54 usr/share/accountsservice/interfaces
55 usr/share/dbus-1/interfaces
56
57=== modified file 'plugins/CMakeLists.txt'
58--- plugins/CMakeLists.txt 2016-08-30 14:06:47 +0000
59+++ plugins/CMakeLists.txt 2016-11-09 15:21:35 +0000
60@@ -26,5 +26,4 @@
61 add_subdirectory(UInput)
62 add_subdirectory(Unity)
63 add_subdirectory(Utils)
64-add_subdirectory(WindowManager)
65 add_subdirectory(Wizard)
66
67=== removed directory 'plugins/WindowManager'
68=== removed file 'plugins/WindowManager/CMakeLists.txt'
69--- plugins/WindowManager/CMakeLists.txt 2016-06-20 09:43:38 +0000
70+++ plugins/WindowManager/CMakeLists.txt 1970-01-01 00:00:00 +0000
71@@ -1,14 +0,0 @@
72-set(WINDOWMANAGER_SRC
73- TopLevelSurfaceList.cpp
74- WindowManagerPlugin.cpp
75- ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
76- ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
77- ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
78- ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
79- )
80-
81-add_library(windowmanager-qml SHARED ${WINDOWMANAGER_SRC})
82-
83-qt5_use_modules(windowmanager-qml Qml Quick Gui)
84-
85-add_unity8_plugin(WindowManager 0.1 WindowManager TARGETS windowmanager-qml)
86
87=== removed file 'plugins/WindowManager/TopLevelSurfaceList.cpp'
88--- plugins/WindowManager/TopLevelSurfaceList.cpp 2016-09-14 15:02:01 +0000
89+++ plugins/WindowManager/TopLevelSurfaceList.cpp 1970-01-01 00:00:00 +0000
90@@ -1,481 +0,0 @@
91-/*
92- * Copyright (C) 2016 Canonical, Ltd.
93- *
94- * This program is free software; you can redistribute it and/or modify
95- * it under the terms of the GNU General Public License as published by
96- * the Free Software Foundation; version 3.
97- *
98- * This program is distributed in the hope that it will be useful,
99- * but WITHOUT ANY WARRANTY; without even the implied warranty of
100- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
101- * GNU General Public License for more details.
102- *
103- * You should have received a copy of the GNU General Public License
104- * along with this program. If not, see <http://www.gnu.org/licenses/>.
105- */
106-
107-#include "TopLevelSurfaceList.h"
108-
109-// unity-api
110-#include <unity/shell/application/ApplicationInfoInterface.h>
111-#include <unity/shell/application/MirSurfaceInterface.h>
112-#include <unity/shell/application/MirSurfaceListInterface.h>
113-
114-#include <QMetaObject>
115-
116-Q_LOGGING_CATEGORY(UNITY_TOPSURFACELIST, "unity.topsurfacelist", QtDebugMsg)
117-
118-#define DEBUG_MSG qCDebug(UNITY_TOPSURFACELIST).nospace().noquote() << __func__
119-
120-using namespace unity::shell::application;
121-
122-TopLevelSurfaceList::TopLevelSurfaceList(QObject *parent) :
123- QAbstractListModel(parent)
124-{
125- DEBUG_MSG << "()";
126-}
127-
128-TopLevelSurfaceList::~TopLevelSurfaceList()
129-{
130- DEBUG_MSG << "()";
131-}
132-
133-int TopLevelSurfaceList::rowCount(const QModelIndex &parent) const
134-{
135- return !parent.isValid() ? m_surfaceList.size() : 0;
136-}
137-
138-QVariant TopLevelSurfaceList::data(const QModelIndex& index, int role) const
139-{
140- if (index.row() < 0 || index.row() >= m_surfaceList.size())
141- return QVariant();
142-
143- if (role == SurfaceRole) {
144- MirSurfaceInterface *surface = m_surfaceList.at(index.row()).surface;
145- return QVariant::fromValue(surface);
146- } else if (role == ApplicationRole) {
147- return QVariant::fromValue(m_surfaceList.at(index.row()).application);
148- } else if (role == IdRole) {
149- return QVariant::fromValue(m_surfaceList.at(index.row()).id);
150- } else {
151- return QVariant();
152- }
153-}
154-
155-void TopLevelSurfaceList::raise(MirSurfaceInterface *surface)
156-{
157- if (!surface)
158- return;
159-
160- DEBUG_MSG << "(MirSurface[" << (void*)surface << "])";
161-
162- int i = indexOf(surface);
163- if (i != -1) {
164- raiseId(m_surfaceList.at(i).id);
165- }
166-}
167-
168-void TopLevelSurfaceList::appendPlaceholder(ApplicationInfoInterface *application)
169-{
170- DEBUG_MSG << "(" << application->appId() << ")";
171-
172- appendSurfaceHelper(nullptr, application);
173-}
174-
175-void TopLevelSurfaceList::appendSurface(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
176-{
177- Q_ASSERT(surface != nullptr);
178-
179- bool filledPlaceholder = false;
180- for (int i = 0; i < m_surfaceList.count() && !filledPlaceholder; ++i) {
181- ModelEntry &entry = m_surfaceList[i];
182- if (entry.application == application && entry.surface == nullptr) {
183- entry.surface = surface;
184- connectSurface(surface);
185- DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface
186- << ", filling out placeholder. after: " << toString();
187- Q_EMIT dataChanged(index(i) /* topLeft */, index(i) /* bottomRight */, QVector<int>() << SurfaceRole);
188- filledPlaceholder = true;
189- }
190- }
191-
192- if (!filledPlaceholder) {
193- DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface << ", adding new row";
194- appendSurfaceHelper(surface, application);
195- }
196-}
197-
198-void TopLevelSurfaceList::appendSurfaceHelper(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
199-{
200- if (m_modelState == IdleState) {
201- m_modelState = InsertingState;
202- beginInsertRows(QModelIndex(), m_surfaceList.size() /*first*/, m_surfaceList.size() /*last*/);
203- } else {
204- Q_ASSERT(m_modelState == ResettingState);
205- // No point in signaling anything if we're resetting the whole model
206- }
207-
208- int id = generateId();
209- m_surfaceList.append(ModelEntry(surface, application, id));
210- if (surface) {
211- connectSurface(surface);
212- }
213-
214- if (m_modelState == InsertingState) {
215- endInsertRows();
216- Q_EMIT countChanged();
217- Q_EMIT listChanged();
218- m_modelState = IdleState;
219- }
220-
221- DEBUG_MSG << " after " << toString();
222-}
223-
224-void TopLevelSurfaceList::connectSurface(MirSurfaceInterface *surface)
225-{
226- connect(surface, &MirSurfaceInterface::liveChanged, this, [this, surface](bool live){
227- if (!live) {
228- onSurfaceDied(surface);
229- }
230- });
231- connect(surface, &QObject::destroyed, this, [this, surface](){ this->onSurfaceDestroyed(surface); });
232-}
233-
234-void TopLevelSurfaceList::onSurfaceDied(MirSurfaceInterface *surface)
235-{
236- int i = indexOf(surface);
237- if (i == -1) {
238- return;
239- }
240-
241- auto application = m_surfaceList[i].application;
242-
243- // can't be starting if it already has a surface
244- Q_ASSERT(application->state() != ApplicationInfoInterface::Starting);
245-
246- if (application->state() == ApplicationInfoInterface::Running) {
247- m_surfaceList[i].removeOnceSurfaceDestroyed = true;
248- } else {
249- // assume it got killed by the out-of-memory daemon.
250- //
251- // So leave entry in the model and only remove its surface, so shell can display a screenshot
252- // in its place.
253- m_surfaceList[i].removeOnceSurfaceDestroyed = false;
254- }
255-}
256-
257-void TopLevelSurfaceList::onSurfaceDestroyed(MirSurfaceInterface *surface)
258-{
259- int i = indexOf(surface);
260- if (i == -1) {
261- return;
262- }
263-
264- if (m_surfaceList[i].removeOnceSurfaceDestroyed) {
265- removeAt(i);
266- } else {
267- m_surfaceList[i].surface = nullptr;
268- Q_EMIT dataChanged(index(i) /* topLeft */, index(i) /* bottomRight */, QVector<int>() << SurfaceRole);
269- DEBUG_MSG << " Removed surface from entry. After: " << toString();
270- }
271-}
272-
273-void TopLevelSurfaceList::removeAt(int index)
274-{
275- if (m_modelState == IdleState) {
276- beginRemoveRows(QModelIndex(), index, index);
277- m_modelState = RemovingState;
278- } else {
279- Q_ASSERT(m_modelState == ResettingState);
280- // No point in signaling anything if we're resetting the whole model
281- }
282-
283- m_surfaceList.removeAt(index);
284-
285- if (m_modelState == RemovingState) {
286- endRemoveRows();
287- Q_EMIT countChanged();
288- Q_EMIT listChanged();
289- m_modelState = IdleState;
290- }
291-
292- DEBUG_MSG << " after " << toString();
293-}
294-
295-int TopLevelSurfaceList::indexOf(MirSurfaceInterface *surface)
296-{
297- for (int i = 0; i < m_surfaceList.count(); ++i) {
298- if (m_surfaceList.at(i).surface == surface) {
299- return i;
300- }
301- }
302- return -1;
303-}
304-
305-void TopLevelSurfaceList::move(int from, int to)
306-{
307- if (from == to) return;
308- DEBUG_MSG << " from=" << from << " to=" << to;
309-
310- if (from >= 0 && from < m_surfaceList.size() && to >= 0 && to < m_surfaceList.size()) {
311- QModelIndex parent;
312- /* When moving an item down, the destination index needs to be incremented
313- by one, as explained in the documentation:
314- http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
315-
316- Q_ASSERT(m_modelState == IdleState);
317- m_modelState = MovingState;
318-
319- beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
320- m_surfaceList.move(from, to);
321- endMoveRows();
322- Q_EMIT listChanged();
323-
324- m_modelState = IdleState;
325-
326- DEBUG_MSG << " after " << toString();
327- }
328-}
329-
330-MirSurfaceInterface *TopLevelSurfaceList::surfaceAt(int index) const
331-{
332- if (index >=0 && index < m_surfaceList.count()) {
333- return m_surfaceList[index].surface;
334- } else {
335- return nullptr;
336- }
337-}
338-
339-ApplicationInfoInterface *TopLevelSurfaceList::applicationAt(int index) const
340-{
341- if (index >=0 && index < m_surfaceList.count()) {
342- return m_surfaceList[index].application;
343- } else {
344- return nullptr;
345- }
346-}
347-
348-int TopLevelSurfaceList::idAt(int index) const
349-{
350- if (index >=0 && index < m_surfaceList.count()) {
351- return m_surfaceList[index].id;
352- } else {
353- return 0;
354- }
355-}
356-
357-int TopLevelSurfaceList::indexForId(int id) const
358-{
359- for (int i = 0; i < m_surfaceList.count(); ++i) {
360- if (m_surfaceList[i].id == id) {
361- return i;
362- }
363- }
364- return -1;
365-}
366-
367-void TopLevelSurfaceList::doRaiseId(int id)
368-{
369- int fromIndex = indexForId(id);
370- if (fromIndex != -1) {
371- move(fromIndex, 0 /* toIndex */);
372- }
373-}
374-
375-void TopLevelSurfaceList::raiseId(int id)
376-{
377- if (m_modelState == IdleState) {
378- DEBUG_MSG << "(id=" << id << ") - do it now.";
379- doRaiseId(id);
380- } else {
381- DEBUG_MSG << "(id=" << id << ") - Model busy (modelState=" << m_modelState << "). Try again in the next event loop.";
382- // The model has just signalled some change. If we have a Repeater responding to this update, it will get nuts
383- // if we perform yet another model change straight away.
384- //
385- // A bad sympton of this problem is a Repeater.itemAt(index) call returning null event though Repeater.count says
386- // the index is definitely within bounds.
387- QMetaObject::invokeMethod(this, "raiseId", Qt::QueuedConnection, Q_ARG(int, id));
388- }
389-}
390-
391-int TopLevelSurfaceList::generateId()
392-{
393- int id = m_nextId;
394- m_nextId = nextFreeId(m_nextId + 1);
395- Q_EMIT nextIdChanged();
396- return id;
397-}
398-
399-int TopLevelSurfaceList::nextFreeId(int candidateId)
400-{
401- if (candidateId > m_maxId) {
402- return nextFreeId(1);
403- } else {
404- if (indexForId(candidateId) == -1) {
405- // it's indeed free
406- return candidateId;
407- } else {
408- return nextFreeId(candidateId + 1);
409- }
410- }
411-}
412-
413-QString TopLevelSurfaceList::toString()
414-{
415- QString str;
416- for (int i = 0; i < m_surfaceList.count(); ++i) {
417- auto item = m_surfaceList.at(i);
418-
419- QString itemStr = QString("(index=%1,appId=%2,surface=0x%3,id=%4)")
420- .arg(i)
421- .arg(item.application->appId())
422- .arg((qintptr)item.surface, 0, 16)
423- .arg(item.id);
424-
425- if (i > 0) {
426- str.append(",");
427- }
428- str.append(itemStr);
429- }
430- return str;
431-}
432-
433-void TopLevelSurfaceList::addApplication(ApplicationInfoInterface *application)
434-{
435- DEBUG_MSG << "(" << application->appId() << ")";
436- Q_ASSERT(!m_applications.contains(application));
437- m_applications.append(application);
438-
439- MirSurfaceListInterface *surfaceList = application->surfaceList();
440-
441- if (application->state() != ApplicationInfoInterface::Stopped) {
442- if (surfaceList->count() == 0) {
443- appendPlaceholder(application);
444- } else {
445- for (int i = 0; i < surfaceList->count(); ++i) {
446- appendSurface(surfaceList->get(i), application);
447- }
448- }
449- }
450-
451- connect(surfaceList, &QAbstractItemModel::rowsInserted, this,
452- [this, application, surfaceList](const QModelIndex & /*parent*/, int first, int last)
453- {
454- for (int i = last; i >= first; --i) {
455- this->appendSurface(surfaceList->get(i), application);
456- }
457- });
458-}
459-
460-void TopLevelSurfaceList::removeApplication(ApplicationInfoInterface *application)
461-{
462- DEBUG_MSG << "(" << application->appId() << ")";
463- Q_ASSERT(m_applications.contains(application));
464-
465- MirSurfaceListInterface *surfaceList = application->surfaceList();
466-
467- disconnect(surfaceList, 0, this, 0);
468-
469- Q_ASSERT(m_modelState == IdleState);
470- m_modelState = RemovingState;
471-
472- int i = 0;
473- while (i < m_surfaceList.count()) {
474- if (m_surfaceList.at(i).application == application) {
475- beginRemoveRows(QModelIndex(), i, i);
476- m_surfaceList.removeAt(i);
477- endRemoveRows();
478- Q_EMIT countChanged();
479- Q_EMIT listChanged();
480- } else {
481- ++i;
482- }
483- }
484-
485- m_modelState = IdleState;
486-
487- DEBUG_MSG << " after " << toString();
488-
489- m_applications.removeAll(application);
490-}
491-
492-QAbstractListModel *TopLevelSurfaceList::applicationsModel() const
493-{
494- return m_applicationsModel;
495-}
496-
497-void TopLevelSurfaceList::setApplicationsModel(QAbstractListModel* value)
498-{
499- if (m_applicationsModel == value) {
500- return;
501- }
502-
503- DEBUG_MSG << "(" << value << ")";
504-
505- Q_ASSERT(m_modelState == IdleState);
506- m_modelState = ResettingState;
507-
508- beginResetModel();
509-
510- if (m_applicationsModel) {
511- m_surfaceList.clear();
512- m_applications.clear();
513- disconnect(m_applicationsModel, 0, this, 0);
514- }
515-
516- m_applicationsModel = value;
517-
518- if (m_applicationsModel) {
519- findApplicationRole();
520-
521- connect(m_applicationsModel, &QAbstractItemModel::rowsInserted,
522- this, [this](const QModelIndex &/*parent*/, int first, int last) {
523- for (int i = first; i <= last; ++i) {
524- auto application = getApplicationFromModelAt(i);
525- addApplication(application);
526- }
527- });
528-
529- connect(m_applicationsModel, &QAbstractItemModel::rowsAboutToBeRemoved,
530- this, [this](const QModelIndex &/*parent*/, int first, int last) {
531- for (int i = first; i <= last; ++i) {
532- auto application = getApplicationFromModelAt(i);
533- removeApplication(application);
534- }
535- });
536-
537- for (int i = 0; i < m_applicationsModel->rowCount(); ++i) {
538- auto application = getApplicationFromModelAt(i);
539- addApplication(application);
540- }
541- }
542-
543- endResetModel();
544- m_modelState = IdleState;
545-}
546-
547-ApplicationInfoInterface *TopLevelSurfaceList::getApplicationFromModelAt(int index)
548-{
549- QModelIndex modelIndex = m_applicationsModel->index(index);
550-
551- QVariant variant = m_applicationsModel->data(modelIndex, m_applicationRole);
552-
553- // variant.value<ApplicationInfoInterface*>() returns null for some reason.
554- return static_cast<ApplicationInfoInterface*>(variant.value<QObject*>());
555-}
556-
557-void TopLevelSurfaceList::findApplicationRole()
558-{
559- QHash<int, QByteArray> namesHash = m_applicationsModel->roleNames();
560-
561- m_applicationRole = -1;
562- for (auto i = namesHash.begin(); i != namesHash.end() && m_applicationRole == -1; ++i) {
563- if (i.value() == "application") {
564- m_applicationRole = i.key();
565- }
566- }
567-
568- if (m_applicationRole == -1) {
569- qFatal("TopLevelSurfaceList: applicationsModel must have a \"application\" role.");
570- }
571-}
572
573=== removed file 'plugins/WindowManager/TopLevelSurfaceList.h'
574--- plugins/WindowManager/TopLevelSurfaceList.h 2016-04-04 13:39:44 +0000
575+++ plugins/WindowManager/TopLevelSurfaceList.h 1970-01-01 00:00:00 +0000
576@@ -1,223 +0,0 @@
577-/*
578- * Copyright (C) 2016 Canonical, Ltd.
579- *
580- * This program is free software; you can redistribute it and/or modify
581- * it under the terms of the GNU General Public License as published by
582- * the Free Software Foundation; version 3.
583- *
584- * This program is distributed in the hope that it will be useful,
585- * but WITHOUT ANY WARRANTY; without even the implied warranty of
586- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
587- * GNU General Public License for more details.
588- *
589- * You should have received a copy of the GNU General Public License
590- * along with this program. If not, see <http://www.gnu.org/licenses/>.
591- */
592-
593-#ifndef TOPLEVELSURFACELIST_H
594-#define TOPLEVELSURFACELIST_H
595-
596-#include <QAbstractListModel>
597-#include <QList>
598-#include <QLoggingCategory>
599-
600-Q_DECLARE_LOGGING_CATEGORY(UNITY_TOPSURFACELIST)
601-
602-namespace unity {
603- namespace shell {
604- namespace application {
605- class ApplicationInfoInterface;
606- class MirSurfaceInterface;
607- }
608- }
609-}
610-
611-/**
612- * @brief A model of top-level surfaces
613- *
614- * It's an abstraction of top-level application windows.
615- *
616- * When an entry first appears, it normaly doesn't have a surface yet, meaning that the application is
617- * still starting up. A shell should then display a splash screen or saved screenshot of the application
618- * until its surface comes up.
619- *
620- * As applications can have multiple surfaces and you can also have entries without surfaces at all,
621- * the only way to unambiguously refer to an entry in this model is through its id.
622- */
623-class TopLevelSurfaceList : public QAbstractListModel
624-{
625-
626- Q_OBJECT
627-
628- /**
629- * @brief A list model of applications.
630- *
631- * It's expected to have a role called "application" which returns a ApplicationInfoInterface
632- */
633- Q_PROPERTY(QAbstractListModel* applicationsModel READ applicationsModel
634- WRITE setApplicationsModel
635- NOTIFY applicationsModelChanged)
636-
637- /**
638- * @brief Number of top-level surfaces in this model
639- *
640- * This is the same as rowCount, added in order to keep compatibility with QML ListModels.
641- */
642- Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
643-
644- /**
645- The id to be used on the next entry created
646- Useful for tests
647- */
648- Q_PROPERTY(int nextId READ nextId NOTIFY nextIdChanged)
649-public:
650-
651- /**
652- * @brief The Roles supported by the model
653- *
654- * SurfaceRole - A MirSurfaceInterface. It will be null if the application is still starting up
655- * ApplicationRole - An ApplicationInfoInterface
656- * IdRole - A unique identifier for this entry. Useful to unambiguosly track elements as they move around in the list
657- */
658- enum Roles {
659- SurfaceRole = Qt::UserRole,
660- ApplicationRole = Qt::UserRole + 1,
661- IdRole = Qt::UserRole + 2,
662- };
663-
664- explicit TopLevelSurfaceList(QObject *parent = nullptr);
665- virtual ~TopLevelSurfaceList();
666-
667- // QAbstractItemModel methods
668- int rowCount(const QModelIndex &parent = QModelIndex()) const override;
669- QVariant data(const QModelIndex& index, int role) const override;
670- QHash<int, QByteArray> roleNames() const override {
671- QHash<int, QByteArray> roleNames { {SurfaceRole, "surface"},
672- {ApplicationRole, "application"},
673- {IdRole, "id"} };
674- return roleNames;
675- }
676-
677- int nextId() const { return m_nextId; }
678-
679- QAbstractListModel *applicationsModel() const;
680- void setApplicationsModel(QAbstractListModel*);
681-
682-public Q_SLOTS:
683- /**
684- * @brief Returns the surface at the given index
685- *
686- * It will be a nullptr if the application is still starting up and thus hasn't yet created
687- * and drawn into a surface.
688- */
689- unity::shell::application::MirSurfaceInterface *surfaceAt(int index) const;
690-
691- /**
692- * @brief Returns the application at the given index
693- */
694- unity::shell::application::ApplicationInfoInterface *applicationAt(int index) const;
695-
696- /**
697- * @brief Returns the unique id of the element at the given index
698- */
699- int idAt(int index) const;
700-
701- /**
702- * @brief Returns the index where the row with the given id is located
703- *
704- * Returns -1 if there's no row with the given id.
705- */
706- int indexForId(int id) const;
707-
708- /**
709- * @brief Raises the row with the given id to index 0
710- */
711- void raiseId(int id);
712-
713- void doRaiseId(int id);
714-
715-Q_SIGNALS:
716- void countChanged();
717-
718- /**
719- * @brief Emitted when the list changes
720- *
721- * Emitted when model gains an element, loses an element or when elements exchange positions.
722- */
723- void listChanged();
724-
725- void nextIdChanged();
726-
727- void applicationsModelChanged();
728-
729-private:
730- void addApplication(unity::shell::application::ApplicationInfoInterface *application);
731- void removeApplication(unity::shell::application::ApplicationInfoInterface *application);
732-
733- int indexOf(unity::shell::application::MirSurfaceInterface *surface);
734- void raise(unity::shell::application::MirSurfaceInterface *surface);
735- void move(int from, int to);
736- void appendSurfaceHelper(unity::shell::application::MirSurfaceInterface *surface,
737- unity::shell::application::ApplicationInfoInterface *application);
738- void connectSurface(unity::shell::application::MirSurfaceInterface *surface);
739- int generateId();
740- int nextFreeId(int candidateId);
741- QString toString();
742- void onSurfaceDestroyed(unity::shell::application::MirSurfaceInterface *surface);
743- void onSurfaceDied(unity::shell::application::MirSurfaceInterface *surface);
744- void removeAt(int index);
745- void findApplicationRole();
746-
747- unity::shell::application::ApplicationInfoInterface *getApplicationFromModelAt(int index);
748-
749- /*
750- Placeholder for a future surface from a starting or running application.
751- Enables shell to give immediate feedback to the user by showing, eg,
752- a splash screen.
753-
754- It's a model row containing a null surface and the given application.
755- */
756- void appendPlaceholder(unity::shell::application::ApplicationInfoInterface *application);
757-
758- /*
759- Adds a model row with the given surface and application
760-
761- Alternatively, if a placeholder exists for the given application it's
762- filled with the given surface instead.
763- */
764- void appendSurface(unity::shell::application::MirSurfaceInterface *surface,
765- unity::shell::application::ApplicationInfoInterface *application);
766-
767- struct ModelEntry {
768- ModelEntry(unity::shell::application::MirSurfaceInterface *surface, unity::shell::application::ApplicationInfoInterface *application, int id)
769- : surface(surface), application(application), id(id) {}
770- unity::shell::application::MirSurfaceInterface *surface;
771- unity::shell::application::ApplicationInfoInterface *application;
772- int id;
773- bool removeOnceSurfaceDestroyed{false};
774- };
775-
776- QList<ModelEntry> m_surfaceList;
777- int m_nextId{1};
778- static const int m_maxId{1000000};
779-
780- // applications that are being monitored
781- QList<unity::shell::application::ApplicationInfoInterface *> m_applications;
782-
783- QAbstractListModel* m_applicationsModel{nullptr};
784- int m_applicationRole{-1};
785-
786- enum ModelState {
787- IdleState,
788- InsertingState,
789- RemovingState,
790- MovingState,
791- ResettingState
792- };
793- ModelState m_modelState{IdleState};
794-};
795-
796-Q_DECLARE_METATYPE(TopLevelSurfaceList*)
797-Q_DECLARE_METATYPE(QAbstractListModel*)
798-
799-#endif // TOPLEVELSURFACELIST_H
800
801=== removed file 'plugins/WindowManager/WindowManagerPlugin.cpp'
802--- plugins/WindowManager/WindowManagerPlugin.cpp 2016-04-04 13:37:49 +0000
803+++ plugins/WindowManager/WindowManagerPlugin.cpp 1970-01-01 00:00:00 +0000
804@@ -1,28 +0,0 @@
805-/*
806- * Copyright (C) 2016 Canonical, Ltd.
807- *
808- * This program is free software; you can redistribute it and/or modify
809- * it under the terms of the GNU General Public License as published by
810- * the Free Software Foundation; version 3.
811- *
812- * This program is distributed in the hope that it will be useful,
813- * but WITHOUT ANY WARRANTY; without even the implied warranty of
814- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
815- * GNU General Public License for more details.
816- *
817- * You should have received a copy of the GNU General Public License
818- * along with this program. If not, see <http://www.gnu.org/licenses/>.
819- */
820-
821-#include "WindowManagerPlugin.h"
822-
823-#include "TopLevelSurfaceList.h"
824-
825-#include <QtQml>
826-
827-void WindowManagerPlugin::registerTypes(const char *uri)
828-{
829- qmlRegisterType<TopLevelSurfaceList>(uri, 0, 1, "TopLevelSurfaceList");
830-
831- qRegisterMetaType<QAbstractListModel*>("QAbstractListModel*");
832-}
833
834=== removed file 'plugins/WindowManager/WindowManagerPlugin.h'
835--- plugins/WindowManager/WindowManagerPlugin.h 2016-04-04 13:37:49 +0000
836+++ plugins/WindowManager/WindowManagerPlugin.h 1970-01-01 00:00:00 +0000
837@@ -1,32 +0,0 @@
838-/*
839- * Copyright (C) 2016 Canonical, Ltd.
840- *
841- * This program is free software; you can redistribute it and/or modify
842- * it under the terms of the GNU General Public License as published by
843- * the Free Software Foundation; version 3.
844- *
845- * This program is distributed in the hope that it will be useful,
846- * but WITHOUT ANY WARRANTY; without even the implied warranty of
847- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
848- * GNU General Public License for more details.
849- *
850- * You should have received a copy of the GNU General Public License
851- * along with this program. If not, see <http://www.gnu.org/licenses/>.
852- */
853-
854-#ifndef WINDOWMANAGER_PLUGIN_H
855-#define WINDOWMANAGER_PLUGIN_H
856-
857-#include <QtQml/QQmlEngine>
858-#include <QtQml/QQmlExtensionPlugin>
859-
860-class WindowManagerPlugin : public QQmlExtensionPlugin
861-{
862- Q_OBJECT
863- Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
864-
865-public:
866- void registerTypes(const char *uri) override;
867-};
868-
869-#endif // WINDOWMANAGER_PLUGIN_H
870
871=== removed file 'plugins/WindowManager/qmldir'
872--- plugins/WindowManager/qmldir 2016-04-04 13:37:49 +0000
873+++ plugins/WindowManager/qmldir 1970-01-01 00:00:00 +0000
874@@ -1,2 +0,0 @@
875-module WindowManager
876-plugin windowmanager-qml
877
878=== modified file 'qml/Components/InputMethod.qml'
879--- qml/Components/InputMethod.qml 2016-10-06 13:06:31 +0000
880+++ qml/Components/InputMethod.qml 2016-11-09 15:21:35 +0000
881@@ -23,6 +23,8 @@
882
883 readonly property rect visibleRect: surfaceItem.surface && visible ? surfaceItem.surface.inputBounds : Qt.rect(0, 0, 0, 0)
884
885+ property var surface
886+
887 MirSurfaceItem {
888 id: surfaceItem
889 anchors.fill: parent
890@@ -31,7 +33,7 @@
891
892 surfaceWidth: root.enabled ? width : -1
893 surfaceHeight: root.enabled ? height : -1
894- surface: SurfaceManager.inputMethodSurface
895+ surface: root.surface
896
897 onLiveChanged: {
898 if (surface !== null && !live) {
899@@ -50,7 +52,6 @@
900 }
901
902 visible: surfaceItem.surface &&
903- surfaceItem.surfaceState != Mir.HiddenState &&
904- surfaceItem.surfaceState != Mir.MinimizedState &&
905- root.enabled
906+ surfaceItem.surface.visible &&
907+ root.enabled
908 }
909
910=== modified file 'qml/Components/KeymapSwitcher.qml'
911--- qml/Components/KeymapSwitcher.qml 2016-07-11 14:40:56 +0000
912+++ qml/Components/KeymapSwitcher.qml 2016-11-09 15:21:35 +0000
913@@ -23,6 +23,9 @@
914 QtObject {
915 id: root
916
917+ // to be set from outside
918+ property var focusedSurface: null
919+
920 property GlobalShortcut shortcutNext: GlobalShortcut {
921 shortcut: Qt.MetaModifier|Qt.Key_Space
922 onTriggered: root.nextKeymap()
923@@ -60,7 +63,7 @@
924 }
925
926 property Binding surfaceKeymapBinding: Binding {
927- target: MirFocusController.focusedSurface
928+ target: root.focusedSurface
929 property: "keymap"
930 value: root.currentKeymap
931 }
932
933=== modified file 'qml/Shell.qml'
934--- qml/Shell.qml 2016-10-03 11:15:27 +0000
935+++ qml/Shell.qml 2016-11-09 15:21:35 +0000
936@@ -43,7 +43,6 @@
937 import Unity.DashCommunicator 0.1
938 import Unity.Indicators 0.1 as Indicators
939 import Cursor 1.1
940-import WindowManager 0.1
941
942
943 StyledItem {
944@@ -264,10 +263,9 @@
945 width: parent.width
946 height: parent.height
947
948- TopLevelSurfaceList {
949+ TopLevelWindowModel {
950 id: topLevelSurfaceList
951 objectName: "topLevelSurfaceList"
952- applicationsModel: ApplicationManager
953 }
954
955 Stage {
956@@ -320,6 +318,7 @@
957 InputMethod {
958 id: inputMethod
959 objectName: "inputMethod"
960+ surface: topLevelSurfaceList.inputMethodSurface
961 anchors {
962 fill: parent
963 topMargin: panel.panelHeight
964@@ -485,8 +484,8 @@
965 greeterShown: greeter.shown
966 }
967
968- readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
969- ? MirFocusController.focusedSurface.state === Mir.FullscreenState
970+ readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
971+ ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
972 : false
973 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
974 || greeter.hasLockedApp
975@@ -785,7 +784,9 @@
976 }
977
978 // non-visual object
979- KeymapSwitcher {}
980+ KeymapSwitcher {
981+ focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
982+ }
983
984 Rectangle {
985 id: shutdownFadeOutRectangle
986
987=== modified file 'qml/Stage/DecoratedWindow.qml'
988--- qml/Stage/DecoratedWindow.qml 2016-10-07 11:20:09 +0000
989+++ qml/Stage/DecoratedWindow.qml 2016-11-09 15:21:35 +0000
990@@ -70,11 +70,13 @@
991
992 readonly property Item clientAreaItem: applicationWindow
993
994+ readonly property real contentX: applicationWindow.x
995+ readonly property real contentY: applicationWindow.y
996+
997 signal closeClicked()
998 signal maximizeClicked()
999 signal maximizeHorizontallyClicked()
1000 signal maximizeVerticallyClicked()
1001- signal minimizeClicked()
1002 signal decorationPressed()
1003 signal decorationReleased()
1004
1005@@ -163,7 +165,11 @@
1006 onMaximizeClicked: { root.decorationPressed(); root.maximizeClicked(); }
1007 onMaximizeHorizontallyClicked: { root.decorationPressed(); root.maximizeHorizontallyClicked(); }
1008 onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); }
1009- onMinimizeClicked: root.minimizeClicked();
1010+ onMinimizeClicked: {
1011+ if (applicationWindow.surface) {
1012+ applicationWindow.surface.requestState(Mir.MinimizedState);
1013+ }
1014+ }
1015 onPressed: root.decorationPressed();
1016
1017 onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
1018
1019=== modified file 'qml/Stage/FakeMaximizeDelegate.qml'
1020--- qml/Stage/FakeMaximizeDelegate.qml 2016-09-13 11:53:40 +0000
1021+++ qml/Stage/FakeMaximizeDelegate.qml 2016-11-09 15:21:35 +0000
1022@@ -98,19 +98,19 @@
1023 function commit() {
1024 if (progress > hintThreshold && edge != -1) {
1025 if (edge == Item.Top) {
1026- target.maximize();
1027+ target.requestMaximize();
1028 } else if (edge == Item.Left) {
1029- target.maximizeLeft();
1030+ target.requestMaximizeLeft();
1031 } else if (edge == Item.Right) {
1032- target.maximizeRight();
1033+ target.requestMaximizeRight();
1034 } else if (edge == Item.TopLeft) {
1035- target.maximizeTopLeft();
1036+ target.requestMaximizeTopLeft();
1037 } else if (edge == Item.TopRight) {
1038- target.maximizeTopRight();
1039+ target.requestMaximizeTopRight();
1040 } else if (edge == Item.BottomLeft) {
1041- target.maximizeBottomLeft();
1042+ target.requestMaximizeBottomLeft();
1043 } else if (edge == Item.BottomRight) {
1044- target.maximizeBottomRight();
1045+ target.requestMaximizeBottomRight();
1046 }
1047 } else {
1048 stop();
1049
1050=== modified file 'qml/Stage/Stage.qml'
1051--- qml/Stage/Stage.qml 2016-10-24 11:34:20 +0000
1052+++ qml/Stage/Stage.qml 2016-11-09 15:21:35 +0000
1053@@ -92,8 +92,7 @@
1054
1055 onAltTabPressedChanged: priv.goneToSpread = altTabPressed
1056
1057- property Item itemConfiningMouseCursor: !spreadShown && priv.focusedAppDelegate && priv.focusedAppDelegate.surface &&
1058- priv.focusedAppDelegate.surface.confinesMousePointer ?
1059+ property Item itemConfiningMouseCursor: !spreadShown && priv.focusedAppDelegate && priv.focusedAppDelegate.window.confinesMousePointer ?
1060 priv.focusedAppDelegate.clientAreaItem : null;
1061
1062 signal itemSnapshotRequested(Item item)
1063@@ -152,29 +151,34 @@
1064 GlobalShortcut {
1065 id: maximizeWindowShortcut
1066 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Up
1067- onTriggered: priv.focusedAppDelegate.maximize()
1068+ onTriggered: priv.focusedAppDelegate.requestMaximize()
1069 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximized
1070 }
1071
1072 GlobalShortcut {
1073 id: maximizeWindowLeftShortcut
1074 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Left
1075- onTriggered: priv.focusedAppDelegate.maximizeLeft()
1076+ onTriggered: priv.focusedAppDelegate.requestMaximizeLeft()
1077 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximizedLeftRight
1078 }
1079
1080 GlobalShortcut {
1081 id: maximizeWindowRightShortcut
1082 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Right
1083- onTriggered: priv.focusedAppDelegate.maximizeRight()
1084+ onTriggered: priv.focusedAppDelegate.requestMaximizeRight()
1085 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximizedLeftRight
1086 }
1087
1088 GlobalShortcut {
1089 id: minimizeRestoreShortcut
1090 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
1091- onTriggered: priv.focusedAppDelegate.anyMaximized
1092- ? priv.focusedAppDelegate.restoreFromMaximized() : priv.focusedAppDelegate.minimize()
1093+ onTriggered: {
1094+ if (priv.focusedAppDelegate.anyMaximized) {
1095+ priv.focusedAppDelegate.requestRestore();
1096+ } else {
1097+ priv.focusedAppDelegate.requestMinimize();
1098+ }
1099+ }
1100 active: root.state == "windowed" && priv.focusedAppDelegate
1101 }
1102
1103@@ -210,20 +214,10 @@
1104 }
1105
1106 function minimizeAllWindows() {
1107- for (var i = 0; i < appRepeater.count; i++) {
1108- var appDelegate = appRepeater.itemAt(i);
1109- if (appDelegate && !appDelegate.minimized) {
1110- appDelegate.minimize();
1111- }
1112- }
1113- }
1114-
1115- function focusNext() {
1116- for (var i = 0; i < appRepeater.count; i++) {
1117- var appDelegate = appRepeater.itemAt(i);
1118- if (appDelegate && !appDelegate.minimized) {
1119- appDelegate.focus = true;
1120- return;
1121+ for (var i = appRepeater.count - 1; i >= 0; i--) {
1122+ var appDelegate = appRepeater.itemAt(i);
1123+ if (appDelegate && !appDelegate.minimized) {
1124+ appDelegate.requestMinimize();
1125 }
1126 }
1127 }
1128@@ -251,7 +245,7 @@
1129 priv.sideStageItemId = 0;
1130 priv.sideStageAppId = "";
1131 priv.mainStageDelegate = appRepeater.itemAt(0);
1132- priv.mainStageAppId = topLevelSurfaceList.idAt(0);
1133+ priv.mainStageItemId = topLevelSurfaceList.idAt(0);
1134 priv.mainStageAppId = topLevelSurfaceList.applicationAt(0) ? topLevelSurfaceList.applicationAt(0).appId : ""
1135 return;
1136 }
1137@@ -318,8 +312,8 @@
1138 Connections {
1139 target: PanelState
1140 onCloseClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
1141- onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.minimize(); } }
1142- onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.restoreFromMaximized(); } }
1143+ onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestMinimize(); } }
1144+ onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestRestore(); } }
1145 }
1146
1147 Binding {
1148@@ -394,13 +388,6 @@
1149 }
1150 }
1151
1152- Binding {
1153- target: MirFocusController
1154- property: "focusedSurface"
1155- value: priv.focusedAppDelegate ? priv.focusedAppDelegate.focusedSurface : null
1156- when: !appRepeater.startingUp && root.parent
1157- }
1158-
1159 states: [
1160 State {
1161 name: "spread"; when: priv.goneToSpread
1162@@ -567,7 +554,7 @@
1163
1164 onShownChanged: {
1165 if (!shown && priv.mainStageDelegate) {
1166- priv.mainStageDelegate.claimFocus();
1167+ priv.mainStageDelegate.requestFocus();
1168 }
1169 }
1170
1171@@ -600,14 +587,23 @@
1172 }
1173 }
1174
1175- TopLevelSurfaceRepeater {
1176+ Repeater {
1177 id: appRepeater
1178 model: topLevelSurfaceList
1179 objectName: "appRepeater"
1180
1181+ function indexOf(delegateItem) {
1182+ for (var i = 0; i < count; i++) {
1183+ if (itemAt(i) === delegateItem) {
1184+ return i;
1185+ }
1186+ }
1187+ return -1;
1188+ }
1189+
1190 delegate: FocusScope {
1191 id: appDelegate
1192- objectName: "appDelegate_" + model.id
1193+ objectName: "appDelegate_" + model.window.id
1194 property int itemIndex: index // We need this from outside the repeater
1195 // z might be overriden in some cases by effects, but we need z ordering
1196 // to calculate occlusion detection
1197@@ -619,11 +615,11 @@
1198 }
1199 z: normalZ
1200
1201- // Normally we want x/y where we request it to be. Width/height of our delegate will
1202+ // Normally we want x/y where the surface thinks it is. Width/height of our delegate will
1203 // match what the actual surface size is.
1204 // Don't write to those, they will be set by states
1205- x: requestedX
1206- y: requestedY
1207+ x: model.window.position.x - decoratedWindow.contentX
1208+ y: model.window.position.y - decoratedWindow.contentY
1209 width: decoratedWindow.implicitWidth
1210 height: decoratedWindow.implicitHeight
1211
1212@@ -633,6 +629,12 @@
1213 property real requestedY: windowedY
1214 property real requestedWidth: windowedWidth
1215 property real requestedHeight: windowedHeight
1216+ Binding {
1217+ target: model.window; property: "requestedPosition"
1218+ // miral doesn't know about our window decorations. So we have to deduct them
1219+ value: Qt.point(appDelegate.requestedX + decoratedWindow.contentX,
1220+ appDelegate.requestedY + decoratedWindow.contentY)
1221+ }
1222
1223 // In those are for windowed mode. Those values basically store the window's properties
1224 // when having a floating window. If you want to move/resize a window in normal mode, this is what you want to write to.
1225@@ -649,7 +651,7 @@
1226 Binding {
1227 target: appDelegate
1228 property: "y"
1229- value: appDelegate.requestedY -
1230+ value: model.window.position.y -
1231 Math.min(appDelegate.requestedY - PanelState.panelHeight,
1232 Math.max(0, priv.virtualKeyboardHeight - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
1233 when: root.oskEnabled && appDelegate.focus && (appDelegate.state == "normal" || appDelegate.state == "restored")
1234@@ -705,7 +707,7 @@
1235 maximizedTopLeft || maximizedTopRight || maximizedBottomLeft || maximizedBottomRight
1236
1237 readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
1238- readonly property bool fullscreen: surface ? surface.state === Mir.FullscreenState : application.fullscreen
1239+ readonly property bool fullscreen: window.state === Mir.FullscreenState
1240
1241 readonly property bool canBeMaximized: canBeMaximizedHorizontally && canBeMaximizedVertically
1242 readonly property bool canBeMaximizedLeftRight: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
1243@@ -731,7 +733,9 @@
1244 priv.updateMainAndSideStageIndexes()
1245 }
1246
1247- readonly property var surface: model.surface
1248+ readonly property var surface: model.window.surface
1249+ readonly property var window: model.window
1250+
1251 readonly property alias resizeArea: resizeArea
1252 readonly property alias focusedSurface: decoratedWindow.focusedSurface
1253 readonly property bool dragging: touchControls.overlayShown ? touchControls.dragging : decoratedWindow.dragging
1254@@ -740,6 +744,19 @@
1255 readonly property bool isDash: appId == "unity8-dash"
1256 readonly property alias clientAreaItem: decoratedWindow.clientAreaItem
1257
1258+ function requestFocus() { model.window.requestFocus(); }
1259+ function requestMaximize() { model.window.requestState(Mir.MaximizedState); }
1260+ function requestMaximizeVertically() { model.window.requestState(Mir.VertMaximizedState); }
1261+ function requestMaximizeHorizontally() { model.window.requestState(Mir.HorizMaximizedState); }
1262+ function requestMaximizeLeft() { model.window.requestState(Mir.MaximizedLeftState); }
1263+ function requestMaximizeRight() { model.window.requestState(Mir.MaximizedRightState); }
1264+ function requestMaximizeTopLeft() { model.window.requestState(Mir.MaximizedTopLeftState); }
1265+ function requestMaximizeTopRight() { model.window.requestState(Mir.MaximizedTopRightState); }
1266+ function requestMaximizeBottomLeft() { model.window.requestState(Mir.MaximizedBottomLeftState); }
1267+ function requestMaximizeBottomRight() { model.window.requestState(Mir.MaximizedBottomRightState); }
1268+ function requestMinimize() { model.window.requestState(Mir.MinimizedState); }
1269+ function requestRestore() { model.window.requestState(Mir.RestoredState); }
1270+
1271 function claimFocus() {
1272 if (root.state == "spread") {
1273 spreadItem.highlightedIndex = index
1274@@ -751,45 +768,63 @@
1275 }
1276 priv.updateMainAndSideStageIndexes();
1277 }
1278-
1279 if (root.mode == "windowed") {
1280 appDelegate.restore(true /* animated */, appDelegate.windowState);
1281- } else {
1282- appDelegate.focus = true;
1283 }
1284+ appDelegate.focus = true;
1285 }
1286- Connections {
1287- target: model.surface
1288- onFocusRequested: {
1289+
1290+ function updateQmlFocusFromMirSurfaceFocus() {
1291+ if (model.window.focused) {
1292 claimFocus();
1293+ priv.focusedAppDelegate = appDelegate;
1294+ //} else if (priv.focusedAppDelegate === appDelegate && root.state != "spread") {
1295+ // priv.focusedAppDelegate = null;
1296 }
1297 }
1298+
1299 Connections {
1300- target: model.application
1301+ target: model.window
1302+ onFocusedChanged: {
1303+ updateQmlFocusFromMirSurfaceFocus();
1304+ }
1305 onFocusRequested: {
1306- if (!model.surface) {
1307- // when an app has no surfaces, we assume there's only one entry representing it:
1308- // this delegate.
1309+ if (model.window.focused && priv.focusedAppDelegate === appDelegate) {
1310+ // it's already properly focused. Just ensure we show it on foreground
1311 claimFocus();
1312- } else {
1313- // if the application has surfaces, focus request should be at surface-level.
1314- }
1315- }
1316- }
1317-
1318- onFocusChanged: {
1319- if (appRepeater.startingUp)
1320- return;
1321-
1322- if (focus) {
1323- topLevelSurfaceList.raiseId(model.id);
1324- priv.focusedAppDelegate = appDelegate;
1325- } else if (!focus && priv.focusedAppDelegate === appDelegate && root.state != "spread") {
1326- priv.focusedAppDelegate = null;
1327- // FIXME: No idea why the Binding{} doens't update when focusedAppDelegate turns null
1328- MirFocusController.focusedSurface = null;
1329- }
1330- }
1331+ }
1332+ }
1333+ onStateChanged: {
1334+ if (model.window.state === Mir.MinimizedState) {
1335+ appDelegate.minimize();
1336+ } else if (model.window.state === Mir.MaximizedState) {
1337+ appDelegate.maximize();
1338+ } else if (model.window.state === Mir.VertMaximizedState) {
1339+ appDelegate.maximizeVertically();
1340+ } else if (model.window.state === Mir.HorizMaximizedState) {
1341+ appDelegate.maximizeHorizontally();
1342+ } else if (model.window.state === Mir.MaximizedLeftState) {
1343+ appDelegate.maximizeLeft();
1344+ } else if (model.window.state === Mir.MaximizedRightState) {
1345+ appDelegate.maximizeRight();
1346+ } else if (model.window.state === Mir.MaximizedTopLeftState) {
1347+ appDelegate.maximizeTopLeft();
1348+ } else if (model.window.state === Mir.MaximizedTopRightState) {
1349+ appDelegate.maximizeTopRight();
1350+ } else if (model.window.state === Mir.MaximizedBottomLeftState) {
1351+ appDelegate.maximizeBottomLeft();
1352+ } else if (model.window.state === Mir.MaximizedBottomRightState) {
1353+ appDelegate.maximizeBottomRight();
1354+ } else if (model.window.state === Mir.RestoredState) {
1355+ if (appDelegate.anyMaximized) {
1356+ appDelegate.restoreFromMaximized()
1357+ } else {
1358+ appDelegate.restore();
1359+ }
1360+ }
1361+ }
1362+ }
1363+
1364 Component.onCompleted: {
1365 if (application && application.rotatesWindowContents) {
1366 decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
1367@@ -803,16 +838,7 @@
1368 // Now load any saved state. This needs to happen *after* the cascading!
1369 resizeArea.loadWindowState();
1370
1371- // NB: We're differentiating if this delegate was created in response to a new entry in the model
1372- // or if the Repeater is just populating itself with delegates to match the model it received.
1373- if (!appRepeater.startingUp) {
1374- // a top level window is always the focused one when it first appears, unfocusing
1375- // any preexisting one
1376- if (root.state == "spread") {
1377- spreadItem.highlightedIndex = index;
1378- }
1379- claimFocus();
1380- }
1381+ updateQmlFocusFromMirSurfaceFocus();
1382
1383 refreshStage();
1384 _constructing = false;
1385@@ -827,16 +853,6 @@
1386 priv.updateForegroundMaximizedApp();
1387 }
1388
1389- if (focus) {
1390- // focus some other window
1391- for (var i = 0; i < appRepeater.count; i++) {
1392- var appDelegate = appRepeater.itemAt(i);
1393- if (appDelegate && !appDelegate.minimized && i != index) {
1394- appDelegate.focus = true;
1395- return;
1396- }
1397- }
1398- }
1399 }
1400
1401 onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp()
1402@@ -857,7 +873,7 @@
1403 || focusAnimation.running || rightEdgeFocusAnimation.running || hidingAnimation.running
1404
1405 function close() {
1406- model.surface.close();
1407+ model.window.close();
1408 }
1409
1410 function maximize(animated) {
1411@@ -908,7 +924,6 @@
1412 animationsEnabled = (animated === undefined) || animated;
1413 windowState = state || WindowStateStorage.WindowStateRestored;
1414 windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
1415- focus = true;
1416 }
1417
1418 function playFocusAnimation() {
1419@@ -922,7 +937,7 @@
1420 rightEdgeFocusAnimation.start()
1421 }
1422 } else if (state == "windowedRightEdge" || state == "windowed") {
1423- claimFocus();
1424+ requestFocus();
1425 } else {
1426 focusAnimation.start()
1427 }
1428@@ -960,10 +975,10 @@
1429 to: 1
1430 duration: UbuntuAnimation.SnapDuration
1431 onStarted: {
1432- topLevelSurfaceList.raiseId(model.id);
1433+ topLevelSurfaceList.raiseId(model.window.id);
1434 }
1435 onStopped: {
1436- appDelegate.claimFocus();
1437+ appDelegate.requestFocus();
1438 }
1439 }
1440 ParallelAnimation {
1441@@ -973,7 +988,7 @@
1442 UbuntuNumberAnimation { target: decoratedWindow; properties: "angle"; to: 0; duration: priv.animationDuration }
1443 UbuntuNumberAnimation { target: decoratedWindow; properties: "itemScale"; to: 1; duration: priv.animationDuration }
1444 onStopped: {
1445- appDelegate.focus = true
1446+ appDelegate.requestFocus();
1447 }
1448 }
1449 ParallelAnimation {
1450@@ -1333,14 +1348,6 @@
1451 SequentialAnimation {
1452 UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
1453 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1454- ScriptAction {
1455- script: {
1456- if (appDelegate.minimized) {
1457- appDelegate.focus = false;
1458- priv.focusNext();
1459- }
1460- }
1461- }
1462 }
1463 },
1464 Transition {
1465@@ -1430,7 +1437,7 @@
1466 visible: enabled
1467
1468 onPressed: {
1469- appDelegate.focus = true;
1470+ appDelegate.requestFocus();
1471 }
1472
1473 Component.onDestruction: {
1474@@ -1444,7 +1451,7 @@
1475 anchors.left: appDelegate.left
1476 anchors.top: appDelegate.top
1477 application: model.application
1478- surface: model.surface
1479+ surface: model.window.surface
1480 active: appDelegate.focus
1481 focus: true
1482 interactive: root.interactive
1483@@ -1467,11 +1474,20 @@
1484 onRequestedHeightChanged: oldRequestedHeight = requestedHeight
1485
1486 onCloseClicked: { appDelegate.close(); }
1487- onMaximizeClicked: appDelegate.anyMaximized ? appDelegate.restoreFromMaximized() : appDelegate.maximize();
1488- onMaximizeHorizontallyClicked: appDelegate.maximizedHorizontally ? appDelegate.restoreFromMaximized() : appDelegate.maximizeHorizontally()
1489- onMaximizeVerticallyClicked: appDelegate.maximizedVertically ? appDelegate.restoreFromMaximized() : appDelegate.maximizeVertically()
1490- onMinimizeClicked: appDelegate.minimize()
1491- onDecorationPressed: { appDelegate.focus = true; }
1492+ onMaximizeClicked: appDelegate.anyMaximized ? appDelegate.requestRestore() : appDelegate.requestMaximize();
1493+ onMaximizeHorizontallyClicked: {
1494+ if (appDelegate.maximizedHorizontally)
1495+ appDelegate.requestRestore();
1496+ else
1497+ appDelegate.requestMaximizeHorizontally();
1498+ }
1499+ onMaximizeVerticallyClicked: {
1500+ if (appDelegate.maximizedVertically)
1501+ appDelegate.requestRestore();
1502+ else
1503+ appDelegate.requestMaximizeVertically();
1504+ }
1505+ onDecorationPressed: { appDelegate.requestFocus(); }
1506 onDecorationReleased: fakeRectangle.commit();
1507
1508 property real angle: 0
1509@@ -1518,12 +1534,12 @@
1510 WindowedFullscreenPolicy {
1511 id: windowedFullscreenPolicy
1512 active: root.mode == "windowed"
1513- surface: model.surface
1514+ surface: model.window.surface
1515 }
1516 StagedFullscreenPolicy {
1517 id: stagedFullscreenPolicy
1518 active: root.mode == "staged" || root.mode == "stagedWithSideStage"
1519- surface: model.surface
1520+ surface: model.window.surface
1521 }
1522
1523 SpreadDelegateInputArea {
1524@@ -1536,16 +1552,13 @@
1525 onClicked: {
1526 spreadItem.highlightedIndex = index;
1527 if (distance == 0) {
1528+ model.window.requestFocus();
1529 priv.goneToSpread = false;
1530 }
1531 }
1532 onClose: {
1533 priv.closingIndex = index
1534- if (model.surface) { // could be stopped by OOM
1535- model.surface.close()
1536- } else if (model.application) {
1537- root.applicationManager.stopApplication(model.application.appId);
1538- }
1539+ model.window.close();
1540 }
1541 }
1542
1543
1544=== modified file 'qml/Stage/StagedFullscreenPolicy.qml'
1545--- qml/Stage/StagedFullscreenPolicy.qml 2016-04-04 13:41:19 +0000
1546+++ qml/Stage/StagedFullscreenPolicy.qml 2016-11-09 15:21:35 +0000
1547@@ -32,7 +32,7 @@
1548 onSurfaceChanged: {
1549 if (!active || !surface) return;
1550 if (surface.shellChrome === Mir.LowChrome) {
1551- surface.state = Mir.FullscreenState;
1552+ surface.requestState(Mir.FullscreenState);
1553 }
1554 }
1555
1556@@ -41,15 +41,15 @@
1557 onShellChromeChanged: {
1558 if (!active || !surface) return;
1559 if (surface.shellChrome === Mir.LowChrome) {
1560- surface.state = Mir.FullscreenState;
1561+ surface.requestState(Mir.FullscreenState);
1562 } else {
1563- surface.state = Mir.RestoredState;
1564+ surface.requestState(Mir.RestoredState);
1565 }
1566 }
1567 onStateChanged: {
1568 if (!active) return;
1569 if (surface.state === Mir.RestoredState && surface.shellChrome === Mir.LowChrome) {
1570- surface.state = Mir.FullscreenState;
1571+ surface.requestState(Mir.FullscreenState);
1572 }
1573 }
1574 }
1575
1576=== removed file 'qml/Stage/TopLevelSurfaceRepeater.qml'
1577--- qml/Stage/TopLevelSurfaceRepeater.qml 2016-09-12 17:05:43 +0000
1578+++ qml/Stage/TopLevelSurfaceRepeater.qml 1970-01-01 00:00:00 +0000
1579@@ -1,67 +0,0 @@
1580-/*
1581- * Copyright (C) 2016 Canonical, Ltd.
1582- *
1583- * This program is free software; you can redistribute it and/or modify
1584- * it under the terms of the GNU General Public License as published by
1585- * the Free Software Foundation; version 3.
1586- *
1587- * This program is distributed in the hope that it will be useful,
1588- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1589- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1590- * GNU General Public License for more details.
1591- *
1592- * You should have received a copy of the GNU General Public License
1593- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1594- */
1595-
1596-import QtQuick 2.4
1597-
1598-Repeater {
1599- id: root
1600- // FIXME: This is a hack around us not knowing whether the Repeater has finished creating its
1601- // delegates on start up.
1602- // This is a problem when the stage gets a TopLevelSurfaceList already populated with several
1603- // rows.
1604- property bool startingUp: true
1605- onStartingUpChanged: {
1606- if (!startingUp) {
1607- // the top-most surface must be the focused one.
1608- var topmostDelegate = itemAt(0);
1609- if (topmostDelegate.focus) {
1610- // Delegate got focused while we were still starting up. Not good.
1611- // Force signal handler to run again
1612- topmostDelegate.onFocusChanged(true);
1613- } else {
1614- topmostDelegate.focus = true;
1615- }
1616- }
1617- }
1618-
1619- onItemAdded: {
1620- if (startingUp) {
1621- checkIfStillStartingUp();
1622- }
1623- }
1624-
1625- function checkIfStillStartingUp() {
1626- var i = 0;
1627- var missingDelegate = false;
1628- for (i = 0; i < model.count && !missingDelegate; ++i) {
1629- if (!itemAt(i)) {
1630- missingDelegate = true;
1631- }
1632- }
1633- if (!missingDelegate) {
1634- startingUp = false;
1635- }
1636- }
1637-
1638- function indexOf(delegateItem) {
1639- for (var i = 0; i < count; i++) {
1640- if (itemAt(i) === delegateItem) {
1641- return i;
1642- }
1643- }
1644- return -1;
1645- }
1646-}
1647
1648=== modified file 'qml/Stage/WindowedFullscreenPolicy.qml'
1649--- qml/Stage/WindowedFullscreenPolicy.qml 2016-04-04 13:37:49 +0000
1650+++ qml/Stage/WindowedFullscreenPolicy.qml 2016-11-09 15:21:35 +0000
1651@@ -33,7 +33,7 @@
1652 _firstTimeSurface = false;
1653
1654 if (surface.state === Mir.FullscreenState && surface.shellChrome === Mir.LowChrome) {
1655- surface.state = Mir.RestoredState;
1656+ surface.requestState(Mir.RestoredState);
1657 }
1658 }
1659 }
1660
1661=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.cpp'
1662--- tests/mocks/Unity/Application/ApplicationInfo.cpp 2016-09-12 17:05:43 +0000
1663+++ tests/mocks/Unity/Application/ApplicationInfo.cpp 2016-11-09 15:21:35 +0000
1664@@ -90,14 +90,17 @@
1665 return;
1666 }
1667
1668+ bool wasFocused = focused();
1669+
1670 auto surface = surfaceManager->createSurface(surfaceName,
1671 Mir::NormalType,
1672- fullscreen() ? Mir::FullscreenState : Mir::MaximizedState,
1673- m_screenshotFileName);
1674+ fullscreen() ? Mir::FullscreenState : Mir::RestoredState,
1675+ m_screenshotFileName,
1676+ this);
1677
1678 surface->setShellChrome(m_shellChrome);
1679
1680- m_surfaceList->appendSurface(surface);
1681+ m_surfaceList->addSurface(surface);
1682
1683 ++m_liveSurfaceCount;
1684 connect(surface, &MirSurface::liveChanged, this, [this, surface](){
1685@@ -125,8 +128,19 @@
1686 setState(Running);
1687 }
1688 });
1689+ connect(surface, &MirSurfaceInterface::focusedChanged, this, [&](bool /*value*/) {
1690+ #if APPLICATION_DEBUG
1691+ qDebug().nospace() << "Application[" << appId() << "].focusedChanged(" << focused() << ")";
1692+ #endif
1693+ Q_EMIT focusedChanged(focused());
1694+ });
1695+
1696 connect(surface, &MirSurface::focusRequested, this, &ApplicationInfo::focusRequested);
1697
1698+ if (wasFocused != focused()) {
1699+ Q_EMIT focusedChanged(focused());
1700+ }
1701+
1702 if (m_state == Starting) {
1703 if (m_requestedState == RequestedRunning) {
1704 setState(Running);
1705@@ -218,7 +232,7 @@
1706 {
1707 m_fullscreen = value;
1708 if (m_surfaceList->rowCount() > 0) {
1709- m_surfaceList->get(0)->setState(Mir::FullscreenState);
1710+ m_surfaceList->get(0)->requestState(Mir::FullscreenState);
1711 }
1712 }
1713
1714
1715=== modified file 'tests/mocks/Unity/Application/ApplicationManager.cpp'
1716--- tests/mocks/Unity/Application/ApplicationManager.cpp 2016-08-23 12:04:11 +0000
1717+++ tests/mocks/Unity/Application/ApplicationManager.cpp 2016-11-09 15:21:35 +0000
1718@@ -40,10 +40,14 @@
1719
1720 namespace unityapi = unity::shell::application;
1721
1722+
1723 ApplicationManager::ApplicationManager(QObject *parent)
1724 : ApplicationManagerInterface(parent)
1725 {
1726 DEBUG_MSG("");
1727+
1728+ ApplicationManagerNotifier::instance()->setApplicationManager(this);
1729+
1730 buildListOfAvailableApplications();
1731
1732 // polling to find out when the toplevel window has been created as there's
1733@@ -53,11 +57,6 @@
1734 m_windowCreatedTimer.setSingleShot(false);
1735 m_windowCreatedTimer.start(200);
1736
1737- Q_ASSERT(MirFocusController::instance());
1738- connect(MirFocusController::instance(), &MirFocusController::focusedSurfaceChanged,
1739- this, &ApplicationManager::updateFocusedApplication, Qt::QueuedConnection);
1740-
1741-
1742 // Emit signal to notify Upstart that Mir is ready to receive client connections
1743 // see http://upstart.ubuntu.com/cookbook/#expect-stop
1744 // We do this because some autopilot tests actually use this mock Unity.Application module,
1745@@ -69,6 +68,7 @@
1746
1747 ApplicationManager::~ApplicationManager()
1748 {
1749+ ApplicationManagerNotifier::instance()->setApplicationManager(nullptr);
1750 }
1751
1752 void ApplicationManager::onWindowCreatedTimerTimeout()
1753@@ -158,6 +158,10 @@
1754 QModelIndex appIndex = findIndex(application);
1755 if (!appIndex.isValid()) return;
1756 Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << ApplicationManager::RoleFocused);
1757+ Q_EMIT focusedApplicationIdChanged();
1758+ if (application->focused()) {
1759+ raiseApp(application->appId());
1760+ }
1761 });
1762 connect(application, &ApplicationInfo::stateChanged, this, [application, this]() {
1763 QModelIndex appIndex = findIndex(application);
1764@@ -181,15 +185,40 @@
1765
1766 void ApplicationManager::remove(ApplicationInfo *application) {
1767 int i = m_runningApplications.indexOf(application);
1768+ application->disconnect(this);
1769 if (i != -1) {
1770 DEBUG_MSG(application->appId());
1771+ Q_ASSERT(!m_modelBusy);
1772+ m_modelBusy = true;
1773 beginRemoveRows(QModelIndex(), i, i);
1774 m_runningApplications.removeAt(i);
1775 endRemoveRows();
1776+ m_modelBusy = false;
1777 Q_EMIT countChanged();
1778 if (isEmpty()) Q_EMIT emptyChanged(isEmpty());
1779- }
1780- application->disconnect(this);
1781+ DEBUG_MSG(application->appId() << " after: " << qPrintable(toString()));
1782+ }
1783+}
1784+
1785+void ApplicationManager::raiseApp(const QString &appId)
1786+{
1787+
1788+ int index = -1;
1789+ for (int i = 0; i < m_runningApplications.count() && index == -1; ++i) {
1790+ if (m_runningApplications[i]->appId() == appId) {
1791+ index = i;
1792+ }
1793+ }
1794+
1795+ if (index >= 0) {
1796+ if (m_modelBusy) {
1797+ DEBUG_MSG(appId << " - model busy. Try again later.");
1798+ QMetaObject::invokeMethod(this, "raiseApp", Qt::QueuedConnection, Q_ARG(QString, appId));
1799+ } else {
1800+ DEBUG_MSG(appId);
1801+ move(index, 0);
1802+ }
1803+ }
1804 }
1805
1806 void ApplicationManager::move(int from, int to) {
1807@@ -197,12 +226,15 @@
1808
1809 if (from >= 0 && from < m_runningApplications.size() && to >= 0 && to < m_runningApplications.size()) {
1810 QModelIndex parent;
1811+ Q_ASSERT(!m_modelBusy);
1812+ m_modelBusy = true;
1813 /* When moving an item down, the destination index needs to be incremented
1814 * by one, as explained in the documentation:
1815 * http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
1816 beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
1817 m_runningApplications.move(from, to);
1818 endMoveRows();
1819+ m_modelBusy = false;
1820 }
1821 }
1822
1823@@ -473,40 +505,6 @@
1824 return m_runningApplications.isEmpty();
1825 }
1826
1827-void ApplicationManager::updateFocusedApplication()
1828-{
1829- ApplicationInfo *focusedApplication = nullptr;
1830- ApplicationInfo *previouslyFocusedApplication = nullptr;
1831-
1832- auto controller = MirFocusController::instance();
1833- if (!controller) {
1834- return;
1835- }
1836-
1837- MirSurface *surface = static_cast<MirSurface*>(controller->focusedSurface());
1838- if (surface) {
1839- focusedApplication = findApplication(surface);
1840- }
1841-
1842- surface = static_cast<MirSurface*>(controller->previouslyFocusedSurface());
1843- if (surface) {
1844- previouslyFocusedApplication = findApplication(surface);
1845- }
1846-
1847- if (focusedApplication != previouslyFocusedApplication) {
1848- if (focusedApplication) {
1849- DEBUG_MSG("focused " << focusedApplication->appId());
1850- Q_EMIT focusedApplication->focusedChanged(true);
1851- this->move(this->m_runningApplications.indexOf(focusedApplication), 0);
1852- }
1853- if (previouslyFocusedApplication) {
1854- DEBUG_MSG("unfocused " << previouslyFocusedApplication->appId());
1855- Q_EMIT previouslyFocusedApplication->focusedChanged(false);
1856- }
1857- Q_EMIT focusedApplicationIdChanged();
1858- }
1859-}
1860-
1861 ApplicationInfo *ApplicationManager::findApplication(MirSurface* surface)
1862 {
1863 for (ApplicationInfo *app : m_runningApplications) {
1864@@ -517,3 +515,42 @@
1865 }
1866 return nullptr;
1867 }
1868+
1869+QString ApplicationManager::toString()
1870+{
1871+ QString str;
1872+ for (int i = 0; i < m_runningApplications.count(); ++i) {
1873+ auto *application = m_runningApplications.at(i);
1874+
1875+ QString itemStr = QString("(index=%1,appId=%2)")
1876+ .arg(i)
1877+ .arg(application->appId());
1878+
1879+ if (i > 0) {
1880+ str.append(",");
1881+ }
1882+ str.append(itemStr);
1883+ }
1884+ return str;
1885+}
1886+
1887+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1888+// ApplicationManagerNotifier
1889+
1890+ApplicationManagerNotifier *ApplicationManagerNotifier::m_instance = nullptr;
1891+
1892+ApplicationManagerNotifier *ApplicationManagerNotifier::instance()
1893+{
1894+ if (!m_instance) {
1895+ m_instance = new ApplicationManagerNotifier;
1896+ }
1897+ return m_instance;
1898+}
1899+
1900+void ApplicationManagerNotifier::setApplicationManager(ApplicationManager *appMan)
1901+{
1902+ if (appMan != m_applicationManager) {
1903+ m_applicationManager = appMan;
1904+ Q_EMIT applicationManagerChanged(m_applicationManager);
1905+ }
1906+}
1907
1908=== modified file 'tests/mocks/Unity/Application/ApplicationManager.h'
1909--- tests/mocks/Unity/Application/ApplicationManager.h 2016-04-27 15:01:10 +0000
1910+++ tests/mocks/Unity/Application/ApplicationManager.h 2016-11-09 15:21:35 +0000
1911@@ -77,17 +77,44 @@
1912
1913 private Q_SLOTS:
1914 void onWindowCreatedTimerTimeout();
1915- void updateFocusedApplication();
1916+ void raiseApp(const QString &appId);
1917
1918 private:
1919 bool add(ApplicationInfo *application);
1920 void remove(ApplicationInfo* application);
1921 void buildListOfAvailableApplications();
1922 void onWindowCreated();
1923+ QString toString();
1924 ApplicationInfo *findApplication(MirSurface* surface);
1925 QList<ApplicationInfo*> m_runningApplications;
1926 QList<ApplicationInfo*> m_availableApplications;
1927 QTimer m_windowCreatedTimer;
1928+ bool m_modelBusy{false};
1929+};
1930+
1931+/*
1932+ Lifecycle of the ApplicationManager instance belongs to the QML plugin.
1933+ So this guy here is used to notify other parts of the system when the plugin creates and destroys
1934+ the ApplicationManager.
1935+
1936+ Unlike ApplicationManager, we create ApplicationManagerNotifier whenever we want.
1937+ */
1938+class ApplicationManagerNotifier : public QObject {
1939+ Q_OBJECT
1940+public:
1941+ static ApplicationManagerNotifier *instance();
1942+
1943+ ApplicationManager *applicationManager() { return m_applicationManager; }
1944+
1945+Q_SIGNALS:
1946+ void applicationManagerChanged(ApplicationManager *applicationManager);
1947+
1948+private:
1949+ void setApplicationManager(ApplicationManager *);
1950+ static ApplicationManagerNotifier *m_instance;
1951+ ApplicationManager *m_applicationManager{nullptr};
1952+
1953+friend class ApplicationManager;
1954 };
1955
1956 Q_DECLARE_METATYPE(ApplicationManager*)
1957
1958=== modified file 'tests/mocks/Unity/Application/CMakeLists.txt'
1959--- tests/mocks/Unity/Application/CMakeLists.txt 2016-06-30 13:51:32 +0000
1960+++ tests/mocks/Unity/Application/CMakeLists.txt 2016-11-09 15:21:35 +0000
1961@@ -7,14 +7,17 @@
1962 MirSurfaceListModel.cpp
1963 ObjectListModel.h
1964 SurfaceManager.cpp
1965+ TopLevelWindowModel.cpp
1966 VirtualKeyboard.cpp
1967+ Window.cpp
1968 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
1969 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
1970 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
1971 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
1972 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceItemInterface.h
1973 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
1974- ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirFocusControllerInterface.h
1975+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/TopLevelWindowModelInterface.h
1976+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/WindowInterface.h
1977 resources/surfaces.qrc
1978 )
1979
1980
1981=== modified file 'tests/mocks/Unity/Application/MirSurface.cpp'
1982--- tests/mocks/Unity/Application/MirSurface.cpp 2016-09-26 12:25:19 +0000
1983+++ tests/mocks/Unity/Application/MirSurface.cpp 2016-11-09 15:21:35 +0000
1984@@ -26,7 +26,7 @@
1985
1986 const char *stateToStr(Mir::State state)
1987 {
1988- switch(state) {
1989+ switch (state) {
1990 case Mir::UnknownState:
1991 return "unknown";
1992 case Mir::RestoredState:
1993@@ -36,16 +36,28 @@
1994 case Mir::MaximizedState:
1995 return "maximized";
1996 case Mir::VertMaximizedState:
1997- return "vert-maximized";
1998+ return "vertMaximized";
1999 case Mir::FullscreenState:
2000 return "fullscreen";
2001 case Mir::HorizMaximizedState:
2002- return "horiz-maximized";
2003+ return "horizMaximized";
2004+ case Mir::MaximizedLeftState:
2005+ return "maximizedLeft";
2006+ case Mir::MaximizedRightState:
2007+ return "maximizedRight";
2008+ case Mir::MaximizedTopLeftState:
2009+ return "maximizedTopLeft";
2010+ case Mir::MaximizedTopRightState:
2011+ return "maximizedTopRight";
2012+ case Mir::MaximizedBottomLeftState:
2013+ return "maximizedBottomLeft";
2014+ case Mir::MaximizedBottomRightState:
2015+ return "maximizedBottomRight";
2016 case Mir::HiddenState:
2017 return "hidden";
2018 default:
2019 return "???";
2020- };
2021+ }
2022 }
2023
2024 #else
2025@@ -54,8 +66,6 @@
2026
2027 using namespace unity::shell::application;
2028
2029-MirFocusController *MirFocusController::m_instance = nullptr;
2030-
2031 MirSurface::MirSurface(const QString& name,
2032 Mir::Type type,
2033 Mir::State state,
2034@@ -69,7 +79,7 @@
2035 , m_screenshotUrl(screenshot)
2036 , m_qmlFilePath(qmlFilePath)
2037 , m_live(true)
2038- , m_visible(true)
2039+ , m_focused(false)
2040 , m_activeFocus(false)
2041 , m_width(-1)
2042 , m_height(-1)
2043@@ -95,11 +105,8 @@
2044 {
2045 DEBUG_MSG("");
2046
2047- // controller instance might have been already destroyed by QQmlEngine destructor
2048- auto controller = MirFocusController::instance();
2049- if (controller && controller->focusedSurface() == this) {
2050- controller->clear();
2051- }
2052+ // Early warning, while MirSurface methods can still be accessed.
2053+ Q_EMIT destroyed(this);
2054 }
2055
2056 QString MirSurface::name() const
2057@@ -122,14 +129,30 @@
2058 return m_state;
2059 }
2060
2061+void MirSurface::requestState(Mir::State state)
2062+{
2063+ if (state == m_state) {
2064+ return;
2065+ }
2066+ DEBUG_MSG(stateToStr(state));
2067+ Q_EMIT stateRequested(state);
2068+}
2069+
2070 void MirSurface::setState(Mir::State state)
2071 {
2072- if (state == m_state)
2073+ if (state == m_state) {
2074 return;
2075-
2076+ }
2077 DEBUG_MSG(stateToStr(state));
2078+
2079+ bool oldVisible = visible();
2080+
2081 m_state = state;
2082 Q_EMIT stateChanged(state);
2083+
2084+ if (visible() != oldVisible) {
2085+ Q_EMIT visibleChanged(visible());
2086+ }
2087 }
2088
2089 bool MirSurface::live() const
2090@@ -139,7 +162,7 @@
2091
2092 bool MirSurface::visible() const
2093 {
2094- return m_visible;
2095+ return m_state != Mir::MinimizedState && m_state != Mir::HiddenState;
2096 }
2097
2098 void MirSurface::setLive(bool live)
2099@@ -231,31 +254,30 @@
2100 if (!m_live && m_views.count() == 0) {
2101 deleteLater();
2102 }
2103- updateVisibility();
2104+ updateExposure();
2105 }
2106
2107-void MirSurface::setViewVisibility(qintptr viewId, bool visible)
2108+void MirSurface::setViewExposure(qintptr viewId, bool visible)
2109 {
2110 if (!m_views.contains(viewId)) return;
2111
2112 m_views[viewId].visible = visible;
2113- updateVisibility();
2114+ updateExposure();
2115 }
2116
2117-void MirSurface::updateVisibility()
2118+void MirSurface::updateExposure()
2119 {
2120- bool newVisible = false;
2121+ bool newExposure = false;
2122 QHashIterator<qintptr, View> i(m_views);
2123 while (i.hasNext()) {
2124 i.next();
2125- newVisible |= i.value().visible;
2126+ newExposure |= i.value().visible;
2127 }
2128
2129- if (newVisible != visible()) {
2130-// qDebug().nospace() << "MirSurface[" << name() << "]::updateVisibility(" << newVisible << ")";
2131-
2132- m_visible = newVisible;
2133- Q_EMIT visibleChanged(m_visible);
2134+ if (newExposure != m_exposed) {
2135+ m_exposed = newExposure;
2136+ DEBUG_MSG(m_exposed);
2137+ Q_EMIT exposedChanged(m_exposed);
2138 updateInputBoundsAfterResize();
2139 }
2140 }
2141@@ -273,6 +295,10 @@
2142 m_activeFocus = value;
2143
2144 Q_EMIT activeFocusChanged(value);
2145+
2146+ if (m_activeFocus && !m_focused) {
2147+ requestFocus();
2148+ }
2149 }
2150
2151 int MirSurface::width() const
2152@@ -431,27 +457,18 @@
2153
2154 void MirSurface::setFocused(bool value)
2155 {
2156- DEBUG_MSG(value);
2157-
2158- auto controller = MirFocusController::instance();
2159- // controller instance might have been already destroyed by QQmlEngine destructor
2160- if (!controller) {
2161+ if (m_focused == value)
2162 return;
2163- }
2164-
2165- if (value) {
2166- controller->setFocusedSurface(this);
2167- } else if (controller->focusedSurface() == this) {
2168- controller->setFocusedSurface(nullptr);
2169- }
2170+
2171+ DEBUG_MSG("(" << value << ")");
2172+
2173+ m_focused = value;
2174+ Q_EMIT focusedChanged(value);
2175 }
2176
2177 bool MirSurface::focused() const
2178 {
2179- auto controller = MirFocusController::instance();
2180-
2181- // controller instance might have been already destroyed by QQmlEngine destructor
2182- return controller ? controller->focusedSurface() == this : false;
2183+ return m_focused;
2184 }
2185
2186 QRect MirSurface::inputBounds() const
2187@@ -466,55 +483,15 @@
2188 Q_EMIT inputBoundsChanged(m_inputBounds);
2189 }
2190 }
2191-#if MIRSURFACE_DEBUG
2192-#undef DEBUG_MSG
2193-#define DEBUG_MSG(params) qDebug().nospace() << "MirFocusController::" << __func__ << " " << params
2194-#endif
2195-
2196-void MirFocusController::setFocusedSurface(MirSurfaceInterface *surface)
2197-{
2198- if (m_focusedSurface == surface) {
2199- return;
2200- }
2201- DEBUG_MSG("MirSurface[" << (void*)surface << "," << (surface?surface->name():"") << "]");
2202-
2203- m_previouslyFocusedSurface = m_focusedSurface;
2204- m_focusedSurface = surface;
2205-
2206- if (m_previouslyFocusedSurface != m_focusedSurface) {
2207- Q_EMIT focusedSurfaceChanged();
2208- }
2209-
2210- if (m_previouslyFocusedSurface) {
2211- Q_EMIT m_previouslyFocusedSurface->focusedChanged(false);
2212- }
2213-
2214- if (m_focusedSurface) {
2215- Q_EMIT m_focusedSurface->focusedChanged(true);
2216- m_focusedSurface->raise();
2217- }
2218-}
2219-
2220-MirFocusController* MirFocusController::instance()
2221-{
2222- return m_instance;
2223-}
2224-
2225-MirFocusController::MirFocusController()
2226-{
2227- DEBUG_MSG("");
2228- Q_ASSERT(m_instance == nullptr);
2229- m_instance = this;
2230-}
2231-
2232-MirFocusController::~MirFocusController()
2233-{
2234- Q_ASSERT(m_instance == this);
2235- m_instance = nullptr;
2236-}
2237-
2238-void MirFocusController::clear()
2239-{
2240- m_focusedSurface = m_previouslyFocusedSurface = nullptr;
2241- Q_EMIT focusedSurfaceChanged();
2242+
2243+void MirSurface::setRequestedPosition(const QPoint &value)
2244+{
2245+ if (value != m_requestedPosition) {
2246+ m_requestedPosition = value;
2247+ Q_EMIT requestedPositionChanged(value);
2248+
2249+ // fake-miral: always comply
2250+ m_position = m_requestedPosition;
2251+ Q_EMIT positionChanged(m_position);
2252+ }
2253 }
2254
2255=== modified file 'tests/mocks/Unity/Application/MirSurface.h'
2256--- tests/mocks/Unity/Application/MirSurface.h 2016-09-07 08:50:19 +0000
2257+++ tests/mocks/Unity/Application/MirSurface.h 2016-11-09 15:21:35 +0000
2258@@ -23,31 +23,12 @@
2259 #include <QHash>
2260
2261 // unity-api
2262-#include <unity/shell/application/MirFocusControllerInterface.h>
2263 #include <unity/shell/application/MirSurfaceInterface.h>
2264
2265 #include "MirSurfaceListModel.h"
2266
2267 class MirSurface;
2268
2269-class MirFocusController : public unity::shell::application::MirFocusControllerInterface
2270-{
2271- Q_OBJECT
2272-public:
2273- MirFocusController();
2274- virtual ~MirFocusController();
2275- static MirFocusController* instance();
2276-
2277- void setFocusedSurface(unity::shell::application::MirSurfaceInterface *surface) override;
2278- unity::shell::application::MirSurfaceInterface* focusedSurface() const override { return m_focusedSurface; }
2279- unity::shell::application::MirSurfaceInterface* previouslyFocusedSurface() { return m_previouslyFocusedSurface; }
2280- void clear();
2281-private:
2282- static MirFocusController *m_instance;
2283- unity::shell::application::MirSurfaceInterface* m_previouslyFocusedSurface{nullptr};
2284- unity::shell::application::MirSurfaceInterface* m_focusedSurface{nullptr};
2285-};
2286-
2287 class MirSurface : public unity::shell::application::MirSurfaceInterface
2288 {
2289 Q_OBJECT
2290@@ -58,6 +39,7 @@
2291 Q_PROPERTY(int height READ height NOTIFY heightChanged)
2292 Q_PROPERTY(bool activeFocus READ activeFocus NOTIFY activeFocusChanged)
2293 Q_PROPERTY(bool slowToResize READ isSlowToResize WRITE setSlowToResize NOTIFY slowToResizeChanged)
2294+ Q_PROPERTY(bool exposed READ exposed NOTIFY exposedChanged)
2295
2296 public:
2297 MirSurface(const QString& name,
2298@@ -76,13 +58,14 @@
2299
2300 QString persistentId() const override;
2301
2302+ QPoint position() const override { return m_position; }
2303+
2304 QSize size() const override { return QSize(width(),height()); }
2305 void resize(int width, int height) override;
2306 void resize(const QSize &size) override { resize(size.width(), size.height()); }
2307
2308
2309 Mir::State state() const override;
2310- Q_INVOKABLE void setState(Mir::State) override;
2311
2312 bool live() const override;
2313
2314@@ -108,6 +91,9 @@
2315
2316 bool confinesMousePointer() const override { return false; }
2317
2318+ QPoint requestedPosition() const override { return m_requestedPosition; }
2319+ void setRequestedPosition(const QPoint &) override;
2320+
2321 Q_INVOKABLE void requestFocus() override;
2322
2323 Q_INVOKABLE void close() override;
2324@@ -126,6 +112,8 @@
2325 bool isSlowToResize() const;
2326 void setSlowToResize(bool value);
2327
2328+ bool exposed() const { return m_exposed; }
2329+
2330 Q_INVOKABLE void setMinimumWidth(int);
2331 Q_INVOKABLE void setMaximumWidth(int);
2332 Q_INVOKABLE void setMinimumHeight(int);
2333@@ -148,17 +136,25 @@
2334
2335 void registerView(qintptr viewId);
2336 void unregisterView(qintptr viewId);
2337- void setViewVisibility(qintptr viewId, bool visible);
2338+ void setViewExposure(qintptr viewId, bool visible);
2339 int viewCount() const { return m_views.count(); }
2340
2341 void setFocused(bool value);
2342
2343+ void setState(Mir::State state);
2344+
2345+public Q_SLOTS:
2346+ ////
2347+ // unity.shell.application.MirSurface
2348+ void requestState(Mir::State) override;
2349+
2350 Q_SIGNALS:
2351 ////
2352 // API for tests
2353 void widthChanged();
2354 void heightChanged();
2355 void slowToResizeChanged();
2356+ void exposedChanged(bool exposed);
2357
2358 ////
2359 // internal mock stuff
2360@@ -166,6 +162,7 @@
2361 void activeFocusChanged(bool);
2362 void raiseRequested();
2363 void closeRequested();
2364+ void stateRequested(Mir::State);
2365
2366 protected:
2367 virtual void updateInputBoundsAfterResize();
2368@@ -175,7 +172,7 @@
2369
2370 private:
2371 void doResize(int width, int height);
2372- void updateVisibility();
2373+ void updateExposure();
2374
2375 const QString m_name;
2376 const Mir::Type m_type;
2377@@ -185,7 +182,7 @@
2378 QUrl m_screenshotUrl;
2379 QUrl m_qmlFilePath;
2380 bool m_live;
2381- bool m_visible;
2382+ bool m_focused;
2383 bool m_activeFocus;
2384 int m_width;
2385 int m_height;
2386@@ -210,10 +207,14 @@
2387 bool visible;
2388 };
2389 QHash<qintptr, View> m_views;
2390+ bool m_exposed{false};
2391
2392 QTimer m_zombieTimer;
2393
2394 QRect m_inputBounds;
2395+
2396+ QPoint m_position;
2397+ QPoint m_requestedPosition;
2398 };
2399
2400 #endif // MOCK_MIR_SURFACE_H
2401
2402=== modified file 'tests/mocks/Unity/Application/MirSurfaceItem.cpp'
2403--- tests/mocks/Unity/Application/MirSurfaceItem.cpp 2016-08-23 11:11:32 +0000
2404+++ tests/mocks/Unity/Application/MirSurfaceItem.cpp 2016-11-09 15:21:35 +0000
2405@@ -59,7 +59,7 @@
2406 Qt::ExtraButton12 | Qt::ExtraButton13);
2407
2408 connect(this, &QQuickItem::activeFocusChanged, this, &MirSurfaceItem::updateMirSurfaceActiveFocus);
2409- connect(this, &QQuickItem::visibleChanged, this, &MirSurfaceItem::updateMirSurfaceVisibility);
2410+ connect(this, &QQuickItem::visibleChanged, this, &MirSurfaceItem::updateMirSurfaceExposure);
2411
2412 connect(this, &MirSurfaceItem::consumesInputChanged, this, [this]() {
2413 updateMirSurfaceActiveFocus(hasActiveFocus());
2414@@ -278,7 +278,7 @@
2415 m_qmlSurface->registerView((qintptr)this);
2416
2417 updateSurfaceSize();
2418- updateMirSurfaceVisibility();
2419+ updateMirSurfaceExposure();
2420
2421 if (m_orientationAngle) {
2422 m_qmlSurface->setOrientationAngle(*m_orientationAngle);
2423@@ -335,11 +335,11 @@
2424 }
2425 }
2426
2427-void MirSurfaceItem::updateMirSurfaceVisibility()
2428+void MirSurfaceItem::updateMirSurfaceExposure()
2429 {
2430 if (!m_qmlSurface) return;
2431
2432- m_qmlSurface->setViewVisibility((qintptr)this, isVisible());
2433+ m_qmlSurface->setViewExposure((qintptr)this, isVisible());
2434 }
2435
2436 void MirSurfaceItem::setConsumesInput(bool value)
2437
2438=== modified file 'tests/mocks/Unity/Application/MirSurfaceItem.h'
2439--- tests/mocks/Unity/Application/MirSurfaceItem.h 2016-06-22 13:53:00 +0000
2440+++ tests/mocks/Unity/Application/MirSurfaceItem.h 2016-11-09 15:21:35 +0000
2441@@ -50,7 +50,6 @@
2442 Mir::ShellChrome shellChrome() const override;
2443
2444 Mir::State surfaceState() const override;
2445- void setSurfaceState(Mir::State) override {}
2446
2447 Mir::OrientationAngle orientationAngle() const override;
2448 void setOrientationAngle(Mir::OrientationAngle angle) override;
2449@@ -102,7 +101,7 @@
2450 private Q_SLOTS:
2451 void onComponentStatusChanged(QQmlComponent::Status status);
2452 void updateScreenshot(QUrl screenshot);
2453- void updateMirSurfaceVisibility();
2454+ void updateMirSurfaceExposure();
2455 void updateMirSurfaceActiveFocus(bool focused);
2456
2457 private:
2458
2459=== modified file 'tests/mocks/Unity/Application/MirSurfaceListModel.cpp'
2460--- tests/mocks/Unity/Application/MirSurfaceListModel.cpp 2016-09-23 13:38:46 +0000
2461+++ tests/mocks/Unity/Application/MirSurfaceListModel.cpp 2016-11-09 15:21:35 +0000
2462@@ -21,8 +21,9 @@
2463
2464 #define MIRSURFACELISTMODEL_DEBUG 0
2465
2466-#ifdef MIRSURFACELISTMODEL_DEBUG
2467-#define DEBUG_MSG(params) qDebug().nospace() << "MirSurfaceListModel::" << __func__ << " " << params
2468+#if MIRSURFACELISTMODEL_DEBUG
2469+#include <QDebug>
2470+#define DEBUG_MSG(params) qDebug().nospace() << "MirSurfaceListModel::" << __func__ << params
2471 #else
2472 #define DEBUG_MSG(params) ((void)0)
2473 #endif
2474@@ -54,14 +55,25 @@
2475
2476 void MirSurfaceListModel::raise(MirSurface *surface)
2477 {
2478+ DEBUG_MSG("(" << surface << ")");
2479 int i = m_surfaceList.indexOf(surface);
2480 if (i != -1) {
2481 moveSurface(i, 0);
2482 }
2483 }
2484
2485+void MirSurfaceListModel::addSurface(MirSurface *surface)
2486+{
2487+ if (surface->focused()) {
2488+ prependSurface(surface);
2489+ } else {
2490+ appendSurface(surface);
2491+ }
2492+}
2493+
2494 void MirSurfaceListModel::appendSurface(MirSurface *surface)
2495 {
2496+ DEBUG_MSG("(" << surface << ")");
2497 beginInsertRows(QModelIndex(), m_surfaceList.size(), m_surfaceList.size());
2498 m_surfaceList.append(surface);
2499 connectSurface(surface);
2500@@ -74,6 +86,7 @@
2501
2502 void MirSurfaceListModel::prependSurface(MirSurface *surface)
2503 {
2504+ DEBUG_MSG("(" << surface << ")");
2505 beginInsertRows(QModelIndex(), 0, 0);
2506 m_surfaceList.prepend(surface);
2507 connectSurface(surface);
2508@@ -85,6 +98,11 @@
2509 void MirSurfaceListModel::connectSurface(MirSurface *surface)
2510 {
2511 connect(surface, &QObject::destroyed, this, [this, surface](){ this->removeSurface(surface); });
2512+ connect(surface, &MirSurfaceInterface::focusedChanged, this, [this, surface](bool surfaceFocused){
2513+ if (surfaceFocused) {
2514+ raise(surface);
2515+ }
2516+ });
2517 }
2518
2519 void MirSurfaceListModel::removeSurface(MirSurface *surface)
2520
2521=== modified file 'tests/mocks/Unity/Application/MirSurfaceListModel.h'
2522--- tests/mocks/Unity/Application/MirSurfaceListModel.h 2016-06-02 12:02:35 +0000
2523+++ tests/mocks/Unity/Application/MirSurfaceListModel.h 2016-11-09 15:21:35 +0000
2524@@ -38,7 +38,7 @@
2525 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
2526 QVariant data(const QModelIndex& index, int role) const override;
2527
2528- void appendSurface(MirSurface *surface);
2529+ void addSurface(MirSurface *surface);
2530 void removeSurface(MirSurface *surface);
2531
2532 bool contains(MirSurface *surface) const { return m_surfaceList.contains(surface); }
2533@@ -49,6 +49,7 @@
2534 Q_INVOKABLE unity::shell::application::MirSurfaceInterface *createSurface();
2535
2536 private:
2537+ void appendSurface(MirSurface *surface);
2538 void prependSurface(MirSurface *surface);
2539 void raise(MirSurface *surface);
2540 void moveSurface(int from, int to);
2541
2542=== modified file 'tests/mocks/Unity/Application/SurfaceManager.cpp'
2543--- tests/mocks/Unity/Application/SurfaceManager.cpp 2016-04-27 15:01:10 +0000
2544+++ tests/mocks/Unity/Application/SurfaceManager.cpp 2016-11-09 15:21:35 +0000
2545@@ -21,19 +21,19 @@
2546
2547 #include <paths.h>
2548
2549-SurfaceManager *SurfaceManager::the_surface_manager = nullptr;
2550+SurfaceManager *SurfaceManager::m_instance = nullptr;
2551
2552 SurfaceManager *SurfaceManager::instance()
2553 {
2554- return the_surface_manager;
2555+ return m_instance;
2556 }
2557
2558 SurfaceManager::SurfaceManager(QObject *parent) :
2559 QObject(parent)
2560 , m_virtualKeyboard(nullptr)
2561 {
2562- Q_ASSERT(the_surface_manager == nullptr);
2563- the_surface_manager = this;
2564+ Q_ASSERT(m_instance == nullptr);
2565+ m_instance = this;
2566
2567 m_virtualKeyboard = new VirtualKeyboard;
2568 connect(m_virtualKeyboard, &QObject::destroyed, this, [this](QObject *obj) {
2569@@ -46,14 +46,15 @@
2570
2571 SurfaceManager::~SurfaceManager()
2572 {
2573- Q_ASSERT(the_surface_manager == this);
2574- the_surface_manager = nullptr;
2575+ Q_ASSERT(m_instance == this);
2576+ m_instance = nullptr;
2577 }
2578
2579 MirSurface *SurfaceManager::createSurface(const QString& name,
2580 Mir::Type type,
2581 Mir::State state,
2582- const QUrl& screenshot)
2583+ const QUrl& screenshot,
2584+ ApplicationInfo *application)
2585 {
2586 MirSurface* surface = new MirSurface(name, type, state, screenshot);
2587 connect(surface, &QObject::destroyed, this, [this](QObject *obj) {
2588@@ -68,7 +69,7 @@
2589 surface->setWidthIncrement(m_newSurfaceWidthIncrement);
2590 surface->setHeightIncrement(m_newSurfaceHeightIncrement);
2591
2592- Q_EMIT surfaceCreated(surface);
2593+ Q_EMIT surfaceCreated(surface, application);
2594 return surface;
2595 }
2596
2597
2598=== modified file 'tests/mocks/Unity/Application/SurfaceManager.h'
2599--- tests/mocks/Unity/Application/SurfaceManager.h 2016-04-13 18:33:15 +0000
2600+++ tests/mocks/Unity/Application/SurfaceManager.h 2016-11-09 15:21:35 +0000
2601@@ -44,7 +44,8 @@
2602 Q_INVOKABLE MirSurface* createSurface(const QString& name,
2603 Mir::Type type,
2604 Mir::State state,
2605- const QUrl& screenshot);
2606+ const QUrl& screenshot,
2607+ ApplicationInfo *application);
2608
2609 MirSurface* inputMethodSurface() const;
2610
2611@@ -69,7 +70,7 @@
2612 Q_SIGNALS:
2613 void inputMethodSurfaceChanged();
2614 void countChanged();
2615- void surfaceCreated(MirSurface *surface);
2616+ void surfaceCreated(MirSurface *surface, ApplicationInfo *application);
2617 void surfaceDestroyed(MirSurface*surface);
2618
2619 void newSurfaceMinimumWidthChanged(int value);
2620@@ -80,7 +81,7 @@
2621 void newSurfaceHeightIncrementChanged(int value);
2622
2623 private:
2624- static SurfaceManager *the_surface_manager;
2625+ static SurfaceManager *m_instance;
2626 VirtualKeyboard *m_virtualKeyboard;
2627
2628 int m_newSurfaceMinimumWidth{0};
2629
2630=== added file 'tests/mocks/Unity/Application/TopLevelWindowModel.cpp'
2631--- tests/mocks/Unity/Application/TopLevelWindowModel.cpp 1970-01-01 00:00:00 +0000
2632+++ tests/mocks/Unity/Application/TopLevelWindowModel.cpp 2016-11-09 15:21:35 +0000
2633@@ -0,0 +1,610 @@
2634+/*
2635+ * Copyright (C) 2016 Canonical, Ltd.
2636+ *
2637+ * This program is free software; you can redistribute it and/or modify
2638+ * it under the terms of the GNU General Public License as published by
2639+ * the Free Software Foundation; version 3.
2640+ *
2641+ * This program is distributed in the hope that it will be useful,
2642+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2643+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2644+ * GNU General Public License for more details.
2645+ *
2646+ * You should have received a copy of the GNU General Public License
2647+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2648+ */
2649+
2650+#include "TopLevelWindowModel.h"
2651+
2652+#include "SurfaceManager.h"
2653+
2654+Q_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL, "toplevelwindowmodel", QtWarningMsg)
2655+
2656+#define DEBUG_MSG qCDebug(TOPLEVELWINDOWMODEL).nospace().noquote() << __func__
2657+
2658+namespace unityapi = unity::shell::application;
2659+
2660+TopLevelWindowModel::TopLevelWindowModel()
2661+{
2662+ DEBUG_MSG;
2663+ auto *appManNotifier = ApplicationManagerNotifier::instance();
2664+
2665+ if (appManNotifier->applicationManager()) {
2666+ setApplicationManager(appManNotifier->applicationManager());
2667+ } else {
2668+ connect(appManNotifier, &ApplicationManagerNotifier::applicationManagerChanged,
2669+ this, &TopLevelWindowModel::setApplicationManager);
2670+ }
2671+}
2672+
2673+TopLevelWindowModel::~TopLevelWindowModel()
2674+{
2675+ DEBUG_MSG;
2676+}
2677+
2678+unityapi::MirSurfaceInterface* TopLevelWindowModel::inputMethodSurface() const
2679+{
2680+ return m_inputMethodWindow ? m_inputMethodWindow->surface() : nullptr;
2681+}
2682+
2683+void TopLevelWindowModel::connectSurfaceManager()
2684+{
2685+ if (m_connectedSurfaceManager)
2686+ return;
2687+
2688+ auto surfaceManager = SurfaceManager::instance();
2689+ Q_ASSERT(surfaceManager != nullptr);
2690+
2691+ setInputMethodSurface(surfaceManager->inputMethodSurface());
2692+
2693+ connect(surfaceManager, &SurfaceManager::inputMethodSurfaceChanged,
2694+ this, [&]() {
2695+ setInputMethodSurface(surfaceManager->inputMethodSurface());
2696+ });
2697+
2698+ connect(surfaceManager, &SurfaceManager::surfaceCreated,
2699+ this, &TopLevelWindowModel::prependSurface);
2700+
2701+ m_connectedSurfaceManager = true;
2702+}
2703+
2704+void TopLevelWindowModel::setApplicationManager(ApplicationManager* value)
2705+{
2706+ if (m_applicationManager == value) {
2707+ return;
2708+ }
2709+
2710+ Q_ASSERT(m_modelState == IdleState);
2711+ m_modelState = ResettingState;
2712+
2713+ beginResetModel();
2714+
2715+ if (m_applicationManager) {
2716+ m_windowModel.clear();
2717+ disconnect(m_applicationManager, 0, this, 0);
2718+ }
2719+
2720+ m_applicationManager = value;
2721+
2722+ if (m_applicationManager) {
2723+ // we're in business!
2724+ connectSurfaceManager();
2725+
2726+ connect(m_applicationManager, &QAbstractItemModel::rowsInserted,
2727+ this, [this](const QModelIndex &/*parent*/, int first, int last) {
2728+ for (int i = first; i <= last; ++i) {
2729+ auto application = m_applicationManager->get(i);
2730+ addApplication(static_cast<ApplicationInfo*>(application));
2731+ }
2732+ });
2733+
2734+ connect(m_applicationManager, &QAbstractItemModel::rowsAboutToBeRemoved,
2735+ this, [this](const QModelIndex &/*parent*/, int first, int last) {
2736+ for (int i = first; i <= last; ++i) {
2737+ auto application = m_applicationManager->get(i);
2738+ removeApplication(static_cast<ApplicationInfo*>(application));
2739+ }
2740+ // fake-miral
2741+ // Do it after ApplicationManager has finished changing its model
2742+ QMetaObject::invokeMethod(this, "focusTopMostAvailableWindow", Qt::QueuedConnection);
2743+ });
2744+
2745+ for (int i = 0; i < m_applicationManager->rowCount(); ++i) {
2746+ auto application = m_applicationManager->get(i);
2747+ addApplication(static_cast<ApplicationInfo*>(application));
2748+ }
2749+ }
2750+
2751+ endResetModel();
2752+ m_modelState = IdleState;
2753+}
2754+
2755+void TopLevelWindowModel::focusTopMostAvailableWindow()
2756+{
2757+ DEBUG_MSG << "()";
2758+ setFocusedWindow(findFocusableWindow(0));
2759+}
2760+
2761+void TopLevelWindowModel::addApplication(ApplicationInfo *application)
2762+{
2763+ DEBUG_MSG << "(" << application->appId() << ")";
2764+
2765+ if (application->state() != unityapi::ApplicationInfoInterface::Stopped && application->surfaceList()->count() == 0) {
2766+ prependPlaceholder(application);
2767+ } else {
2768+ auto *surfaceList = application->surfaceList();
2769+ for (int i = 0; i < surfaceList->count(); ++i) {
2770+ prependSurface(static_cast<MirSurface*>(surfaceList->get(i)), application);
2771+ }
2772+ }
2773+}
2774+
2775+void TopLevelWindowModel::removeApplication(ApplicationInfo *application)
2776+{
2777+ DEBUG_MSG << "(" << application->appId() << ")";
2778+
2779+ Q_ASSERT(m_modelState == IdleState);
2780+
2781+ int i = 0;
2782+ while (i < m_windowModel.count()) {
2783+ if (m_windowModel.at(i).application == application) {
2784+ removeAt(i);
2785+ } else {
2786+ ++i;
2787+ }
2788+ }
2789+
2790+ DEBUG_MSG << " after " << toString();
2791+}
2792+
2793+void TopLevelWindowModel::prependPlaceholder(ApplicationInfo *application)
2794+{
2795+ DEBUG_MSG << "(" << application->appId() << ")";
2796+
2797+ prependSurfaceHelper(nullptr, application);
2798+}
2799+
2800+void TopLevelWindowModel::prependSurface(MirSurface *surface, ApplicationInfo *application)
2801+{
2802+ Q_ASSERT(surface != nullptr);
2803+
2804+ bool filledPlaceholder = false;
2805+ for (int i = 0; i < m_windowModel.count() && !filledPlaceholder; ++i) {
2806+ ModelEntry &entry = m_windowModel[i];
2807+ if (entry.application == application && entry.window->surface() == nullptr) {
2808+ // fake-miral: focus the newly added surface
2809+ if (entry.window->focused()) {
2810+ surface->setFocused(true);
2811+ }
2812+
2813+ entry.window->setSurface(surface);
2814+ connectSurface(surface);
2815+ DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface
2816+ << ", filling out placeholder. after: " << toString();
2817+ filledPlaceholder = true;
2818+ }
2819+ }
2820+
2821+ if (!filledPlaceholder) {
2822+ DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface << ", adding new row";
2823+ prependSurfaceHelper(surface, application);
2824+ }
2825+}
2826+
2827+void TopLevelWindowModel::prependSurfaceHelper(MirSurface *surface, ApplicationInfo *application)
2828+{
2829+ if (m_modelState == IdleState) {
2830+ m_modelState = InsertingState;
2831+ beginInsertRows(QModelIndex(), 0 /*first*/, 0 /*last*/);
2832+ } else {
2833+ Q_ASSERT(m_modelState == ResettingState);
2834+ // No point in signaling anything if we're resetting the whole model
2835+ }
2836+
2837+ int id = generateId();
2838+ Window *window = new Window(id);
2839+ if (surface) {
2840+ window->setSurface(surface);
2841+ }
2842+ m_windowModel.prepend(ModelEntry(window, application));
2843+ if (surface) {
2844+ connectSurface(surface);
2845+ }
2846+
2847+ connect(window, &WindowInterface::focusRequested, this, [this, window]() {
2848+ // fake-miral: just comply
2849+ setFocusedWindow(window);
2850+ });
2851+
2852+ connect(window, &Window::closeRequested, this, [this, window]() {
2853+ onWindowCloseRequested(window);
2854+ });
2855+
2856+ connect(window, &Window::stateRequested, this, [this, window](Mir::State requestedState) {
2857+ onStateChangeRequested(window, requestedState);
2858+ });
2859+
2860+ if (m_modelState == InsertingState) {
2861+ endInsertRows();
2862+ Q_EMIT countChanged();
2863+ Q_EMIT listChanged();
2864+ m_modelState = IdleState;
2865+ }
2866+
2867+ // fake-miral: focus the newly added window
2868+ setFocusedWindow(window);
2869+
2870+ DEBUG_MSG << " after " << toString();
2871+}
2872+
2873+void TopLevelWindowModel::onWindowCloseRequested(Window *window)
2874+{
2875+ if (!window->surface()) {
2876+ int index = indexOf(window);
2877+ Q_ASSERT(index >= 0);
2878+ m_windowModel[index].application->close();
2879+ }
2880+}
2881+
2882+void TopLevelWindowModel::connectSurface(MirSurface *surface)
2883+{
2884+ connect(surface, &MirSurfaceInterface::liveChanged, this, [this, surface](bool live){
2885+ if (!live) {
2886+ onSurfaceDied(surface);
2887+ }
2888+ });
2889+
2890+ connect(surface, &QObject::destroyed, this, [this, surface](){ this->onSurfaceDestroyed(surface); });
2891+}
2892+
2893+void TopLevelWindowModel::onStateChangeRequested(Window *window, Mir::State requestedState)
2894+{
2895+ if (requestedState == window->state()) {
2896+ return;
2897+ }
2898+
2899+ // fake-miral
2900+
2901+ window->setState(requestedState);
2902+
2903+ if (requestedState == Mir::MinimizedState) {
2904+ if (m_focusedWindow && m_focusedWindow == window) {
2905+ setFocusedWindow(findFocusableWindow(indexOf(window)+1));
2906+ }
2907+ } else if (window->state() == Mir::MinimizedState || window->state() == Mir::HiddenState) {
2908+ if (requestedState != Mir::MinimizedState && requestedState != Mir::HiddenState) {
2909+ setFocusedWindow(window);
2910+ }
2911+ }
2912+}
2913+
2914+Window *TopLevelWindowModel::findFocusableWindow(int index)
2915+{
2916+ Q_ASSERT(index >= 0);
2917+
2918+ // the simplest thing possible. this is a fake implementation afterall
2919+ if (index < m_windowModel.count()) {
2920+ auto candidate = m_windowModel[index].window;
2921+ if (candidate->state() != Mir::MinimizedState && candidate->state() != Mir::HiddenState) {
2922+ return candidate;
2923+ } else {
2924+ return findFocusableWindow(index + 1);
2925+ }
2926+ } else {
2927+ return nullptr;
2928+ }
2929+}
2930+
2931+void TopLevelWindowModel::onSurfaceDied(MirSurfaceInterface *surface)
2932+{
2933+ int i = indexOf(surface);
2934+ if (i == -1) {
2935+ return;
2936+ }
2937+
2938+ auto application = m_windowModel[i].application;
2939+
2940+ // can't be starting if it already has a surface
2941+ Q_ASSERT(application->state() != unityapi::ApplicationInfoInterface::Starting);
2942+
2943+ if (application->state() == unityapi::ApplicationInfoInterface::Running) {
2944+ m_windowModel[i].removeOnceSurfaceDestroyed = true;
2945+ } else {
2946+ // assume it got killed by the out-of-memory daemon.
2947+ //
2948+ // So leave entry in the model and only remove its surface, so shell can display a screenshot
2949+ // in its place.
2950+ m_windowModel[i].removeOnceSurfaceDestroyed = false;
2951+ }
2952+}
2953+
2954+void TopLevelWindowModel::onSurfaceDestroyed(MirSurfaceInterface *surface)
2955+{
2956+ int i = indexOf(surface);
2957+ if (i == -1) {
2958+ return;
2959+ }
2960+
2961+ if (m_windowModel[i].removeOnceSurfaceDestroyed) {
2962+ removeAt(i);
2963+ } else {
2964+ m_windowModel[i].window->setSurface(nullptr);
2965+ DEBUG_MSG << " Removed surface from entry. After: " << toString();
2966+ }
2967+
2968+ disconnect(surface, 0, this, 0);
2969+}
2970+
2971+void TopLevelWindowModel::removeAt(int index)
2972+{
2973+ Q_ASSERT(index >= 0 && index < m_windowModel.count());
2974+
2975+ if (m_modelState == IdleState) {
2976+ beginRemoveRows(QModelIndex(), index, index);
2977+ m_modelState = RemovingState;
2978+ } else {
2979+ Q_ASSERT(m_modelState == ResettingState);
2980+ // No point in signaling anything if we're resetting the whole model
2981+ }
2982+
2983+ auto window = m_windowModel[index].window;
2984+ if (window == focusedWindow()) {
2985+ setFocusedWindow(nullptr);
2986+ }
2987+
2988+ m_windowModel.removeAt(index);
2989+
2990+ delete window;
2991+
2992+ if (m_modelState == RemovingState) {
2993+ endRemoveRows();
2994+ Q_EMIT countChanged();
2995+ Q_EMIT listChanged();
2996+ m_modelState = IdleState;
2997+ }
2998+
2999+ DEBUG_MSG << " after " << toString();
3000+}
3001+
3002+void TopLevelWindowModel::setInputMethodSurface(MirSurface *surface)
3003+{
3004+ if (m_inputMethodWindow) {
3005+ qDebug("Multiple Input Method Surfaces created, removing the old one!");
3006+ delete m_inputMethodWindow;
3007+ }
3008+ m_inputMethodWindow = new Window(generateId());
3009+ m_inputMethodWindow->setSurface(surface);
3010+ if (surface) {
3011+ connectSurface(surface);
3012+ }
3013+ Q_EMIT inputMethodSurfaceChanged(m_inputMethodWindow->surface());
3014+}
3015+
3016+void TopLevelWindowModel::removeInputMethodWindow()
3017+{
3018+ if (m_inputMethodWindow) {
3019+ delete m_inputMethodWindow;
3020+ m_inputMethodWindow = nullptr;
3021+ Q_EMIT inputMethodSurfaceChanged(nullptr);
3022+ }
3023+}
3024+
3025+int TopLevelWindowModel::rowCount(const QModelIndex &/*parent*/) const
3026+{
3027+ return m_windowModel.count();
3028+}
3029+
3030+QVariant TopLevelWindowModel::data(const QModelIndex& index, int role) const
3031+{
3032+ if (index.row() < 0 || index.row() >= m_windowModel.size())
3033+ return QVariant();
3034+
3035+ if (role == WindowRole) {
3036+ unityapi::WindowInterface *window = m_windowModel.at(index.row()).window;
3037+ return QVariant::fromValue(window);
3038+ } else if (role == ApplicationRole) {
3039+ return QVariant::fromValue(m_windowModel.at(index.row()).application);
3040+ } else {
3041+ return QVariant();
3042+ }
3043+}
3044+
3045+int TopLevelWindowModel::generateId()
3046+{
3047+ int id = m_nextId;
3048+ m_nextId = nextFreeId(m_nextId + 1);
3049+ Q_EMIT nextIdChanged();
3050+ return id;
3051+}
3052+
3053+int TopLevelWindowModel::nextFreeId(int candidateId)
3054+{
3055+ if (candidateId > m_maxId) {
3056+ return nextFreeId(1);
3057+ } else {
3058+ if (indexForId(candidateId) == -1) {
3059+ // it's indeed free
3060+ return candidateId;
3061+ } else {
3062+ return nextFreeId(candidateId + 1);
3063+ }
3064+ }
3065+}
3066+
3067+QString TopLevelWindowModel::toString()
3068+{
3069+ QString str;
3070+ for (int i = 0; i < m_windowModel.count(); ++i) {
3071+ auto item = m_windowModel.at(i);
3072+
3073+ QString itemStr = QString("(index=%1,appId=%2,surface=0x%3,id=%4)")
3074+ .arg(i)
3075+ .arg(item.application->appId())
3076+ .arg((qintptr)item.window->surface(), 0, 16)
3077+ .arg(item.window->id());
3078+
3079+ if (i > 0) {
3080+ str.append(",");
3081+ }
3082+ str.append(itemStr);
3083+ }
3084+ return str;
3085+}
3086+
3087+int TopLevelWindowModel::indexOf(WindowInterface *window)
3088+{
3089+ for (int i = 0; i < m_windowModel.count(); ++i) {
3090+ if (m_windowModel.at(i).window == window) {
3091+ return i;
3092+ }
3093+ }
3094+ return -1;
3095+}
3096+
3097+int TopLevelWindowModel::indexOf(MirSurfaceInterface *surface)
3098+{
3099+ for (int i = 0; i < m_windowModel.count(); ++i) {
3100+ if (m_windowModel.at(i).window->surface() == surface) {
3101+ return i;
3102+ }
3103+ }
3104+ return -1;
3105+}
3106+
3107+int TopLevelWindowModel::indexForId(int id) const
3108+{
3109+ for (int i = 0; i < m_windowModel.count(); ++i) {
3110+ if (m_windowModel[i].window->id() == id) {
3111+ return i;
3112+ }
3113+ }
3114+ return -1;
3115+}
3116+
3117+unityapi::WindowInterface *TopLevelWindowModel::windowAt(int index) const
3118+{
3119+ if (index >=0 && index < m_windowModel.count()) {
3120+ return m_windowModel[index].window;
3121+ } else {
3122+ return nullptr;
3123+ }
3124+}
3125+
3126+unityapi::MirSurfaceInterface *TopLevelWindowModel::surfaceAt(int index) const
3127+{
3128+ auto window = windowAt(index);
3129+ return window ? window->surface() : nullptr;
3130+}
3131+
3132+unityapi::ApplicationInfoInterface *TopLevelWindowModel::applicationAt(int index) const
3133+{
3134+ if (index >=0 && index < m_windowModel.count()) {
3135+ return m_windowModel[index].application;
3136+ } else {
3137+ return nullptr;
3138+ }
3139+}
3140+
3141+int TopLevelWindowModel::idAt(int index) const
3142+{
3143+ if (index >=0 && index < m_windowModel.count()) {
3144+ return m_windowModel[index].window->id();
3145+ } else {
3146+ return 0;
3147+ }
3148+}
3149+
3150+void TopLevelWindowModel::raiseId(int id)
3151+{
3152+ if (m_modelState == IdleState) {
3153+ DEBUG_MSG << "(id=" << id << ") - do it now.";
3154+ doRaiseId(id);
3155+ } else {
3156+ DEBUG_MSG << "(id=" << id << ") - Model busy (modelState=" << m_modelState << "). Try again in the next event loop.";
3157+ // The model has just signalled some change. If we have a Repeater responding to this update, it will get nuts
3158+ // if we perform yet another model change straight away.
3159+ //
3160+ // A bad sympton of this problem is a Repeater.itemAt(index) call returning null event though Repeater.count says
3161+ // the index is definitely within bounds.
3162+ QMetaObject::invokeMethod(this, "raiseId", Qt::QueuedConnection, Q_ARG(int, id));
3163+ }
3164+}
3165+
3166+void TopLevelWindowModel::doRaiseId(int id)
3167+{
3168+ int fromIndex = indexForId(id);
3169+ if (fromIndex != -1) {
3170+ move(fromIndex, 0);
3171+ }
3172+}
3173+
3174+Window *TopLevelWindowModel::findWindowWithSurface(MirSurface *surface)
3175+{
3176+ for (int i = 0; i < m_windowModel.count(); ++i) {
3177+ Window *window = m_windowModel[i].window;
3178+ if (window->surface() == surface) {
3179+ return window;
3180+ }
3181+ }
3182+ return nullptr;
3183+}
3184+
3185+void TopLevelWindowModel::setFocusedWindow(Window *window)
3186+{
3187+ if (window != m_focusedWindow) {
3188+ DEBUG_MSG << "(" << (window ? window->toString() : "null") << ")";
3189+
3190+ Window* previousWindow = m_focusedWindow;
3191+
3192+ // fake-miral: restore window if needed
3193+ if (window && (window->state() == Mir::MinimizedState || window->state() == Mir::HiddenState)) {
3194+ window->setState(Mir::RestoredState);
3195+ }
3196+
3197+ m_focusedWindow = window;
3198+ Q_EMIT focusedWindowChanged(m_focusedWindow);
3199+
3200+ if (previousWindow) {
3201+ // fake-miral
3202+ previousWindow->setFocused(false);
3203+ }
3204+
3205+ if (m_focusedWindow) {
3206+ // fake-miral
3207+ m_focusedWindow->setFocused(true);
3208+ raiseId(m_focusedWindow->id());
3209+ }
3210+ }
3211+}
3212+
3213+unityapi::WindowInterface* TopLevelWindowModel::focusedWindow() const
3214+{
3215+ return m_focusedWindow;
3216+}
3217+
3218+void TopLevelWindowModel::move(int from, int to)
3219+{
3220+ if (from == to) return;
3221+ DEBUG_MSG << " from=" << from << " to=" << to;
3222+
3223+ if (from >= 0 && from < m_windowModel.size() && to >= 0 && to < m_windowModel.size()) {
3224+ QModelIndex parent;
3225+ /* When moving an item down, the destination index needs to be incremented
3226+ by one, as explained in the documentation:
3227+ http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
3228+
3229+ Q_ASSERT(m_modelState == IdleState);
3230+ m_modelState = MovingState;
3231+
3232+ beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
3233+ ModelEntry entry = m_windowModel.takeAt(from);
3234+ m_windowModel.insert(to, entry);
3235+ endMoveRows();
3236+
3237+ m_modelState = IdleState;
3238+
3239+ DEBUG_MSG << " after " << toString();
3240+
3241+ Q_EMIT listChanged();
3242+ }
3243+}
3244
3245=== added file 'tests/mocks/Unity/Application/TopLevelWindowModel.h'
3246--- tests/mocks/Unity/Application/TopLevelWindowModel.h 1970-01-01 00:00:00 +0000
3247+++ tests/mocks/Unity/Application/TopLevelWindowModel.h 2016-11-09 15:21:35 +0000
3248@@ -0,0 +1,146 @@
3249+/*
3250+ * Copyright (C) 2016 Canonical, Ltd.
3251+ *
3252+ * This program is free software; you can redistribute it and/or modify
3253+ * it under the terms of the GNU General Public License as published by
3254+ * the Free Software Foundation; version 3.
3255+ *
3256+ * This program is distributed in the hope that it will be useful,
3257+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3258+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3259+ * GNU General Public License for more details.
3260+ *
3261+ * You should have received a copy of the GNU General Public License
3262+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3263+ */
3264+
3265+#pragma once
3266+
3267+#include <QLoggingCategory>
3268+
3269+#include "ApplicationManager.h"
3270+
3271+// unity-api
3272+#include <unity/shell/application/TopLevelWindowModelInterface.h>
3273+
3274+// local
3275+#include "Window.h"
3276+
3277+Q_DECLARE_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL)
3278+
3279+/*
3280+ This is a copy of TopLevelWindowModel from qtmir (the real deal) with some small changes.
3281+
3282+ Yes, this code duplication sucks
3283+
3284+ IDEA: Move as much as possible of the implementation to TopLevelWindowModelInterface.h
3285+ in unity-api
3286+*/
3287+class TopLevelWindowModel : public unity::shell::application::TopLevelWindowModelInterface
3288+{
3289+ Q_OBJECT
3290+
3291+ /**
3292+ The id to be used on the next entry created
3293+ Useful for tests
3294+ */
3295+ Q_PROPERTY(int nextId READ nextId NOTIFY nextIdChanged)
3296+
3297+public:
3298+ TopLevelWindowModel();
3299+ virtual ~TopLevelWindowModel();
3300+
3301+ // From unity::shell::aplication::TopLevelWindowModelInterface
3302+ unity::shell::application::MirSurfaceInterface* inputMethodSurface() const override;
3303+ unity::shell::application::WindowInterface* focusedWindow() const override;
3304+
3305+ // From QAbstractItemModel
3306+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
3307+ QVariant data(const QModelIndex& index, int role) const override;
3308+
3309+ // Own API
3310+ int nextId() const { return m_nextId; }
3311+
3312+public Q_SLOTS:
3313+ // From unity::shell::aplication::TopLevelWindowModelInterface
3314+ unity::shell::application::MirSurfaceInterface *surfaceAt(int index) const override;
3315+ unity::shell::application::WindowInterface *windowAt(int index) const override;
3316+ unity::shell::application::ApplicationInfoInterface *applicationAt(int index) const override;
3317+ int idAt(int index) const override;
3318+ int indexForId(int id) const override;
3319+ void raiseId(int id) override;
3320+
3321+Q_SIGNALS:
3322+ // Own API
3323+ void nextIdChanged();
3324+
3325+private Q_SLOTS:
3326+ void focusTopMostAvailableWindow();
3327+
3328+private:
3329+ void connectSurfaceManager();
3330+ void setApplicationManager(ApplicationManager*);
3331+ void doRaiseId(int id);
3332+ int generateId();
3333+ int nextFreeId(int candidateId);
3334+ QString toString();
3335+ int indexOf(WindowInterface *window);
3336+ int indexOf(MirSurfaceInterface *surface);
3337+
3338+ void setInputMethodSurface(MirSurface *surface);
3339+ void setFocusedWindow(Window *window);
3340+ void removeInputMethodWindow();
3341+ void removeAt(int index);
3342+
3343+ void addApplication(ApplicationInfo *application);
3344+ void removeApplication(ApplicationInfo *application);
3345+
3346+ void prependPlaceholder(ApplicationInfo *application);
3347+ void prependSurface(MirSurface *surface, ApplicationInfo *application);
3348+ void prependSurfaceHelper(MirSurface *surface, ApplicationInfo *application);
3349+
3350+ void connectSurface(MirSurface *surface);
3351+
3352+ void onStateChangeRequested(Window *window, Mir::State requestedState);
3353+ void onSurfaceDied(MirSurfaceInterface *surface);
3354+ void onSurfaceDestroyed(MirSurfaceInterface *surface);
3355+ void onWindowCloseRequested(Window *window);
3356+
3357+ Window *findWindowWithSurface(MirSurface *surface);
3358+
3359+ Window *findFocusableWindow(int index);
3360+
3361+ void move(int from, int to);
3362+
3363+ struct ModelEntry {
3364+ ModelEntry() {}
3365+ ModelEntry(Window *window,
3366+ ApplicationInfo *application)
3367+ : window(window), application(application) {}
3368+ Window *window{nullptr};
3369+ ApplicationInfo *application{nullptr};
3370+ bool removeOnceSurfaceDestroyed{false};
3371+ };
3372+
3373+ QVector<ModelEntry> m_windowModel;
3374+ Window* m_inputMethodWindow{nullptr};
3375+ Window* m_focusedWindow{nullptr};
3376+ int m_nextId{1};
3377+ // Just something big enough that we don't risk running out of unused id numbers.
3378+ // Not sure if QML int type supports something close to std::numeric_limits<int>::max() and
3379+ // there's no reason to try out its limits.
3380+ static const int m_maxId{1000000};
3381+
3382+ ApplicationManagerInterface* m_applicationManager{nullptr};
3383+
3384+ enum ModelState {
3385+ IdleState,
3386+ InsertingState,
3387+ RemovingState,
3388+ MovingState,
3389+ ResettingState
3390+ };
3391+ ModelState m_modelState{IdleState};
3392+
3393+ bool m_connectedSurfaceManager{false};
3394+};
3395
3396=== added file 'tests/mocks/Unity/Application/Window.cpp'
3397--- tests/mocks/Unity/Application/Window.cpp 1970-01-01 00:00:00 +0000
3398+++ tests/mocks/Unity/Application/Window.cpp 2016-11-09 15:21:35 +0000
3399@@ -0,0 +1,229 @@
3400+/*
3401+ * Copyright (C) 2016 Canonical, Ltd.
3402+ *
3403+ * This program is free software; you can redistribute it and/or modify
3404+ * it under the terms of the GNU General Public License as published by
3405+ * the Free Software Foundation; version 3.
3406+ *
3407+ * This program is distributed in the hope that it will be useful,
3408+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3409+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3410+ * GNU General Public License for more details.
3411+ *
3412+ * You should have received a copy of the GNU General Public License
3413+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3414+ */
3415+
3416+#include "Window.h"
3417+#include "MirSurface.h"
3418+
3419+#include <QQmlEngine>
3420+
3421+#define WINDOW_DEBUG 0
3422+
3423+#if WINDOW_DEBUG
3424+#include <QDebug>
3425+#define DEBUG_MSG(params) qDebug().nospace() << qPrintable(toString()) << "::" << __func__ << " " << params
3426+#else
3427+#define DEBUG_MSG(params) ((void)0)
3428+#endif
3429+
3430+using namespace unity::shell::application;
3431+
3432+Window::Window(int id)
3433+ : WindowInterface(nullptr)
3434+ , m_id(id)
3435+{
3436+ DEBUG_MSG("");
3437+ QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
3438+}
3439+
3440+Window::~Window()
3441+{
3442+ DEBUG_MSG("");
3443+}
3444+
3445+QPoint Window::position() const
3446+{
3447+ return m_position;
3448+}
3449+
3450+QPoint Window::requestedPosition() const
3451+{
3452+ return m_requestedPosition;
3453+}
3454+
3455+void Window::setRequestedPosition(const QPoint &value)
3456+{
3457+ if (value != m_requestedPosition) {
3458+ m_requestedPosition = value;
3459+ Q_EMIT requestedPositionChanged(m_requestedPosition);
3460+ if (m_surface) {
3461+ m_surface->setRequestedPosition(value);
3462+ } else {
3463+ // fake-miral: always comply
3464+ m_position = m_requestedPosition;
3465+ Q_EMIT positionChanged(m_position);
3466+ }
3467+ }
3468+}
3469+
3470+Mir::State Window::state() const
3471+{
3472+ return m_state;
3473+}
3474+
3475+bool Window::focused() const
3476+{
3477+ return m_focused;
3478+}
3479+
3480+bool Window::confinesMousePointer() const
3481+{
3482+ if (m_surface) {
3483+ return m_surface->confinesMousePointer();
3484+ } else {
3485+ return false;
3486+ }
3487+}
3488+
3489+int Window::id() const
3490+{
3491+ return m_id;
3492+}
3493+
3494+MirSurfaceInterface* Window::surface() const
3495+{
3496+ return m_surface;
3497+}
3498+
3499+void Window::requestState(Mir::State state)
3500+{
3501+ Q_EMIT stateRequested(state);
3502+}
3503+
3504+void Window::setState(Mir::State state)
3505+{
3506+ if (m_surface) {
3507+ m_surface->setState(state);
3508+ } else {
3509+ m_state = state;
3510+ Q_EMIT stateChanged(m_state);
3511+ }
3512+}
3513+
3514+void Window::requestFocus()
3515+{
3516+ if (m_surface) {
3517+ m_surface->requestFocus();
3518+ } else {
3519+ Q_EMIT focusRequested();
3520+ }
3521+}
3522+
3523+void Window::close()
3524+{
3525+ if (m_surface) {
3526+ m_surface->close();
3527+ } else {
3528+ Q_EMIT closeRequested();
3529+ }
3530+}
3531+
3532+void Window::setSurface(MirSurface *surface)
3533+{
3534+ if (m_surface) {
3535+ disconnect(m_surface, 0, this, 0);
3536+ }
3537+
3538+ m_surface = surface;
3539+
3540+ if (m_surface) {
3541+ connect(surface, &MirSurfaceInterface::focusRequested, this, [this]() {
3542+ Q_EMIT focusRequested();
3543+ });
3544+
3545+ connect(surface, &MirSurface::closeRequested, this, &Window::closeRequested);
3546+
3547+ connect(surface, &MirSurface::stateRequested, this, [this](Mir::State state) {
3548+ Q_EMIT stateRequested(state);
3549+ });
3550+
3551+ connect(surface, &MirSurfaceInterface::positionChanged, this, [this]() {
3552+ updatePosition();
3553+ });
3554+
3555+ connect(surface, &MirSurfaceInterface::stateChanged, this, [this]() {
3556+ updateState();
3557+ });
3558+
3559+ connect(surface, &MirSurfaceInterface::focusedChanged, this, [this]() {
3560+ updateFocused();
3561+ });
3562+
3563+ // bring it up to speed
3564+ m_surface->setRequestedPosition(m_requestedPosition);
3565+ m_surface->requestState(m_state);
3566+
3567+ // and sync with surface
3568+ updatePosition();
3569+ updateState();
3570+ updateFocused();
3571+ }
3572+
3573+ Q_EMIT surfaceChanged(surface);
3574+}
3575+
3576+void Window::updatePosition()
3577+{
3578+ if (m_surface->position() != m_position) {
3579+ m_position = m_surface->position();
3580+ Q_EMIT positionChanged(m_position);
3581+ }
3582+}
3583+
3584+void Window::updateState()
3585+{
3586+ if (m_surface->state() != m_state) {
3587+ m_state = m_surface->state();
3588+ Q_EMIT stateChanged(m_state);
3589+ }
3590+}
3591+
3592+void Window::updateFocused()
3593+{
3594+ if (m_surface->focused() != m_focused) {
3595+ m_focused = m_surface->focused();
3596+ Q_EMIT focusedChanged(m_focused);
3597+ }
3598+}
3599+
3600+
3601+void Window::setFocused(bool value)
3602+{
3603+ if (value != m_focused) {
3604+ DEBUG_MSG("(" << value << ")");
3605+ m_focused = value;
3606+ Q_EMIT focusedChanged(m_focused);
3607+ if (m_surface) {
3608+ m_surface->setFocused(m_focused);
3609+ }
3610+ } else {
3611+ DEBUG_MSG("(" << value << ") - NOOP");
3612+ }
3613+}
3614+
3615+QString Window::toString() const
3616+{
3617+ if (surface()) {
3618+ return QString("Window[0x%1, id=%2, MirSurface[0x%3,\"%4\"]]")
3619+ .arg((quintptr)this, 0, 16)
3620+ .arg(id())
3621+ .arg((quintptr)surface(), 0, 16)
3622+ .arg(surface()->name());
3623+ } else {
3624+ return QString("Window[0x%1, id=%2, null]")
3625+ .arg((quintptr)this, 0, 16)
3626+ .arg(id());
3627+ }
3628+}
3629
3630=== added file 'tests/mocks/Unity/Application/Window.h'
3631--- tests/mocks/Unity/Application/Window.h 1970-01-01 00:00:00 +0000
3632+++ tests/mocks/Unity/Application/Window.h 2016-11-09 15:21:35 +0000
3633@@ -0,0 +1,69 @@
3634+/*
3635+ * Copyright (C) 2016 Canonical, Ltd.
3636+ *
3637+ * This program is free software; you can redistribute it and/or modify
3638+ * it under the terms of the GNU General Public License as published by
3639+ * the Free Software Foundation; version 3.
3640+ *
3641+ * This program is distributed in the hope that it will be useful,
3642+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3643+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3644+ * GNU General Public License for more details.
3645+ *
3646+ * You should have received a copy of the GNU General Public License
3647+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3648+ */
3649+
3650+#ifndef MOCK_WINDOW_H
3651+#define MOCK_WINDOW_H
3652+
3653+// unity-api
3654+#include <unity/shell/application/WindowInterface.h>
3655+
3656+class MirSurface;
3657+
3658+class Window : public unity::shell::application::WindowInterface
3659+{
3660+ Q_OBJECT
3661+
3662+public:
3663+ Window(int id);
3664+ virtual ~Window();
3665+ QPoint position() const override;
3666+ QPoint requestedPosition() const override;
3667+ void setRequestedPosition(const QPoint &) override;
3668+ Mir::State state() const override;
3669+ bool focused() const override;
3670+ bool confinesMousePointer() const override;
3671+ int id() const override;
3672+ unity::shell::application::MirSurfaceInterface* surface() const override;
3673+
3674+ void setSurface(MirSurface *surface);
3675+ void setFocused(bool value);
3676+ void setState(Mir::State state);
3677+
3678+ QString toString() const;
3679+
3680+Q_SIGNALS:
3681+ void stateRequested(Mir::State);
3682+ void closeRequested();
3683+
3684+public Q_SLOTS:
3685+ void requestState(Mir::State state) override;
3686+ void requestFocus() override;
3687+ void close() override;
3688+
3689+private:
3690+ void updatePosition();
3691+ void updateState();
3692+ void updateFocused();
3693+
3694+ QPoint m_position;
3695+ QPoint m_requestedPosition;
3696+ bool m_focused{false};
3697+ int m_id;
3698+ Mir::State m_state{Mir::RestoredState};
3699+ MirSurface *m_surface{nullptr};
3700+};
3701+
3702+#endif // MOCK_WINDOW_H
3703
3704=== modified file 'tests/mocks/Unity/Application/plugin.cpp'
3705--- tests/mocks/Unity/Application/plugin.cpp 2016-06-30 13:51:32 +0000
3706+++ tests/mocks/Unity/Application/plugin.cpp 2016-11-09 15:21:35 +0000
3707@@ -19,6 +19,8 @@
3708 #include "ApplicationManager.h"
3709 #include "MirSurfaceItem.h"
3710 #include "SurfaceManager.h"
3711+#include "TopLevelWindowModel.h"
3712+#include "Window.h"
3713
3714 // unity-api
3715 #include <unity/shell/application/Mir.h>
3716@@ -32,9 +34,6 @@
3717 void createUnityApplicationSharedSingletons()
3718 {
3719 // they have to be created in a specific order
3720- if (!MirFocusController::instance()) {
3721- new MirFocusController;
3722- }
3723 if (!SurfaceManager::instance()) {
3724 new SurfaceManager;
3725 }
3726@@ -48,16 +47,12 @@
3727
3728 QObject* surfaceManagerSingleton(QQmlEngine*, QJSEngine*)
3729 {
3730- createUnityApplicationSharedSingletons();
3731+ if (!SurfaceManager::instance()) {
3732+ new SurfaceManager;
3733+ }
3734 return SurfaceManager::instance();
3735 }
3736
3737-QObject* mirFocusControllerSingleton(QQmlEngine*, QJSEngine*)
3738-{
3739- createUnityApplicationSharedSingletons();
3740- return MirFocusController::instance();
3741-}
3742-
3743 } // anonymous namespace
3744
3745 void FakeUnityApplicationQmlPlugin::registerTypes(const char *uri)
3746@@ -67,18 +62,19 @@
3747 qRegisterMetaType<unity::shell::application::MirSurfaceListInterface*>("unity::shell::application::MirSurfaceListInterface*");
3748 qRegisterMetaType<Mir::Type>("Mir::Type");
3749 qRegisterMetaType<Mir::State>("Mir::State");
3750+ qRegisterMetaType<unity::shell::application::WindowInterface*>("unity::shell::application::WindowInterface*");
3751
3752 qmlRegisterUncreatableType<unity::shell::application::ApplicationManagerInterface>(uri, 0, 1, "ApplicationManagerInterface", "Abstract interface. Cannot be created in QML");
3753 qmlRegisterUncreatableType<unity::shell::application::ApplicationInfoInterface>(uri, 0, 1, "ApplicationInfoInterface", "Abstract interface. Cannot be created in QML");
3754 qmlRegisterUncreatableType<MirSurface>(uri, 0, 1, "MirSurface", "MirSurface can't be instantiated from QML");
3755 qmlRegisterUncreatableType<unity::shell::application::MirSurfaceInterface>(
3756 uri, 0, 1, "MirSurface", "MirSurface can't be instantiated from QML");
3757- qmlRegisterSingletonType<MirFocusController>(uri, 0, 1, "MirFocusController", mirFocusControllerSingleton);
3758 qmlRegisterType<MirSurfaceItem>(uri, 0, 1, "MirSurfaceItem");
3759 qmlRegisterType<ApplicationInfo>(uri, 0, 1, "ApplicationInfo");
3760
3761 qmlRegisterSingletonType<ApplicationManager>(uri, 0, 1, "ApplicationManager", applicationManagerSingleton);
3762 qmlRegisterSingletonType<SurfaceManager>(uri, 0, 1, "SurfaceManager", surfaceManagerSingleton);
3763+ qmlRegisterType<TopLevelWindowModel>(uri, 0, 1, "TopLevelWindowModel");
3764
3765 qmlRegisterUncreatableType<Mir>(uri, 0, 1, "Mir", "Mir provides enum values, it can't be instantiated");
3766 }
3767
3768=== modified file 'tests/qmltests/Stage/tst_DesktopStage.qml'
3769--- tests/qmltests/Stage/tst_DesktopStage.qml 2016-09-27 15:03:00 +0000
3770+++ tests/qmltests/Stage/tst_DesktopStage.qml 2016-11-09 15:21:35 +0000
3771@@ -20,7 +20,6 @@
3772 import Ubuntu.Components.ListItems 1.3
3773 import Unity.Application 0.1
3774 import Unity.Test 0.1
3775-import WindowManager 0.1
3776 import Utils 0.1
3777
3778 import ".." // For EdgeBarrierControls
3779@@ -57,9 +56,8 @@
3780 }
3781 }
3782
3783- TopLevelSurfaceList {
3784+ TopLevelWindowModel {
3785 id: topSurfaceList
3786- applicationsModel: ApplicationManager
3787 }
3788
3789 Loader {
3790@@ -170,8 +168,9 @@
3791 tryCompare(dashApp, "state", ApplicationInfoInterface.Running);
3792
3793 tryCompare(topSurfaceList, "count", 1);
3794- tryCompareFunction(function(){return topSurfaceList.surfaceAt(0) != null;}, true);
3795- compare(MirFocusController.focusedSurface, topSurfaceList.surfaceAt(0));
3796+ tryCompareFunction(function(){return topSurfaceList.windowAt(0) != null;}, true);
3797+ topSurfaceList.windowAt(0).requestFocus();
3798+ tryCompare(topSurfaceList, "focusedWindow", topSurfaceList.windowAt(0));
3799 }
3800
3801 function cleanup() {
3802@@ -284,13 +283,13 @@
3803 verify(fromAppWindow);
3804 tap(fromAppWindow);
3805 compare(fromDelegate.surface.activeFocus, true);
3806- compare(MirFocusController.focusedSurface, fromDelegate.surface);
3807+ compare(topSurfaceList.focusedWindow, fromDelegate.window);
3808
3809 var toAppWindow = findChild(toDelegate, "appWindow");
3810 verify(toAppWindow);
3811 tap(toAppWindow);
3812 compare(toDelegate.surface.activeFocus, true);
3813- compare(MirFocusController.focusedSurface, toDelegate.surface);
3814+ compare(topSurfaceList.focusedWindow, toDelegate.window);
3815 }
3816
3817 function test_clickingOnWindowChangesFocusedApp_data() {
3818@@ -309,13 +308,13 @@
3819 verify(fromAppWindow);
3820 mouseClick(fromAppWindow);
3821 compare(fromDelegate.surface.activeFocus, true);
3822- compare(MirFocusController.focusedSurface, fromDelegate.surface);
3823+ compare(topSurfaceList.focusedWindow, fromDelegate.window);
3824
3825 var toAppWindow = findChild(toDelegate, "appWindow");
3826 verify(toAppWindow);
3827 mouseClick(toAppWindow);
3828 compare(toDelegate.surface.activeFocus, true);
3829- compare(MirFocusController.focusedSurface, toDelegate.surface);
3830+ compare(topSurfaceList.focusedWindow, toDelegate.window);
3831 }
3832
3833 function test_tappingOnDecorationFocusesApplication_data() {
3834@@ -343,6 +342,13 @@
3835 return findChild(appDelegate, "appWindowDecoration");
3836 }
3837
3838+ function maximizeDelegate(appDelegate) {
3839+ var maximizeButton = findChild(appDelegate, "maximizeWindowButton");
3840+ verify(maximizeButton);
3841+ mouseClick(maximizeButton);
3842+ tryCompare(appDelegate, "visuallyMaximized", true);
3843+ }
3844+
3845 function test_tappingOnDecorationFocusesApplication(data) {
3846 var appDelegates = [];
3847 for (var i = 0; i < data.apps.length; i++) {
3848@@ -512,11 +518,11 @@
3849 apps.forEach(startApplication);
3850 verify(topSurfaceList.count == 3);
3851 keyClick(Qt.Key_D, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+D shortcut to minimize all
3852- tryCompare(MirFocusController, "focusedSurface", null); // verify no surface is focused
3853+ tryCompare(topSurfaceList, "focusedWindow", null); // verify no window is focused
3854
3855 // now try pressing all 4 arrow keys + ctrl + meta
3856 keyClick(Qt.Key_Up | Qt.Key_Down | Qt.Key_Left | Qt.Key_Right, Qt.MetaModifier|Qt.ControlModifier); // smash it!!!
3857- tryCompare(MirFocusController, "focusedSurface", null); // verify still no surface is focused
3858+ tryCompare(topSurfaceList, "focusedWindow", null); // verify still no window is focused
3859 }
3860
3861 function test_minimizeApplicationHidesSurface() {
3862@@ -527,9 +533,12 @@
3863 var decoratedWindow = findDecoratedWindow(dashSurfaceId);
3864 verify(decoratedWindow);
3865
3866- tryCompare(dashSurface, "visible", true);
3867- decoratedWindow.minimizeClicked();
3868- tryCompare(dashSurface, "visible", false);
3869+ var minimizeButton = findChild(decoratedWindow, "minimizeWindowButton");
3870+ verify(minimizeButton);
3871+
3872+ tryCompare(dashSurface, "exposed", true);
3873+ mouseClick(minimizeButton);
3874+ tryCompare(dashSurface, "exposed", false);
3875 }
3876
3877 function test_maximizeApplicationHidesSurfacesBehindIt() {
3878@@ -538,16 +547,16 @@
3879 var gmailDelegate = startApplication("gmail-webapp");
3880
3881 // maximize without raising
3882- dialerDelegate.maximize();
3883+ dialerDelegate.requestMaximize();
3884 tryCompare(dialerDelegate, "visuallyMaximized", true);
3885
3886- tryCompare(dashDelegate.surface, "visible", false);
3887- compare(gmailDelegate.surface.visible, true);
3888+ tryCompare(dashDelegate.surface, "exposed", false);
3889+ compare(gmailDelegate.surface.exposed, true);
3890
3891 // restore without raising
3892- dialerDelegate.restoreFromMaximized();
3893- compare(dashDelegate.surface.visible, true);
3894- compare(gmailDelegate.surface.visible, true);
3895+ dialerDelegate.requestRestore();
3896+ compare(dashDelegate.surface.exposed, true);
3897+ compare(gmailDelegate.surface.exposed, true);
3898 }
3899
3900 function test_applicationsBecomeVisibleWhenOccludingAppRemoved() {
3901@@ -580,26 +589,22 @@
3902 tryCompare(dialerDelegate, "visuallyMaximized", true);
3903 tryCompare(gmailDelegate, "visuallyMaximized", true);
3904
3905- tryCompare(dashApp.surfaceList.get(0), "visible", false);
3906- tryCompare(dialerApp.surfaceList.get(0), "visible", false);
3907- tryCompare(mapApp.surfaceList.get(0), "visible", false);
3908+ tryCompare(dashApp.surfaceList.get(0), "exposed", false);
3909+ tryCompare(dialerApp.surfaceList.get(0), "exposed", false);
3910+ tryCompare(mapApp.surfaceList.get(0), "exposed", false);
3911
3912 ApplicationManager.stopApplication("gmail-webapp");
3913 wait(2000)
3914
3915- tryCompare(mapApp.surfaceList.get(0), "visible", true);
3916- tryCompare(dialerApp.surfaceList.get(0), "visible", true);
3917- tryCompare(dashApp.surfaceList.get(0), "visible", false); // still occluded by maximised dialer
3918+ tryCompare(mapApp.surfaceList.get(0), "exposed", true);
3919+ tryCompare(dialerApp.surfaceList.get(0), "exposed", true);
3920+ tryCompare(dashApp.surfaceList.get(0), "exposed", false); // still occluded by maximised dialer
3921 }
3922
3923 function test_maximisedAppStaysVisibleWhenAppStarts() {
3924 var dashDelegate = startApplication("unity8-dash");
3925
3926- // maximize
3927- var dashMaximizeButton = findChild(dashDelegate, "maximizeWindowButton");
3928- verify(dashMaximizeButton);
3929- mouseClick(dashMaximizeButton);
3930- tryCompare(dashDelegate, "visuallyMaximized", true);
3931+ maximizeDelegate(dashDelegate);
3932
3933 var dialerDelegate = startApplication("dialer-app");
3934 verify(dialerDelegate);
3935@@ -621,25 +626,25 @@
3936 tryCompare(facebookAppDelegate, "visible", true);
3937
3938 // Maximize the topmost and make sure the other two are hidden
3939- facebookAppDelegate.maximize();
3940+ maximizeDelegate(facebookAppDelegate);
3941 tryCompare(dashAppDelegate, "visible", false);
3942 tryCompare(dialerAppDelegate, "visible", false);
3943 tryCompare(facebookAppDelegate, "visible", true);
3944
3945 // Bring dash to front. make sure dash and the maximized facebook are visible, the restored one behind is hidden
3946- dashAppDelegate.focus = true;
3947+ dashAppDelegate.requestFocus();
3948 tryCompare(dashAppDelegate, "visible", true);
3949 tryCompare(dialerAppDelegate, "visible", false);
3950 tryCompare(facebookAppDelegate, "visible", true);
3951
3952 // Now focus the dialer app. all 3 should be visible again
3953- dialerAppDelegate.focus = true;
3954+ dialerAppDelegate.requestFocus();
3955 tryCompare(dashAppDelegate, "visible", true);
3956 tryCompare(dialerAppDelegate, "visible", true);
3957 tryCompare(facebookAppDelegate, "visible", true);
3958
3959 // Maximize the dialer app. The other 2 should hide
3960- dialerAppDelegate.maximize();
3961+ maximizeDelegate(dialerAppDelegate);
3962 tryCompare(dashAppDelegate, "visible", false);
3963 tryCompare(dialerAppDelegate, "visible", true);
3964 tryCompare(facebookAppDelegate, "visible", false);
3965@@ -648,7 +653,7 @@
3966 function test_dropShadow() {
3967 // start an app, maximize it
3968 var facebookAppDelegate = startApplication("facebook-webapp");
3969- facebookAppDelegate.maximize();
3970+ maximizeDelegate(facebookAppDelegate);
3971
3972 // verify the drop shadow is still not visible
3973 verify(PanelState.dropShadow == false);
3974
3975=== modified file 'tests/qmltests/Stage/tst_PhoneStage.qml'
3976--- tests/qmltests/Stage/tst_PhoneStage.qml 2016-10-08 16:45:37 +0000
3977+++ tests/qmltests/Stage/tst_PhoneStage.qml 2016-11-09 15:21:35 +0000
3978@@ -22,7 +22,6 @@
3979 import "../../../qml/Stage"
3980 import Ubuntu.Components 1.3
3981 import Unity.Application 0.1
3982-import WindowManager 0.1
3983
3984 Item {
3985 id: root
3986@@ -41,9 +40,8 @@
3987 orientations: Orientations {}
3988 applicationManager: ApplicationManager
3989 mode: "staged"
3990- topLevelSurfaceList: TopLevelSurfaceList {
3991+ topLevelSurfaceList: TopLevelWindowModel {
3992 id: topLevelSurfaceList
3993- applicationsModel: ApplicationManager
3994 }
3995 }
3996
3997@@ -505,7 +503,7 @@
3998 function test_selectSuspendedAppWithoutSurface() {
3999 compare(topLevelSurfaceList.applicationAt(0).appId, "unity8-dash");
4000 var dashSurfaceId = topLevelSurfaceList.idAt(0);
4001- var dashSurface = topLevelSurfaceList.surfaceAt(0);
4002+ var dashWindow = topLevelSurfaceList.windowAt(0);
4003
4004 var webbrowserSurfaceId = topLevelSurfaceList.nextId;
4005 var webbrowserApp = ApplicationManager.startApplication("webbrowser-app");
4006@@ -513,7 +511,7 @@
4007
4008 switchToSurface(dashSurfaceId);
4009
4010- tryCompare(MirFocusController, "focusedSurface", dashSurface);
4011+ tryCompare(topLevelSurfaceList, "focusedWindow", dashWindow);
4012 tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
4013
4014 compare(webbrowserApp.surfaceList.count, 1);
4015@@ -549,7 +547,7 @@
4016 {
4017 compare(topLevelSurfaceList.applicationAt(0).appId, "unity8-dash");
4018 var dashSurfaceId = topLevelSurfaceList.idAt(0);
4019- var dashSurface = topLevelSurfaceList.surfaceAt(0);
4020+ var dashWindow = topLevelSurfaceList.windowAt(0);
4021
4022 var webbrowserSurfaceId = topLevelSurfaceList.nextId;
4023 var webbrowserApp = ApplicationManager.startApplication("webbrowser-app");
4024@@ -557,7 +555,7 @@
4025
4026 switchToSurface(dashSurfaceId);
4027
4028- tryCompare(MirFocusController, "focusedSurface", dashSurface);
4029+ tryCompare(topLevelSurfaceList, "focusedWindow", dashWindow);
4030 tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
4031
4032 compare(webbrowserApp.surfaceList.count, 1);
4033
4034=== modified file 'tests/qmltests/Stage/tst_TabletStage.qml'
4035--- tests/qmltests/Stage/tst_TabletStage.qml 2016-10-08 16:45:37 +0000
4036+++ tests/qmltests/Stage/tst_TabletStage.qml 2016-11-09 15:21:35 +0000
4037@@ -21,7 +21,6 @@
4038 import Unity.Application 0.1
4039 import Unity.Test 0.1
4040 import Utils 0.1
4041-import WindowManager 0.1
4042
4043 import ".."
4044 import "../../../qml/Stage"
4045@@ -50,9 +49,8 @@
4046 focus: true
4047 mode: "stagedWithSideStage"
4048 applicationManager: ApplicationManager
4049- topLevelSurfaceList: TopLevelSurfaceList {
4050+ topLevelSurfaceList: TopLevelWindowModel {
4051 id: topLevelSurfaceList
4052- applicationsModel: ApplicationManager
4053 }
4054 }
4055
4056@@ -628,7 +626,7 @@
4057 function test_selectSuspendedAppWithoutSurface() {
4058 compare(topSurfaceList.applicationAt(0).appId, "unity8-dash");
4059 var dashSurfaceId = topSurfaceList.idAt(0);
4060- var dashSurface = topSurfaceList.surfaceAt(0);
4061+ var dashWindow = topSurfaceList.windowAt(0);
4062
4063 var webbrowserSurfaceId = topSurfaceList.nextId;
4064 webbrowserCheckBox.checked = true;
4065@@ -637,7 +635,7 @@
4066
4067 switchToSurface(dashSurfaceId);
4068
4069- tryCompare(MirFocusController, "focusedSurface", dashSurface);
4070+ tryCompare(topLevelSurfaceList, "focusedWindow", dashWindow);
4071 tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
4072
4073 compare(webbrowserApp.surfaceList.count, 1);
4074
4075=== modified file 'tests/qmltests/tst_Shell.qml'
4076--- tests/qmltests/tst_Shell.qml 2016-10-03 11:15:27 +0000
4077+++ tests/qmltests/tst_Shell.qml 2016-11-09 15:21:35 +0000
4078@@ -52,6 +52,15 @@
4079 }
4080
4081 property var shell: shellLoader.item ? shellLoader.item : null
4082+ onShellChanged: {
4083+ if (shell) {
4084+ topLevelSurfaceList = testCase.findInvisibleChild(shell, "topLevelSurfaceList");
4085+ } else {
4086+ topLevelSurfaceList = null;
4087+ }
4088+ }
4089+
4090+ property var topLevelSurfaceList: null
4091
4092 Item {
4093 id: shellContainer
4094@@ -318,21 +327,21 @@
4095 id: fullscreeAppCheck
4096
4097 onTriggered: {
4098- if (!MirFocusController.focusedSurface) return;
4099- if (MirFocusController.focusedSurface.state == Mir.FullscreenState) {
4100- MirFocusController.focusedSurface.state = Mir.RestoredState;
4101+ if (!topLevelSurfaceList.focusedSurface) return;
4102+ if (topLevelSurfaceList.focusedSurface.state == Mir.FullscreenState) {
4103+ topLevelSurfaceList.focusedSurface.requestState(Mir.RestoredState);
4104 } else {
4105- MirFocusController.focusedSurface.state = Mir.FullscreenState;
4106+ topLevelSurfaceList.focusedSurface.requestState(Mir.FullscreenState);
4107 }
4108 }
4109
4110 Binding {
4111 target: fullscreeAppCheck
4112- when: MirFocusController.focusedSurface
4113+ when: topLevelSurfaceList && topLevelSurfaceList.focusedSurface
4114 property: "checked"
4115 value: {
4116- if (!MirFocusController.focusedSurface) return false;
4117- return MirFocusController.focusedSurface.state === Mir.FullscreenState
4118+ if (!topLevelSurfaceList || !topLevelSurfaceList.focusedSurface) return false;
4119+ return topLevelSurfaceList.focusedSurface.state === Mir.FullscreenState
4120 }
4121 }
4122 }
4123@@ -346,21 +355,21 @@
4124 id: chromeAppCheck
4125
4126 onTriggered: {
4127- if (!MirFocusController.focusedSurface) return;
4128- if (MirFocusController.focusedSurface.shellChrome == Mir.LowChrome) {
4129- MirFocusController.focusedSurface.setShellChrome(Mir.NormalChrome);
4130+ if (!topLevelSurfaceList.focusedSurface) return;
4131+ if (topLevelSurfaceList.focusedSurface.shellChrome == Mir.LowChrome) {
4132+ topLevelSurfaceList.focusedSurface.setShellChrome(Mir.NormalChrome);
4133 } else {
4134- MirFocusController.focusedSurface.setShellChrome(Mir.LowChrome);
4135+ topLevelSurfaceList.focusedSurface.setShellChrome(Mir.LowChrome);
4136 }
4137 }
4138
4139 Binding {
4140 target: chromeAppCheck
4141- when: MirFocusController.focusedSurface !== null
4142+ when: topLevelSurfaceList && topLevelSurfaceList.focusedSurface !== null
4143 property: "checked"
4144 value: {
4145- if (!MirFocusController.focusedSurface) return false;
4146- MirFocusController.focusedSurface.shellChrome === Mir.LowChrome
4147+ if (!topLevelSurfaceList || !topLevelSurfaceList.focusedSurface) return false;
4148+ topLevelSurfaceList.focusedSurface.shellChrome === Mir.LowChrome
4149 }
4150 }
4151 }
4152@@ -950,21 +959,21 @@
4153 loadShell("phone");
4154 swipeAwayGreeter();
4155 var item = findChild(shell, "inputMethod");
4156- var surface = SurfaceManager.inputMethodSurface;
4157-
4158- surface.setState(Mir.MinimizedState);
4159- tryCompare(item, "visible", false);
4160-
4161- surface.setState(Mir.RestoredState);
4162- tryCompare(item, "visible", true);
4163-
4164- surface.setState(Mir.MinimizedState);
4165- tryCompare(item, "visible", false);
4166-
4167- surface.setState(Mir.MaximizedState);
4168- tryCompare(item, "visible", true);
4169-
4170- surface.setState(Mir.MinimizedState);
4171+ var surface = topLevelSurfaceList.inputMethodSurface;
4172+
4173+ surface.requestState(Mir.MinimizedState);
4174+ tryCompare(item, "visible", false);
4175+
4176+ surface.requestState(Mir.RestoredState);
4177+ tryCompare(item, "visible", true);
4178+
4179+ surface.requestState(Mir.MinimizedState);
4180+ tryCompare(item, "visible", false);
4181+
4182+ surface.requestState(Mir.MaximizedState);
4183+ tryCompare(item, "visible", true);
4184+
4185+ surface.requestState(Mir.MinimizedState);
4186 tryCompare(item, "visible", false);
4187 }
4188
4189@@ -2397,7 +2406,7 @@
4190
4191 compare(launcher.lockedVisible, true);
4192
4193- app.surfaceList.get(0).state = Mir.FullscreenState;
4194+ app.surfaceList.get(0).requestState(Mir.FullscreenState);
4195
4196 tryCompare(launcher, "lockedVisible", false);
4197 }
4198@@ -2584,7 +2593,7 @@
4199 verify(promptSurface);
4200 tryCompare(appSurface, "keymap", promptSurface.keymap);
4201 // ... and that the controller's surface keymap is also the same
4202- tryCompare(MirFocusController.focusedSurface, "keymap", "sk");
4203+ tryCompare(topLevelSurfaceList.focusedSurface, "keymap", "sk");
4204 app.promptSurfaceList.get(0).close();
4205
4206 // switch to next keymap, should go to "cz+qwerty"
4207@@ -2702,7 +2711,6 @@
4208 return [
4209 {tag: "no need to displace", windowHeight: units.gu(10), windowY: units.gu(5), targetDisplacement: units.gu(5), oskEnabled: true},
4210 {tag: "displace to top", windowHeight: units.gu(50), windowY: units.gu(10), targetDisplacement: PanelState.panelHeight, oskEnabled: true},
4211- {tag: "displace to top", windowHeight: units.gu(50), windowY: units.gu(10), targetDisplacement: PanelState.panelHeight, oskEnabled: true},
4212 {tag: "osk not on this screen", windowHeight: units.gu(40), windowY: units.gu(10), targetDisplacement: units.gu(10), oskEnabled: false},
4213 ]
4214 }
4215@@ -2714,24 +2722,24 @@
4216 swipeAwayGreeter();
4217 shell.oskEnabled = data.oskEnabled;
4218
4219- var oldOSKState = SurfaceManager.inputMethodSurface.state;
4220- SurfaceManager.inputMethodSurface.state = Mir.RestoredState;
4221+ var oldOSKState = topLevelSurfaceList.inputMethodSurface.state;
4222+ topLevelSurfaceList.inputMethodSurface.requestState(Mir.RestoredState);
4223 var appRepeater = findChild(shell, "appRepeater");
4224 var dashAppDelegate = appRepeater.itemAt(0);
4225 verify(dashAppDelegate);
4226 dashAppDelegate.requestedHeight = data.windowHeight;
4227 dashAppDelegate.requestedY = data.windowY;
4228- SurfaceManager.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
4229+ topLevelSurfaceList.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
4230 var initialY = dashAppDelegate.y;
4231 print("intial", initialY, "panel", PanelState.panelHeight);
4232 verify(initialY > PanelState.panelHeight);
4233
4234- SurfaceManager.inputMethodSurface.setInputBounds(Qt.rect(0, root.height / 2, root.width, root.height / 2));
4235+ topLevelSurfaceList.inputMethodSurface.setInputBounds(Qt.rect(0, root.height / 2, root.width, root.height / 2));
4236 tryCompare(dashAppDelegate, "y", data.targetDisplacement);
4237
4238- SurfaceManager.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
4239+ topLevelSurfaceList.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
4240 tryCompare(dashAppDelegate, "y", initialY);
4241- SurfaceManager.inputMethodSurface.state = oldOSKState;
4242+ topLevelSurfaceList.inputMethodSurface.requestState(oldOSKState);
4243 }
4244
4245 function test_cursorHidingWithFullscreenApp() {

Subscribers

People subscribed via source and target branches