~mhr3/unity8/fix-1297246

« back to all changes in this revision

Viewing changes to Shell.qml

  • Committer: Michał Sawicz
  • Date: 2013-06-05 22:03:08 UTC
  • Revision ID: michal.sawicz@canonical.com-20130605220308-yny8fv3futtr04fg
Inital unity8 commit.

Previous history can be found at https://code.launchpad.net/~unity-team/unity/phablet

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2013 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.Application 0.1
 
19
import Ubuntu.Components 0.1
 
20
import LightDM 0.1 as LightDM
 
21
import "Dash"
 
22
import "Greeter"
 
23
import "Launcher"
 
24
import "Panel"
 
25
import "Hud"
 
26
import "Components"
 
27
import "Components/Math.js" as MathLocal
 
28
import "Bottombar"
 
29
import "SideStage"
 
30
 
 
31
FocusScope {
 
32
    id: shell
 
33
 
 
34
    // this is only here to select the width / height of the window if not running fullscreen
 
35
    property bool tablet: false
 
36
    width: tablet ? units.gu(160) : units.gu(40)
 
37
    height: tablet ? units.gu(100) : units.gu(71)
 
38
 
 
39
    property real edgeSize: units.gu(2)
 
40
    property url default_background: shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg"
 
41
    property url background: default_background
 
42
    readonly property real panelHeight: panel.panelHeight
 
43
 
 
44
    property bool dashShown: dash.shown
 
45
    property bool stageScreenshotsReady: {
 
46
        if (sideStage.shown) {
 
47
            if (mainStage.applications.count > 0) {
 
48
                return mainStage.usingScreenshots && sideStage.usingScreenshots;
 
49
            } else {
 
50
                return sideStage.usingScreenshots;
 
51
            }
 
52
        } else {
 
53
            return mainStage.usingScreenshots;
 
54
        }
 
55
    }
 
56
 
 
57
    property ListModel searchHistory: SearchHistoryModel {}
 
58
 
 
59
    property var applicationManager: ApplicationManagerWrapper {}
 
60
 
 
61
    Component.onCompleted: {
 
62
        applicationManager.sideStageEnabled = Qt.binding(function() { return sideStage.enabled })
 
63
 
 
64
        // FIXME: if application focused before shell starts, shell draws on top of it only.
 
65
        // We should detect already running applications on shell start and bring them to the front.
 
66
        applicationManager.unfocusCurrentApplication();
 
67
    }
 
68
 
 
69
    readonly property bool fullscreenMode: {
 
70
        if (greeter.shown) {
 
71
            return false;
 
72
        } else if (mainStage.usingScreenshots) { // Window Manager animating so want to re-evaluate fullscreen mode
 
73
            return mainStage.switchingFromFullscreenToFullscreen;
 
74
        } else if (applicationManager.mainStageFocusedApplication) {
 
75
            return applicationManager.mainStageFocusedApplication.fullscreen;
 
76
        } else {
 
77
            return false;
 
78
        }
 
79
    }
 
80
 
 
81
    Connections {
 
82
        target: applicationManager
 
83
        ignoreUnknownSignals: true
 
84
        onFocusRequested: {
 
85
            // TODO: this should be protected to only unlock for certain applications / certain usecases
 
86
            // potentially only in connection with a notification
 
87
            greeter.hide();
 
88
            shell.activateApplication(desktopFile);
 
89
        }
 
90
    }
 
91
 
 
92
    function activateApplication(desktopFile, argument) {
 
93
        if (applicationManager) {
 
94
            // For newly started applications, as it takes them time to draw their first frame
 
95
            // we add a delay before we hide the animation screenshots to compensate.
 
96
            var addDelay = !applicationManager.getApplicationFromDesktopFile(desktopFile);
 
97
 
 
98
            var application;
 
99
            application = applicationManager.activateApplication(desktopFile, argument);
 
100
            if (application == null) {
 
101
                return;
 
102
            }
 
103
            if (application.stage == ApplicationInfo.MainStage || !sideStage.enabled) {
 
104
                mainStage.activateApplication(desktopFile, addDelay);
 
105
            } else {
 
106
                sideStage.activateApplication(desktopFile, addDelay);
 
107
            }
 
108
            stages.show();
 
109
        }
 
110
    }
 
111
 
 
112
    VolumeControl {
 
113
        id: volumeControl
 
114
    }
 
115
 
 
116
    Keys.onVolumeUpPressed: volumeControl.volumeUp()
 
117
    Keys.onVolumeDownPressed: volumeControl.volumeDown()
 
118
 
 
119
    Keys.onReleased: {
 
120
        if (event.key == Qt.Key_PowerOff) {
 
121
            greeter.show()
 
122
        }
 
123
    }
 
124
 
 
125
    Item {
 
126
        id: underlay
 
127
 
 
128
        anchors.fill: parent
 
129
        visible: !(panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth)
 
130
                 && (stages.fullyHidden
 
131
                     || (stages.fullyShown && mainStage.usingScreenshots)
 
132
                     || !stages.fullyShown && (mainStage.usingScreenshots || (sideStage.shown && sideStage.usingScreenshots)))
 
133
 
 
134
        Image {
 
135
            id: backgroundImage
 
136
            source: shell.background
 
137
            sourceSize.width: parent.width
 
138
            sourceSize.height: parent.height
 
139
            anchors.fill: parent
 
140
        }
 
141
 
 
142
        Rectangle {
 
143
            anchors.fill: parent
 
144
            color: "black"
 
145
            opacity: dash.disappearingAnimationProgress
 
146
        }
 
147
 
 
148
        Dash {
 
149
            id: dash
 
150
 
 
151
            available: !greeter.shown
 
152
            hides: [stages, launcher, panel.indicators]
 
153
            shown: disappearingAnimationProgress !== 1.0
 
154
            enabled: disappearingAnimationProgress === 0.0
 
155
            // FIXME: unfocus all applications when going back to the dash
 
156
            onEnabledChanged: {
 
157
                if (enabled) {
 
158
                    shell.applicationManager.unfocusCurrentApplication()
 
159
                }
 
160
            }
 
161
 
 
162
            anchors {
 
163
                fill: parent
 
164
                topMargin: panel.panelHeight
 
165
            }
 
166
 
 
167
            contentScale: 1.0 - 0.2 * disappearingAnimationProgress
 
168
            opacity: 1.0 - disappearingAnimationProgress
 
169
            property real disappearingAnimationProgress: ((greeter.shown) ? greeterRevealer.animatedProgress : stagesRevealer.animatedProgress)
 
170
            // FIXME: only necessary because stagesRevealer.animatedProgress and
 
171
            // greeterRevealer.animatedProgress are not animated
 
172
            Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
 
173
        }
 
174
    }
 
175
 
 
176
 
 
177
    Item {
 
178
 
 
179
        width: parent.width
 
180
        height: parent.height
 
181
        x: launcher.progress
 
182
        Behavior on x {SmoothedAnimation{velocity: 600}}
 
183
 
 
184
 
 
185
        Showable {
 
186
            id: stages
 
187
 
 
188
            property bool fullyShown: shown && stages[stagesRevealer.boundProperty] == stagesRevealer.openedValue
 
189
                                      && parent.x == 0
 
190
            property bool fullyHidden: !shown && stages[stagesRevealer.boundProperty] == stagesRevealer.closedValue
 
191
            available: !greeter.shown
 
192
            hides: [launcher, panel.indicators]
 
193
            shown: false
 
194
            opacity: 1.0
 
195
            showAnimation: StandardAnimation { property: "x"; duration: 350; to: stagesRevealer.openedValue; easing.type: Easing.OutCubic }
 
196
            hideAnimation: StandardAnimation { property: "x"; duration: 350; to: stagesRevealer.closedValue; easing.type: Easing.OutCubic }
 
197
 
 
198
            width: parent.width
 
199
            height: parent.height
 
200
 
 
201
            // close the stages when no focused application remains
 
202
            Connections {
 
203
                target: shell.applicationManager
 
204
                onMainStageFocusedApplicationChanged: stages.closeIfNoApplications()
 
205
                onSideStageFocusedApplicationChanged: stages.closeIfNoApplications()
 
206
                ignoreUnknownSignals: true
 
207
            }
 
208
 
 
209
            function closeIfNoApplications() {
 
210
                if (!shell.applicationManager.mainStageFocusedApplication
 
211
                 && !shell.applicationManager.sideStageFocusedApplication
 
212
                 && shell.applicationManager.mainStageApplications.count == 0
 
213
                 && shell.applicationManager.sideStageApplications.count == 0) {
 
214
                    stages.hide();
 
215
                }
 
216
            }
 
217
 
 
218
            // show the stages when an application gets the focus
 
219
            Connections {
 
220
                target: shell.applicationManager
 
221
                onMainStageFocusedApplicationChanged: {
 
222
                    handleFocusedApplicationChange(mainStage, shell.applicationManager.mainStageFocusedApplication);
 
223
                }
 
224
                onSideStageFocusedApplicationChanged: {
 
225
                    handleFocusedApplicationChange(sideStage, shell.applicationManager.sideStageFocusedApplication);
 
226
                }
 
227
                ignoreUnknownSignals: true
 
228
 
 
229
                function handleFocusedApplicationChange(stage, application) {
 
230
                    if (stages.shown) {
 
231
                        if (application) {
 
232
                            stage.show();
 
233
                            stages.show();
 
234
                        }
 
235
                    } else {
 
236
                        // focus changed while shell in foreground, ensure app remains unfocused
 
237
                        if (application) {
 
238
                            shell.applicationManager.unfocusCurrentApplication();
 
239
                        }
 
240
                    }
 
241
                }
 
242
            }
 
243
 
 
244
 
 
245
            Stage {
 
246
                id: mainStage
 
247
 
 
248
                anchors.fill: parent
 
249
                fullyShown: stages.fullyShown
 
250
                shouldUseScreenshots: !fullyShown
 
251
                rightEdgeEnabled: !sideStage.enabled
 
252
 
 
253
                applicationManager: shell.applicationManager
 
254
                rightEdgeDraggingAreaWidth: shell.edgeSize
 
255
                normalApplicationY: shell.panelHeight
 
256
 
 
257
                shown: true
 
258
                function show() {
 
259
                    stages.show();
 
260
                }
 
261
                function showWithoutAnimation() {
 
262
                    stages.showWithoutAnimation();
 
263
                }
 
264
                function hide() {
 
265
                }
 
266
 
 
267
                // FIXME: workaround the fact that focusing a main stage application
 
268
                // raises its surface on top of all other surfaces including the ones
 
269
                // that belong to side stage applications.
 
270
                onFocusedApplicationChanged: {
 
271
                    if (focusedApplication && sideStage.focusedApplication && sideStage.fullyShown) {
 
272
                        shell.applicationManager.focusApplication(sideStage.focusedApplication);
 
273
                    }
 
274
                }
 
275
            }
 
276
 
 
277
            SideStage {
 
278
                id: sideStage
 
279
 
 
280
                applicationManager: shell.applicationManager
 
281
                rightEdgeDraggingAreaWidth: shell.edgeSize
 
282
                normalApplicationY: shell.panelHeight
 
283
 
 
284
                onShownChanged: {
 
285
                    if (!shown && mainStage.applications.count == 0) {
 
286
                        stages.hide();
 
287
                    }
 
288
                }
 
289
                // FIXME: when hiding the side stage, refocus the main stage
 
290
                // application so that it goes in front of the side stage
 
291
                // application and hides it
 
292
                onFullyShownChanged: {
 
293
                    if (!fullyShown && stages.fullyShown && sideStage.focusedApplication != null) {
 
294
                        shell.applicationManager.focusApplication(mainStage.focusedApplication);
 
295
                    }
 
296
                }
 
297
 
 
298
                enabled: shell.width >= units.gu(60)
 
299
                visible: enabled
 
300
                fullyShown: stages.fullyShown && shown
 
301
                            && sideStage[sideStageRevealer.boundProperty] == sideStageRevealer.openedValue
 
302
                shouldUseScreenshots: !fullyShown || mainStage.usingScreenshots || sideStageRevealer.pressed
 
303
 
 
304
                available: !greeter.shown && enabled
 
305
                hides: [launcher, panel.indicators]
 
306
                shown: false
 
307
                showAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.openedValue; easing.type: Easing.OutQuint }
 
308
                hideAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.closedValue; easing.type: Easing.OutQuint }
 
309
 
 
310
                width: units.gu(40)
 
311
                height: stages.height
 
312
                handleExpanded: sideStageRevealer.pressed
 
313
            }
 
314
 
 
315
            Revealer {
 
316
                id: sideStageRevealer
 
317
 
 
318
                enabled: mainStage.applications.count > 0 && sideStage.applications.count > 0
 
319
                         && sideStage.available
 
320
                direction: Qt.RightToLeft
 
321
                openedValue: parent.width - sideStage.width
 
322
                hintDisplacement: units.gu(3)
 
323
                /* The size of the sidestage handle needs to be bigger than the
 
324
                   typical size used for edge detection otherwise it is really
 
325
                   hard to grab.
 
326
                */
 
327
                handleSize: sideStage.shown ? units.gu(4) : shell.edgeSize
 
328
                closedValue: parent.width + sideStage.handleSizeCollapsed
 
329
                target: sideStage
 
330
                x: parent.width - width
 
331
                width: sideStage.width + handleSize * 0.7
 
332
                height: sideStage.height
 
333
                orientation: Qt.Horizontal
 
334
            }
 
335
        }
 
336
    }
 
337
 
 
338
 
 
339
    Revealer {
 
340
        id: stagesRevealer
 
341
 
 
342
        property real animatedProgress: MathLocal.clamp((-dragPosition - launcher.progress) / closedValue, 0, 1)
 
343
        enabled: mainStage.applications.count > 0 || sideStage.applications.count > 0
 
344
        direction: Qt.RightToLeft
 
345
        openedValue: 0
 
346
        hintDisplacement: units.gu(3)
 
347
        handleSize: shell.edgeSize
 
348
        closedValue: width
 
349
        target: stages
 
350
        width: stages.width
 
351
        height: stages.height
 
352
        orientation: Qt.Horizontal
 
353
    }
 
354
 
 
355
    Greeter {
 
356
        id: greeter
 
357
 
 
358
        available: true
 
359
        hides: [launcher, panel.indicators, hud]
 
360
        shown: true
 
361
        showAnimation: StandardAnimation { property: "x"; to: greeterRevealer.openedValue }
 
362
        hideAnimation: StandardAnimation { property: "x"; to: greeterRevealer.closedValue }
 
363
 
 
364
        y: panel.panelHeight
 
365
        width: parent.width
 
366
        height: parent.height - panel.panelHeight
 
367
 
 
368
        property var previousMainApp: null
 
369
        property var previousSideApp: null
 
370
 
 
371
        onShownChanged: {
 
372
            if (shown) {
 
373
                greeter.forceActiveFocus();
 
374
                // FIXME: *FocusedApplication are not updated when unfocused, hence the need to check whether
 
375
                // the stage was actually shown
 
376
                if (mainStage.fullyShown) greeter.previousMainApp = applicationManager.mainStageFocusedApplication;
 
377
                if (sideStage.fullyShown) greeter.previousSideApp = applicationManager.sideStageFocusedApplication;
 
378
                applicationManager.unfocusCurrentApplication();
 
379
            } else {
 
380
                if (greeter.previousMainApp) {
 
381
                    applicationManager.focusApplication(greeter.previousMainApp);
 
382
                    greeter.previousMainApp = null;
 
383
                }
 
384
                if (greeter.previousSideApp) {
 
385
                    applicationManager.focusApplication(greeter.previousSideApp);
 
386
                    greeter.previousSideApp = null;
 
387
                }
 
388
            }
 
389
        }
 
390
 
 
391
        onUnlocked: greeter.hide()
 
392
        onSelected: {
 
393
            var bgPath = greeter.model.data(uid, LightDM.UserRoles.BackgroundPathRole)
 
394
            shell.background = bgPath ? bgPath : default_background
 
395
        }
 
396
    }
 
397
 
 
398
    InputFilterArea {
 
399
        anchors.fill: parent
 
400
        blockInput: greeter.shown
 
401
    }
 
402
 
 
403
    Revealer {
 
404
        id: greeterRevealer
 
405
 
 
406
        property real animatedProgress: MathLocal.clamp(-dragPosition / closedValue, 0, 1)
 
407
        target: greeter
 
408
        width: greeter.width
 
409
        height: greeter.height
 
410
        handleSize: shell.edgeSize
 
411
        orientation: Qt.Horizontal
 
412
        visible: greeter.shown
 
413
        enabled: !greeter.locked
 
414
    }
 
415
 
 
416
    Item {
 
417
        id: overlay
 
418
 
 
419
        anchors.fill: parent
 
420
 
 
421
        Panel {
 
422
            id: panel
 
423
            anchors.fill: parent //because this draws indicator menus
 
424
            indicatorsMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
 
425
            indicators {
 
426
                hides: [launcher]
 
427
            }
 
428
            fullscreenMode: shell.fullscreenMode
 
429
            searchVisible: !greeter.shown
 
430
 
 
431
            InputFilterArea {
 
432
                anchors.fill: parent
 
433
                blockInput: panel.indicators.shown
 
434
            }
 
435
        }
 
436
 
 
437
        Hud {
 
438
            id: hud
 
439
 
 
440
            width: parent.width > units.gu(60) ? units.gu(40) : parent.width
 
441
            height: parent.height
 
442
 
 
443
            available: !greeter.shown && !panel.indicators.shown
 
444
            shown: false
 
445
            showAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: 0; easing.type: Easing.Linear }
 
446
            hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear }
 
447
 
 
448
            Connections {
 
449
                target: shell.applicationManager
 
450
                onMainStageFocusedApplicationChanged: hud.hide()
 
451
                onSideStageFocusedApplicationChanged: hud.hide()
 
452
            }
 
453
 
 
454
            InputFilterArea {
 
455
                anchors.fill: parent
 
456
                blockInput: hud.shown
 
457
            }
 
458
        }
 
459
 
 
460
        Revealer {
 
461
            id: hudRevealer
 
462
 
 
463
            enabled: hud.shown
 
464
            width: hud.width
 
465
            anchors.left: hud.left
 
466
            height: parent.height
 
467
            target: hud.revealerTarget
 
468
            closedValue: height
 
469
            openedValue: 0
 
470
            direction: Qt.RightToLeft
 
471
            orientation: Qt.Vertical
 
472
            handleSize: hud.handleHeight
 
473
            onCloseClicked: target.hide()
 
474
        }
 
475
 
 
476
        Bottombar {
 
477
            theHud: hud
 
478
            anchors.fill: parent
 
479
            enabled: !panel.indicators.shown
 
480
        }
 
481
 
 
482
        InputFilterArea {
 
483
            blockInput: launcher.shown
 
484
            anchors {
 
485
                top: parent.top
 
486
                bottom: parent.bottom
 
487
                left: parent.left
 
488
            }
 
489
            width: launcher.width
 
490
        }
 
491
 
 
492
        Launcher {
 
493
            id: launcher
 
494
 
 
495
            anchors.top: parent.top
 
496
            anchors.bottom: parent.bottom
 
497
            width: parent.width
 
498
            dragAreaWidth: shell.edgeSize
 
499
            available: !greeter.locked
 
500
            teasing: available && greeter.leftTeaserPressed
 
501
            onDashItemSelected: {
 
502
                greeter.hide()
 
503
                // Animate if moving between application and dash
 
504
                if (!stages.shown) {
 
505
                    dash.setCurrentLens("home.lens", true, false)
 
506
                } else {
 
507
                    dash.setCurrentLens("home.lens", false, false)
 
508
                }
 
509
                stages.hide();
 
510
            }
 
511
            onDash: {
 
512
                dash.setCurrentLens("applications.lens", true, false)
 
513
                stages.hide();
 
514
            }
 
515
            onLauncherApplicationSelected:{
 
516
                greeter.hide()
 
517
                shell.activateApplication(desktopFile)
 
518
            }
 
519
            onShownChanged: {
 
520
                if (shown) {
 
521
                    panel.indicators.hide()
 
522
                    hud.hide()
 
523
                }
 
524
            }
 
525
        }
 
526
    }
 
527
 
 
528
    focus: true
 
529
 
 
530
    InputFilterArea {
 
531
        anchors {
 
532
            top: parent.top
 
533
            bottom: parent.bottom
 
534
            left: parent.left
 
535
        }
 
536
        width: shell.edgeSize
 
537
        blockInput: true
 
538
    }
 
539
 
 
540
    InputFilterArea {
 
541
        anchors {
 
542
            top: parent.top
 
543
            bottom: parent.bottom
 
544
            right: parent.right
 
545
        }
 
546
        width: shell.edgeSize
 
547
        blockInput: true
 
548
    }
 
549
 
 
550
    Binding {
 
551
        target: i18n
 
552
        property: "domain"
 
553
        value: "unity8"
 
554
    }
 
555
 
 
556
    //FIXME: This should be handled in the input stack, keyboard shouldnt propagate
 
557
    MouseArea {
 
558
        anchors.bottom: parent.bottom
 
559
        anchors.left: parent.left
 
560
        anchors.right: parent.right
 
561
        height: shell.applicationManager ? shell.applicationManager.keyboardHeight : 0
 
562
 
 
563
        enabled: shell.applicationManager && shell.applicationManager.keyboardVisible
 
564
    }
 
565
}