on xenial help sign in
Move prompt surfaces from MirSurface to Application
3a30844Daniel d'Andrada
qml/Stages/ApplicationWindow.qml+36−1
@@ -120,6 +120,17 @@ FocusScope {
120 || (application.supportedOrientations & Qt.InvertedLandscapeOrientation))
120 || (application.supportedOrientations & Qt.InvertedLandscapeOrientation))
121
121
122 property bool surfaceOldEnoughToBeResized: false
122 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+ }
123 }
134 }
124
135
125 Binding {
136 Binding {
@@ -183,7 +194,31 @@ FocusScope {
183 requestedWidth: root.requestedWidth
194 requestedWidth: root.requestedWidth
184 requestedHeight: root.requestedHeight
195 requestedHeight: root.requestedHeight
185 surfaceOrientationAngle: application && application.rotatesWindowContents ? root.surfaceOrientationAngle : 0
196 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+ }
187 }
222 }
188
223
189 // SurfaceContainer size drives ApplicationWindow size
224 // SurfaceContainer size drives ApplicationWindow size
qml/Stages/SurfaceContainer.qml+4−63
@@ -33,7 +33,7 @@ FocusScope {
33 property bool interactive
33 property bool interactive
34 property int surfaceOrientationAngle: 0
34 property int surfaceOrientationAngle: 0
35 property bool resizeSurface: true
35 property bool resizeSurface: true
36− property bool inPromptSession: false
36+ property bool isPromptSurface: false
37
37
38 onSurfaceChanged: {
38 onSurfaceChanged: {
39 // Not a binding because animations might remove the surface from the surfaceItem
39 // Not a binding because animations might remove the surface from the surfaceItem
@@ -56,6 +56,8 @@ FocusScope {
56 id: surfaceItem
56 id: surfaceItem
57 objectName: "surfaceItem"
57 objectName: "surfaceItem"
58
58
59+ focus: true
60+
59 fillMode: MirSurfaceItem.PadOrCrop
61 fillMode: MirSurfaceItem.PadOrCrop
60 consumesInput: true
62 consumesInput: true
61
63
@@ -122,59 +124,12 @@ FocusScope {
122 when: root.requestedHeight < 0
124 when: root.requestedHeight < 0
123 }
125 }
124
126
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−
172 Loader {
127 Loader {
173 id: animationsLoader
128 id: animationsLoader
174 objectName: "animationsLoader"
129 objectName: "animationsLoader"
175 active: root.surface
130 active: root.surface
176 source: {
131 source: {
177− if (root.inPromptSession) {
132+ if (root.isPromptSurface) {
178 return "PromptSurfaceAnimations.qml";
133 return "PromptSurfaceAnimations.qml";
179 } else {
134 } else {
180 // Let ApplicationWindow do the animations
135 // Let ApplicationWindow do the animations
@@ -194,18 +149,4 @@ FocusScope {
194 value: root
149 value: root
195 }
150 }
196 }
151 }
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− }
211}
152}
tests/mocks/Unity/Application/ApplicationInfo.cpp+9−1
@@ -25,7 +25,7 @@
25#include <QQuickView>
25#include <QQuickView>
26#include <QQmlComponent>
26#include <QQmlComponent>
27
27
28−#define APPLICATION_DEBUG 0
28+#define APPLICATION_DEBUG 1
29
29
30#if APPLICATION_DEBUG
30#if APPLICATION_DEBUG
31#define DEBUG_MSG(params) qDebug().nospace() << "Application["<<appId()<<"]::" << __func__ << " " << params
31#define DEBUG_MSG(params) qDebug().nospace() << "Application["<<appId()<<"]::" << __func__ << " " << params
@@ -194,6 +194,10 @@ void ApplicationInfo::setState(State value)
194 MirSurface *surface = static_cast<MirSurface*>(m_surfaceList.get(i));
194 MirSurface *surface = static_cast<MirSurface*>(m_surfaceList.get(i));
195 surface->setLive(false);
195 surface->setLive(false);
196 }
196 }
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+ }
197 }
201 }
198
202
199 m_state = value;
203 m_state = value;
@@ -238,6 +242,10 @@ void ApplicationInfo::setManualSurfaceCreation(bool value)
238 if (value != m_manualSurfaceCreation) {
242 if (value != m_manualSurfaceCreation) {
239 m_manualSurfaceCreation = value;
243 m_manualSurfaceCreation = value;
240 Q_EMIT manualSurfaceCreationChanged(value);
244 Q_EMIT manualSurfaceCreationChanged(value);
245+
246+ if (m_manualSurfaceCreation && m_surfaceCreationTimer.isActive()) {
247+ m_surfaceCreationTimer.stop();
248+ }
241 }
249 }
242}
250}
243
251
tests/mocks/Unity/Application/ApplicationInfo.h+2
@@ -108,6 +108,7 @@ public:
108 Q_INVOKABLE void setShellChrome(Mir::ShellChrome shellChrome);
108 Q_INVOKABLE void setShellChrome(Mir::ShellChrome shellChrome);
109
109
110 MirSurfaceListInterface* surfaceList() override { return &m_surfaceList; }
110 MirSurfaceListInterface* surfaceList() override { return &m_surfaceList; }
111+ MirSurfaceListInterface* promptSurfaceList() override { return &m_promptSurfaceList; }
111
112
112 void setFocused(bool value);
113 void setFocused(bool value);
113
114
@@ -148,6 +149,7 @@ private:
148 bool m_exemptFromLifecycle{false};
149 bool m_exemptFromLifecycle{false};
149 QSize m_initialSurfaceSize;
150 QSize m_initialSurfaceSize;
150 MirSurfaceListModel m_surfaceList;
151 MirSurfaceListModel m_surfaceList;
152+ MirSurfaceListModel m_promptSurfaceList;
151 int m_liveSurfaceCount{0};
153 int m_liveSurfaceCount{0};
152 QTimer m_surfaceCreationTimer;
154 QTimer m_surfaceCreationTimer;
153 QList<MirSurface*> m_closingSurfaces;
155 QList<MirSurface*> m_closingSurfaces;
tests/mocks/Unity/Application/MirSurface.cpp+2−22
@@ -19,7 +19,7 @@
19#include <QDebug>
19#include <QDebug>
20#include <QQmlEngine>
20#include <QQmlEngine>
21
21
22−#define MIRSURFACE_DEBUG 0
22+#define MIRSURFACE_DEBUG 1
23
23
24#if MIRSURFACE_DEBUG
24#if MIRSURFACE_DEBUG
25#define DEBUG_MSG(params) qDebug().nospace() << "MirSurface[" << (void*)this << "," << m_name << "]::" << __func__ << " " << params
25#define DEBUG_MSG(params) qDebug().nospace() << "MirSurface[" << (void*)this << "," << m_name << "]::" << __func__ << " " << params
@@ -408,27 +408,6 @@ void MirSurface::close()
408 }
408 }
409}
409}
410
410
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−
432void MirSurface::requestFocus()
411void MirSurface::requestFocus()
433{
412{
434 DEBUG_MSG("");
413 DEBUG_MSG("");
@@ -485,6 +464,7 @@ void MirFocusController::setFocusedSurface(MirSurfaceInterface *surface)
485
464
486 if (m_focusedSurface) {
465 if (m_focusedSurface) {
487 Q_EMIT m_focusedSurface->focusedChanged(true);
466 Q_EMIT m_focusedSurface->focusedChanged(true);
467+ m_focusedSurface->raise();
488 }
468 }
489}
469}
490
470
tests/mocks/Unity/Application/MirSurface.h−5
@@ -107,8 +107,6 @@ public:
107
107
108 Q_INVOKABLE void close() override;
108 Q_INVOKABLE void close() override;
109
109
110− unity::shell::application::MirSurfaceListInterface* promptSurfaceList() override;
111−
112 Q_INVOKABLE void raise() override;
110 Q_INVOKABLE void raise() override;
113
111
114 ////
112 ////
@@ -130,8 +128,6 @@ public:
130 Q_INVOKABLE void setWidthIncrement(int);
128 Q_INVOKABLE void setWidthIncrement(int);
131 Q_INVOKABLE void setHeightIncrement(int);
129 Q_INVOKABLE void setHeightIncrement(int);
132
130
133− Q_INVOKABLE void createPromptSurface();
134−
135 /////
131 /////
136 // internal mock stuff
132 // internal mock stuff
137
133
@@ -199,7 +195,6 @@ private:
199 QSize m_pendingResize;
195 QSize m_pendingResize;
200
196
201 Mir::ShellChrome m_shellChrome;
197 Mir::ShellChrome m_shellChrome;
202− MirSurfaceListModel m_promptSurfaceList;
203
198
204 struct View {
199 struct View {
205 bool visible;
200 bool visible;
tests/mocks/Unity/Application/MirSurfaceListModel.cpp+28
@@ -67,6 +67,9 @@ void MirSurfaceListModel::appendSurface(MirSurface *surface)
67 connectSurface(surface);
67 connectSurface(surface);
68 endInsertRows();
68 endInsertRows();
69 Q_EMIT countChanged();
69 Q_EMIT countChanged();
70+ if (m_surfaceList.count() == 1) {
71+ Q_EMIT firstChanged();
72+ }
70}
73}
71
74
72void MirSurfaceListModel::connectSurface(MirSurface *surface)
75void MirSurfaceListModel::connectSurface(MirSurface *surface)
@@ -83,6 +86,9 @@ void MirSurfaceListModel::removeSurface(MirSurface *surface)
83 m_surfaceList.removeAt(i);
86 m_surfaceList.removeAt(i);
84 endRemoveRows();
87 endRemoveRows();
85 Q_EMIT countChanged();
88 Q_EMIT countChanged();
89+ if (m_surfaceList.count() == 0) {
90+ Q_EMIT firstChanged();
91+ }
86 }
92 }
87}
93}
88
94
@@ -100,6 +106,10 @@ void MirSurfaceListModel::moveSurface(int from, int to)
100 m_surfaceList.move(from, to);
106 m_surfaceList.move(from, to);
101 endMoveRows();
107 endMoveRows();
102 }
108 }
109+
110+ if ((from == 0 || to == 0) && m_surfaceList.count() > 1) {
111+ Q_EMIT firstChanged();
112+ }
103}
113}
104
114
105MirSurfaceInterface *MirSurfaceListModel::get(int index)
115MirSurfaceInterface *MirSurfaceListModel::get(int index)
@@ -119,3 +129,21 @@ const MirSurfaceInterface *MirSurfaceListModel::get(int index) const
119 return nullptr;
129 return nullptr;
120 }
130 }
121}
131}
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:
43
43
44 bool contains(MirSurface *surface) const { return m_surfaceList.contains(surface); }
44 bool contains(MirSurface *surface) const { return m_surfaceList.contains(surface); }
45
45
46+ ////
47+ // API for tests
48+
49+ Q_INVOKABLE unity::shell::application::MirSurfaceInterface *createSurface();
50+
46private:
51private:
47 void raise(MirSurface *surface);
52 void raise(MirSurface *surface);
48 void moveSurface(int from, int to);
53 void moveSurface(int from, int to);
tests/plugins/Unity/Launcher/launchermodeltest.cpp+1
@@ -67,6 +67,7 @@ public:
67 QSize initialSurfaceSize() const override { return QSize(); }
67 QSize initialSurfaceSize() const override { return QSize(); }
68 void setInitialSurfaceSize(const QSize &) override {}
68 void setInitialSurfaceSize(const QSize &) override {}
69 MirSurfaceListInterface* surfaceList() override { return nullptr; }
69 MirSurfaceListInterface* surfaceList() override { return nullptr; }
70+ MirSurfaceListInterface* promptSurfaceList() override { return nullptr; }
70
71
71 // Methods used for mocking (not in the interface)
72 // Methods used for mocking (not in the interface)
72 void setFocused(bool focused) { m_focused = focused; Q_EMIT focusedChanged(focused); }
73 void setFocused(bool focused) { m_focused = focused; Q_EMIT focusedChanged(focused); }
tests/qmltests/Stages/ApplicationCheckBox.qml+33−3
@@ -44,6 +44,7 @@ Column {
44 d.bindGuard = false;
44 d.bindGuard = false;
45 }
45 }
46
46
47+ // Application checkbox row
47 RowLayout {
48 RowLayout {
48
49
49 QtObject {
50 QtObject {
@@ -128,17 +129,47 @@ Column {
128 visible: enabled
129 visible: enabled
129 Label {
130 Label {
130 text: "➕"
131 text: "➕"
131− color: "white"
132 anchors.centerIn: parent
132 anchors.centerIn: parent
133 }
133 }
134 }
134 }
135 }
135 }
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
136 Repeater {
166 Repeater {
137 model: d.application ? d.application.surfaceList : null
167 model: d.application ? d.application.surfaceList : null
138 RowLayout {
168 RowLayout {
169+ anchors.left: parent.left
170+ anchors.leftMargin: units.gu(2)
139 spacing: units.gu(1)
171 spacing: units.gu(1)
140 Label {
172 Label {
141− color: "white"
142 text: "- " + model.surface.name
173 text: "- " + model.surface.name
143 }
174 }
144 MouseArea {
175 MouseArea {
@@ -150,7 +181,6 @@ Column {
150 onClicked: model.surface.setLive(false);
181 onClicked: model.surface.setLive(false);
151 Label {
182 Label {
152 text: "⛒"
183 text: "⛒"
153− color: "white"
154 anchors.centerIn: parent
184 anchors.centerIn: parent
155 }
185 }
156 }
186 }
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 {
33 Component.onCompleted: {
33 Component.onCompleted: {
34 root.fakeApplication = ApplicationManager.add("gallery-app");
34 root.fakeApplication = ApplicationManager.add("gallery-app");
35 root.fakeApplication.manualSurfaceCreation = true;
35 root.fakeApplication.manualSurfaceCreation = true;
36− root.fakeApplication.setState(ApplicationInfoInterface.Starting);
37 applicationWindowLoader.item.application = root.fakeApplication;
36 applicationWindowLoader.item.application = root.fakeApplication;
38 }
37 }
39 property QtObject fakeApplication: null
38 property QtObject fakeApplication: null
@@ -78,7 +77,7 @@ Rectangle {
78 Layout.fillWidth: true
77 Layout.fillWidth: true
79
78
80 CheckBox {
79 CheckBox {
81− id: surfaceCheckbox;
80+ id: surfaceCheckbox
82 checked: false;
81 checked: false;
83 activeFocusOnPress: false
82 activeFocusOnPress: false
84 onCheckedChanged: {
83 onCheckedChanged: {
@@ -86,10 +85,8 @@ Rectangle {
86 return;
85 return;
87
86
88 if (checked) {
87 if (checked) {
88+ root.fakeApplication.createSurface();
89− applicationWindowLoader.item.surface = SurfaceManager.createSurface("foo",
89+ applicationWindowLoader.item.surface = root.fakeApplication.surfaceList.get(0);
90− Mir.NormalType, Mir.MaximizedState, root.fakeApplication);
91−
92− root.fakeApplication.setState(ApplicationInfoInterface.Running);
93 } else {
90 } else {
94 if (applicationWindowLoader.item.surface) {
91 if (applicationWindowLoader.item.surface) {
95 applicationWindowLoader.item.surface.setLive(false);
92 applicationWindowLoader.item.surface.setLive(false);
@@ -103,22 +100,20 @@ Rectangle {
103 }
100 }
104 }
101 }
105
102
106− Rectangle {
103+ RowLayout {
107− border {
104+ property var promptSurfaceList: root.fakeApplication ? root.fakeApplication.promptSurfaceList : null
108− color: "black"
105+ Button {
109− width: 1
106+ enabled: parent.promptSurfaceList !== null && parent.promptSurfaceList.count > 0
110− }
107+ activeFocusOnPress: false
111− anchors {
108+ text: "Remove"
112− left: parent.left
109+ onClicked: { parent.promptSurfaceList.get(parent.promptSurfaceList.count - 1).close(); }
113− right: parent.right
114 }
110 }
115− Layout.preferredHeight: sessionControl.height
116
111
117− RecursingChildSessionControl {
112+ Button {
118− id: sessionControl
113+ enabled: parent.promptSurfaceList !== null
119− anchors { left: parent.left; right: parent.right; }
114+ activeFocusOnPress: false
120−
115+ text: "Add Prompt Surface"
121− surface: applicationWindowLoader.item ? applicationWindowLoader.item.surface : null
116+ onClicked: { parent.promptSurfaceList.createSurface(); }
122 }
117 }
123 }
118 }
124
119
@@ -244,19 +239,29 @@ Rectangle {
244 // deleted, so you end up with two instances in memory.
239 // deleted, so you end up with two instances in memory.
245 tryCompare(applicationWindowLoader, "itemDestroyed", true);
240 tryCompare(applicationWindowLoader, "itemDestroyed", true);
246
241
247− setApplicationState(appStarting);
248 surfaceCheckbox.checked = false;
242 surfaceCheckbox.checked = false;
249
243
250− ApplicationManager.stopApplication("gallery-app");
244+ console.log("killApps() start");
245+ killApps();
246+ console.log("killApps() end");
247+
251 root.fakeApplication = ApplicationManager.add("gallery-app");
248 root.fakeApplication = ApplicationManager.add("gallery-app");
252 root.fakeApplication.manualSurfaceCreation = true;
249 root.fakeApplication.manualSurfaceCreation = true;
253− root.fakeApplication.setState(ApplicationInfoInterface.Starting);
254
250
255 applicationWindowLoader.active = true;
251 applicationWindowLoader.active = true;
256
252
257 applicationWindowLoader.item.application = root.fakeApplication;
253 applicationWindowLoader.item.application = root.fakeApplication;
258 }
254 }
259
255
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+
260 function test_showSplashUntilAppFullyInit_data() {
265 function test_showSplashUntilAppFullyInit_data() {
261 return [
266 return [
262 {tag: "state=Running then create surface", swapInitOrder: false},
267 {tag: "state=Running then create surface", swapInitOrder: false},
@@ -432,5 +437,66 @@ Rectangle {
432 applicationWindow.interactive = true;
437 applicationWindow.interactive = true;
433 compare(applicationWindow.surface.activeFocus, true);
438 compare(applicationWindow.surface.activeFocus, true);
434 }
439 }
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+ }
435 }
501 }
436}
502}
tests/qmltests/Stages/tst_SurfaceContainer.qml+28−155
@@ -42,6 +42,7 @@ Rectangle {
42 anchors.fill: parent
42 anchors.fill: parent
43 focus: true
43 focus: true
44 interactive: interactiveCheckbox.checked
44 interactive: interactiveCheckbox.checked
45+ isPromptSurface: promptCheckbox.checked
45 Component.onDestruction: {
46 Component.onDestruction: {
46 surfaceContainerLoader.itemDestroyed = true;
47 surfaceContainerLoader.itemDestroyed = true;
47 }
48 }
@@ -75,17 +76,19 @@ Rectangle {
75 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
76 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
76 spacing: units.gu(1)
77 spacing: units.gu(1)
77
78
78− RowLayout {
79− Layout.fillWidth: true
80
79
81− Row {
80+ Row {
82− CheckBox {id: fullscreenCheckbox; checked: true; activeFocusOnPress: false }
81+ CheckBox {id: fullscreenCheckbox; checked: true; activeFocusOnPress: false }
83− Label { text: "fullscreen" }
82+ Label { text: "fullscreen" }
84− }
83+ }
85− Row {
84+ Row {
86− CheckBox {id: interactiveCheckbox; checked: true; activeFocusOnPress: false }
85+ CheckBox {id: interactiveCheckbox; checked: true; activeFocusOnPress: false }
87− Label { text: "interactive" }
86+ Label { text: "interactive" }
88− }
87+ }
88+ Row {
89+ CheckBox {id: promptCheckbox; checked: false; activeFocusOnPress: false
90+ enabled: surfaceContainerLoader.item.surface === null }
91+ Label { text: "isPromptSurface" }
89 }
92 }
90
93
91 RowLayout {
94 RowLayout {
@@ -102,10 +105,9 @@ Rectangle {
102 if (checked) {
105 if (checked) {
103 var application = ApplicationManager.add("music-app");
106 var application = ApplicationManager.add("music-app");
104 application.manualSurfaceCreation = true;
107 application.manualSurfaceCreation = true;
105− application.setState(ApplicationInfoInterface.Starting);
106
108
109+ application.createSurface();
107− surfaceContainerLoader.item.surface = SurfaceManager.createSurface("foo",
110+ surfaceContainerLoader.item.surface = application.surfaceList.get(0);
108− Mir.NormalType, Mir.MaximizedState, application.screenshot);
109
111
110 application.setState(ApplicationInfoInterface.Running);
112 application.setState(ApplicationInfoInterface.Running);
111 } else {
113 } else {
@@ -123,22 +125,11 @@ Rectangle {
123 }
125 }
124 }
126 }
125
127
126− Rectangle {
128+ Button {
127− border {
129+ text: "Reload"
128− color: "black"
130+ enabled: !surfaceCheckbox.checked
129− width: 1
131+ onClicked: {
130− }
132+ testCase.cleanup();
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
142 }
133 }
143 }
134 }
144 }
135 }
@@ -164,6 +155,7 @@ Rectangle {
164
155
165 surfaceCheckbox.checked = false;
156 surfaceCheckbox.checked = false;
166 interactiveCheckbox.checked = true;
157 interactiveCheckbox.checked = true;
158+ promptCheckbox.checked = false;
167
159
168 surfaceContainerLoader.active = true;
160 surfaceContainerLoader.active = true;
169 tryCompare(surfaceContainerLoader, "status", Loader.Ready);
161 tryCompare(surfaceContainerLoader, "status", Loader.Ready);
@@ -171,121 +163,6 @@ Rectangle {
171
163
172 when: windowShown
164 when: windowShown
173
165
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−
289 function isContainerAnimating(container) {
166 function isContainerAnimating(container) {
290 var animationsLoader = findChild(container, "animationsLoader");
167 var animationsLoader = findChild(container, "animationsLoader");
291 if (!animationsLoader || animationsLoader.status != Loader.Ready) {
168 if (!animationsLoader || animationsLoader.status != Loader.Ready) {
@@ -303,25 +180,21 @@ Rectangle {
303 return false;
180 return false;
304 }
181 }
305
182
306− function test_childrenAnimate() {
183+ function test_promptSurfaceAnimates() {
184+ promptCheckbox.checked = true;
307 surfaceCheckbox.checked = true;
185 surfaceCheckbox.checked = true;
308
186
309− surfaceContainer.surface.createPromptSurface();
310−
311− var delegate = findChild(surfaceContainer, "childDelegate0");
312− var childContainer = findChild(delegate, "surfaceContainer");
313−
314 // wait for animation to begin
187 // wait for animation to begin
315− tryCompareFunction(function() { return isContainerAnimating(childContainer); }, true);
188+ tryCompareFunction(function() { return isContainerAnimating(surfaceContainer); }, true);
316 // wait for animation to end
189 // wait for animation to end
317− tryCompareFunction(function() { return isContainerAnimating(childContainer); }, false);
190+ tryCompareFunction(function() { return isContainerAnimating(surfaceContainer); }, false);
318
191
319− surfaceContainer.surface.promptSurfaceList.get(0).close();
192+ surfaceContainer.surface.close();
320
193
321 // wait for animation to begin
194 // wait for animation to begin
322− tryCompareFunction(function() { return isContainerAnimating(childContainer); }, true);
195+ tryCompareFunction(function() { return isContainerAnimating(surfaceContainer); }, true);
323 // wait for animation to end
196 // wait for animation to end
324− tryCompareFunction(function() { return isContainerAnimating(childContainer); }, false);
197+ tryCompareFunction(function() { return isContainerAnimating(surfaceContainer); }, false);
325 }
198 }
326
199
327 function test_surfaceItemGetsNoTouchesWhenContainerNotInteractive() {
200 function test_surfaceItemGetsNoTouchesWhenContainerNotInteractive() {