on xenial help sign in
merge prereq
commit6049d1fc789f31a74ea027d504d9b02d4b894fb6 authorMichael Zanetti <michael.zanetti@canonical.com> dateFri 25 Jul 2014, 01:40 (12 years ago) parentcd69592964e899
22 files changed+700−81
qml/Dash/DashContent.qml+2−2 qml/Stages/Animations/BaseSurfaceAnimation.qml+93 qml/Stages/Animations/DarkenAndFadeInAnimation.qml+48 qml/Stages/Animations/SwipeFromBottomAnimation.qml+48 qml/Stages/Animations/SwipeUpAnimation.qml+49 qml/Stages/SpreadDelegate.qml+10−16 qml/Stages/SurfaceContainer.qml+123 tests/mocks/Unity/Application/ApplicationDBusAdaptor.cpp+73 tests/mocks/Unity/Application/ApplicationDBusAdaptor.h+43 tests/mocks/Unity/Application/ApplicationInfo.cpp+21−12 tests/mocks/Unity/Application/ApplicationInfo.h+1−1 tests/mocks/Unity/Application/ApplicationManager.cpp+21 tests/mocks/Unity/Application/ApplicationManager.h+4 tests/mocks/Unity/Application/CMakeLists.txt+3−2 tests/mocks/Unity/Application/MirSurfaceItem.cpp+104 tests/mocks/Unity/Application/MirSurfaceItem.h+25−3 tests/mocks/Unity/Application/SurfaceManager.cpp+1 tests/mocks/Unity/Application/plugin.cpp+2−8 tests/qmltests/Launcher/tst_Launcher.qml+11−2 tests/qmltests/Panel/tst_ActiveCallHint.qml+1 tests/qmltests/Stages/tst_PhoneStage.qml+4−35 tests/qmltests/tst_Shell.qml+13
qml/Dash/DashContent.qml+2−2
@@ -121,9 +121,9 @@ Item {
121121 Loader {
122122 width: ListView.view.width
123123 height: ListView.view.height
124− visible: { // hide delegate if offscreen
124+ opacity: { // hide delegate if offscreen
125125 var xPositionRelativetoView = ListView.view.contentX - x
126− return xPositionRelativetoView > -width && xPositionRelativetoView < width
126+ return (xPositionRelativetoView > -width && xPositionRelativetoView < width) ? 1 : 0
127127 }
128128 asynchronous: true
129129 source: "GenericScopeView.qml"
qml/Stages/Animations/BaseSurfaceAnimation.qmlnew file+93
@@ -0,0 +1,93 @@
1+/*
2+ * Copyright (C) 2014 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.0
18+
19+/* This is the base case for surface animations, used when adding/removing * child surfaces.
20+ * The class is meant to be overridden and changes/animations provided for state changes.
21+ * NB. It is important to release the surface at the end of the "out" animation.
22+ *
23+ * Example - Simple fade in/out
24+ *
25+ * BaseSurfaceAnimation {
26+ * outChanges: [ PropertyChanges { target: animation.surface; opacity: 0.0 } ]
27+ * outAnimations: [
28+ SequentialAnimation {
29+ * NumberAnimation { target: animation.surface; property: "opacity"; duration: 300 }
30+ * ScriptAction { script: { if (animation.parent.removing) animation.surface.release(); } }
31+ * }
32+ * ]
33+ *
34+ * inChanges: [ PropertyChanges { target: animation.surface; opacity: 1.0 } ]
35+ * inAnimations: [ NumberAnimation { target: animation.surface; property: "opacity"; duration: 300 } ]
36+ * }
37+ */
38+Item {
39+ id: base
40+ property Item surface: null
41+
42+ // changes applied when state changes to "in"
43+ property list<QtObject> outChanges
44+ // transition animations when changing state to "in"
45+ property list<QtObject> outAnimations
46+
47+ // changes applied when state changes to "out"
48+ property list<QtObject> inChanges
49+ // transition animations when changing state to "out"
50+ property list<QtObject> inAnimations
51+
52+ function start() {
53+ // "prep" state forces outChanges without transition animations.
54+ state = "prep"
55+ state = "in";
56+ }
57+ function end() {
58+ state = "out";
59+ }
60+
61+ states: [
62+ State {
63+ name: "baseAnimation"
64+ PropertyChanges { target: base.surface; anchors.fill: undefined }
65+ },
66+
67+ State {
68+ name: "prep"
69+ extend: "baseAnimation"
70+ changes: outChanges
71+ },
72+ State {
73+ name: "out"
74+ extend: "prep"
75+ },
76+ State {
77+ name: "in"
78+ extend: "baseAnimation"
79+ changes: inChanges
80+ }
81+ ]
82+
83+ transitions: [
84+ Transition {
85+ to: "out"
86+ animations: outAnimations
87+ },
88+ Transition {
89+ to: "in"
90+ animations: inAnimations
91+ }
92+ ]
93+}
qml/Stages/Animations/DarkenAndFadeInAnimation.qmlnew file+48
@@ -0,0 +1,48 @@
1+/*
2+ * Copyright (C) 2014 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.0
18+import Ubuntu.Components 1.1
19+
20+BaseSurfaceAnimation {
21+ id: animation
22+
23+ property Rectangle darkenItem: Rectangle {
24+ parent: animation.surface.parent
25+ anchors.fill: parent
26+ color: Qt.rgba(0,0,0,0.0)
27+ }
28+
29+ outChanges: [ PropertyChanges { target: animation.surface; opacity: 0.0 } ]
30+ outAnimations: [
31+ SequentialAnimation {
32+ UbuntuNumberAnimation { target: animation.surface; property: "opacity"; duration: UbuntuAnimation.FastDuration }
33+ ColorAnimation { target: darkenItem; duration: UbuntuAnimation.FastDuration }
34+ ScriptAction { script: { if (animation.parent.removing) animation.surface.release(); } }
35+ }
36+ ]
37+
38+ inChanges: [
39+ PropertyChanges { target: darkenItem; color: Qt.rgba(0,0,0,0.7) },
40+ PropertyChanges { target: animation.surface; opacity: 1.0 }
41+ ]
42+ inAnimations: [
43+ SequentialAnimation {
44+ ColorAnimation { target: darkenItem; duration: UbuntuAnimation.FastDuration }
45+ UbuntuNumberAnimation { target: animation.surface; property: "opacity"; duration: UbuntuAnimation.FastDuration }
46+ }
47+ ]
48+}
qml/Stages/Animations/SwipeFromBottomAnimation.qmlnew file+48
@@ -0,0 +1,48 @@
1+/*
2+ * Copyright (C) 2014 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.0
18+
19+BaseSurfaceAnimation {
20+ id: animation
21+
22+ outChanges: [ AnchorChanges { target: animation.surface; anchors.top: animation.parent.bottom } ]
23+ outAnimations: [
24+ SequentialAnimation {
25+ PropertyAction { target: animation.parent; property: "clip"; value: true }
26+ AnchorAnimation { easing.type: Easing.InOutQuad; duration: 400 }
27+ PropertyAction { target: animation.surface; property: "visible"; value: !animation.parent.removing }
28+ PropertyAction { target: animation.parent; property: "clip"; value: false }
29+ ScriptAction { script: { if (animation.parent.removing) animation.surface.release(); } }
30+ }
31+ ]
32+
33+ inChanges: [
34+ AnchorChanges {
35+ target: animation.surface;
36+ anchors.top: animation.parent.top
37+ anchors.right: undefined
38+ anchors.bottom: undefined
39+ anchors.left: undefined
40+ } ]
41+ inAnimations: [
42+ SequentialAnimation {
43+ PropertyAction { target: animation.parent; property: "clip"; value: true }
44+ AnchorAnimation { easing.type: Easing.InOutQuad; duration: 400 }
45+ PropertyAction { target: animation.parent; property: "clip"; value: false }
46+ }
47+ ]
48+}
qml/Stages/Animations/SwipeUpAnimation.qmlnew file+49
@@ -0,0 +1,49 @@
1+/*
2+ * Copyright (C) 2014 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.0
18+
19+BaseSurfaceAnimation {
20+ id: animation
21+
22+ outAnimations: [
23+ SequentialAnimation {
24+ PropertyAction { target: animation.parent; property: "clip"; value: true }
25+ PropertyAction { target: animation.surface; property: "visible"; value: true }
26+ AnchorAnimation { easing.type: Easing.InOutQuad; duration: 400 }
27+ PropertyAction { target: animation.parent; property: "clip"; value: false }
28+ ScriptAction { script: { if (animation.parent.removing) animation.surface.release(); } }
29+ }
30+ ]
31+
32+ inChanges: [
33+ AnchorChanges {
34+ target: animation.surface;
35+ anchors.top: undefined
36+ anchors.right: undefined
37+ anchors.bottom: animation.parent.top
38+ anchors.left: undefined
39+ }
40+ ]
41+ inAnimations: [
42+ SequentialAnimation {
43+ PropertyAction { target: animation.parent; property: "clip"; value: true }
44+ AnchorAnimation { easing.type: Easing.InOutQuad; duration: 400 }
45+ PropertyAction { target: animation.surface; property: "visible"; value: false}
46+ PropertyAction { target: animation.parent; property: "clip"; value: false }
47+ }
48+ ]
49+}
qml/Stages/SpreadDelegate.qml+10−16
@@ -35,11 +35,11 @@ Item {
3535 signal clicked()
3636 signal closed()
3737
38− Item {
38+ SurfaceContainer {
3939 id: surfaceContainer
4040 objectName: "surfaceContainer" + index
4141 anchors.fill: parent
42− readonly property var surface: model.surface
42+ surface: model.surface
4343 property bool appHasCreatedASurface: false
4444
4545 onSurfaceChanged: {
@@ -48,19 +48,6 @@ Item {
4848 surface.visible = false; // hide until splash screen removed
4949 appHasCreatedASurface = true;
5050 }
51−
52− surface.parent = surfaceContainer;
53− surface.anchors.fill = surfaceContainer;
54− surfaceContainer.checkFullscreen(surface);
55− surface.z = 1;
56− }
57− }
58−
59− function checkFullscreen(surface) {
60− if (surface.state === MirSurfaceItem.Fullscreen) {
61− surface.anchors.topMargin = 0;
62− } else {
63− surface.anchors.topMargin = maximizedAppTopMargin;
6451 }
6552 }
6653
@@ -69,6 +56,14 @@ Item {
6956 splashLoader.source = "";
7057 }
7158
59+ Binding {
60+ target: surfaceContainer.surface
61+ property: "anchors.topMargin"
62+ value: {
63+ return surfaceContainer.surface.state === MirSurfaceItem.Fullscreen ? 0 : maximizedAppTopMargin;
64+ }
65+ }
66+
7267 Binding {
7368 target: surface
7469 property: "enabled"
@@ -88,7 +83,6 @@ Item {
8883
8984 Connections {
9085 target: surface
91− onStateChanged: surfaceContainer.checkFullscreen(surface);
9286 // FIXME: I would rather not need to do this, but currently it doesn't get
9387 // active focus without it and I don't know why.
9488 onFocusChanged: forceSurfaceActiveFocusIfReady();
qml/Stages/SurfaceContainer.qmlnew file+123
@@ -0,0 +1,123 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+*/
16+
17+import QtQuick 2.0
18+import "Animations"
19+
20+Item {
21+ id: container
22+ property Item surface: null
23+ property bool removing: false
24+
25+ onSurfaceChanged: {
26+ if (surface) {
27+ surface.parent = container;
28+ surface.z = 1;
29+ state = "initial"
30+ }
31+ }
32+
33+ Connections {
34+ target: surface
35+ onRemoved: {
36+ container.removing = true;
37+
38+ var childSurfaces = surface.childSurfaces;
39+ for (var i=0; i<childSurfaces.length; i++) {
40+ childSurfaces[i].removed();
41+ }
42+
43+ //if we don't have children, nothing will tell us to animate out, so do it.
44+ if (childSurfaces.length === 0) {
45+ animateOut();
46+ }
47+ // tell our parent to animate out.
48+ if (surface.parentSurface) {
49+ surface.parentSurface.parent.animateOut();
50+ }
51+ }
52+ }
53+
54+ Repeater {
55+ model: container.surface ? container.surface.childSurfaces : 0
56+
57+ delegate: Loader {
58+ z: 2
59+ anchors {
60+ fill: container
61+ topMargin: container.surface.anchors.topMargin
62+ rightMargin: container.surface.anchors.rightMargin
63+ bottomMargin: container.surface.anchors.bottomMargin
64+ leftMargin: container.surface.anchors.leftMargin
65+ }
66+
67+ // Only way to do recursive qml items.
68+ source: Qt.resolvedUrl("SurfaceContainer.qml")
69+ onLoaded: {
70+ item.surface = modelData;
71+ item.animateIn(swipeFromBottom);
72+ container.animateIn(swipeUp);
73+ }
74+ }
75+ }
76+
77+ function animateIn(component) {
78+ var animation = component.createObject(container, { "surface": container.surface });
79+ animation.start();
80+
81+ var tmp = d.animations;
82+ tmp.push(animation);
83+ d.animations = tmp;
84+ }
85+
86+ function animateOut() {
87+ if (d.animations.length > 0) {
88+ var tmp = d.animations;
89+ var popped = tmp.pop();
90+ popped.end();
91+ d.animations = tmp;
92+ } else {
93+ container.state = "initial";
94+ }
95+ }
96+
97+ QtObject {
98+ id: d
99+ property var animations: []
100+ property var currentAnimation: animations.length > 0 ? animations[animations.length-1] : undefined
101+ }
102+
103+ Component {
104+ id: swipeFromBottom
105+ SwipeFromBottomAnimation {}
106+ }
107+ Component {
108+ id: swipeUp
109+ SwipeUpAnimation {}
110+ }
111+ Component {
112+ id: darkenFade
113+ DarkenAndFadeInAnimation {}
114+ }
115+
116+ states: [
117+ State {
118+ name: "initial"
119+ PropertyChanges { target: surface; anchors.fill: container }
120+ }
121+ // TODO: more animations!
122+ ]
123+}
tests/mocks/Unity/Application/ApplicationDBusAdaptor.cppnew file+73
@@ -0,0 +1,73 @@
1+/*
2+ * Copyright (C) 2014 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+#include "ApplicationInfo.h"
18+#include "ApplicationDBusAdaptor.h"
19+#include "ApplicationManager.h"
20+#include "SurfaceManager.h"
21+#include "MirSurfaceItem.h"
22+
23+quint32 nextId = 0;
24+
25+ApplicationDBusAdaptor::ApplicationDBusAdaptor(ApplicationManager* applicationManager)
26+ : QDBusAbstractAdaptor(applicationManager)
27+ , m_applicationManager(applicationManager)
28+{
29+}
30+
31+MirSurfaceItem* topSurface(MirSurfaceItem* surface)
32+{
33+ if (!surface)
34+ return nullptr;
35+
36+ MirSurfaceItem* child = NULL;
37+ surface->foreachChildSurface([&](MirSurfaceItem* childSurface) {
38+ if (!child)
39+ child = topSurface(childSurface);
40+ });
41+ return child ? child : surface;
42+}
43+
44+quint32 ApplicationDBusAdaptor::addChildSurface(const QString &appId, const QString &surfaceImage)
45+{
46+ qDebug() << "ApplicationDBusAdaptor::addChildSurface - " << appId;
47+
48+ ApplicationInfo* application = m_applicationManager->findApplication(appId);
49+ if (!application) {
50+ qDebug() << "ApplicationDBusAdaptor::addChildSurface - No application found for " << appId;
51+ return 0;
52+ }
53+ quint32 surfaceId = ++nextId;
54+ MirSurfaceItem* surface = new MirSurfaceItem(QString("ChildSurface%1").arg(surfaceId),
55+ MirSurfaceItem::Normal,
56+ MirSurfaceItem::Maximized,
57+ QUrl(surfaceImage));
58+ m_childItems[surfaceId] = surface;
59+ surface->setParentSurface(topSurface(application->surface()));
60+ return surfaceId;
61+}
62+
63+void ApplicationDBusAdaptor::removeChildSurface(int surfaceId)
64+{
65+ qDebug() << "ApplicationDBusAdaptor::removeChildSurface - " << surfaceId;
66+
67+ if (!m_childItems.contains(surfaceId)) {
68+ qDebug() << "ApplicationDBusAdaptor::removeChildSurface - No added surface for " << surfaceId;
69+ return;
70+ }
71+ MirSurfaceItem* surface = m_childItems.take(surfaceId);
72+ Q_EMIT surface->removed();
73+}
tests/mocks/Unity/Application/ApplicationDBusAdaptor.hnew file+43
@@ -0,0 +1,43 @@
1+/*
2+ * Copyright (C) 2014 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+#ifndef APPLICATIONDBUSADAPTOR_H
18+#define APPLICATIONDBUSADAPTOR_H
19+
20+#include <QtDBus/QtDBus>
21+
22+class ApplicationManager;
23+class SurfaceManager;
24+class MirSurfaceItem;
25+
26+class ApplicationDBusAdaptor : public QDBusAbstractAdaptor
27+{
28+ Q_OBJECT
29+ Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity8.Mocks.Application")
30+public:
31+ ApplicationDBusAdaptor(ApplicationManager* applicationManager);
32+
33+public Q_SLOTS:
34+ quint32 addChildSurface(const QString& appId, const QString& surfaceImage);
35+ void removeChildSurface(int surfaceId);
36+
37+private:
38+ ApplicationManager* m_applicationManager;
39+
40+ QHash<quint32, MirSurfaceItem*> m_childItems;
41+};
42+
43+#endif // APPLICATIONDBUSADAPTOR_H
tests/mocks/Unity/Application/ApplicationInfo.cpp+21−12
@@ -72,7 +72,7 @@ void ApplicationInfo::onStateChanged(State state)
7272 if (state == ApplicationInfo::Running) {
7373 QTimer::singleShot(1000, this, SLOT(createSurface()));
7474 } else if (state == ApplicationInfo::Stopped) {
75− destroySurface();
75+ setSurface(nullptr);
7676 }
7777}
7878
@@ -80,24 +80,33 @@ void ApplicationInfo::createSurface()
8080{
8181 if (m_surface || state() == ApplicationInfo::Stopped) return;
8282
83− m_surface = new MirSurfaceItem(name(),
83+ setSurface(new MirSurfaceItem(name(),
8484 MirSurfaceItem::Normal,
8585 fullscreen() ? MirSurfaceItem::Fullscreen : MirSurfaceItem::Maximized,
86− screenshot());
87− Q_EMIT surfaceChanged(m_surface);
88− SurfaceManager::singleton()->registerSurface(m_surface);
86+ screenshot()));
8987}
9088
91−void ApplicationInfo::destroySurface()
89+void ApplicationInfo::setSurface(MirSurfaceItem* surface)
9290{
93− if (!m_surface) return;
91+ if (m_surface == surface)
92+ return;
93+
94+ if (m_surface) {
95+ m_surface->setApplication(nullptr);
96+ m_surface->setParent(nullptr);
97+ SurfaceManager::singleton()->unregisterSurface(m_surface);
98+ }
9499
95− SurfaceManager::singleton()->unregisterSurface(m_surface);
100+ m_surface = surface;
96101
97− MirSurfaceItem* oldSurface = m_surface;
98− m_surface = nullptr;
99− Q_EMIT surfaceChanged(nullptr);
100− oldSurface->deleteLater();
102+ if (m_surface) {
103+ m_surface->setApplication(this);
104+ m_surface->setParent(this);
105+ SurfaceManager::singleton()->registerSurface(m_surface);
106+ }
107+
108+ Q_EMIT surfaceChanged(m_surface);
109+ SurfaceManager::singleton()->registerSurface(m_surface);
101110}
102111
103112void ApplicationInfo::createWindowComponent()
tests/mocks/Unity/Application/ApplicationInfo.h+1−1
@@ -82,6 +82,7 @@ public:
8282 #undef IMPLEMENT_PROPERTY
8383
8484public:
85+ void setSurface(MirSurfaceItem* surface);
8586 MirSurfaceItem* surface() const { return m_surface; }
8687
8788Q_SIGNALS:
@@ -96,7 +97,6 @@ private Q_SLOTS:
9697 void onStateChanged(State state);
9798
9899 void createSurface();
99− void destroySurface();
100100
101101private:
102102 void createWindowItem();
tests/mocks/Unity/Application/ApplicationManager.cpp+21
@@ -15,6 +15,7 @@
1515 */
1616
1717#include "ApplicationManager.h"
18+#include "ApplicationDBusAdaptor.h"
1819#include "ApplicationInfo.h"
1920#include "MirSurfaceItem.h"
2021
@@ -27,6 +28,22 @@
2728#include <QQmlComponent>
2829#include <QTimer>
2930#include <QDateTime>
31+#include <QtDBus/QtDBus>
32+
33+ApplicationManager *ApplicationManager::the_application_manager = nullptr;
34+
35+ApplicationManager *ApplicationManager::singleton()
36+{
37+ if (!the_application_manager) {
38+ the_application_manager = new ApplicationManager();
39+ new ApplicationDBusAdaptor(the_application_manager);
40+
41+ QDBusConnection connection = QDBusConnection::sessionBus();
42+ connection.registerService("com.canonical.Unity8");
43+ connection.registerObject("/com/canonical/Unity8/Mocks", the_application_manager);
44+ }
45+ return the_application_manager;
46+}
3047
3148ApplicationManager::ApplicationManager(QObject *parent)
3249 : ApplicationManagerInterface(parent)
@@ -284,6 +301,7 @@ bool ApplicationManager::focusApplication(const QString &appId)
284301 if (app->focused() && app->stage() == ApplicationInfo::MainStage) {
285302 app->setFocused(false);
286303 app->hideWindow();
304+ app->setState(ApplicationInfo::Suspended);
287305 }
288306 }
289307
@@ -291,6 +309,7 @@ bool ApplicationManager::focusApplication(const QString &appId)
291309 application->setFocused(true);
292310 if (!m_mainStage)
293311 createMainStage();
312+ application->setState(ApplicationInfo::Running);
294313 application->showWindow(m_mainStage);
295314 m_mainStage->setZ(-1000);
296315 if (m_sideStage)
@@ -301,6 +320,7 @@ bool ApplicationManager::focusApplication(const QString &appId)
301320 if (app->focused() && app->stage() == ApplicationInfo::SideStage) {
302321 app->setFocused(false);
303322 app->hideWindow();
323+ app->setState(ApplicationInfo::Suspended);
304324 }
305325 }
306326
@@ -308,6 +328,7 @@ bool ApplicationManager::focusApplication(const QString &appId)
308328 application->setFocused(true);
309329 if (!m_sideStage)
310330 createSideStage();
331+ application->setState(ApplicationInfo::Running);
311332 application->showWindow(m_sideStage);
312333 m_sideStage->setZ(-1000);
313334 if (m_mainStage)
tests/mocks/Unity/Application/ApplicationManager.h+4
@@ -52,6 +52,8 @@ class ApplicationManager : public ApplicationManagerInterface {
5252 ApplicationManager(QObject *parent = NULL);
5353 virtual ~ApplicationManager();
5454
55+ static ApplicationManager *singleton();
56+
5557 enum MoreRoles {
5658 RoleSurface = RoleScreenshot+1,
5759 RoleFullscreen,
@@ -140,6 +142,8 @@ class ApplicationManager : public ApplicationManagerInterface {
140142 QQuickItem *m_sideStage;
141143
142144 int m_rightMargin;
145+
146+ static ApplicationManager *the_application_manager;
143147};
144148
145149Q_DECLARE_OPERATORS_FOR_FLAGS(ApplicationManager::ExecFlags)
tests/mocks/Unity/Application/CMakeLists.txt+3−2
@@ -2,6 +2,7 @@ pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application)
22
33set(FakeUnityApplicationQml_SOURCES
44 plugin.cpp
5+ ApplicationDBusAdaptor.cpp
56 ApplicationInfo.cpp
67 ApplicationImage.cpp
78 ApplicationManager.cpp
@@ -18,8 +19,8 @@ add_library(FakeUnityApplicationQml MODULE ${FakeUnityApplicationQml_SOURCES})
1819add_library(NonMirUnityApplicationQml MODULE ${FakeUnityApplicationQml_SOURCES})
1920set_target_properties(NonMirUnityApplicationQml PROPERTIES OUTPUT_NAME FakeUnityApplicationQml)
2021
21−qt5_use_modules(FakeUnityApplicationQml Core Quick)
22−qt5_use_modules(NonMirUnityApplicationQml Core Quick)
22+qt5_use_modules(FakeUnityApplicationQml Core Quick DBus)
23+qt5_use_modules(NonMirUnityApplicationQml Core Quick DBus)
2324
2425add_unity8_mock(Unity.Application 0.1 Unity/Application PREFIX mocks TARGETS FakeUnityApplicationQml)
2526add_unity8_mock(Unity.Application 0.1 Unity/Application
tests/mocks/Unity/Application/MirSurfaceItem.cpp+104
@@ -15,8 +15,10 @@
1515 */
1616
1717#include "MirSurfaceItem.h"
18+#include "ApplicationInfo.h"
1819
1920#include <QPainter>
21+#include <QQmlEngine>
2022
2123MirSurfaceItem::MirSurfaceItem(const QString& name,
2224 MirSurfaceItem::Type type,
@@ -24,12 +26,16 @@ MirSurfaceItem::MirSurfaceItem(const QString& name,
2426 const QUrl& screenshot,
2527 QQuickItem *parent)
2628 : QQuickPaintedItem(parent)
29+ , m_application(nullptr)
2730 , m_name(name)
2831 , m_type(type)
2932 , m_state(state)
3033 , m_img(screenshot.isLocalFile() ? screenshot.toLocalFile() : screenshot.toString())
34+ , m_parentSurface(nullptr)
3135 , m_haveInputMethod(false)
3236{
37+ QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
38+
3339 // The virtual keyboard (input method) has a big transparent area so that
3440 // content behind it show through
3541 setFillColor(Qt::transparent);
@@ -38,6 +44,19 @@ MirSurfaceItem::MirSurfaceItem(const QString& name,
3844 this, &MirSurfaceItem::onFocusChanged);
3945}
4046
47+MirSurfaceItem::~MirSurfaceItem()
48+{
49+ QList<MirSurfaceItem*> children(m_children);
50+ for (MirSurfaceItem* child : children) {
51+ child->setParentSurface(nullptr);
52+ }
53+ if (m_parentSurface)
54+ m_parentSurface->removeChildSurface(this);
55+
56+ if (m_application)
57+ m_application->setSurface(nullptr);
58+}
59+
4160void MirSurfaceItem::paint(QPainter * painter)
4261{
4362 if (!m_img.isNull()) {
@@ -45,6 +64,91 @@ void MirSurfaceItem::paint(QPainter * painter)
4564 }
4665}
4766
67+void MirSurfaceItem::release()
68+{
69+ QList<MirSurfaceItem*> children(m_children);
70+ for (MirSurfaceItem* child : children) {
71+ child->release();
72+ }
73+ if (m_parentSurface) {
74+ m_parentSurface->removeChildSurface(this);
75+ }
76+
77+ if (m_application) {
78+ m_application->setSurface(nullptr);
79+ }
80+ if (!parent()) {
81+ deleteLater();
82+ }
83+}
84+
85+void MirSurfaceItem::setApplication(ApplicationInfo* application)
86+{
87+ m_application = application;
88+}
89+
90+void MirSurfaceItem::setParentSurface(MirSurfaceItem* surface)
91+{
92+ if (m_parentSurface == surface || surface == this)
93+ return;
94+
95+ if (m_parentSurface) {
96+ m_parentSurface->removeChildSurface(this);
97+ }
98+
99+ m_parentSurface = surface;
100+
101+ if (m_parentSurface) {
102+ m_parentSurface->addChildSurface(this);
103+ }
104+ Q_EMIT parentSurfaceChanged(surface);
105+}
106+
107+void MirSurfaceItem::removeChildSurface(MirSurfaceItem* surface)
108+{
109+ if (m_children.contains(surface)) {
110+ m_children.removeOne(surface);
111+ Q_EMIT childSurfacesChanged();
112+ }
113+}
114+
115+void MirSurfaceItem::addChildSurface(MirSurfaceItem* surface)
116+{
117+ m_children.append(surface);
118+ Q_EMIT childSurfacesChanged();
119+}
120+
121+void MirSurfaceItem::foreachChildSurface(std::function<void(MirSurfaceItem*)> f) const
122+{
123+ for(MirSurfaceItem* child : m_children) {
124+ f(child);
125+ }
126+}
127+
128+QQmlListProperty<MirSurfaceItem> MirSurfaceItem::childSurfaces()
129+{
130+ return QQmlListProperty<MirSurfaceItem>(this,
131+ 0,
132+ MirSurfaceItem::childSurfaceCount,
133+ MirSurfaceItem::childSurfaceAt);
134+}
135+
136+int MirSurfaceItem::childSurfaceCount(QQmlListProperty<MirSurfaceItem> *prop)
137+{
138+ MirSurfaceItem *p = qobject_cast<MirSurfaceItem*>(prop->object);
139+ return p->m_children.count();
140+}
141+
142+MirSurfaceItem* MirSurfaceItem::childSurfaceAt(QQmlListProperty<MirSurfaceItem> *prop, int index)
143+{
144+ MirSurfaceItem *p = qobject_cast<MirSurfaceItem*>(prop->object);
145+
146+ if (index < 0 || index >= p->m_children.count())
147+ return nullptr;
148+ return p->m_children[index];
149+}
150+
151+
48152void MirSurfaceItem::touchEvent(QTouchEvent * event)
49153{
50154 if (event->type() == QEvent::TouchBegin && hasFocus()) {
tests/mocks/Unity/Application/MirSurfaceItem.h+25−3
@@ -20,6 +20,8 @@
2020#include <QQuickPaintedItem>
2121#include <QImage>
2222
23+class ApplicationInfo;
24+
2325class MirSurfaceItem : public QQuickPaintedItem
2426{
2527 Q_OBJECT
@@ -29,6 +31,8 @@ class MirSurfaceItem : public QQuickPaintedItem
2931 Q_PROPERTY(Type type READ type NOTIFY typeChanged)
3032 Q_PROPERTY(State state READ state NOTIFY stateChanged)
3133 Q_PROPERTY(QString name READ name NOTIFY nameChanged)
34+ Q_PROPERTY(MirSurfaceItem* parentSurface READ parentSurface NOTIFY parentSurfaceChanged)
35+ Q_PROPERTY(QQmlListProperty<MirSurfaceItem> childSurfaces READ childSurfaces NOTIFY childSurfacesChanged DESIGNABLE false)
3236
3337public:
3438 enum Type {
@@ -56,24 +60,33 @@ public:
5660 State state,
5761 const QUrl& screenshot,
5862 QQuickItem *parent = 0);
63+ ~MirSurfaceItem();
5964
6065 //getters
66+ ApplicationInfo* application() const { return m_application; }
6167 Type type() const { return m_type; }
6268 State state() const { return m_state; }
6369 QString name() const { return m_name; }
70+ MirSurfaceItem* parentSurface() const { return m_parentSurface; }
71+ void foreachChildSurface(std::function<void(MirSurfaceItem*)> f) const;
6472
65− Q_INVOKABLE void setState(State newState);
73+ void setApplication(ApplicationInfo* item);
74+ void setParentSurface(MirSurfaceItem* item);
6675
67− Q_INVOKABLE void release() {}
76+ Q_INVOKABLE void setState(State newState);
77+ Q_INVOKABLE void release();
6878
6979 void paint(QPainter * painter) override;
70−
7180 void touchEvent(QTouchEvent * event) override;
7281
7382Q_SIGNALS:
7483 void typeChanged(Type);
7584 void stateChanged(State);
7685 void nameChanged(QString);
86+ void parentSurfaceChanged(MirSurfaceItem*);
87+ void childSurfacesChanged();
88+
89+ void removed();
7790
7891 void inputMethodRequested();
7992 void inputMethodDismissed();
@@ -85,12 +98,21 @@ protected:
8598 const QImage &screenshotImage() { return m_img; }
8699
87100private:
101+ void addChildSurface(MirSurfaceItem* surface);
102+ void removeChildSurface(MirSurfaceItem* surface);
103+
104+ QQmlListProperty<MirSurfaceItem> childSurfaces();
105+ static int childSurfaceCount(QQmlListProperty<MirSurfaceItem> *prop);
106+ static MirSurfaceItem* childSurfaceAt(QQmlListProperty<MirSurfaceItem> *prop, int index);
88107
108+ ApplicationInfo* m_application;
89109 const QString m_name;
90110 const Type m_type;
91111 State m_state;
92112 const QImage m_img;
93113
114+ MirSurfaceItem* m_parentSurface;
115+ QList<MirSurfaceItem*> m_children;
94116 bool m_haveInputMethod;
95117};
96118
tests/mocks/Unity/Application/SurfaceManager.cpp+1
@@ -13,6 +13,7 @@
1313 * You should have received a copy of the GNU General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
16+
1617#include "SurfaceManager.h"
1718
1819#include "MirSurfaceItem.h"
tests/mocks/Unity/Application/plugin.cpp+2−8
@@ -25,15 +25,10 @@
2525#include <qqml.h>
2626#include <QQmlEngine>
2727
28−ApplicationManager *s_appManager = 0;
29−
3028static QObject* applicationManagerSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
3129 Q_UNUSED(engine);
3230 Q_UNUSED(scriptEngine);
33− if (!s_appManager) {
34− s_appManager = new ApplicationManager();
35− }
36− return s_appManager;
31+ return ApplicationManager::singleton();
3732}
3833
3934static QObject* surfaceManagerSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
@@ -62,6 +57,5 @@ void FakeUnityApplicationQmlPlugin::initializeEngine(QQmlEngine *engine, const c
6257{
6358 QQmlExtensionPlugin::initializeEngine(engine, uri);
6459
65− ApplicationManager* appManager = static_cast<ApplicationManager*>(applicationManagerSingleton(engine, NULL));
66− engine->addImageProvider(QLatin1String("application"), new ApplicationScreenshotProvider(appManager));
60+ engine->addImageProvider(QLatin1String("application"), new ApplicationScreenshotProvider(ApplicationManager::singleton()));
6761}
tests/qmltests/Launcher/tst_Launcher.qml+11−2
@@ -94,9 +94,16 @@ Item {
9494 name: "Launcher"
9595 when: windowShown && initTestCase.completed
9696
97+ function cleanup() {
98+ mouseClick(root, root.width / 2, root.height / 2);
99+ revealer.waitUntilLauncherDisappears();
100+ }
101+
97102 // Drag from the left edge of the screen rightwards and check that the launcher
98103 // appears (as if being dragged by the finger/pointer)
99104 function test_dragLeftEdgeToRevealLauncherAndTapCenterToDismiss() {
105+ revealer.waitUntilLauncherDisappears();
106+
100107 var panel = findChild(launcher, "launcherPanel")
101108 verify(panel != undefined)
102109
@@ -117,10 +124,9 @@ Item {
117124 corresponding desktop file. E.g. clicking on phone icon should yield
118125 launcherApplicationSelected("[...]dialer-app.desktop") */
119126 function test_clickingOnAppIconCausesSignalEmission() {
127+ revealer.dragLauncherIntoView();
120128 launcher.lastSelectedApplication = ""
121129
122− revealer.dragLauncherIntoView()
123−
124130 var listView = findChild(launcher, "launcherListView");
125131 listView.positionViewAtEnd();
126132
@@ -184,6 +190,7 @@ Item {
184190 }
185191
186192 function test_countEmblems() {
193+ revealer.dragLauncherIntoView();
187194 var launcherListView = findChild(launcher, "launcherListView");
188195 for (var i = 0; i < launcherListView.count; ++i) {
189196 var delegate = findChild(launcherListView, "launcherDelegate" + i)
@@ -192,6 +199,7 @@ Item {
192199 }
193200
194201 function test_progressOverlays() {
202+ revealer.dragLauncherIntoView();
195203 var launcherListView = findChild(launcher, "launcherListView");
196204 for (var i = 0; i < launcherListView.count; ++i) {
197205 var delegate = findChild(launcherListView, "launcherDelegate" + i)
@@ -200,6 +208,7 @@ Item {
200208 }
201209
202210 function test_focusedHighlight() {
211+ revealer.dragLauncherIntoView();
203212 var launcherListView = findChild(launcher, "launcherListView");
204213 for (var i = 0; i < launcherListView.count; ++i) {
205214 var delegate = findChild(launcherListView, "launcherDelegate" + i)
tests/qmltests/Panel/tst_ActiveCallHint.qml+1
@@ -95,6 +95,7 @@ Item {
9595 function test_activeHint(data) {
9696 if (data.dialer) {
9797 var application = ApplicationManager.startApplication("dialer-app");
98+ ApplicationManager.focusApplication("dialer-app");
9899 tryCompare(ApplicationManager, "focusedApplicationId", "dialer-app");
99100 tryCompare(application, "state", ApplicationInfo.Running);
100101
tests/qmltests/Stages/tst_PhoneStage.qml+4−35
@@ -44,7 +44,6 @@ Item {
4444
4545 Rectangle {
4646 anchors { fill: parent; leftMargin: phoneStage.width }
47−// color: "blue"
4847
4948 Column {
5049 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
@@ -58,7 +57,10 @@ Item {
5857 }
5958 Button {
6059 anchors { left: parent.left; right: parent.right }
61− text: "Add App"
60+ text: "Remove App"
61+ onClicked: {
62+ ApplicationManager.stopApplication(ApplicationManager.get(0).appId)
63+ }
6264 }
6365 }
6466 }
@@ -228,39 +230,6 @@ Item {
228230 compare(ApplicationManager.focusedApplicationId, selectedApp.appId);
229231 }
230232
231− function test_fullscreenMode() {
232− var fullscreenApp = null;
233− var normalApp = null;
234−
235− for (var i = 0; i < 5; i++) {
236− addApps(1);
237− var newApp = ApplicationManager.get(0);
238− tryCompare(phoneStage, "fullscreen", newApp.fullscreen);
239− if (newApp.fullscreen && fullscreenApp == null) {
240− fullscreenApp = newApp;
241− } else if (!newApp.fullscreen && normalApp == null){
242− normalApp = newApp;
243− }
244− }
245− verify(fullscreenApp != null); // Can't continue the test without having a fullscreen app
246− verify(normalApp != null); // Can't continue the test without having a non-fullscreen app
247−
248− // Select a normal app
249− goToSpread();
250− phoneStage.select(normalApp.appId);
251− tryCompare(phoneStage, "fullscreen", false);
252−
253− // Select a fullscreen app
254− goToSpread();
255− phoneStage.select(fullscreenApp.appId);
256− tryCompare(phoneStage, "fullscreen", true);
257−
258− // Select a normal app
259− goToSpread();
260− phoneStage.select(normalApp.appId);
261− tryCompare(phoneStage, "fullscreen", false);
262− }
263−
264233 function cleanup() {
265234 while (ApplicationManager.count > 0) {
266235 var oldCount = ApplicationManager.count;
tests/qmltests/tst_Shell.qml+13
@@ -382,5 +382,18 @@ Item {
382382 swipeAwayGreeter()
383383 tryCompare(sessionSpy, "count", 1)
384384 }
385+
386+ function test_fullscreen() {
387+ var panel = findChild(shell, "panel");
388+ compare(panel.fullscreenMode, false);
389+ ApplicationManager.startApplication("camera-app");
390+ tryCompare(panel, "fullscreenMode", true);
391+ ApplicationManager.startApplication("gallery-app");
392+ tryCompare(panel, "fullscreenMode", false);
393+ ApplicationManager.requestFocusApplication("camera-app");
394+ tryCompare(panel, "fullscreenMode", true);
395+ ApplicationManager.requestFocusApplication("gallery-app");
396+ tryCompare(panel, "fullscreenMode", false);
397+ }
385398 }
386399}