on xenial help sign in
try to get the sidestage on tablet working as before
commit1cd43133e5f5548783a43515f25d5ca8c223ddf7 authorMichael Zanetti <michael.zanetti@canonical.com> dateMon 10 Feb 2014, 13:38 (13 years ago) parentfcd72d0
8 files changed+476−16
cmake/modules/QmlTest.cmake−1 qml/Dash/Apps/RunningApplicationsGrid.qml+1−1 qml/Shell.qml+38−7 qml/Stages/StageWithSideStage.qml+360 qml/Stages/SwitchingApplicationImage.qml+65 tests/mocks/Unity/Application/ApplicationInfo.h−1 tests/mocks/Unity/Application/ApplicationManager.cpp+3−2 tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp+9−4
cmake/modules/QmlTest.cmake−1
@@ -86,7 +86,6 @@ macro(add_qml_test SUBPATH COMPONENT_NAME)
8686 ${qmltestrunner_imports}
8787 -o ${CMAKE_BINARY_DIR}/${qmltest_TARGET}.xml,xunitxml
8888 -o -,txt
89− $(TESTCASE)
9089 )
9190 add_custom_target(${qmltest_TARGET} ${qmltest_command})
9291
qml/Dash/Apps/RunningApplicationsGrid.qml+1−1
@@ -77,7 +77,7 @@ ResponsiveFlowView {
7777 ApplicationManager.stopApplication(model.appId)
7878 }
7979 onRequestedApplicationActivation: {
80− ApplicationManager.focusApplication(model.appId)
80+ ApplicationManager.activateApplication(model.appId)
8181 }
8282
8383 terminationModeEnabled: root.terminationModeEnabled
qml/Shell.qml+38−7
@@ -39,7 +39,7 @@ FocusScope {
3939 id: shell
4040
4141 // this is only here to select the width / height of the window if not running fullscreen
42− property bool tablet: false
42+ property bool tablet: true
4343 width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
4444 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
4545
@@ -113,10 +113,17 @@ FocusScope {
113113 Keys.onVolumeUpPressed: volumeControl.volumeUp()
114114 Keys.onVolumeDownPressed: volumeControl.volumeDown()
115115
116+ Item {
117+ id: underlayClipper
118+ anchors.fill: parent
119+ anchors.rightMargin: stages.overlayWidth
120+ clip: stages.overlayMode && !stages.painting
121+
116122 Item {
117123 id: underlay
118124 objectName: "underlay"
119125 anchors.fill: parent
126+ anchors.rightMargin: -parent.anchors.rightMargin
120127
121128 // Whether the underlay is fully covered by opaque UI elements.
122129 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
@@ -124,7 +131,7 @@ FocusScope {
124131 readonly property bool applicationRunning: ApplicationManager.focusedApplicationId.length > 0
125132
126133 // Whether the user should see the topmost application surface (if there's one at all).
127− readonly property bool applicationSurfaceShouldBeSeen: stages.shown && !stages.painting
134+ readonly property bool applicationSurfaceShouldBeSeen: stages.shown && !stages.painting && !stages.overlayMode
128135
129136 // NB! Application surfaces are stacked behind the shell one. So they can only be seen by the user
130137 // through the translucent parts of the shell surface.
@@ -161,13 +168,23 @@ FocusScope {
161168
162169 contentScale: 1.0 - 0.2 * disappearingAnimationProgress
163170 opacity: 1.0 - disappearingAnimationProgress
164− property real disappearingAnimationProgress: greeter.shown ? greeter.showProgress : stages.showProgress
171+ property real disappearingAnimationProgress: {
172+ if (greeter.shown) {
173+ return greeter.showProgress;
174+ } else {
175+ if (stages.overlayMode) {
176+ return 0;
177+ }
178+ return stages.showProgress
179+ }
180+ }
165181
166182 // FIXME: only necessary because stages.showProgress and
167183 // greeterRevealer.animatedProgress are not animated
168184 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
169185 }
170186 }
187+ }
171188
172189 EdgeDragArea {
173190 id: stagesDragHandle
@@ -204,9 +221,18 @@ FocusScope {
204221 width: parent.width
205222 height: parent.height
206223
207− x: shown ? launcher.progress : stagesDragHandle.progress
224+ x: {
225+ if (shown) {
226+ if (overlayMode) {
227+ return 0;
228+ }
229+ return launcher.progress
230+ } else {
231+ return stagesDragHandle.progress
232+ }
233+ }
208234
209− Behavior on x { SmoothedAnimation { velocity: 600; duration: UbuntuAnimation.FastDuration } }
235+ Behavior on x { SmoothedAnimation { velocity: 600; duration: UbuntuAnimation.SlowDuration } }
210236
211237 property bool shown: false
212238
@@ -217,6 +243,8 @@ FocusScope {
217243
218244 property bool painting: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.painting : false
219245 property bool fullscreen: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.fullscreen : false
246+ property bool overlayMode: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.overlayMode : false
247+ property int overlayWidth: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.overlayWidth : false
220248
221249 function show() {
222250 shown = true;
@@ -239,7 +267,10 @@ FocusScope {
239267 if (ApplicationManager.focusedApplicationId.length > 0) {
240268 stages.show();
241269 } else {
242− stages.hide();
270+ print("app unfocused", stages.overlayMode)
271+ if (!stages.overlayMode) {
272+ stages.hide();
273+ }
243274 }
244275 }
245276
@@ -515,7 +546,7 @@ FocusScope {
515546 showHome()
516547 }
517548 onDash: {
518− if (stages.shown) {
549+ if (stages.shown && !stages.overlayMode) {
519550 stages.hide();
520551 launcher.hide();
521552 }
qml/Stages/StageWithSideStage.qmlnew file+360
@@ -0,0 +1,360 @@
1+import QtQuick 2.0
2+import Ubuntu.Components 0.1
3+import "../Components"
4+import "../SideStage"
5+import Unity.Application 0.1
6+import Ubuntu.Gestures 0.1
7+
8+Item {
9+ id: root
10+ objectName: "stages"
11+ anchors.fill: parent
12+
13+ // Controls to be set from outside
14+ property bool shown: false
15+ property bool moving: false
16+ property int dragAreaWidth
17+
18+ // State information propagated to the outside
19+ readonly property bool painting: mainStageImage.visible || sideStageImage.visible || sideStageSnapAnimation.running
20+ property bool fullscreen: priv.focusedApplication ? priv.focusedApplication.fullscreen : false
21+ property bool overlayMode: (sideStageImage.shown && priv.mainStageAppId.length == 0) || priv.overlayOverride
22+
23+ readonly property int overlayWidth: priv.overlayOverride ? 0 : priv.sideStageWidth
24+
25+
26+ onShownChanged: {
27+// print("shown", shown)
28+ if (!shown) {
29+ priv.mainStageAppId = "";
30+ }
31+ }
32+
33+ onMovingChanged: {
34+// print("moving", moving, priv.haveMainStageApp, priv.haveSideStageApp)
35+ if (moving) {
36+ if (!priv.mainStageAppId && !priv.sideStageAppId) {
37+ // Pulling in from the right, make the last used (topmost) app visible
38+ var application = ApplicationManager.get(0);
39+ if (application.stage == ApplicationInfoInterface.SideStage) {
40+ sideStageImage.application = application;
41+ sideStageImage.x = root.width - sideStageImage.width
42+ sideStageImage.visible = true;
43+ } else {
44+ mainStageImage.application = application;
45+ mainStageImage.visible = true;
46+ }
47+ } else {
48+ priv.requestNewScreenshot(ApplicationInfoInterface.MainStage)
49+ if (priv.focusedApplicationId == priv.sideStageAppId) {
50+ priv.requestNewScreenshot(ApplicationInfoInterface.SideStage)
51+ }
52+ }
53+ } else {
54+ mainStageImage.visible = false;
55+ sideStageImage.visible = false;
56+ }
57+ }
58+
59+// Rectangle { anchors.fill: parent; color: "blue"; opacity: 0.5 }
60+
61+ QtObject {
62+ id: priv
63+
64+ property int sideStageWidth: units.gu(40)
65+
66+
67+ property string sideStageAppId
68+ property string mainStageAppId
69+
70+
71+ property var sideStageApp: ApplicationManager.findApplication(sideStageAppId)
72+ property var mainStageApp: ApplicationManager.findApplication(mainStageAppId)
73+
74+ property string sideStageScreenshot: sideStageApp ? sideStageApp.screenshot : ""
75+ property string mainStageScreenshot: mainStageApp ? mainStageApp.screenshot : ""
76+
77+ property string focusedApplicationId: ApplicationManager.focusedApplicationId
78+ property var focusedApplication: ApplicationManager.findApplication(focusedApplicationId)
79+ property url focusedScreenshot: focusedApplication ? focusedApplication.screenshot : ""
80+
81+ property bool waitingForMainScreenshot: false
82+ property bool waitingForSideScreenshot: false
83+ property bool waitingForScreenshots: waitingForMainScreenshot || waitingForSideScreenshot
84+
85+
86+ // Keep overlayMode even if there is no focused app (to allow pulling in the sidestage from the right)
87+ property bool overlayOverride: false
88+
89+ onFocusedApplicationChanged: {
90+ if (focusedApplication) {
91+ if (focusedApplication.stage == ApplicationInfoInterface.MainStage) {
92+ mainStageAppId = focusedApplicationId;
93+ } else if (focusedApplication.stage == ApplicationInfoInterface.SideStage) {
94+ sideStageAppId = focusedApplicationId;
95+ }
96+ }
97+ }
98+
99+ onMainStageScreenshotChanged: {
100+ print("------------------------------------------0")
101+ waitingForMainScreenshot = false;
102+ }
103+
104+ onSideStageScreenshotChanged: {
105+ print("------------------------------------------1")
106+ waitingForSideScreenshot = false;
107+ }
108+
109+ onFocusedScreenshotChanged: {
110+ print("##########################################", root.overlayMode)
111+ waitingForSideScreenshot = false;
112+ }
113+
114+ onWaitingForScreenshotsChanged: {
115+ print("********************++ waitingForScreenshotsChanged:", waitingForScreenshots);
116+ if (waitingForScreenshots) {
117+ return;
118+ }
119+
120+ if (root.moving) {
121+ if (mainStageAppId) {
122+ mainStageImage.application = mainStageApp
123+ mainStageImage.visible = true;
124+ }
125+ if (sideStageAppId && focusedApplicationId == sideStageAppId) {
126+ sideStageImage.application = sideStageApp;
127+ sideStageImage.x = root.width - sideStageImage.width
128+ print("setting side stage screenshot visiblr")
129+ sideStageImage.visible = true;
130+ }
131+ }
132+ if (sideStageHandleMouseArea.pressed) {
133+ if (sideStageAppId) {
134+ print("setting side stage screenshot to", sideStageAppId)
135+ sideStageImage.application = sideStageApp;
136+ sideStageImage.x = root.width - sideStageImage.width
137+ print("setting side stage screenshot visiblr")
138+ sideStageImage.visible = true;
139+ }
140+ if (mainStageAppId) {
141+ mainStageImage.application = mainStageApp
142+ mainStageImage.visible = true;
143+ }
144+ }
145+ }
146+
147+ function requestNewScreenshot(stage) {
148+ print("** requestNewScreenshot called for stage", stage, mainStageAppId, sideStageAppId)
149+ if (stage == ApplicationInfoInterface.MainStage && mainStageAppId) {
150+ print("requesting new MainStage screenshot")
151+ waitingForMainScreenshot = true;
152+ ApplicationManager.updateScreenshot(mainStageAppId);
153+ } else if (stage == ApplicationInfoInterface.SideStage && sideStageAppId) {
154+ print("requesting new SideStage screenshot")
155+ waitingForSideScreenshot = true;
156+ ApplicationManager.updateScreenshot(sideStageAppId);
157+ }
158+ }
159+
160+ }
161+ // FIXME: the signal connection seems to get lost with the fake application manager.
162+ // Check with Qt 5.2, see if we can remove this Connections object
163+ Connections {
164+ target: priv.sideStageApp
165+ onScreenshotChanged: priv.sideStageScreenshot = priv.sideStageApp.screenshot
166+ }
167+ Connections {
168+ target: priv.mainStageApp
169+ onScreenshotChanged: priv.mainStageScreenshot = priv.mainStageApp.screenshot
170+ }
171+
172+ Connections {
173+ target: ApplicationManager
174+
175+// onApplicationAdded: {
176+// var application = ApplicationManager.findApplication(appId)
177+// print("Application added:", appId, application.state)
178+// if (application.stage == ApplicationInfoInterface.SideStage) {
179+// priv.sideStageAppId = appId;
180+// mainStageImage.switchTo(application)
181+// mainStageImage.visible = true;
182+// } else if (application.stage == ApplicationInfoInterface.MainStage) {
183+// priv.mainStageAppId = appId;
184+// sideStageImage.switchTo(application)
185+// sideStageImage.visible = true;
186+// }
187+// }
188+
189+ onFocusRequested: {
190+ print("focus requested by", appId)
191+
192+ var application = ApplicationManager.findApplication(appId)
193+ if (application.stage == ApplicationInfoInterface.SideStage) {
194+ if (sideStageImage.shown) {
195+ print("switching to:", appId)
196+ sideStageImage.switchTo(application)
197+ mainStageImage.visible = true;
198+ ApplicationManager.focusApplication(appId)
199+ } else {
200+ sideStageImage.application = application
201+ sideStageImage.snapToApp(application)
202+ }
203+ } else if (application.stage == ApplicationInfoInterface.MainStage) {
204+ if (root.shown) {
205+ priv.mainStageAppId = application.appId;
206+ mainStageImage.switchTo(application)
207+ if (sideStageImage.shown) {
208+ sideStageImage.snapTo(root.width)
209+ } else {
210+ ApplicationManager.focusApplication(appId)
211+ }
212+ } else {
213+ ApplicationManager.focusApplication(appId)
214+ }
215+ }
216+ priv.overlayOverride = false;
217+ }
218+
219+ onApplicationRemoved: {
220+ if (priv.mainStageAppId == appId) {
221+ priv.mainStageAppId = "";
222+ }
223+ if (priv.sideStageAppId == appId) {
224+ priv.sideStageAppId = "";
225+ }
226+ }
227+
228+ }
229+
230+ SwitchingApplicationImage {
231+ id: mainStageImage
232+ height: parent.height
233+ width: parent.width
234+ visible: false
235+
236+ onSwitched: {
237+ sideStageImage.visible = false;
238+ }
239+
240+// Rectangle { anchors.fill: parent; color: "red"; opacity: 0.5 }
241+ }
242+
243+ SidestageHandle {
244+ id: sideStageHandle
245+ anchors { top: parent.top; right: sideStageImage.left; bottom: parent.bottom }
246+ width: root.dragAreaWidth
247+ visible: root.shown && priv.sideStageAppId
248+
249+ }
250+ MouseArea {
251+ id: sideStageHandleMouseArea
252+ anchors { top: parent.top; right: parent.right; bottom: parent.bottom; rightMargin: sideStageImage.shown ? sideStageImage.width : 0}
253+ width: root.dragAreaWidth
254+ visible: priv.sideStageAppId
255+
256+ property var dragPoints: new Array()
257+
258+ onPressed: {
259+ print("sidestagehandle pressed. shown:", shown, "sidestageAppId:", priv.sideStageAppId)
260+ priv.requestNewScreenshot(ApplicationInfoInterface.SideStage)
261+ }
262+
263+ onMouseXChanged: {
264+ dragPoints.push(mouseX)
265+ var dragPoint = root.width + mouseX;
266+ if (sideStageImage.shown) {
267+ dragPoint -= sideStageImage.width
268+ }
269+ sideStageImage.x = Math.max(root.width - sideStageImage.width, dragPoint)
270+ }
271+
272+ onReleased: {
273+ var distance = 0;
274+ var lastX = dragPoints[0];
275+ var oneWayFlick = true;
276+ for (var i = 0; i < dragPoints.length; ++i) {
277+ if (dragPoints[i] < lastX) {
278+ oneWayFlick = false;
279+ }
280+ distance += dragPoints[i] - lastX;
281+ lastX = dragPoints[i];
282+ }
283+ dragPoints = [];
284+
285+ if (oneWayFlick || distance > sideStageImage.width / 2) {
286+ sideStageImage.snapTo(root.width)
287+ } else {
288+ print("should snap to:", priv.sideStageAppId)
289+ sideStageImage.snapToApp(priv.sideStageApp)
290+ }
291+ }
292+// Rectangle { anchors.fill: parent; color: "purple"; opacity: .5 }
293+ }
294+
295+
296+ SwitchingApplicationImage {
297+ id: sideStageImage
298+ width: priv.sideStageWidth
299+// height: root.height
300+ x: root.width - width
301+ anchors.bottom: parent.bottom
302+ visible: true
303+ property bool shown: false
304+
305+// Rectangle { anchors.fill: parent; color: "green"; opacity: 0.5 }
306+
307+ onSwitched: {
308+ mainStageImage.visible = false;
309+ }
310+
311+ function snapTo(targetX) {
312+ print("snapping to... app:", sideStageImage.application.appId)
313+ sideStageSnapAnimation.targetX = targetX
314+ sideStageImage.visible = true;
315+ if (priv.mainStageAppId) {
316+ mainStageImage.application = priv.mainStageApp
317+ mainStageImage.visible = true;
318+ }
319+ sideStageSnapAnimation.start();
320+ }
321+
322+ function snapToApp(application) {
323+ sideStageImage.application = application
324+ sideStageSnapAnimation.snapToId = application.appId;
325+ snapTo(root.width - sideStageImage.width);
326+ }
327+
328+ SequentialAnimation {
329+ id: sideStageSnapAnimation
330+ property int targetX: root.width
331+ property string snapToId
332+
333+ UbuntuNumberAnimation { target: sideStageImage; property: "x"; to: sideStageSnapAnimation.targetX; duration: UbuntuAnimation.SlowDuration }
334+ ScriptAction {
335+ script: {
336+ if (sideStageSnapAnimation.targetX == root.width) {
337+ if (priv.mainStageAppId) {
338+ ApplicationManager.focusApplication(priv.mainStageAppId)
339+ } else {
340+ print("unfocusing current app")
341+ priv.overlayOverride = true;
342+ ApplicationManager.unfocusCurrentApplication();
343+ }
344+ sideStageImage.shown = false;
345+ }
346+ print("snapped sidestage to app", sideStageSnapAnimation.snapToId)
347+ if (sideStageSnapAnimation.snapToId) {
348+ print("focusing id", sideStageSnapAnimation.snapToId)
349+ ApplicationManager.focusApplication(sideStageSnapAnimation.snapToId)
350+ sideStageSnapAnimation.snapToId = "";
351+ sideStageImage.shown = true;
352+ }
353+ sideStageImage.visible = false;
354+ mainStageImage.visible = false;
355+ print("snapped sidestage")
356+ }
357+ }
358+ }
359+ }
360+}
qml/Stages/SwitchingApplicationImage.qmlnew file+65
@@ -0,0 +1,65 @@
1+import QtQuick 2.0
2+import Ubuntu.Components 0.1
3+
4+Item {
5+ id: root
6+ implicitHeight: image.implicitHeight
7+ implicitWidth: image.implicitWidth
8+
9+ property var application
10+ onApplicationChanged: print("setting app to", application.appId, application.screenshot)
11+
12+ function switchTo(application) {
13+ if (root.application == application) {
14+ root.switched();
15+ return;
16+ }
17+
18+ priv.newApplication = application
19+ root.visible = true;
20+ switchToAnimation.start()
21+ }
22+
23+ signal switched()
24+
25+ QtObject {
26+ id: priv
27+ property var newApplication
28+ }
29+
30+ Image {
31+ id: newImage
32+ visible: true
33+ anchors.bottom: parent.bottom
34+ width: root.width
35+// height: sourceSize.height
36+ source: priv.newApplication ? priv.newApplication.screenshot : ""
37+ }
38+
39+ Image {
40+ id: image
41+ visible: true
42+ source: root.application ? root.application.screenshot : ""
43+ width: root.width
44+ height: sourceSize.height
45+
46+ }
47+// Rectangle { anchors.fill: parent; color: "yellow" }
48+
49+ SequentialAnimation {
50+ id: switchToAnimation
51+ ParallelAnimation {
52+ UbuntuNumberAnimation { target: image; property: "x"; from: 0; to: root.width; duration: UbuntuAnimation.SlowDuration }
53+ UbuntuNumberAnimation { target: newImage; property: "scale"; from: 0.7; to: 1; duration: UbuntuAnimation.SlowDuration }
54+ }
55+ ScriptAction {
56+ script: {
57+ image.x = 0
58+ root.application = priv.newApplication
59+ root.visible = false;
60+ priv.newApplication = null
61+ root.switched();
62+ }
63+ }
64+ }
65+}
tests/mocks/Unity/Application/ApplicationInfo.h−1
@@ -57,7 +57,6 @@ class ApplicationInfo : public ApplicationInfoInterface {
5757 { \
5858 if (m_##name != value) { \
5959 m_##name = value; \
60− qDebug() << "emitting changed" << this << Q_FUNC_INFO; \
6160 Q_EMIT name##Changed(); \
6261 } \
6362 } \
tests/mocks/Unity/Application/ApplicationManager.cpp+3−2
@@ -100,6 +100,7 @@ void ApplicationManager::add(ApplicationInfo *application) {
100100 endInsertRows();
101101 Q_EMIT applicationAdded(application->appId());
102102 Q_EMIT countChanged();
103+ Q_EMIT focusRequested(application->appId());
103104}
104105
105106void ApplicationManager::remove(ApplicationInfo *application) {
@@ -202,6 +203,7 @@ bool ApplicationManager::stopApplication(const QString &appId)
202203
203204void ApplicationManager::updateScreenshot(const QString &appId)
204205{
206+ qDebug() << "updating screenshot for" << appId;
205207 int idx = -1;
206208 ApplicationInfo *application = nullptr;
207209 for (int i = 0; i < m_availableApplications.count(); ++i) {
@@ -216,10 +218,9 @@ void ApplicationManager::updateScreenshot(const QString &appId)
216218 return;
217219 }
218220
219− qDebug() << "setting new screenshot" << application->screenshot() << application;
220221 application->setScreenshot(QString("image://application/%1/%2").arg(appId).arg(QDateTime::currentMSecsSinceEpoch()));
221− qDebug() << "set new screenshot" << application->screenshot();
222222 QModelIndex appIndex = index(idx);
223+ qDebug() << "emitting dataChanged for RoleScreenshot on" << appId;
223224 Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << RoleScreenshot);
224225}
225226
tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp+9−4
@@ -54,10 +54,15 @@ QImage ApplicationScreenshotProvider::requestImage(const QString &imageId, QSize
5454
5555 QGuiApplication *unity = qobject_cast<QGuiApplication*>(qApp);
5656
57− Q_FOREACH (QWindow *win, unity->allWindows()) {
58− QQuickWindow *quickWin = qobject_cast<QQuickWindow*>(win);
59− if (quickWin) {
60− image = image.scaledToWidth(quickWin->width());
57+ if (app->stage() == ApplicationInfo::SideStage) {
58+ QByteArray gus = qgetenv("GRID_UNIT_PX");
59+ image = image.scaledToWidth(gus.toInt() * 48);
60+ } else {
61+ Q_FOREACH (QWindow *win, unity->allWindows()) {
62+ QQuickWindow *quickWin = qobject_cast<QQuickWindow*>(win);
63+ if (quickWin) {
64+ image = image.scaledToWidth(quickWin->width());
65+ }
6166 }
6267 }
6368