on xenial help sign in
Move prompt surfaces from MirSurface to Application

prompt surfaces can show up even before an application creates its first surface

commit3a30844bd36556c400c0bd981a463912842d39d9 authorDaniel d'Andrada <daniel.dandrada@canonical.com> dateTue 17 May 2016, 16:25 (10 years ago) parentacf340b
13 files changed+237−367
qml/Stages/ApplicationWindow.qml+36−1 qml/Stages/SurfaceContainer.qml+4−63 tests/mocks/Unity/Application/ApplicationInfo.cpp+9−1 tests/mocks/Unity/Application/ApplicationInfo.h+2 tests/mocks/Unity/Application/MirSurface.cpp+2−22 tests/mocks/Unity/Application/MirSurface.h−5 tests/mocks/Unity/Application/MirSurfaceListModel.cpp+28 tests/mocks/Unity/Application/MirSurfaceListModel.h+5 tests/plugins/Unity/Launcher/launchermodeltest.cpp+1 tests/qmltests/Stages/ApplicationCheckBox.qml+33−3 tests/qmltests/Stages/RecursingChildSessionControl.qml−94 tests/qmltests/Stages/tst_ApplicationWindow.qml+89−23 tests/qmltests/Stages/tst_SurfaceContainer.qml+28−155
qml/Stages/ApplicationWindow.qml+36−1
@@ -120,6 +120,17 @@ FocusScope {
120120 || (application.supportedOrientations & Qt.InvertedLandscapeOrientation))
121121
122122 property bool surfaceOldEnoughToBeResized: false
123+
124+ property var focusedSurface: {
125+ if (promptSurfacesRepeater.count == 0) {
126+ return surfaceContainer;
127+ } else {
128+ return promptSurfacesRepeater.itemAt(promptSurfacesRepeater.count - 1);
129+ }
130+ }
131+ onFocusedSurfaceChanged: {
132+ focusedSurface.focus = true;
133+ }
123134 }
124135
125136 Binding {
@@ -183,7 +194,31 @@ FocusScope {
183194 requestedWidth: root.requestedWidth
184195 requestedHeight: root.requestedHeight
185196 surfaceOrientationAngle: application && application.rotatesWindowContents ? root.surfaceOrientationAngle : 0
186− focus: true
197+ }
198+
199+ Repeater {
200+ id: promptSurfacesRepeater
201+ objectName: "promptSurfacesRepeater"
202+ // show only along with the top-most application surface
203+ model: {
204+ if (!root.application) {
205+ return null;
206+ }
207+
208+ if (root.application.surfaceList.count === 0
209+ || root.surface === root.application.surfaceList.first) {
210+ return root.application.promptSurfaceList;
211+ } else {
212+ return null;
213+ }
214+ }
215+ delegate: SurfaceContainer {
216+ interactive: index == (promptSurfacesRepeater.count - 1) && root.interactive
217+ surface: model.surface
218+ width: root.width
219+ height: root.height
220+ isPromptSurface: true
221+ }
187222 }
188223
189224 // SurfaceContainer size drives ApplicationWindow size
qml/Stages/SurfaceContainer.qml+4−63
@@ -33,7 +33,7 @@ FocusScope {
3333 property bool interactive
3434 property int surfaceOrientationAngle: 0
3535 property bool resizeSurface: true
36− property bool inPromptSession: false
36+ property bool isPromptSurface: false
3737
3838 onSurfaceChanged: {
3939 // Not a binding because animations might remove the surface from the surfaceItem
@@ -56,6 +56,8 @@ FocusScope {
5656 id: surfaceItem
5757 objectName: "surfaceItem"
5858
59+ focus: true
60+
5961 fillMode: MirSurfaceItem.PadOrCrop
6062 consumesInput: true
6163
@@ -122,59 +124,12 @@ FocusScope {
122124 when: root.requestedHeight < 0
123125 }
124126
125− Repeater {
126− id: childSurfacesRepeater
127− objectName: "childSurfacesRepeater"
128− model: root.surface ? root.surface.promptSurfaceList : null
129−
130− delegate: Loader {
131− objectName: "childDelegate" + index
132− anchors.fill: root
133−
134− // Only way to do recursive qml items.
135− source: Qt.resolvedUrl("SurfaceContainer.qml")
136−
137− z: index
138−
139− // Since a Loader is a FocusScope, propagate its focus to the loaded Item
140− Binding {
141− target: item; when: item
142− property: "focus"; value: focus
143− }
144−
145− Binding {
146− target: item; when: item
147− property: "interactive"; value: index == (childSurfacesRepeater.count - 1) && root.interactive
148− }
149−
150− Binding {
151− target: item; when: item
152− property: "surface"; value: model.surface
153− }
154−
155− Binding {
156− target: item; when: item
157− property: "width"; value: root.width
158− }
159−
160− Binding {
161− target: item; when: item
162− property: "height"; value: root.height
163− }
164−
165− Binding {
166− target: item; when: item
167− property: "inPromptSession"; value: true
168− }
169− }
170− }
171−
172127 Loader {
173128 id: animationsLoader
174129 objectName: "animationsLoader"
175130 active: root.surface
176131 source: {
177− if (root.inPromptSession) {
132+ if (root.isPromptSurface) {
178133 return "PromptSurfaceAnimations.qml";
179134 } else {
180135 // Let ApplicationWindow do the animations
@@ -194,18 +149,4 @@ FocusScope {
194149 value: root
195150 }
196151 }
197−
198− QtObject {
199− id: d
200− property var focusedChild: {
201− if (childSurfacesRepeater.count == 0) {
202− return surfaceItem;
203− } else {
204− return childSurfacesRepeater.itemAt(childSurfacesRepeater.count - 1);
205− }
206− }
207− onFocusedChildChanged: {
208− focusedChild.focus = true;
209− }
210− }
211152}
tests/mocks/Unity/Application/ApplicationInfo.cpp+9−1
@@ -25,7 +25,7 @@
2525#include <QQuickView>
2626#include <QQmlComponent>
2727
28−#define APPLICATION_DEBUG 0
28+#define APPLICATION_DEBUG 1
2929
3030#if APPLICATION_DEBUG
3131#define DEBUG_MSG(params) qDebug().nospace() << "Application["<<appId()<<"]::" << __func__ << " " << params
@@ -194,6 +194,10 @@ void ApplicationInfo::setState(State value)
194194 MirSurface *surface = static_cast<MirSurface*>(m_surfaceList.get(i));
195195 surface->setLive(false);
196196 }
197+ for (int i = 0; i < m_promptSurfaceList.count(); ++i) {
198+ auto surface = static_cast<MirSurface*>(m_promptSurfaceList.get(i));
199+ surface->setLive(false);
200+ }
197201 }
198202
199203 m_state = value;
@@ -238,6 +242,10 @@ void ApplicationInfo::setManualSurfaceCreation(bool value)
238242 if (value != m_manualSurfaceCreation) {
239243 m_manualSurfaceCreation = value;
240244 Q_EMIT manualSurfaceCreationChanged(value);
245+
246+ if (m_manualSurfaceCreation && m_surfaceCreationTimer.isActive()) {
247+ m_surfaceCreationTimer.stop();
248+ }
241249 }
242250}
243251
tests/mocks/Unity/Application/ApplicationInfo.h+2
@@ -108,6 +108,7 @@ public:
108108 Q_INVOKABLE void setShellChrome(Mir::ShellChrome shellChrome);
109109
110110 MirSurfaceListInterface* surfaceList() override { return &m_surfaceList; }
111+ MirSurfaceListInterface* promptSurfaceList() override { return &m_promptSurfaceList; }
111112
112113 void setFocused(bool value);
113114
@@ -148,6 +149,7 @@ private:
148149 bool m_exemptFromLifecycle{false};
149150 QSize m_initialSurfaceSize;
150151 MirSurfaceListModel m_surfaceList;
152+ MirSurfaceListModel m_promptSurfaceList;
151153 int m_liveSurfaceCount{0};
152154 QTimer m_surfaceCreationTimer;
153155 QList<MirSurface*> m_closingSurfaces;
tests/mocks/Unity/Application/MirSurface.cpp+2−22
@@ -19,7 +19,7 @@
1919#include <QDebug>
2020#include <QQmlEngine>
2121
22−#define MIRSURFACE_DEBUG 0
22+#define MIRSURFACE_DEBUG 1
2323
2424#if MIRSURFACE_DEBUG
2525#define DEBUG_MSG(params) qDebug().nospace() << "MirSurface[" << (void*)this << "," << m_name << "]::" << __func__ << " " << params
@@ -408,27 +408,6 @@ void MirSurface::close()
408408 }
409409}
410410
411−void MirSurface::createPromptSurface()
412−{
413− QStringList screenshotIds = {"gallery", "map", "facebook", "camera", "browser", "music", "twitter"};
414− int i = rand() % screenshotIds.count();
415−
416− QUrl screenshotUrl = QString("qrc:///Unity/Application/screenshots/%1@12.png")
417− .arg(screenshotIds[i]);
418−
419− auto surface = new MirSurface(QString("prompt foo"),
420− Mir::NormalType,
421− Mir::RestoredState,
422− screenshotUrl);
423−
424− m_promptSurfaceList.appendSurface(surface);
425−}
426−
427−MirSurfaceListInterface* MirSurface::promptSurfaceList()
428−{
429− return &m_promptSurfaceList;
430−}
431−
432411void MirSurface::requestFocus()
433412{
434413 DEBUG_MSG("");
@@ -485,6 +464,7 @@ void MirFocusController::setFocusedSurface(MirSurfaceInterface *surface)
485464
486465 if (m_focusedSurface) {
487466 Q_EMIT m_focusedSurface->focusedChanged(true);
467+ m_focusedSurface->raise();
488468 }
489469}
490470
tests/mocks/Unity/Application/MirSurface.h−5
@@ -107,8 +107,6 @@ public:
107107
108108 Q_INVOKABLE void close() override;
109109
110− unity::shell::application::MirSurfaceListInterface* promptSurfaceList() override;
111−
112110 Q_INVOKABLE void raise() override;
113111
114112 ////
@@ -130,8 +128,6 @@ public:
130128 Q_INVOKABLE void setWidthIncrement(int);
131129 Q_INVOKABLE void setHeightIncrement(int);
132130
133− Q_INVOKABLE void createPromptSurface();
134−
135131 /////
136132 // internal mock stuff
137133
@@ -199,7 +195,6 @@ private:
199195 QSize m_pendingResize;
200196
201197 Mir::ShellChrome m_shellChrome;
202− MirSurfaceListModel m_promptSurfaceList;
203198
204199 struct View {
205200 bool visible;
tests/mocks/Unity/Application/MirSurfaceListModel.cpp+28
@@ -67,6 +67,9 @@ void MirSurfaceListModel::appendSurface(MirSurface *surface)
6767 connectSurface(surface);
6868 endInsertRows();
6969 Q_EMIT countChanged();
70+ if (m_surfaceList.count() == 1) {
71+ Q_EMIT firstChanged();
72+ }
7073}
7174
7275void MirSurfaceListModel::connectSurface(MirSurface *surface)
@@ -83,6 +86,9 @@ void MirSurfaceListModel::removeSurface(MirSurface *surface)
8386 m_surfaceList.removeAt(i);
8487 endRemoveRows();
8588 Q_EMIT countChanged();
89+ if (m_surfaceList.count() == 0) {
90+ Q_EMIT firstChanged();
91+ }
8692 }
8793}
8894
@@ -100,6 +106,10 @@ void MirSurfaceListModel::moveSurface(int from, int to)
100106 m_surfaceList.move(from, to);
101107 endMoveRows();
102108 }
109+
110+ if ((from == 0 || to == 0) && m_surfaceList.count() > 1) {
111+ Q_EMIT firstChanged();
112+ }
103113}
104114
105115MirSurfaceInterface *MirSurfaceListModel::get(int index)
@@ -119,3 +129,21 @@ const MirSurfaceInterface *MirSurfaceListModel::get(int index) const
119129 return nullptr;
120130 }
121131}
132+
133+MirSurfaceInterface *MirSurfaceListModel::createSurface()
134+{
135+ QStringList screenshotIds = {"gallery", "map", "facebook", "camera", "browser", "music", "twitter"};
136+ int i = rand() % screenshotIds.count();
137+
138+ QUrl screenshotUrl = QString("qrc:///Unity/Application/screenshots/%1@12.png")
139+ .arg(screenshotIds[i]);
140+
141+ auto surface = new MirSurface(QString("prompt foo"),
142+ Mir::NormalType,
143+ Mir::RestoredState,
144+ screenshotUrl);
145+
146+ appendSurface(surface);
147+
148+ return surface;
149+}
tests/mocks/Unity/Application/MirSurfaceListModel.h+5
@@ -43,6 +43,11 @@ public:
4343
4444 bool contains(MirSurface *surface) const { return m_surfaceList.contains(surface); }
4545
46+ ////
47+ // API for tests
48+
49+ Q_INVOKABLE unity::shell::application::MirSurfaceInterface *createSurface();
50+
4651private:
4752 void raise(MirSurface *surface);
4853 void moveSurface(int from, int to);
tests/plugins/Unity/Launcher/launchermodeltest.cpp+1
@@ -67,6 +67,7 @@ public:
6767 QSize initialSurfaceSize() const override { return QSize(); }
6868 void setInitialSurfaceSize(const QSize &) override {}
6969 MirSurfaceListInterface* surfaceList() override { return nullptr; }
70+ MirSurfaceListInterface* promptSurfaceList() override { return nullptr; }
7071
7172 // Methods used for mocking (not in the interface)
7273 void setFocused(bool focused) { m_focused = focused; Q_EMIT focusedChanged(focused); }
tests/qmltests/Stages/ApplicationCheckBox.qml+33−3
@@ -44,6 +44,7 @@ Column {
4444 d.bindGuard = false;
4545 }
4646
47+ // Application checkbox row
4748 RowLayout {
4849
4950 QtObject {
@@ -128,17 +129,47 @@ Column {
128129 visible: enabled
129130 Label {
130131 text: "➕"
131− color: "white"
132132 anchors.centerIn: parent
133133 }
134134 }
135135 }
136+
137+ // Prompts controls row
138+ RowLayout {
139+ anchors.left: parent.left
140+ anchors.leftMargin: units.gu(2)
141+ visible: root.checked === true && d.application !== null && root.enabled
142+ spacing: units.gu(1)
143+ Label {
144+ property int promptCount: d.application ? d.application.promptSurfaceList.count : 0
145+ id: promptsLabel
146+ text: promptCount + " prompts"
147+ }
148+ MouseArea {
149+ width: height
150+ height: promptsLabel.height * 0.7
151+ anchors.verticalCenter: parent.verticalCenter
152+ onClicked: d.application.promptSurfaceList.createSurface()
153+ Label { text: "➕"; anchors.centerIn: parent }
154+ }
155+ MouseArea {
156+ width: height
157+ height: promptsLabel.height * 0.7
158+ anchors.verticalCenter: parent.verticalCenter
159+ onClicked: d.application.promptSurfaceList.get(d.application.promptSurfaceList.count - 1).close()
160+ enabled: d.application && d.application.promptSurfaceList.count > 0
161+ Label { text: "➖"; anchors.centerIn: parent; enabled: parent.enabled }
162+ }
163+ }
164+
165+ // Rows of application surfaces
136166 Repeater {
137167 model: d.application ? d.application.surfaceList : null
138168 RowLayout {
169+ anchors.left: parent.left
170+ anchors.leftMargin: units.gu(2)
139171 spacing: units.gu(1)
140172 Label {
141− color: "white"
142173 text: "- " + model.surface.name
143174 }
144175 MouseArea {
@@ -150,7 +181,6 @@ Column {
150181 onClicked: model.surface.setLive(false);
151182 Label {
152183 text: "⛒"
153− color: "white"
154184 anchors.centerIn: parent
155185 }
156186 }
tests/qmltests/Stages/RecursingChildSessionControl.qmldeleted−94
@@ -1,94 +0,0 @@
1−/*
2− * Copyright 2014,2016 Canonical Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.4
18−import QtQuick.Layouts 1.1
19−import Ubuntu.Components 1.3
20−import Unity.Application 0.1
21−
22−ColumnLayout {
23− id: root
24−
25− property var surface
26−
27− Column {
28− id: column
29− Layout.fillWidth: true
30− visible: repeater.count
31− spacing: units.gu(1)
32−
33− Repeater {
34− id: repeater
35− model: root.surface ? root.surface.promptSurfaceList : null
36− delegate: Rectangle {
37− border {
38− color: "black"
39− width: 1
40− }
41− anchors {
42− left: column.left
43− right: column.right
44− leftMargin: units.gu(1)
45− rightMargin: units.gu(1)
46− }
47− height: loader.height
48−
49− Rectangle {
50− anchors.fill: parent
51− color: Qt.rgba(Math.random(), Math.random(), Math.random(), 0.4 )
52− }
53−
54− Loader {
55− id: loader
56− visible: status == Loader.Ready
57− source: Qt.resolvedUrl("RecursingChildSessionControl.qml")
58−
59− anchors {
60− left: parent.left
61− right: parent.right
62− }
63−
64− onLoaded: {
65− item.surface = model.surface;
66− }
67− }
68− }
69− }
70− }
71−
72− ColumnLayout {
73− Layout.fillWidth: true
74− spacing: units.gu(1)
75−
76− RowLayout {
77− id: buttonLayout
78−
79− Button {
80− enabled: root.surface
81− activeFocusOnPress: false
82− text: "Remove"
83− onClicked: { root.surface.close(); }
84− }
85−
86− Button {
87− enabled: root.surface !== null
88− activeFocusOnPress: false
89− text: "Add Prompt Surface"
90− onClicked: { root.surface.createPromptSurface(); }
91− }
92− }
93− }
94−}
tests/qmltests/Stages/tst_ApplicationWindow.qml+89−23
@@ -33,7 +33,6 @@ Rectangle {
3333 Component.onCompleted: {
3434 root.fakeApplication = ApplicationManager.add("gallery-app");
3535 root.fakeApplication.manualSurfaceCreation = true;
36− root.fakeApplication.setState(ApplicationInfoInterface.Starting);
3736 applicationWindowLoader.item.application = root.fakeApplication;
3837 }
3938 property QtObject fakeApplication: null
@@ -78,7 +77,7 @@ Rectangle {
7877 Layout.fillWidth: true
7978
8079 CheckBox {
81− id: surfaceCheckbox;
80+ id: surfaceCheckbox
8281 checked: false;
8382 activeFocusOnPress: false
8483 onCheckedChanged: {
@@ -86,10 +85,8 @@ Rectangle {
8685 return;
8786
8887 if (checked) {
89− applicationWindowLoader.item.surface = SurfaceManager.createSurface("foo",
90− Mir.NormalType, Mir.MaximizedState, root.fakeApplication);
91−
92− root.fakeApplication.setState(ApplicationInfoInterface.Running);
88+ root.fakeApplication.createSurface();
89+ applicationWindowLoader.item.surface = root.fakeApplication.surfaceList.get(0);
9390 } else {
9491 if (applicationWindowLoader.item.surface) {
9592 applicationWindowLoader.item.surface.setLive(false);
@@ -103,22 +100,20 @@ Rectangle {
103100 }
104101 }
105102
106− Rectangle {
107− border {
108− color: "black"
109− width: 1
110− }
111− anchors {
112− left: parent.left
113− right: parent.right
103+ RowLayout {
104+ property var promptSurfaceList: root.fakeApplication ? root.fakeApplication.promptSurfaceList : null
105+ Button {
106+ enabled: parent.promptSurfaceList !== null && parent.promptSurfaceList.count > 0
107+ activeFocusOnPress: false
108+ text: "Remove"
109+ onClicked: { parent.promptSurfaceList.get(parent.promptSurfaceList.count - 1).close(); }
114110 }
115− Layout.preferredHeight: sessionControl.height
116111
117− RecursingChildSessionControl {
118− id: sessionControl
119− anchors { left: parent.left; right: parent.right; }
120−
121− surface: applicationWindowLoader.item ? applicationWindowLoader.item.surface : null
112+ Button {
113+ enabled: parent.promptSurfaceList !== null
114+ activeFocusOnPress: false
115+ text: "Add Prompt Surface"
116+ onClicked: { parent.promptSurfaceList.createSurface(); }
122117 }
123118 }
124119
@@ -244,19 +239,29 @@ Rectangle {
244239 // deleted, so you end up with two instances in memory.
245240 tryCompare(applicationWindowLoader, "itemDestroyed", true);
246241
247− setApplicationState(appStarting);
248242 surfaceCheckbox.checked = false;
249243
250− ApplicationManager.stopApplication("gallery-app");
244+ console.log("killApps() start");
245+ killApps();
246+ console.log("killApps() end");
247+
251248 root.fakeApplication = ApplicationManager.add("gallery-app");
252249 root.fakeApplication.manualSurfaceCreation = true;
253− root.fakeApplication.setState(ApplicationInfoInterface.Starting);
254250
255251 applicationWindowLoader.active = true;
256252
257253 applicationWindowLoader.item.application = root.fakeApplication;
258254 }
259255
256+ function waitUntilSurfaceContainerStopsAnimating(container) {
257+ var animationsLoader = findChild(container, "animationsLoader");
258+ verify(animationsLoader);
259+ tryCompare(animationsLoader, "status", Loader.Ready)
260+
261+ var animation = animationsLoader.item;
262+ waitUntilTransitionsEnd(animation);
263+ }
264+
260265 function test_showSplashUntilAppFullyInit_data() {
261266 return [
262267 {tag: "state=Running then create surface", swapInitOrder: false},
@@ -432,5 +437,66 @@ Rectangle {
432437 applicationWindow.interactive = true;
433438 compare(applicationWindow.surface.activeFocus, true);
434439 }
440+
441+ function test_promptSurfaceDestructionReturnsFocusToPreviousSurface() {
442+ surfaceCheckbox.checked = true;
443+ var promptSurfaces = testCase.findChild(applicationWindow, "promptSurfacesRepeater");
444+ var promptSurfaceList = root.fakeApplication.promptSurfaceList;
445+ compare(promptSurfaces.count, 0);
446+
447+ var i;
448+ // 3 surfaces should cover all edge cases
449+ for (i = 0; i < 3; i++) {
450+ promptSurfaceList.createSurface();
451+ compare(promptSurfaces.count, i+1);
452+ waitUntilSurfaceContainerStopsAnimating(promptSurfaces.itemAt(promptSurfaces.count - 1));
453+ }
454+
455+ for (i = 2; i >= 0; --i) {
456+ var promptSurface = promptSurfaceList.get(i);
457+ compare(promptSurface.activeFocus, true);
458+
459+ promptSurface.close();
460+ tryCompareFunction(function() { return promptSurfaces.count; }, i);
461+
462+ if (i > 0) {
463+ // active focus should have gone to the yongest remaining sibling
464+ var previousPromptSurface = promptSurfaceList.get(i-1);
465+ tryCompare(previousPromptSurface, "activeFocus", true);
466+ } else {
467+ // active focus should have gone to the application surface
468+ tryCompare(applicationWindow.surface, "activeFocus", true);
469+ }
470+ }
471+ }
472+
473+ function test_promptSurfaceAdjustsForParentSize() {
474+ var promptSurfaceList = root.fakeApplication.promptSurfaceList;
475+
476+ promptSurfaceList.createSurface();
477+
478+ var promptSurfaces = testCase.findChild(applicationWindow, "promptSurfacesRepeater");
479+
480+ var delegate = promptSurfaces.itemAt(0);
481+ waitUntilSurfaceContainerStopsAnimating(delegate);
482+
483+ var promptSurfaceContainer = findChild(delegate, "surfaceContainer");
484+
485+ tryCompareFunction(function() { return promptSurfaceContainer.height === applicationWindow.height; }, true);
486+ tryCompareFunction(function() { return promptSurfaceContainer.width === applicationWindow.width; }, true);
487+ tryCompareFunction(function() { return promptSurfaceContainer.x === 0; }, true);
488+ tryCompareFunction(function() { return promptSurfaceContainer.y === 0; }, true);
489+
490+ applicationWindow.anchors.margins = units.gu(2);
491+
492+ tryCompareFunction(function() { return promptSurfaceContainer.height === applicationWindow.height; }, true);
493+ tryCompareFunction(function() { return promptSurfaceContainer.width === applicationWindow.width; }, true);
494+ tryCompareFunction(function() { return promptSurfaceContainer.x === 0; }, true);
495+ tryCompareFunction(function() { return promptSurfaceContainer.y === 0; }, true);
496+
497+ // clean up
498+ delegate.surface.close();
499+ tryCompare(promptSurfaces, "count", 0);
500+ }
435501 }
436502}
tests/qmltests/Stages/tst_SurfaceContainer.qml+28−155
@@ -42,6 +42,7 @@ Rectangle {
4242 anchors.fill: parent
4343 focus: true
4444 interactive: interactiveCheckbox.checked
45+ isPromptSurface: promptCheckbox.checked
4546 Component.onDestruction: {
4647 surfaceContainerLoader.itemDestroyed = true;
4748 }
@@ -75,17 +76,19 @@ Rectangle {
7576 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
7677 spacing: units.gu(1)
7778
78− RowLayout {
79− Layout.fillWidth: true
8079
81− Row {
82− CheckBox {id: fullscreenCheckbox; checked: true; activeFocusOnPress: false }
83− Label { text: "fullscreen" }
84− }
85− Row {
86− CheckBox {id: interactiveCheckbox; checked: true; activeFocusOnPress: false }
87− Label { text: "interactive" }
88− }
80+ Row {
81+ CheckBox {id: fullscreenCheckbox; checked: true; activeFocusOnPress: false }
82+ Label { text: "fullscreen" }
83+ }
84+ Row {
85+ CheckBox {id: interactiveCheckbox; checked: true; activeFocusOnPress: false }
86+ Label { text: "interactive" }
87+ }
88+ Row {
89+ CheckBox {id: promptCheckbox; checked: false; activeFocusOnPress: false
90+ enabled: surfaceContainerLoader.item.surface === null }
91+ Label { text: "isPromptSurface" }
8992 }
9093
9194 RowLayout {
@@ -102,10 +105,9 @@ Rectangle {
102105 if (checked) {
103106 var application = ApplicationManager.add("music-app");
104107 application.manualSurfaceCreation = true;
105− application.setState(ApplicationInfoInterface.Starting);
106108
107− surfaceContainerLoader.item.surface = SurfaceManager.createSurface("foo",
108− Mir.NormalType, Mir.MaximizedState, application.screenshot);
109+ application.createSurface();
110+ surfaceContainerLoader.item.surface = application.surfaceList.get(0);
109111
110112 application.setState(ApplicationInfoInterface.Running);
111113 } else {
@@ -123,22 +125,11 @@ Rectangle {
123125 }
124126 }
125127
126− Rectangle {
127− border {
128− color: "black"
129− width: 1
130− }
131− anchors {
132− left: parent.left
133− right: parent.right
134− }
135− Layout.preferredHeight: surfaceChildrenControl.height
136−
137− RecursingChildSessionControl {
138− id: surfaceChildrenControl
139− anchors { left: parent.left; right: parent.right; }
140−
141− surface: surfaceContainerLoader.item ? surfaceContainerLoader.item.surface : null
128+ Button {
129+ text: "Reload"
130+ enabled: !surfaceCheckbox.checked
131+ onClicked: {
132+ testCase.cleanup();
142133 }
143134 }
144135 }
@@ -164,6 +155,7 @@ Rectangle {
164155
165156 surfaceCheckbox.checked = false;
166157 interactiveCheckbox.checked = true;
158+ promptCheckbox.checked = false;
167159
168160 surfaceContainerLoader.active = true;
169161 tryCompare(surfaceContainerLoader, "status", Loader.Ready);
@@ -171,121 +163,6 @@ Rectangle {
171163
172164 when: windowShown
173165
174− function test_addChildSession_data() {
175− return [ { tag: "count=1", count: 1 },
176− { tag: "count=4", count: 4 } ];
177− }
178−
179− function test_addChildSession(data) {
180− surfaceCheckbox.checked = true;
181− var childSurfaces = testCase.findChild(surfaceContainer, "childSurfacesRepeater");
182− compare(childSurfaces.count, 0);
183−
184− var i;
185− for (i = 0; i < data.count; i++) {
186− surfaceContainer.surface.createPromptSurface();
187− compare(childSurfaces.count, i+1);
188− }
189−
190− for (i = data.count-1; i >= 0; i--) {
191− {
192− var childPromptSurface = surfaceContainer.surface.promptSurfaceList.get(i);
193− childPromptSurface.close();
194− }
195− tryCompareFunction(function() { return childSurfaces.count; }, i);
196− }
197− }
198−
199− function test_childSessionDestructionReturnsFocusToSiblingOrParent() {
200− surfaceCheckbox.checked = true;
201− var childSurfaces = testCase.findChild(surfaceContainer, "childSurfacesRepeater");
202− compare(childSurfaces.count, 0);
203−
204− var i;
205− // 3 surfaces should cover all edge cases
206− for (i = 0; i < 3; i++) {
207− surfaceContainer.surface.createPromptSurface();
208− compare(childSurfaces.count, i+1);
209− }
210−
211− for (i = 2; i >= 0; --i) {
212− var childPromptSurface = surfaceContainer.surface.promptSurfaceList.get(i);
213− compare(childPromptSurface.activeFocus, true);
214−
215− childPromptSurface.close();
216− tryCompareFunction(function() { return childSurfaces.count; }, i);
217−
218− if (i > 0) {
219− // active focus should have gone to the yongest remaining sibling
220− var previousSiblingSurface = surfaceContainer.surface.promptSurfaceList.get(i-1);
221− tryCompare(previousSiblingSurface, "activeFocus", true);
222− } else {
223− // active focus should have gone to the parent surface
224− tryCompare(surfaceContainer.surface, "activeFocus", true);
225− }
226− }
227− }
228−
229− function test_nestedChildSessions_data() {
230− return [ { tag: "depth=2", depth: 2 },
231− { tag: "depth=8", depth: 8 }
232− ];
233− }
234− function test_nestedChildSessions(data) {
235− surfaceCheckbox.checked = true;
236−
237− var i;
238− var container = surfaceContainer;
239− var surface = container.surface;
240− var surfaces = [surface];
241− var parent_childSurfaces = [null];
242− for (i = 0; i < data.depth; i++) {
243− surface.createPromptSurface();
244− var childSurfaces = testCase.findChild(container, "childSurfacesRepeater");
245− compare(childSurfaces.count, 1);
246−
247− var childDelegate = childSurfaces.itemAt(0);
248− container = findChild(childDelegate, "surfaceContainer");
249− {
250− var animationsLoader = findChild(container, "animationsLoader");
251− tryCompare(animationsLoader, "status", Loader.Ready);
252− waitUntilTransitionsEnd(animationsLoader.item);
253− }
254− surface = container.surface;
255−
256− surfaces.push(surface);
257− parent_childSurfaces.push(childSurfaces);
258− }
259−
260− for (i = surfaces.length-1; i >= 0; i--) {
261− surfaces[i].close();
262− if (parent_childSurfaces[i]) {
263− tryCompareFunction(function() { return parent_childSurfaces[i].count; }, 0);
264− }
265− }
266− }
267−
268− function test_childrenAdjustForParentSize() {
269− surfaceCheckbox.checked = true;
270−
271− surfaceContainer.surface.createPromptSurface();
272−
273− var delegate = findChild(surfaceContainer, "childDelegate0");
274− var childContainer = findChild(delegate, "surfaceContainer");
275−
276− tryCompareFunction(function() { return childContainer.height === surfaceContainer.height; }, true);
277− tryCompareFunction(function() { return childContainer.width === surfaceContainer.width; }, true);
278− tryCompareFunction(function() { return childContainer.x === 0; }, true);
279− tryCompareFunction(function() { return childContainer.y === 0; }, true);
280−
281− surfaceContainer.anchors.margins = units.gu(2);
282−
283− tryCompareFunction(function() { return childContainer.height === surfaceContainer.height; }, true);
284− tryCompareFunction(function() { return childContainer.width === surfaceContainer.width; }, true);
285− tryCompareFunction(function() { return childContainer.x === 0; }, true);
286− tryCompareFunction(function() { return childContainer.y === 0; }, true);
287− }
288−
289166 function isContainerAnimating(container) {
290167 var animationsLoader = findChild(container, "animationsLoader");
291168 if (!animationsLoader || animationsLoader.status != Loader.Ready) {
@@ -303,25 +180,21 @@ Rectangle {
303180 return false;
304181 }
305182
306− function test_childrenAnimate() {
183+ function test_promptSurfaceAnimates() {
184+ promptCheckbox.checked = true;
307185 surfaceCheckbox.checked = true;
308186
309− surfaceContainer.surface.createPromptSurface();
310−
311− var delegate = findChild(surfaceContainer, "childDelegate0");
312− var childContainer = findChild(delegate, "surfaceContainer");
313−
314187 // wait for animation to begin
315− tryCompareFunction(function() { return isContainerAnimating(childContainer); }, true);
188+ tryCompareFunction(function() { return isContainerAnimating(surfaceContainer); }, true);
316189 // wait for animation to end
317− tryCompareFunction(function() { return isContainerAnimating(childContainer); }, false);
190+ tryCompareFunction(function() { return isContainerAnimating(surfaceContainer); }, false);
318191
319− surfaceContainer.surface.promptSurfaceList.get(0).close();
192+ surfaceContainer.surface.close();
320193
321194 // wait for animation to begin
322− tryCompareFunction(function() { return isContainerAnimating(childContainer); }, true);
195+ tryCompareFunction(function() { return isContainerAnimating(surfaceContainer); }, true);
323196 // wait for animation to end
324− tryCompareFunction(function() { return isContainerAnimating(childContainer); }, false);
197+ tryCompareFunction(function() { return isContainerAnimating(surfaceContainer); }, false);
325198 }
326199
327200 function test_surfaceItemGetsNoTouchesWhenContainerNotInteractive() {