on xenial help sign in
Added application prompt surfaces to allow prompting application which have not yet created a surface.
9f5177eNick Dedekind
debian/control+1−1
@@ -83,7 +83,7 @@ Depends: gsettings-desktop-schemas,
83 qmenumodel-qml (>= 0.2.8),
83 qmenumodel-qml (>= 0.2.8),
84 qml-module-qtquick-xmllistmodel,
84 qml-module-qtquick-xmllistmodel,
85 qtdeclarative5-gsettings1.0,
85 qtdeclarative5-gsettings1.0,
86− qtdeclarative5-qtmir-plugin (>= 0.4),
86+ qtdeclarative5-qtmir-plugin (>= 0.4.1),
87 qtdeclarative5-ubuntu-settings-components (>= 0.3),
87 qtdeclarative5-ubuntu-settings-components (>= 0.3),
88 qtdeclarative5-ubuntu-telephony0.1,
88 qtdeclarative5-ubuntu-telephony0.1,
89 unity-launcher-impl-3,
89 unity-launcher-impl-3,
qml/Stages/AppSurfaceContainer.qmlnew file+96
@@ -0,0 +1,96 @@
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+SurfaceContainer {
21+ id: container
22+ property var promptSurfaces
23+
24+ property bool appHasCreatedASurface: false
25+
26+ onSurfaceChanged: {
27+ if (surface) {
28+ if (!appHasCreatedASurface) {
29+ surface.visible = false; // hide until splash screen removed
30+ appHasCreatedASurface = true;
31+ }
32+ }
33+ }
34+
35+ function revealSurface() {
36+ surface.visible = true;
37+ splashLoader.source = "";
38+ }
39+
40+ Timer { //FIXME - need to delay removing splash screen to allow surface resize to complete
41+ id: surfaceRevealDelay
42+ interval: 100
43+ onTriggered: surfaceContainer.revealSurface()
44+ }
45+
46+ Loader {
47+ z: 3
48+ id: splashLoader
49+ anchors.fill: parent
50+ }
51+
52+ Repeater {
53+ model: container.promptSurfaces
54+
55+ delegate: SurfaceContainer {
56+ anchors {
57+ fill: container
58+ topMargin: container.surface.anchors.topMargin
59+ rightMargin: container.surface.anchors.rightMargin
60+ bottomMargin: container.surface.anchors.bottomMargin
61+ leftMargin: container.surface.anchors.leftMargin
62+ }
63+
64+ z: 4 + index
65+ surface: modelData
66+
67+ Component.onCompleted: {
68+ animateIn();
69+ }
70+ }
71+ }
72+
73+ StateGroup {
74+ id: appSurfaceState
75+ states: [
76+ State {
77+ name: "noSurfaceYet"
78+ when: !surfaceContainer.appHasCreatedASurface
79+ StateChangeScript {
80+ script: { splashLoader.setSource("Splash.qml", { "name": model.name, "image": model.icon }); }
81+ }
82+ },
83+ State {
84+ name: "hasSurface"
85+ when: surfaceContainer.appHasCreatedASurface && (surfaceContainer.surface !== null)
86+ StateChangeScript { script: { surfaceRevealDelay.start(); } }
87+ },
88+ State {
89+ name: "surfaceLostButAppStillAlive"
90+ when: surfaceContainer.appHasCreatedASurface && (surfaceContainer.surface === null)
91+ // TODO - use app snapshot
92+ }
93+ ]
94+ state: "noSurfaceYet"
95+ }
96+}
qml/Stages/SpreadDelegate.qml+2−52
@@ -35,26 +35,12 @@ Item {
35 signal clicked()
35 signal clicked()
36 signal closed()
36 signal closed()
37
37
38− SurfaceContainer {
38+ AppSurfaceContainer {
39 id: surfaceContainer
39 id: surfaceContainer
40 objectName: "surfaceContainer"
40 objectName: "surfaceContainer"
41 anchors.fill: parent
41 anchors.fill: parent
42 surface: model.surface
42 surface: model.surface
43− property bool appHasCreatedASurface: false
43+ promptSurfaces: model.application.promptSurfaces
44−
45− onSurfaceChanged: {
46− if (surface) {
47− if (!appHasCreatedASurface) {
48− surface.visible = false; // hide until splash screen removed
49− appHasCreatedASurface = true;
50− }
51− }
52− }
53−
54− function revealSurface() {
55− surface.visible = true;
56− splashLoader.source = "";
57− }
58
44
59 Binding {
45 Binding {
60 target: surfaceContainer.surface
46 target: surfaceContainer.surface
@@ -75,12 +61,6 @@ Item {
75 value: root.interactive
61 value: root.interactive
76 }
62 }
77
63
78− Timer { //FIXME - need to delay removing splash screen to allow surface resize to complete
79− id: surfaceRevealDelay
80− interval: 100
81− onTriggered: surfaceContainer.revealSurface()
82− }
83−
84 Connections {
64 Connections {
85 target: surface
65 target: surface
86 // FIXME: I would rather not need to do this, but currently it doesn't get
66 // FIXME: I would rather not need to do this, but currently it doesn't get
@@ -115,36 +95,6 @@ Item {
115 }
95 }
116 }
96 }
117
97
118−
119− StateGroup {
120− id: appSurfaceState
121− states: [
122− State {
123− name: "noSurfaceYet"
124− when: !surfaceContainer.appHasCreatedASurface
125− StateChangeScript {
126− script: { splashLoader.setSource("Splash.qml", { "name": model.name, "image": model.icon }); }
127− }
128− },
129− State {
130− name: "hasSurface"
131− when: surfaceContainer.appHasCreatedASurface && (surfaceContainer.surface !== null)
132− StateChangeScript { script: { surfaceRevealDelay.start(); } }
133− },
134− State {
135− name: "surfaceLostButAppStillAlive"
136− when: surfaceContainer.appHasCreatedASurface && (surfaceContainer.surface === null)
137− // TODO - use app snapshot
138− }
139− ]
140− state: "noSurfaceYet"
141− }
142−
143− Loader {
144− id: splashLoader
145− anchors.fill: surfaceContainer
146− }
147−
148 DraggingArea {
98 DraggingArea {
149 id: dragArea
99 id: dragArea
150 anchors.fill: parent
100 anchors.fill: parent
qml/Stages/SurfaceContainer.qml+5−22
@@ -40,19 +40,12 @@ Item {
40 childSurfaces[i].removed();
40 childSurfaces[i].removed();
41 }
41 }
42
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();
43+ animateOut();
50− }
51 }
44 }
52 }
45 }
53
46
54 Repeater {
47 Repeater {
55− model: container.surface ? container.surface.childSurfaces : 0
48+ model: surface.childSurfaces
56
49
57 delegate: Loader {
50 delegate: Loader {
58 z: 2
51 z: 2
@@ -68,14 +61,13 @@ Item {
68 source: Qt.resolvedUrl("SurfaceContainer.qml")
61 source: Qt.resolvedUrl("SurfaceContainer.qml")
69 onLoaded: {
62 onLoaded: {
70 item.surface = modelData;
63 item.surface = modelData;
71− item.animateIn(swipeFromBottom);
64+ item.animateIn();
72− container.animateIn(swipeUp);
73 }
65 }
74 }
66 }
75 }
67 }
76
68
77− function animateIn(component) {
69+ function animateIn() {
78− var animation = component.createObject(container, { "surface": container.surface });
70+ var animation = swipeFromBottom.createObject(container, { "surface": container.surface });
79 animation.start();
71 animation.start();
80
72
81 var tmp = d.animations;
73 var tmp = d.animations;
@@ -104,20 +96,11 @@ Item {
104 id: swipeFromBottom
96 id: swipeFromBottom
105 SwipeFromBottomAnimation {}
97 SwipeFromBottomAnimation {}
106 }
98 }
107− Component {
108− id: swipeUp
109− SwipeUpAnimation {}
110− }
111− Component {
112− id: darkenFade
113− DarkenAndFadeInAnimation {}
114− }
115
99
116 states: [
100 states: [
117 State {
101 State {
118 name: "initial"
102 name: "initial"
119 PropertyChanges { target: surface; anchors.fill: container }
103 PropertyChanges { target: surface; anchors.fill: container }
120 }
104 }
121− // TODO: more animations!
122 ]
105 ]
123}
106}
tests/mocks/Unity/Application/ApplicationDBusAdaptor.cpp+10−4
@@ -43,9 +43,9 @@ MirSurfaceItem* topSurface(MirSurfaceItem* surface)
43 return child ? child : surface;
43 return child ? child : surface;
44}
44}
45
45
46−quint32 ApplicationDBusAdaptor::addChildSurface(const QString &appId, const QString &surfaceImage)
46+quint32 ApplicationDBusAdaptor::addPromptSurface(const QString &appId, const QString &surfaceImage)
47{
47{
48− qDebug() << "ApplicationDBusAdaptor::addChildSurface - " << appId;
48+ qDebug() << "ApplicationDBusAdaptor::addPromptSurface to " << appId;
49
49
50 ApplicationInfo* application = m_applicationManager->findApplication(appId);
50 ApplicationInfo* application = m_applicationManager->findApplication(appId);
51 if (!application) {
51 if (!application) {
@@ -53,12 +53,18 @@ quint32 ApplicationDBusAdaptor::addChildSurface(const QString &appId, const QStr
53 return 0;
53 return 0;
54 }
54 }
55 quint32 surfaceId = ++nextId;
55 quint32 surfaceId = ++nextId;
56− MirSurfaceItem* surface = new MirSurfaceItem(QString("ChildSurface%1").arg(surfaceId),
56+ MirSurfaceItem* surface = new MirSurfaceItem(QString("%1-ChildSurface%2").arg(appId).arg(surfaceId),
57 MirSurfaceItem::Normal,
57 MirSurfaceItem::Normal,
58 MirSurfaceItem::Maximized,
58 MirSurfaceItem::Maximized,
59 QUrl(surfaceImage));
59 QUrl(surfaceImage));
60 m_childItems[surfaceId] = surface;
60 m_childItems[surfaceId] = surface;
61+
62+ if (application->promptSurfaceList().count() > 0) {
61− surface->setParentSurface(topSurface(application->surface()));
63+ surface->setParentSurface(topSurface(application->promptSurfaceList()[0]));
64+ } else {
65+ application->addPromptSurface(surface);
66+ }
67+
62 return surfaceId;
68 return surfaceId;
63}
69}
64
70
tests/mocks/Unity/Application/ApplicationDBusAdaptor.h+1−1
@@ -31,7 +31,7 @@ public:
31 ApplicationDBusAdaptor(ApplicationManager* applicationManager);
31 ApplicationDBusAdaptor(ApplicationManager* applicationManager);
32
32
33public Q_SLOTS:
33public Q_SLOTS:
34− quint32 addChildSurface(const QString& appId, const QString& surfaceImage);
34+ quint32 addPromptSurface(const QString& appId, const QString& surfaceImage);
35 void removeChildSurface(int surfaceId);
35 void removeChildSurface(int surfaceId);
36
36
37private:
37private:
tests/mocks/Unity/Application/ApplicationInfo.cpp+53
@@ -78,6 +78,7 @@ void ApplicationInfo::createSurface()
78
78
79void ApplicationInfo::setSurface(MirSurfaceItem* surface)
79void ApplicationInfo::setSurface(MirSurfaceItem* surface)
80{
80{
81+ qDebug() << "Application::setSurface - appId=" << appId() << "surface=" << surface;
81 if (m_surface == surface)
82 if (m_surface == surface)
82 return;
83 return;
83
84
@@ -98,3 +99,55 @@ void ApplicationInfo::setSurface(MirSurfaceItem* surface)
98 Q_EMIT surfaceChanged(m_surface);
99 Q_EMIT surfaceChanged(m_surface);
99 SurfaceManager::singleton()->registerSurface(m_surface);
100 SurfaceManager::singleton()->registerSurface(m_surface);
100}
101}
102+
103+void ApplicationInfo::addPromptSurface(MirSurfaceItem* surface)
104+{
105+ qDebug() << "ApplicationInfo::addPromptSurface " << surface->name() << " to " << name();
106+ if (surface == m_surface || m_promptSurfaces.contains(surface)) return;
107+
108+ surface->setApplication(this);
109+ m_promptSurfaces.append(surface);
110+ Q_EMIT promptSurfacesChanged();
111+}
112+
113+void ApplicationInfo::removeSurface(MirSurfaceItem* surface)
114+{
115+ if (m_surface == surface) {
116+ setSurface(nullptr);
117+ } else if (m_promptSurfaces.contains(surface)) {
118+ qDebug() << "Application::removeSurface " << surface->name() << " from " << name();
119+
120+ m_promptSurfaces.removeAll(surface);
121+ surface->setApplication(nullptr);
122+
123+ Q_EMIT promptSurfacesChanged();
124+ }
125+}
126+
127+QList<MirSurfaceItem*> ApplicationInfo::promptSurfaceList() const
128+{
129+ return m_promptSurfaces;
130+}
131+
132+QQmlListProperty<MirSurfaceItem> ApplicationInfo::promptSurfaces()
133+{
134+ return QQmlListProperty<MirSurfaceItem>(this,
135+ 0,
136+ ApplicationInfo::promptSurfaceCount,
137+ ApplicationInfo::promptSurfaceAt);
138+}
139+
140+int ApplicationInfo::promptSurfaceCount(QQmlListProperty<MirSurfaceItem> *prop)
141+{
142+ ApplicationInfo *p = qobject_cast<ApplicationInfo*>(prop->object);
143+ return p->m_promptSurfaces.count();
144+}
145+
146+MirSurfaceItem* ApplicationInfo::promptSurfaceAt(QQmlListProperty<MirSurfaceItem> *prop, int index)
147+{
148+ ApplicationInfo *p = qobject_cast<ApplicationInfo*>(prop->object);
149+
150+ if (index < 0 || index >= p->m_promptSurfaces.count())
151+ return nullptr;
152+ return p->m_promptSurfaces[index];
153+}
tests/mocks/Unity/Application/ApplicationInfo.h+13
@@ -38,6 +38,7 @@ class ApplicationInfo : public ApplicationInfoInterface {
38 Q_PROPERTY(bool fullscreen READ fullscreen WRITE setFullscreen NOTIFY fullscreenChanged)
38 Q_PROPERTY(bool fullscreen READ fullscreen WRITE setFullscreen NOTIFY fullscreenChanged)
39 Q_PROPERTY(Stage stage READ stage WRITE setStage NOTIFY stageChanged)
39 Q_PROPERTY(Stage stage READ stage WRITE setStage NOTIFY stageChanged)
40 Q_PROPERTY(MirSurfaceItem* surface READ surface NOTIFY surfaceChanged)
40 Q_PROPERTY(MirSurfaceItem* surface READ surface NOTIFY surfaceChanged)
41+ Q_PROPERTY(QQmlListProperty<MirSurfaceItem> promptSurfaces READ promptSurfaces NOTIFY promptSurfacesChanged DESIGNABLE false)
41
42
42 // Only exists in this fake implementation
43 // Only exists in this fake implementation
43
44
@@ -85,8 +86,15 @@ public:
85 void setSurface(MirSurfaceItem* surface);
86 void setSurface(MirSurfaceItem* surface);
86 MirSurfaceItem* surface() const { return m_surface; }
87 MirSurfaceItem* surface() const { return m_surface; }
87
88
89+ void removeSurface(MirSurfaceItem* surface);
90+
91+ void addPromptSurface(MirSurfaceItem* surface);
92+ QList<MirSurfaceItem*> promptSurfaceList() const;
93+ QQmlListProperty<MirSurfaceItem> promptSurfaces();
94+
88Q_SIGNALS:
95Q_SIGNALS:
89 void surfaceChanged(MirSurfaceItem*);
96 void surfaceChanged(MirSurfaceItem*);
97+ void promptSurfacesChanged();
90
98
91private Q_SLOTS:
99private Q_SLOTS:
92 void onStateChanged(State state);
100 void onStateChanged(State state);
@@ -94,10 +102,15 @@ private Q_SLOTS:
94 void createSurface();
102 void createSurface();
95
103
96private:
104private:
105+ static int promptSurfaceCount(QQmlListProperty<MirSurfaceItem> *prop);
106+ static MirSurfaceItem* promptSurfaceAt(QQmlListProperty<MirSurfaceItem> *prop, int index);
107+
97 QQuickItem *m_parentItem;
108 QQuickItem *m_parentItem;
98 MirSurfaceItem* m_surface;
109 MirSurfaceItem* m_surface;
110+ QList<MirSurfaceItem*> m_promptSurfaces;
99};
111};
100
112
101Q_DECLARE_METATYPE(ApplicationInfo*)
113Q_DECLARE_METATYPE(ApplicationInfo*)
114+Q_DECLARE_METATYPE(QQmlListProperty<MirSurfaceItem>)
102
115
103#endif // APPLICATION_H
116#endif // APPLICATION_H
tests/mocks/Unity/Application/ApplicationManager.cpp+3
@@ -51,6 +51,7 @@ ApplicationManager::ApplicationManager(QObject *parent)
51{
51{
52 m_roleNames.insert(RoleSurface, "surface");
52 m_roleNames.insert(RoleSurface, "surface");
53 m_roleNames.insert(RoleFullscreen, "fullscreen");
53 m_roleNames.insert(RoleFullscreen, "fullscreen");
54+ m_roleNames.insert(RoleApplication, "application");
54
55
55 buildListOfAvailableApplications();
56 buildListOfAvailableApplications();
56
57
@@ -92,6 +93,8 @@ QVariant ApplicationManager::data(const QModelIndex& index, int role) const {
92 return QVariant::fromValue(app->surface());
93 return QVariant::fromValue(app->surface());
93 case RoleFullscreen:
94 case RoleFullscreen:
94 return app->fullscreen();
95 return app->fullscreen();
96+ case RoleApplication:
97+ return QVariant::fromValue(app);
95 default:
98 default:
96 return QVariant();
99 return QVariant();
97 }
100 }
tests/mocks/Unity/Application/ApplicationManager.h+1
@@ -53,6 +53,7 @@ class ApplicationManager : public ApplicationManagerInterface {
53 enum MoreRoles {
53 enum MoreRoles {
54 RoleSurface = RoleScreenshot+1,
54 RoleSurface = RoleScreenshot+1,
55 RoleFullscreen,
55 RoleFullscreen,
56+ RoleApplication,
56 };
57 };
57 enum Role {
58 enum Role {
58 Dash, Default, Indicators, Notifications, Greeter, Launcher, OnScreenKeyboard,
59 Dash, Default, Indicators, Notifications, Greeter, Launcher, OnScreenKeyboard,
tests/mocks/Unity/Application/MirSurfaceItem.cpp+16−8
@@ -34,6 +34,8 @@ MirSurfaceItem::MirSurfaceItem(const QString& name,
34 , m_parentSurface(nullptr)
34 , m_parentSurface(nullptr)
35 , m_haveInputMethod(false)
35 , m_haveInputMethod(false)
36{
36{
37+ qDebug() << "MirSurfaceItem::MirSurfaceItem() " << this->name();
38+
37 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
39 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
38
40
39 // The virtual keyboard (input method) has a big transparent area so that
41 // The virtual keyboard (input method) has a big transparent area so that
@@ -46,6 +48,8 @@ MirSurfaceItem::MirSurfaceItem(const QString& name,
46
48
47MirSurfaceItem::~MirSurfaceItem()
49MirSurfaceItem::~MirSurfaceItem()
48{
50{
51+ qDebug() << "MirSurfaceItem::~MirSurfaceItem() " << name();
52+
49 QList<MirSurfaceItem*> children(m_children);
53 QList<MirSurfaceItem*> children(m_children);
50 for (MirSurfaceItem* child : children) {
54 for (MirSurfaceItem* child : children) {
51 child->setParentSurface(nullptr);
55 child->setParentSurface(nullptr);
@@ -54,7 +58,7 @@ MirSurfaceItem::~MirSurfaceItem()
54 m_parentSurface->removeChildSurface(this);
58 m_parentSurface->removeChildSurface(this);
55
59
56 if (m_application)
60 if (m_application)
57− m_application->setSurface(nullptr);
61+ m_application->removeSurface(this);
58}
62}
59
63
60void MirSurfaceItem::paint(QPainter * painter)
64void MirSurfaceItem::paint(QPainter * painter)
@@ -75,7 +79,7 @@ void MirSurfaceItem::release()
75 }
79 }
76
80
77 if (m_application) {
81 if (m_application) {
78− m_application->setSurface(nullptr);
82+ m_application->removeSurface(this);
79 }
83 }
80 if (!parent()) {
84 if (!parent()) {
81 deleteLater();
85 deleteLater();
@@ -104,20 +108,24 @@ void MirSurfaceItem::setParentSurface(MirSurfaceItem* surface)
104 Q_EMIT parentSurfaceChanged(surface);
108 Q_EMIT parentSurfaceChanged(surface);
105}
109}
106
110
111+void MirSurfaceItem::addChildSurface(MirSurfaceItem* surface)
112+{
113+ qDebug() << "MirSurfaceItem::addChildSurface " << surface->name() << " to " << name();
114+
115+ m_children.append(surface);
116+ Q_EMIT childSurfacesChanged();
117+}
118+
107void MirSurfaceItem::removeChildSurface(MirSurfaceItem* surface)
119void MirSurfaceItem::removeChildSurface(MirSurfaceItem* surface)
108{
120{
121+ qDebug() << "MirSurfaceItem::removeChildSurface " << surface->name() << " from " << name();
122+
109 if (m_children.contains(surface)) {
123 if (m_children.contains(surface)) {
110 m_children.removeOne(surface);
124 m_children.removeOne(surface);
111 Q_EMIT childSurfacesChanged();
125 Q_EMIT childSurfacesChanged();
112 }
126 }
113}
127}
114
128
115−void MirSurfaceItem::addChildSurface(MirSurfaceItem* surface)
116−{
117− m_children.append(surface);
118− Q_EMIT childSurfacesChanged();
119−}
120−
121QList<MirSurfaceItem*> MirSurfaceItem::childSurfaceList()
129QList<MirSurfaceItem*> MirSurfaceItem::childSurfaceList()
122{
130{
123 return m_children;
131 return m_children;
tests/mocks/Unity/Application/MirSurfaceItem.h+1
@@ -117,5 +117,6 @@ private:
117};
117};
118
118
119Q_DECLARE_METATYPE(MirSurfaceItem*)
119Q_DECLARE_METATYPE(MirSurfaceItem*)
120+Q_DECLARE_METATYPE(QList<MirSurfaceItem*>)
120
121
121#endif // MIRSURFACEITEM_H
122#endif // MIRSURFACEITEM_H