~allanlesage/unity8/dash-apps-visible-ordering

« back to all changes in this revision

Viewing changes to qml/Stages/StageWithSideStage.qml

  • Committer: Allan LeSage
  • Date: 2014-04-02 17:57:38 UTC
  • mfrom: (784.1.25 unity8)
  • Revision ID: allan.lesage@canonical.com-20140402175738-ocei683rve0pj7wz
Merged trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 0.1
 
19
import "../Components"
 
20
import Unity.Application 0.1
 
21
import Ubuntu.Gestures 0.1
 
22
 
 
23
Item {
 
24
    id: root
 
25
    objectName: "stages"
 
26
    anchors.fill: parent
 
27
 
 
28
    // Controls to be set from outside
 
29
    property bool shown: false
 
30
    property bool moving: false
 
31
    property int dragAreaWidth
 
32
 
 
33
    // State information propagated to the outside
 
34
    readonly property bool painting: mainStageImage.visible || sideStageImage.visible || sideStageSnapAnimation.running
 
35
    property bool fullscreen: priv.focusedApplication ? priv.focusedApplication.fullscreen : false
 
36
    property bool overlayMode: (sideStageImage.shown && priv.mainStageAppId.length == 0) || priv.overlayOverride
 
37
                               || (priv.mainStageAppId.length == 0 && sideStageSnapAnimation.running)
 
38
 
 
39
    readonly property int overlayWidth: priv.overlayOverride ? 0 : priv.sideStageWidth
 
40
 
 
41
    onShownChanged: {
 
42
        if (!shown) {
 
43
            priv.mainStageAppId = "";
 
44
        }
 
45
    }
 
46
 
 
47
    onMovingChanged: {
 
48
        if (moving) {
 
49
            if (!priv.mainStageAppId && !priv.sideStageAppId) {
 
50
                // Pulling in from the right, make the last used (topmost) app visible
 
51
                var application = ApplicationManager.get(0);
 
52
                if (application.stage == ApplicationInfoInterface.SideStage) {
 
53
                    sideStageImage.application = application;
 
54
                    sideStageImage.x = root.width - sideStageImage.width
 
55
                    sideStageImage.visible = true;
 
56
                } else {
 
57
                    mainStageImage.application = application;
 
58
                    mainStageImage.visible = true;
 
59
                }
 
60
            } else {
 
61
                priv.requestNewScreenshot(ApplicationInfoInterface.MainStage)
 
62
                if (priv.focusedApplicationId == priv.sideStageAppId) {
 
63
                    priv.requestNewScreenshot(ApplicationInfoInterface.SideStage)
 
64
                }
 
65
            }
 
66
        } else {
 
67
            mainStageImage.visible = false;
 
68
            sideStageImage.visible = false;
 
69
        }
 
70
    }
 
71
 
 
72
    QtObject {
 
73
        id: priv
 
74
 
 
75
        property int sideStageWidth: units.gu(40)
 
76
 
 
77
 
 
78
        property string sideStageAppId
 
79
        property string mainStageAppId
 
80
 
 
81
 
 
82
        property var sideStageApp: ApplicationManager.findApplication(sideStageAppId)
 
83
        property var mainStageApp: ApplicationManager.findApplication(mainStageAppId)
 
84
 
 
85
        property string sideStageScreenshot: sideStageApp ? sideStageApp.screenshot : ""
 
86
        property string mainStageScreenshot: mainStageApp ? mainStageApp.screenshot : ""
 
87
 
 
88
        property string focusedApplicationId: ApplicationManager.focusedApplicationId
 
89
        property var focusedApplication: ApplicationManager.findApplication(focusedApplicationId)
 
90
        property url focusedScreenshot: focusedApplication ? focusedApplication.screenshot : ""
 
91
 
 
92
        property bool waitingForMainScreenshot: false
 
93
        property bool waitingForSideScreenshot: false
 
94
        property bool waitingForScreenshots: waitingForMainScreenshot || waitingForSideScreenshot
 
95
 
 
96
        property string startingAppId: ""
 
97
 
 
98
        // Keep overlayMode even if there is no focused app (to allow pulling in the sidestage from the right)
 
99
        property bool overlayOverride: false
 
100
 
 
101
        onFocusedApplicationChanged: {
 
102
            if (focusedApplication) {
 
103
                if (focusedApplication.stage == ApplicationInfoInterface.MainStage) {
 
104
                    mainStageAppId = focusedApplicationId;
 
105
                    priv.overlayOverride = false;
 
106
                    if (priv.startingAppId == focusedApplicationId && sideStageImage.shown) {
 
107
                        // There was already a sidestage app on top. bring it back!
 
108
                        ApplicationManager.focusApplication(priv.sideStageAppId)
 
109
                        priv.startingAppId = "";
 
110
                    }
 
111
                } else if (focusedApplication.stage == ApplicationInfoInterface.SideStage) {
 
112
                    sideStageAppId = focusedApplicationId;
 
113
                    if (priv.startingAppId == focusedApplicationId && !sideStageImage.shown) {
 
114
                        sideStageImage.snapToApp(focusedApplication);
 
115
                        priv.startingAppId = "";
 
116
                    }
 
117
                }
 
118
            } else if (root.overlayMode){
 
119
                sideStageImage.snapTo(root.width)
 
120
            }
 
121
        }
 
122
 
 
123
        onMainStageScreenshotChanged: {
 
124
            waitingForMainScreenshot = false;
 
125
        }
 
126
 
 
127
        onSideStageScreenshotChanged: {
 
128
            waitingForSideScreenshot = false;
 
129
        }
 
130
 
 
131
        onFocusedScreenshotChanged: {
 
132
            waitingForSideScreenshot = false;
 
133
        }
 
134
 
 
135
        onWaitingForScreenshotsChanged: {
 
136
            if (waitingForScreenshots) {
 
137
                return;
 
138
            }
 
139
 
 
140
            if (root.moving) {
 
141
                if (mainStageAppId) {
 
142
                    mainStageImage.application = mainStageApp
 
143
                    mainStageImage.visible = true;
 
144
                }
 
145
                if (sideStageAppId && focusedApplicationId == sideStageAppId) {
 
146
                    sideStageImage.application = sideStageApp;
 
147
                    sideStageImage.x = root.width - sideStageImage.width
 
148
                    sideStageImage.visible = true;
 
149
                }
 
150
            }
 
151
            if (sideStageHandleMouseArea.pressed) {
 
152
                if (sideStageAppId) {
 
153
                    sideStageImage.application = sideStageApp;
 
154
                    sideStageImage.x = root.width - sideStageImage.width
 
155
                    sideStageImage.visible = true;
 
156
                }
 
157
                if (mainStageAppId) {
 
158
                    mainStageImage.application = mainStageApp
 
159
                    mainStageImage.visible = true;
 
160
                }
 
161
            }
 
162
        }
 
163
 
 
164
        function requestNewScreenshot(stage) {
 
165
            if (stage == ApplicationInfoInterface.MainStage && mainStageAppId) {
 
166
                waitingForMainScreenshot = true;
 
167
                ApplicationManager.updateScreenshot(mainStageAppId);
 
168
            } else if (stage == ApplicationInfoInterface.SideStage && sideStageAppId) {
 
169
                waitingForSideScreenshot = true;
 
170
                ApplicationManager.updateScreenshot(sideStageAppId);
 
171
            }
 
172
        }
 
173
 
 
174
    }
 
175
    // FIXME: the signal connection seems to get lost with the fake application manager.
 
176
    Connections {
 
177
        target: priv.sideStageApp
 
178
        onScreenshotChanged: priv.sideStageScreenshot = priv.sideStageApp.screenshot
 
179
    }
 
180
    Connections {
 
181
        target: priv.mainStageApp
 
182
        onScreenshotChanged: priv.mainStageScreenshot = priv.mainStageApp.screenshot
 
183
    }
 
184
 
 
185
    Connections {
 
186
        target: ApplicationManager
 
187
 
 
188
        onApplicationAdded: {
 
189
            priv.startingAppId = appId;
 
190
            splashScreenTimer.start();
 
191
            var application = ApplicationManager.findApplication(appId)
 
192
            if (application.stage == ApplicationInfoInterface.SideStage) {
 
193
                sideStageSplash.visible = true;
 
194
            } else if (application.stage == ApplicationInfoInterface.MainStage) {
 
195
                mainStageSplash.visible = true;
 
196
            }
 
197
        }
 
198
 
 
199
        onFocusRequested: {
 
200
            var application = ApplicationManager.findApplication(appId)
 
201
            if (application.stage == ApplicationInfoInterface.SideStage) {
 
202
                if (!root.shown) {
 
203
                    priv.mainStageAppId = "";
 
204
                    mainStageImage.application = null
 
205
                }
 
206
                if (sideStageImage.shown) {
 
207
                    sideStageImage.switchTo(application);
 
208
                    if (priv.mainStageAppId) {
 
209
                        mainStageImage.application = priv.mainStageApp;
 
210
                        mainStageImage.visible = true;
 
211
                    }
 
212
                } else {
 
213
                    sideStageImage.application = application;
 
214
                    sideStageImage.snapToApp(application);
 
215
                }
 
216
            } else if (application.stage == ApplicationInfoInterface.MainStage) {
 
217
                if (root.shown) {
 
218
                    if (sideStageImage.shown) {
 
219
                        sideStageImage.application = priv.sideStageApp;
 
220
                        sideStageImage.visible = true;
 
221
                    }
 
222
                    priv.mainStageAppId = application.appId;
 
223
                    mainStageImage.switchTo(application)
 
224
                    ApplicationManager.focusApplication(appId)
 
225
                    if (sideStageImage.shown) {
 
226
                        // There was already a focused SS app. Bring it back
 
227
                        ApplicationManager.focusApplication(priv.sideStageAppId)
 
228
                    }
 
229
                } else {
 
230
                    if (sideStageImage.shown) {
 
231
                        sideStageImage.visible = false;
 
232
                        sideStageImage.x = root.width;
 
233
                    }
 
234
 
 
235
                    mainStageImage.application = application;
 
236
                    ApplicationManager.focusApplication(appId)
 
237
                }
 
238
            }
 
239
        }
 
240
 
 
241
        onApplicationRemoved: {
 
242
            if (priv.mainStageAppId == appId) {
 
243
                priv.mainStageAppId = "";
 
244
            }
 
245
            if (priv.sideStageAppId == appId) {
 
246
                priv.sideStageAppId = "";
 
247
            }
 
248
            if (priv.sideStageAppId.length == 0) {
 
249
                sideStageImage.shown = false;
 
250
                priv.overlayOverride = false;
 
251
            }
 
252
        }
 
253
 
 
254
    }
 
255
 
 
256
    Timer {
 
257
        id: splashScreenTimer
 
258
        // FIXME: apart from removing this completely in the future and make the app surface paint
 
259
        // meaningful stuff, also check for colin's stuff to land so we can shape 1.4 secs away from here
 
260
        // https://code.launchpad.net/~cjwatson/upstart-app-launch/libclick-manifest/+merge/210520
 
261
        // https://code.launchpad.net/~cjwatson/upstart-app-launch/libclick-pkgdir/+merge/209909
 
262
        interval: 1700
 
263
        repeat: false
 
264
        onTriggered: {
 
265
            mainStageSplash.visible = false;
 
266
            sideStageSplash.visible = false;
 
267
        }
 
268
    }
 
269
 
 
270
    SwitchingApplicationImage {
 
271
        id: mainStageImage
 
272
        anchors.bottom: parent.bottom
 
273
        width: parent.width
 
274
        visible: false
 
275
 
 
276
        onSwitched: {
 
277
            sideStageImage.visible = false;
 
278
        }
 
279
    }
 
280
 
 
281
    Rectangle {
 
282
        id: mainStageSplash
 
283
        anchors.fill: root
 
284
        anchors.rightMargin: root.width - sideStageImage.x
 
285
        color: "white"
 
286
    }
 
287
 
 
288
    SidestageHandle {
 
289
        id: sideStageHandle
 
290
        anchors { top: parent.top; right: sideStageImage.left; bottom: parent.bottom }
 
291
        width: root.dragAreaWidth
 
292
        visible: root.shown && priv.sideStageAppId && sideStageImage.x < root.width
 
293
 
 
294
    }
 
295
    MouseArea {
 
296
        id: sideStageHandleMouseArea
 
297
        anchors { top: parent.top; right: parent.right; bottom: parent.bottom; rightMargin: sideStageImage.shown ? sideStageImage.width : 0}
 
298
        width: root.dragAreaWidth
 
299
        visible: priv.sideStageAppId
 
300
 
 
301
        property var dragPoints: new Array()
 
302
 
 
303
        onPressed: {
 
304
            priv.requestNewScreenshot(ApplicationInfoInterface.SideStage)
 
305
            if (priv.mainStageAppId) {
 
306
                priv.requestNewScreenshot(ApplicationInfoInterface.MainStage)
 
307
            }
 
308
        }
 
309
 
 
310
        onMouseXChanged: {
 
311
            dragPoints.push(mouseX)
 
312
 
 
313
            var dragPoint = root.width + mouseX;
 
314
            if (sideStageImage.shown) {
 
315
                dragPoint -= sideStageImage.width
 
316
            }
 
317
            sideStageImage.x = Math.max(root.width - sideStageImage.width, dragPoint)
 
318
        }
 
319
 
 
320
        onReleased: {
 
321
            var distance = 0;
 
322
            var lastX = dragPoints[0];
 
323
            var oneWayFlick = true;
 
324
            for (var i = 0; i < dragPoints.length; ++i) {
 
325
                if (dragPoints[i] < lastX) {
 
326
                    oneWayFlick = false;
 
327
                }
 
328
                distance += dragPoints[i] - lastX;
 
329
                lastX = dragPoints[i];
 
330
            }
 
331
            dragPoints = [];
 
332
 
 
333
            if (oneWayFlick || distance > sideStageImage.width / 2) {
 
334
                sideStageImage.snapTo(root.width)
 
335
            } else {
 
336
                sideStageImage.snapToApp(priv.sideStageApp)
 
337
            }
 
338
        }
 
339
    }
 
340
 
 
341
    SwitchingApplicationImage {
 
342
        id: sideStageImage
 
343
        width: priv.sideStageWidth
 
344
        height: root.height
 
345
        x: root.width
 
346
        anchors.bottom: parent.bottom
 
347
        visible: true
 
348
        property bool shown: false
 
349
 
 
350
        onSwitched: {
 
351
            mainStageImage.visible = false;
 
352
            ApplicationManager.focusApplication(application.appId)
 
353
        }
 
354
 
 
355
        function snapTo(targetX) {
 
356
            sideStageSnapAnimation.targetX = targetX
 
357
            sideStageImage.visible = true;
 
358
            if (priv.mainStageAppId) {
 
359
                mainStageImage.application = priv.mainStageApp
 
360
                mainStageImage.visible = true;
 
361
            }
 
362
            sideStageSnapAnimation.start();
 
363
        }
 
364
 
 
365
        function snapToApp(application) {
 
366
            sideStageImage.application = application
 
367
            sideStageSnapAnimation.snapToId = application.appId;
 
368
            snapTo(root.width - sideStageImage.width);
 
369
        }
 
370
 
 
371
        SequentialAnimation {
 
372
            id: sideStageSnapAnimation
 
373
            property int targetX: root.width
 
374
            property string snapToId
 
375
 
 
376
            UbuntuNumberAnimation { target: sideStageImage; property: "x"; to: sideStageSnapAnimation.targetX; duration: UbuntuAnimation.SlowDuration }
 
377
            ScriptAction {
 
378
                script: {
 
379
                    if (sideStageSnapAnimation.targetX == root.width) {
 
380
                        if (priv.mainStageAppId) {
 
381
                            ApplicationManager.focusApplication(priv.mainStageAppId)
 
382
                        } else {
 
383
                            priv.overlayOverride = true;
 
384
                            ApplicationManager.unfocusCurrentApplication();
 
385
                        }
 
386
                        sideStageImage.shown = false;
 
387
                    }
 
388
                    if (sideStageSnapAnimation.snapToId) {
 
389
                        ApplicationManager.focusApplication(sideStageSnapAnimation.snapToId)
 
390
                        sideStageSnapAnimation.snapToId = "";
 
391
                        sideStageImage.shown = true;
 
392
                        priv.overlayOverride = false;
 
393
                    }
 
394
                    sideStageImage.visible = false;
 
395
                    mainStageImage.visible = false;
 
396
                }
 
397
            }
 
398
        }
 
399
    }
 
400
 
 
401
    Rectangle {
 
402
        id: sideStageSplash
 
403
        anchors.fill: parent
 
404
        anchors.leftMargin: sideStageImage.x
 
405
        color: "white"
 
406
    }
 
407
}