on xenial help sign in
Support server->client visibility change to stop rendering (lp:#1475678) Fixes: #1475678
9794345Michał Sawicz
debian/control+1−1
@@ -29,7 +29,7 @@ Build-Depends: cmake,
29 libqt5xmlpatterns5-dev,
29 libqt5xmlpatterns5-dev,
30 libsystemsettings-dev,
30 libsystemsettings-dev,
31 libudev-dev,
31 libudev-dev,
32− libunity-api-dev (>= 7.101),
32+ libunity-api-dev (>= 7.102),
33 libusermetricsoutput1-dev,
33 libusermetricsoutput1-dev,
34 libxcb1-dev,
34 libxcb1-dev,
35 pkg-config,
35 pkg-config,
qml/Stages/DesktopStage.qml+77−7
@@ -68,6 +68,22 @@ AbstractStage {
68 var index = indexOf(focusedAppId);
68 var index = indexOf(focusedAppId);
69 return index >= 0 && index < appRepeater.count ? appRepeater.itemAt(index) : null
69 return index >= 0 && index < appRepeater.count ? appRepeater.itemAt(index) : null
70 }
70 }
71+ property int foregroundMaximizedAppIdIndex: -1
72+
73+ function updateForegroundMaximizedApp() {
74+ for (var i = 0; i < appRepeater.count; i++) {
75+ var item = appRepeater.itemAt(i);
76+
77+ if (item && item.visuallyMaximized) {
78+ var app = ApplicationManager.get(i);
79+ if (app) {
80+ foregroundMaximizedAppIdIndex = i;
81+ return;
82+ }
83+ }
84+ }
85+ foregroundMaximizedAppIdIndex = -1;
86+ }
71
87
72 function indexOf(appId) {
88 function indexOf(appId) {
73 for (var i = 0; i < ApplicationManager.count; i++) {
89 for (var i = 0; i < ApplicationManager.count; i++) {
@@ -114,8 +130,12 @@ AbstractStage {
114 model: ApplicationManager
130 model: ApplicationManager
115 objectName: "appRepeater"
131 objectName: "appRepeater"
116
132
133+ onItemAdded: priv.updateForegroundMaximizedApp()
134+ onItemRemoved: priv.updateForegroundMaximizedApp()
135+
117 delegate: FocusScope {
136 delegate: FocusScope {
118 id: appDelegate
137 id: appDelegate
138+ objectName: "stageDelegate_" + model.appId
119 z: ApplicationManager.count - index
139 z: ApplicationManager.count - index
120 y: units.gu(3)
140 y: units.gu(3)
121 width: units.gu(60)
141 width: units.gu(60)
@@ -126,12 +146,25 @@ AbstractStage {
126 property bool minimized: false
146 property bool minimized: false
127 property bool animationsEnabled: true
147 property bool animationsEnabled: true
128
148
149+ property bool visuallyMaximized: false
150+ property bool visuallyMinimized: false
151+
129 onFocusChanged: {
152 onFocusChanged: {
130 if (focus && ApplicationManager.focusedApplicationId !== model.appId) {
153 if (focus && ApplicationManager.focusedApplicationId !== model.appId) {
131 ApplicationManager.focusApplication(model.appId);
154 ApplicationManager.focusApplication(model.appId);
132 }
155 }
133 }
156 }
134
157
158+ onZChanged: priv.updateForegroundMaximizedApp()
159+ onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp()
160+
161+ visible: !visuallyMinimized &&
162+ !greeter.fullyShown &&
163+ (priv.foregroundMaximizedAppIdIndex === -1 || priv.foregroundMaximizedAppIdIndex >= index) ||
164+ (spread.state == "altTab" && index === spread.highlightedIndex)
165+
166+ onVisibleChanged: console.log("VISIBLE", model.appId, visible)
167+
135 Binding {
168 Binding {
136 target: ApplicationManager.get(index)
169 target: ApplicationManager.get(index)
137 property: "requestedState"
170 property: "requestedState"
@@ -161,23 +194,60 @@ AbstractStage {
161
194
162 states: [
195 states: [
163 State {
196 State {
197+ name: "normal";
164− name: "normal"; when: !appDelegate.maximized && !appDelegate.minimized
198+ when: !appDelegate.maximized && !appDelegate.minimized
199+ PropertyChanges {
200+ target: appDelegate;
201+ visuallyMinimized: false;
202+ visuallyMaximized: false
203+ }
165 },
204 },
166 State {
205 State {
167 name: "maximized"; when: appDelegate.maximized
206 name: "maximized"; when: appDelegate.maximized
207+ PropertyChanges {
208+ target: appDelegate;
209+ x: 0; y: 0;
168− PropertyChanges { target: appDelegate; x: 0; y: 0; width: root.width; height: root.height }
210+ width: root.width; height: root.height;
211+ visuallyMinimized: false;
212+ visuallyMaximized: true
213+ }
169 },
214 },
170 State {
215 State {
171 name: "minimized"; when: appDelegate.minimized
216 name: "minimized"; when: appDelegate.minimized
217+ PropertyChanges {
218+ target: appDelegate;
219+ x: -appDelegate.width / 2;
172− PropertyChanges { target: appDelegate; x: -appDelegate.width / 2; scale: units.gu(5) / appDelegate.width; opacity: 0 }
220+ scale: units.gu(5) / appDelegate.width;
221+ opacity: 0
222+ visuallyMinimized: true;
223+ visuallyMaximized: false
224+ }
173 }
225 }
174 ]
226 ]
175 transitions: [
227 transitions: [
176 Transition {
228 Transition {
177− from: "maximized,minimized,normal,"
229+ to: "normal"
178− to: "maximized,minimized,normal,"
230+ enabled: appDelegate.animationsEnabled
231+ PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
232+ PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale,opacity" }
233+ },
234+ Transition {
235+ to: "maximized"
179 enabled: appDelegate.animationsEnabled
236 enabled: appDelegate.animationsEnabled
237+ PropertyAction { target: appDelegate; property: "visuallyMinimized" }
238+ SequentialAnimation {
180− PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale" }
239+ PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale,opacity" }
240+ PropertyAction { target: appDelegate; property: "visuallyMaximized" }
241+ }
242+ },
243+ Transition {
244+ to: "minimized"
245+ enabled: appDelegate.animationsEnabled
246+ PropertyAction { target: appDelegate; property: "visuallyMaximized" }
247+ SequentialAnimation {
248+ PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale,opacity" }
249+ PropertyAction { target: appDelegate; property: "visuallyMinimized" }
250+ }
181 },
251 },
182 Transition {
252 Transition {
183 from: ""
253 from: ""
@@ -224,7 +294,7 @@ AbstractStage {
224 focus: true
294 focus: true
225
295
226 onClose: ApplicationManager.stopApplication(model.appId)
296 onClose: ApplicationManager.stopApplication(model.appId)
227− onMaximize: appDelegate.maximize()
297+ onMaximize: appDelegate.maximized ? appDelegate.unmaximize() : appDelegate.maximize()
228 onMinimize: appDelegate.minimize()
298 onMinimize: appDelegate.minimize()
229 onDecorationPressed: { ApplicationManager.focusApplication(model.appId) }
299 onDecorationPressed: { ApplicationManager.focusApplication(model.appId) }
230 }
300 }
qml/Stages/PhoneStage.qml+16−5
@@ -20,6 +20,7 @@ import Ubuntu.Gestures 0.1
20import Unity.Application 0.1
20import Unity.Application 0.1
21import Unity.Session 0.1
21import Unity.Session 0.1
22import Utils 0.1
22import Utils 0.1
23+import Powerd 0.1
23import "../Components"
24import "../Components"
24
25
25AbstractStage {
26AbstractStage {
@@ -182,7 +183,8 @@ AbstractStage {
182 }
183 }
183 }
184 }
184
185
185− property bool focusedAppDelegateIsDislocated: focusedAppDelegate && focusedAppDelegate.x !== 0
186+ property bool focusedAppDelegateIsDislocated: focusedAppDelegate &&
187+ (focusedAppDelegate.x !== 0 || focusedAppDelegate.xBehavior.running)
186
188
187 function indexOf(appId) {
189 function indexOf(appId) {
188 for (var i = 0; i < root.applicationManager.count; i++) {
190 for (var i = 0; i < root.applicationManager.count; i++) {
@@ -473,7 +475,6 @@ AbstractStage {
473
475
474 property var xBehavior: xBehavior
476 property var xBehavior: xBehavior
475 Behavior on x {
477 Behavior on x {
476− id: xBehavior
477 enabled: root.spreadEnabled &&
478 enabled: root.spreadEnabled &&
478 !spreadView.active &&
479 !spreadView.active &&
479 !snapAnimation.running &&
480 !snapAnimation.running &&
@@ -481,6 +482,7 @@ AbstractStage {
481 priv.animateX &&
482 priv.animateX &&
482 !root.beingResized
483 !root.beingResized
483 UbuntuNumberAnimation {
484 UbuntuNumberAnimation {
485+ id: xBehavior
484 duration: UbuntuAnimation.BriskDuration
486 duration: UbuntuAnimation.BriskDuration
485 }
487 }
486 }
488 }
@@ -520,9 +522,18 @@ AbstractStage {
520 return progress;
522 return progress;
521 }
523 }
522
524
523− // Hiding tiles when their progress is negative or reached the maximum
525+ // Hide tile when progress is such that it will be off screen.
526+ property bool occluded: {
524− visible: (progress >= 0 && progress < 1.7)
527+ if (spreadView.active && (progress >= 0 && progress < 1.7)) return false;
528+ else if (!spreadView.active && isFocused) return false;
529+ else if (xBehavior.running) return false;
525− || (isDash && priv.focusedAppDelegateIsDislocated)
530+ else if (z <= 1 && priv.focusedAppDelegateIsDislocated) return false;
531+ return true;
532+ }
533+
534+ visible: Powerd.status == Powerd.On &&
535+ !greeter.fullyShown &&
536+ !occluded
526
537
527 shellOrientationAngle: root.shellOrientationAngle
538 shellOrientationAngle: root.shellOrientationAngle
528 shellOrientation: root.shellOrientation
539 shellOrientation: root.shellOrientation
qml/Stages/TabletStage.qml+17−1
@@ -1,4 +1,4 @@
1−/*
1+/*
2 * Copyright (C) 2014-2015 Canonical, Ltd.
2 * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
@@ -19,6 +19,7 @@ import Ubuntu.Components 1.3
19import Ubuntu.Gestures 0.1
19import Ubuntu.Gestures 0.1
20import Unity.Application 0.1
20import Unity.Application 0.1
21import Utils 0.1
21import Utils 0.1
22+import Powerd 0.1
22import "../Components"
23import "../Components"
23
24
24AbstractStage {
25AbstractStage {
@@ -146,6 +147,8 @@ AbstractStage {
146 }
147 }
147 }
148 }
148
149
150+ property bool focusedAppDelegateIsDislocated: focusedAppDelegate &&
151+ (focusedAppDelegate.dragOffset !== 0 || focusedAppDelegate.xTranslateAnimating)
149 function indexOf(appId) {
152 function indexOf(appId) {
150 for (var i = 0; i < ApplicationManager.count; i++) {
153 for (var i = 0; i < ApplicationManager.count; i++) {
151 if (ApplicationManager.get(i).appId == appId) {
154 if (ApplicationManager.get(i).appId == appId) {
@@ -659,6 +662,19 @@ AbstractStage {
659 return tileProgress;
662 return tileProgress;
660 }
663 }
661
664
665+ // TODO: Hiding tile when progress is such that it will be off screen.
666+ property bool occluded: {
667+ if (spreadView.active) return false;
668+ else if (spreadTile.active) return false;
669+ else if (xTranslateAnimating) return false;
670+ else if (z <= 1 && priv.focusedAppDelegateIsDislocated) return false;
671+ return true;
672+ }
673+
674+ visible: Powerd.status == Powerd.On &&
675+ !greeter.fullyShown &&
676+ !occluded
677+
662 animatedProgress: {
678 animatedProgress: {
663 if (spreadView.phase == 0 && (spreadTile.active || spreadView.nextInStack == index)) {
679 if (spreadView.phase == 0 && (spreadTile.active || spreadView.nextInStack == index)) {
664 if (progress < spreadView.positionMarker1) {
680 if (progress < spreadView.positionMarker1) {
qml/Stages/TransformedTabletSpreadDelegate.qml+2
@@ -48,6 +48,7 @@ SpreadDelegate {
48 property bool isInSideStage: false
48 property bool isInSideStage: false
49
49
50 property int dragOffset: 0
50 property int dragOffset: 0
51+ readonly property alias xTranslateAnimating: xTranslateAnimation.running
51
52
52 dropShadow: spreadView.active ||
53 dropShadow: spreadView.active ||
53 (active
54 (active
@@ -144,6 +145,7 @@ SpreadDelegate {
144 spreadView.animateX &&
145 spreadView.animateX &&
145 !spreadView.beingResized
146 !spreadView.beingResized
146 UbuntuNumberAnimation {
147 UbuntuNumberAnimation {
148+ id: xTranslateAnimation
147 duration: UbuntuAnimation.FastDuration
149 duration: UbuntuAnimation.FastDuration
148 }
150 }
149 }
151 }
tests/mocks/Unity/Application/MirSurface.cpp+41−14
@@ -31,7 +31,7 @@ MirSurface::MirSurface(const QString& name,
31 , m_screenshotUrl(screenshot)
31 , m_screenshotUrl(screenshot)
32 , m_qmlFilePath(qmlFilePath)
32 , m_qmlFilePath(qmlFilePath)
33 , m_live(true)
33 , m_live(true)
34− , m_viewCount(0)
34+ , m_visible(true)
35 , m_activeFocus(false)
35 , m_activeFocus(false)
36 , m_width(-1)
36 , m_width(-1)
37 , m_height(-1)
37 , m_height(-1)
@@ -73,6 +73,11 @@ bool MirSurface::live() const
73 return m_live;
73 return m_live;
74}
74}
75
75
76+bool MirSurface::visible() const
77+{
78+ return m_visible;
79+}
80+
76void MirSurface::setLive(bool live)
81void MirSurface::setLive(bool live)
77{
82{
78// qDebug().nospace() << "MirSurface::setLive("<<live<<") " << name();
83// qDebug().nospace() << "MirSurface::setLive("<<live<<") " << name();
@@ -82,7 +87,7 @@ void MirSurface::setLive(bool live)
82 m_live = live;
87 m_live = live;
83 Q_EMIT liveChanged(live);
88 Q_EMIT liveChanged(live);
84
89
85− if (!m_live && m_viewCount == 0) {
90+ if (!m_live && m_views.count() == 0) {
86 deleteLater();
91 deleteLater();
87 }
92 }
88}
93}
@@ -120,27 +125,49 @@ void MirSurface::setOrientationAngle(Mir::OrientationAngle angle)
120 Q_EMIT orientationAngleChanged(angle);
125 Q_EMIT orientationAngleChanged(angle);
121}
126}
122
127
123−void MirSurface::incrementViewCount()
128+
129+
130+void MirSurface::registerView(qintptr viewId)
124{
131{
125− ++m_viewCount;
132+ m_views.insert(viewId, MirSurface::View{false});
126−// qDebug().nospace() << "MirSurface::incrementViewCount() viewCount(after)=" << m_viewCount << " " << name();
133+// qDebug().nospace() << "MirSurface[" << name() << "]::registerView(" << viewId << ")"
134+// << " after=" << m_views.count();
127}
135}
128
136
129−void MirSurface::decrementViewCount()
137+void MirSurface::unregisterView(qintptr viewId)
130{
138{
131− --m_viewCount;
139+// qDebug().nospace() << "MirSurface[" << name() << "]::unregisterView(" << viewId << ")"
132−// qDebug().nospace() << "MirSurface::decrementViewCount() viewCount(after)=" << m_viewCount << " " << name();
140+// << " after=" << m_views.count() << " live=" << m_live;
133−
141+ m_views.remove(viewId);
134− Q_ASSERT(m_viewCount >= 0);
135−
136− if (!m_live && m_viewCount == 0) {
142+ if (!m_live && m_views.count() == 0) {
137 deleteLater();
143 deleteLater();
138 }
144 }
145+ updateVisibility();
139}
146}
140
147
141−int MirSurface::viewCount() const
148+void MirSurface::setViewVisibility(qintptr viewId, bool visible)
142{
149{
143− return m_viewCount;
150+ if (!m_views.contains(viewId)) return;
151+
152+ m_views[viewId].visible = visible;
153+ updateVisibility();
154+}
155+
156+void MirSurface::updateVisibility()
157+{
158+ bool newVisible = false;
159+ QHashIterator<qintptr, View> i(m_views);
160+ while (i.hasNext()) {
161+ i.next();
162+ newVisible |= i.value().visible;
163+ }
164+
165+ if (newVisible != visible()) {
166+// qDebug().nospace() << "MirSurface[" << name() << "]::updateVisibility(" << newVisible << ")";
167+
168+ m_visible = newVisible;
169+ Q_EMIT visibleChanged(m_visible);
170+ }
144}
171}
145
172
146bool MirSurface::activeFocus() const
173bool MirSurface::activeFocus() const
tests/mocks/Unity/Application/MirSurface.h+14−4
@@ -19,6 +19,7 @@
19
19
20#include <QObject>
20#include <QObject>
21#include <QUrl>
21#include <QUrl>
22+#include <QHash>
22
23
23// unity-api
24// unity-api
24#include <unity/shell/application/MirSurfaceInterface.h>
25#include <unity/shell/application/MirSurfaceInterface.h>
@@ -58,6 +59,8 @@ public:
58
59
59 bool live() const override;
60 bool live() const override;
60
61
62+ bool visible() const override;
63+
61 Mir::OrientationAngle orientationAngle() const override;
64 Mir::OrientationAngle orientationAngle() const override;
62 void setOrientationAngle(Mir::OrientationAngle) override;
65 void setOrientationAngle(Mir::OrientationAngle) override;
63
66
@@ -66,9 +69,10 @@ public:
66
69
67 Q_INVOKABLE void setLive(bool live);
70 Q_INVOKABLE void setLive(bool live);
68
71
69− void incrementViewCount();
72+ void registerView(qintptr viewId);
70− void decrementViewCount();
73+ void unregisterView(qintptr viewId);
74+ void setViewVisibility(qintptr viewId, bool visible);
71− int viewCount() const;
75+ int viewCount() const { return m_views.count(); }
72
76
73 int width() const;
77 int width() const;
74 int height() const;
78 int height() const;
@@ -97,6 +101,8 @@ Q_SIGNALS:
97 void activeFocusChanged(bool);
101 void activeFocusChanged(bool);
98
102
99private:
103private:
104+ void updateVisibility();
105+
100 const QString m_name;
106 const QString m_name;
101 const Mir::Type m_type;
107 const Mir::Type m_type;
102 Mir::State m_state;
108 Mir::State m_state;
@@ -104,10 +110,14 @@ private:
104 QUrl m_screenshotUrl;
110 QUrl m_screenshotUrl;
105 QUrl m_qmlFilePath;
111 QUrl m_qmlFilePath;
106 bool m_live;
112 bool m_live;
107− int m_viewCount;
113+ bool m_visible;
108 bool m_activeFocus;
114 bool m_activeFocus;
109 int m_width;
115 int m_width;
110 int m_height;
116 int m_height;
117+ struct View {
118+ bool visible;
119+ };
120+ QHash<qintptr, View> m_views;
111};
121};
112
122
113#endif // MOCK_MIR_SURFACE_H
123#endif // MOCK_MIR_SURFACE_H
tests/mocks/Unity/Application/MirSurfaceItem.cpp+12−2
@@ -44,6 +44,8 @@ MirSurfaceItem::MirSurfaceItem(QQuickItem *parent)
44 Qt::ExtraButton5 | Qt::ExtraButton6 | Qt::ExtraButton7 | Qt::ExtraButton8 |
44 Qt::ExtraButton5 | Qt::ExtraButton6 | Qt::ExtraButton7 | Qt::ExtraButton8 |
45 Qt::ExtraButton9 | Qt::ExtraButton10 | Qt::ExtraButton11 |
45 Qt::ExtraButton9 | Qt::ExtraButton10 | Qt::ExtraButton11 |
46 Qt::ExtraButton12 | Qt::ExtraButton13);
46 Qt::ExtraButton12 | Qt::ExtraButton13);
47+
48+ connect(this, &QQuickItem::visibleChanged, this, &MirSurfaceItem::updateMirSurfaceVisibility);
47}
49}
48
50
49MirSurfaceItem::~MirSurfaceItem()
51MirSurfaceItem::~MirSurfaceItem()
@@ -194,17 +196,18 @@ void MirSurfaceItem::setSurface(MirSurfaceInterface* surface)
194 m_qmlContentComponent = nullptr;
196 m_qmlContentComponent = nullptr;
195
197
196 disconnect(m_qmlSurface, nullptr, this, nullptr);
198 disconnect(m_qmlSurface, nullptr, this, nullptr);
197− m_qmlSurface->decrementViewCount();
199+ m_qmlSurface->unregisterView((qintptr)this);
198 }
200 }
199
201
200 m_qmlSurface = static_cast<MirSurface*>(surface);
202 m_qmlSurface = static_cast<MirSurface*>(surface);
201
203
202 if (m_qmlSurface) {
204 if (m_qmlSurface) {
203− m_qmlSurface->incrementViewCount();
205+ m_qmlSurface->registerView((qintptr)this);
204
206
205 m_qmlSurface->setActiveFocus(hasActiveFocus());
207 m_qmlSurface->setActiveFocus(hasActiveFocus());
206
208
207 updateSurfaceSize();
209 updateSurfaceSize();
210+ updateMirSurfaceVisibility();
208
211
209 connect(m_qmlSurface, &MirSurface::orientationAngleChanged, this, &MirSurfaceItem::orientationAngleChanged);
212 connect(m_qmlSurface, &MirSurface::orientationAngleChanged, this, &MirSurfaceItem::orientationAngleChanged);
210 connect(m_qmlSurface, &MirSurface::screenshotUrlChanged, this, &MirSurfaceItem::updateScreenshot);
213 connect(m_qmlSurface, &MirSurface::screenshotUrlChanged, this, &MirSurfaceItem::updateScreenshot);
@@ -253,6 +256,13 @@ void MirSurfaceItem::itemChange(ItemChange change, const ItemChangeData & value)
253 }
256 }
254}
257}
255
258
259+void MirSurfaceItem::updateMirSurfaceVisibility()
260+{
261+ if (!m_qmlSurface) return;
262+
263+ m_qmlSurface->setViewVisibility((qintptr)this, isVisible());
264+}
265+
256void MirSurfaceItem::setConsumesInput(bool value)
266void MirSurfaceItem::setConsumesInput(bool value)
257{
267{
258 if (m_consumesInput != value) {
268 if (m_consumesInput != value) {
tests/mocks/Unity/Application/MirSurfaceItem.h+1
@@ -87,6 +87,7 @@ protected:
87private Q_SLOTS:
87private Q_SLOTS:
88 void onComponentStatusChanged(QQmlComponent::Status status);
88 void onComponentStatusChanged(QQmlComponent::Status status);
89 void updateScreenshot(QUrl screenshot);
89 void updateScreenshot(QUrl screenshot);
90+ void updateMirSurfaceVisibility();
90
91
91private:
92private:
92 void createQmlContentItem();
93 void createQmlContentItem();
tests/qmltests/Stages/tst_DesktopStage.qml+81−5
@@ -23,24 +23,31 @@ import Unity.Test 0.1
23import Utils 0.1
23import Utils 0.1
24
24
25import "../../../qml/Stages"
25import "../../../qml/Stages"
26+import "../../../qml/Components"
26
27
27Item {
28Item {
28 id: root
29 id: root
29 width: desktopStageLoader.width + controls.width
30 width: desktopStageLoader.width + controls.width
30 height: desktopStageLoader.height
31 height: desktopStageLoader.height
31
32
33+ property var greeter: { fullyShown: true }
34+
32 Binding {
35 Binding {
33 target: MouseTouchAdaptor
36 target: MouseTouchAdaptor
34 property: "enabled"
37 property: "enabled"
35 value: false
38 value: false
36 }
39 }
37
40
38− Component.onCompleted: {
41+ Component.onCompleted: resetGeometry()
42+
43+ function resetGeometry() {
39 // ensures apps which are tested decorations are in view.
44 // ensures apps which are tested decorations are in view.
45+ WindowStateStorage.clear();
40 WindowStateStorage.geometry = {
46 WindowStateStorage.geometry = {
41 'unity8-dash': Qt.rect(0, units.gu(3), units.gu(50), units.gu(40)),
47 'unity8-dash': Qt.rect(0, units.gu(3), units.gu(50), units.gu(40)),
42 'dialer-app': Qt.rect(units.gu(51), units.gu(3), units.gu(50), units.gu(40)),
48 'dialer-app': Qt.rect(units.gu(51), units.gu(3), units.gu(50), units.gu(40)),
43 'camera-app': Qt.rect(0, units.gu(44), units.gu(50), units.gu(40)),
49 'camera-app': Qt.rect(0, units.gu(44), units.gu(50), units.gu(40)),
50+ 'gallery-app': Qt.rect(units.gu(51), units.gu(44), units.gu(50), units.gu(40))
44 }
51 }
45 }
52 }
46
53
@@ -61,6 +68,7 @@ Item {
61 Component.onDestruction: {
68 Component.onDestruction: {
62 desktopStageLoader.itemDestroyed = true;
69 desktopStageLoader.itemDestroyed = true;
63 }
70 }
71+ orientations: Orientations {}
64 }
72 }
65 }
73 }
66 }
74 }
@@ -110,14 +118,14 @@ Item {
110
118
111 desktopStageLoader.active = true;
119 desktopStageLoader.active = true;
112 tryCompare(desktopStageLoader, "status", Loader.Ready);
120 tryCompare(desktopStageLoader, "status", Loader.Ready);
121+ root.resetGeometry();
113 }
122 }
114
123
115 function killAllRunningApps() {
124 function killAllRunningApps() {
116− while (ApplicationManager.count > 1) {
125+ while (ApplicationManager.count > 0) {
117− var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
118− ApplicationManager.stopApplication(ApplicationManager.get(appIndex).appId);
126+ ApplicationManager.stopApplication(ApplicationManager.get(0).appId);
119 }
127 }
120− compare(ApplicationManager.count, 1)
128+ compare(ApplicationManager.count, 0)
121 }
129 }
122
130
123 function waitUntilAppSurfaceShowsUp(appId) {
131 function waitUntilAppSurfaceShowsUp(appId) {
@@ -210,5 +218,73 @@ Item {
210 tap(toAppDecoration);
218 tap(toAppDecoration);
211 tryCompare(ApplicationManager.findApplication(data.apps[data.focusTo]).session.surface, "activeFocus", true);
219 tryCompare(ApplicationManager.findApplication(data.apps[data.focusTo]).session.surface, "activeFocus", true);
212 }
220 }
221+
222+ function test_minimizeApplicationHidesSurface() {
223+ var dashApp = startApplication("unity8-dash");
224+
225+ var dashDelegate = findChild(desktopStage, "stageDelegate_unity8-dash");
226+ verify(dashDelegate);
227+
228+ findChild(dashDelegate, "decoratedWindow").minimize();
229+ tryCompare(dashApp.session.surface, "visible", false);
230+ }
231+
232+ function test_maximizeApplicationHidesSurfacesBehindIt() {
233+ var dashApp = startApplication("unity8-dash");
234+ var dialerApp = startApplication("dialer-app");
235+ var cameraApp = startApplication("camera-app");
236+
237+ var dashDelegate = findChild(desktopStage, "stageDelegate_unity8-dash");
238+ verify(dashDelegate);
239+ var dialerDelegate = findChild(desktopStage, "stageDelegate_dialer-app");
240+ verify(dialerDelegate);
241+ var cameraDelegate = findChild(desktopStage, "stageDelegate_camera-app");
242+ verify(cameraDelegate);
243+
244+ // maximize
245+ findChild(dialerDelegate, "decoratedWindow").maximize();
246+ tryCompare(dialerDelegate, "visuallyMaximized", true);
247+
248+ tryCompare(dashApp.session.surface, "visible", false);
249+ compare(cameraApp.session.surface.visible, true);
250+
251+ // restore
252+ findChild(dialerDelegate, "decoratedWindow").maximize();
253+ compare(dashApp.session.surface.visible, true);
254+ compare(cameraApp.session.surface.visible, true);
255+ }
256+
257+ function test_applicationsBecomeVisibleWhenOccludingAppRemoved() {
258+ var dashApp = startApplication("unity8-dash");
259+ var dashDelegate = findChild(desktopStage, "stageDelegate_unity8-dash");
260+ verify(dashDelegate);
261+
262+ var dialerApp = startApplication("dialer-app");
263+ var dialerDelegate = findChild(desktopStage, "stageDelegate_dialer-app");
264+ verify(dialerDelegate);
265+
266+ var cameraApp = startApplication("camera-app");
267+ var cameraDelegate = findChild(desktopStage, "stageDelegate_camera-app");
268+ verify(cameraDelegate);
269+ findChild(dialerDelegate, "decoratedWindow").maximize();
270+
271+ var galleryApp = startApplication("gallery-app");
272+ var galleryDelegate = findChild(desktopStage, "stageDelegate_gallery-app");
273+ verify(galleryDelegate);
274+ findChild(galleryDelegate, "decoratedWindow").maximize();
275+
276+ tryCompare(dialerDelegate, "visuallyMaximized", true);
277+ tryCompare(galleryDelegate, "visuallyMaximized", true);
278+
279+ tryCompare(dashApp.session.surface, "visible", false);
280+ tryCompare(dialerApp.session.surface, "visible", false);
281+ tryCompare(cameraApp.session.surface, "visible", false);
282+
283+ ApplicationManager.stopApplication("gallery-app");
284+
285+ compare(cameraApp.session.surface.visible, true);
286+ tryCompare(dialerApp.session.surface, "visible", true);
287+ tryCompare(dashApp.session.surface, "visible", false); // still occluded by maximised dialer
288+ }
213 }
289 }
214}
290}
tests/qmltests/Stages/tst_PhoneStage.qml+4
@@ -18,6 +18,7 @@ import QtQuick 2.4
18import QtTest 1.0
18import QtTest 1.0
19import Unity.Test 0.1 as UT
19import Unity.Test 0.1 as UT
20import ".."
20import ".."
21+import "../../../qml/Components"
21import "../../../qml/Stages"
22import "../../../qml/Stages"
22import Ubuntu.Components 1.3
23import Ubuntu.Components 1.3
23import Unity.Application 0.1
24import Unity.Application 0.1
@@ -26,6 +27,8 @@ Item {
26 width: units.gu(70)
27 width: units.gu(70)
27 height: units.gu(70)
28 height: units.gu(70)
28
29
30+ property var greeter: { fullyShown: true }
31+
29 PhoneStage {
32 PhoneStage {
30 id: phoneStage
33 id: phoneStage
31 anchors { fill: parent; rightMargin: units.gu(30) }
34 anchors { fill: parent; rightMargin: units.gu(30) }
@@ -34,6 +37,7 @@ Item {
34 maximizedAppTopMargin: units.gu(3) + units.dp(2)
37 maximizedAppTopMargin: units.gu(3) + units.dp(2)
35 interactive: true
38 interactive: true
36 shellOrientation: Qt.PortraitOrientation
39 shellOrientation: Qt.PortraitOrientation
40+ orientations: Orientations {}
37 }
41 }
38
42
39 Binding {
43 Binding {
tests/qmltests/Stages/tst_TabletStage.qml+6−1
@@ -30,6 +30,8 @@ Rectangle {
30 width: tabletStageLoader.width + controls.width
30 width: tabletStageLoader.width + controls.width
31 height: tabletStageLoader.height
31 height: tabletStageLoader.height
32
32
33+ property var greeter: { fullyShown: true }
34+
33 Loader {
35 Loader {
34 id: tabletStageLoader
36 id: tabletStageLoader
35
37
@@ -53,7 +55,10 @@ Rectangle {
53 shellOrientation: Qt.LandscapeOrientation
55 shellOrientation: Qt.LandscapeOrientation
54 nativeWidth: width
56 nativeWidth: width
55 nativeHeight: height
57 nativeHeight: height
56− orientations: Orientations{} // Defaults are fine for testing
58+ orientations: Orientations {
59+ native_: Qt.LandscapeOrientation
60+ primary: Qt.LandscapeOrientation
61+ }
57 focus: true
62 focus: true
58 }
63 }
59 }
64 }
tests/qmltests/tst_Shell.qml+14−2
@@ -61,6 +61,10 @@ Rectangle {
61
61
62 anchors.centerIn: parent
62 anchors.centerIn: parent
63
63
64+ property int shellOrientation: Qt.PortraitOrientation
65+ property int nativeOrientation: Qt.PortraitOrientation
66+ property int primaryOrientation: Qt.PortraitOrientation
67+
64 state: "phone"
68 state: "phone"
65 states: [
69 states: [
66 State {
70 State {
@@ -77,6 +81,9 @@ Rectangle {
77 target: shellLoader
81 target: shellLoader
78 width: units.gu(100)
82 width: units.gu(100)
79 height: units.gu(71)
83 height: units.gu(71)
84+ shellOrientation: Qt.LandscapeOrientation
85+ nativeOrientation: Qt.LandscapeOrientation
86+ primaryOrientation: Qt.LandscapeOrientation
80 }
87 }
81 },
88 },
82 State {
89 State {
@@ -99,8 +106,13 @@ Rectangle {
99 Shell {
106 Shell {
100 id: __shell
107 id: __shell
101 usageScenario: usageScenarioSelector.model[usageScenarioSelector.selectedIndex]
108 usageScenario: usageScenarioSelector.model[usageScenarioSelector.selectedIndex]
102− orientation: Qt.PortraitOrientation
109+ nativeWidth: width
110+ nativeHeight: height
111+ orientation: shellLoader.shellOrientation
103− orientations: Orientations{} // Defaults are fine for testing
112+ orientations: Orientations {
113+ native_: shellLoader.nativeOrientation
114+ primary: shellLoader.primaryOrientation
115+ }
104 Component.onDestruction: {
116 Component.onDestruction: {
105 shellLoader.itemDestroyed = true;
117 shellLoader.itemDestroyed = true;
106 }
118 }