on 6dc418d help sign in
unity8/qml/Shell.qml
849 lines 30 kB history raw
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 */
17import QtQuick 2.0
18import QtQuick.Window 2.0
19import AccountsService 0.1
20import GSettings 1.0
21import Unity.Application 0.1
22import Ubuntu.Components 0.1
23import Ubuntu.Components.Popups 1.0
24import Ubuntu.Gestures 0.1
25import Ubuntu.SystemImage 0.1
26import Ubuntu.Telephony 0.1 as Telephony
27import Unity.Connectivity 0.1
28import Unity.Launcher 0.1
29import Utils 0.1
30import LightDM 0.1 as LightDM
31import Powerd 0.1
32import SessionBroadcast 0.1
33import "Greeter"
34import "Launcher"
35import "Panel"
36import "Components"
37import "Notifications"
38import "Stages"
39import "Panel/Indicators"
40import "Wizard"
41import Unity.Notifications 1.0 as NotificationBackend
42import Unity.Session 0.1
43import Unity.DashCommunicator 0.1
45Item {
46 id: shell
48 // Disable everything so that user can't swipe greeter or launcher until
49 // we get first prompt/authenticate, which will re-enable the shell.
50 enabled: false
52 // this is only here to select the width / height of the window if not running fullscreen
53 property bool tablet: false
54 width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
55 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
57 property real edgeSize: units.gu(2)
58 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
59 property url background: asImageTester.status == Image.Ready ? asImageTester.source
60 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
61 readonly property real panelHeight: panel.panelHeight
63 readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock
64 readonly property alias hasLockedApp: greeter.hasLockedApp
65 readonly property bool forcedUnlock: edgeDemo.running
66 onForcedUnlockChanged: if (forcedUnlock) lockscreen.hide()
68 property bool sideStageEnabled: shell.width >= units.gu(100)
69 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
71 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
72 property int failedLoginsDelayAttempts: 7 // number of failed logins
73 property int failedLoginsDelayMinutes: 5 // minutes of forced waiting
75 property int orientation
76 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
77 onDeviceOrientationAngleChanged: {
78 if (!OrientationLock.enabled) {
79 orientation = Screen.orientation;
80 }
81 }
82 readonly property bool orientationLockEnabled: OrientationLock.enabled
83 onOrientationLockEnabledChanged: {
84 if (orientationLockEnabled) {
85 OrientationLock.savedOrientation = Screen.orientation;
86 } else {
87 orientation = Screen.orientation;
88 }
89 }
91 function activateApplication(appId) {
92 if (ApplicationManager.findApplication(appId)) {
93 ApplicationManager.requestFocusApplication(appId);
94 } else {
95 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
96 ApplicationManager.startApplication(appId, execFlags);
97 }
98 }
100 function startLockedApp(app) {
101 if (shell.locked) {
102 greeter.lockedApp = app;
103 }
104 shell.activateApplication(app);
105 }
107 // This is a dummy image to detect if the custom AS set wallpaper loads successfully.
108 Image {
109 id: asImageTester
110 source: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
111 height: 0
112 width: 0
113 sourceSize.height: 0
114 sourceSize.width: 0
115 }
117 GSettings {
118 id: backgroundSettings
119 schema.id: "org.gnome.desktop.background"
120 }
122 // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
123 Image {
124 id: gsImageTester
125 source: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
126 height: 0
127 width: 0
128 sourceSize.height: 0
129 sourceSize.width: 0
130 }
132 Binding {
133 target: LauncherModel
134 property: "applicationManager"
135 value: ApplicationManager
136 }
138 Component.onCompleted: {
139 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
140 if (ApplicationManager.count > 0) {
141 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
142 }
143 if (orientationLockEnabled) {
144 orientation = OrientationLock.savedOrientation;
145 }
146 }
148 VolumeControl {
149 id: volumeControl
150 }
152 DashCommunicator {
153 id: dash
154 objectName: "dashCommunicator"
155 }
157 PhysicalKeysFilter {
158 id: physicalKeysFilter
160 onVolumeDownPressed: volumeControl.volumeDown()
161 onVolumeUpPressed: volumeControl.volumeUp()
162 onScreenshotPressed: screenGrabber.capture()
163 }
165 ScreenGrabber {
166 id: screenGrabber
167 z: edgeDemo.z + 10
168 enabled: Powerd.status === Powerd.On
169 }
171 Binding {
172 target: ApplicationManager
173 property: "forceDashActive"
174 value: launcher.shown || launcher.dashSwipe
175 }
177 WindowKeysFilter {
178 Keys.onPressed: {
179 event.accepted = physicalKeysFilter.onKeyPressed(event.key);
180 }
182 Keys.onReleased: {
183 event.accepted = physicalKeysFilter.onKeyReleased(event.key);
184 }
185 }
187 Item {
188 id: stages
189 objectName: "stages"
190 width: parent.width
191 height: parent.height
192 visible: !ApplicationManager.empty
194 Connections {
195 target: ApplicationManager
196 onFocusRequested: {
197 if (greeter.narrowMode) {
198 if (appId === "dialer-app" && callManager.hasCalls && shell.locked) {
199 // If we are in the middle of a call, make dialer lockedApp and show it.
200 // This can happen if user backs out of dialer back to greeter, then
201 // launches dialer again.
202 greeter.lockedApp = appId;
203 }
204 if (greeter.hasLockedApp) {
205 if (appId === greeter.lockedApp) {
206 lockscreen.hide() // show locked app
207 } else {
208 greeter.startUnlock() // show lockscreen if necessary
209 }
210 }
211 greeter.hide();
212 } else {
213 if (LightDM.Greeter.active) {
214 greeter.startUnlock()
215 }
216 }
217 }
219 onFocusedApplicationIdChanged: {
220 if (greeter.hasLockedApp && greeter.lockedApp !== ApplicationManager.focusedApplicationId) {
221 greeter.startUnlock()
222 }
223 panel.indicators.hide();
224 }
226 onApplicationAdded: {
227 if (greeter.shown && appId != "unity8-dash") {
228 greeter.startUnlock()
230 // If this happens on first boot, we may be in edge
231 // tutorial or wizard while receiving a call. But a call
232 // is more important than wizard so just bail out of those.
233 if (edgeDemo.running) {
234 edgeDemo.hideEdgeDemos();
235 wizard.hide();
236 }
237 }
238 if (greeter.narrowMode && greeter.hasLockedApp && appId === greeter.lockedApp) {
239 lockscreen.hide() // show locked app
240 }
241 launcher.hide();
242 }
243 }
245 Loader {
246 id: applicationsDisplayLoader
247 objectName: "applicationsDisplayLoader"
248 anchors.fill: parent
250 // When we have a locked app, we only want to show that one app.
251 // FIXME: do this in a less traumatic way. We currently only allow
252 // locked apps in phone mode (see FIXME in Lockscreen component in
253 // this same file). When that changes, we need to do something
254 // nicer here. But this code is currently just to prevent a
255 // theoretical attack where user enters lockedApp mode, then makes
256 // the screen larger (maybe connects to monitor) and tries to enter
257 // tablet mode.
258 property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
259 source: tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
261 Binding {
262 target: applicationsDisplayLoader.item
263 property: "objectName"
264 value: "stage"
265 }
266 Binding {
267 target: applicationsDisplayLoader.item
268 property: "dragAreaWidth"
269 value: shell.edgeSize
270 }
271 Binding {
272 target: applicationsDisplayLoader.item
273 property: "maximizedAppTopMargin"
274 // Not just using panel.panelHeight as that changes depending on the focused app.
275 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
276 }
277 Binding {
278 target: applicationsDisplayLoader.item
279 property: "interactive"
280 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
281 }
282 Binding {
283 target: applicationsDisplayLoader.item
284 property: "spreadEnabled"
285 value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
286 }
287 Binding {
288 target: applicationsDisplayLoader.item
289 property: "inverseProgress"
290 value: launcher.progress
291 }
292 Binding {
293 target: applicationsDisplayLoader.item
294 property: "orientation"
295 value: shell.orientation
296 }
297 }
298 }
300 InputMethod {
301 id: inputMethod
302 objectName: "inputMethod"
303 anchors { fill: parent; topMargin: panel.panelHeight }
304 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
305 }
307 Connections {
308 target: SurfaceManager
309 onSurfaceCreated: {
310 if (surface.type == MirSurfaceItem.InputMethod) {
311 inputMethod.surface = surface;
312 }
313 }
315 onSurfaceDestroyed: {
316 if (inputMethod.surface == surface) {
317 inputMethod.surface = null;
318 surface.parent = null;
319 }
320 if (!surface.parent) {
321 // there's no one displaying it. delete it right away
322 surface.release();
323 }
324 }
325 }
326 Connections {
327 target: SessionManager
328 onSessionStopping: {
329 if (!session.parentSession && !session.application) {
330 // nothing is using it. delete it right away
331 session.release();
332 }
333 }
334 }
336 Lockscreen {
337 id: lockscreen
338 objectName: "lockscreen"
340 hides: [launcher, panel.indicators]
341 shown: false
342 enabled: true
343 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
344 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
345 y: panel.panelHeight
346 visible: required
347 width: parent.width
348 height: parent.height - panel.panelHeight
349 background: shell.background
350 darkenBackground: 0.4
351 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
352 minPinLength: 4
353 maxPinLength: 4
355 property string promptText
356 infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) :
357 alphaNumeric ? i18n.tr("Enter passphrase") :
358 i18n.tr("Enter passcode")
359 errorText: promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) :
360 alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" +
361 i18n.tr("Please re-enter") :
362 i18n.tr("Sorry, incorrect passcode")
364 // FIXME: We *should* show emergency dialer if there is a SIM present,
365 // regardless of whether the side stage is enabled. But right now,
366 // the assumption is that narrow screens are phones which have SIMs
367 // and wider screens are tablets which don't. When we do allow this
368 // on devices with a side stage and a SIM, work should be done to
369 // ensure that the main stage is disabled while the dialer is present
370 // in the side stage. See the FIXME in the stage loader in this file.
371 showEmergencyCallButton: !shell.sideStageEnabled
373 onEntered: LightDM.Greeter.respond(passphrase);
374 onCancel: greeter.show()
375 onEmergencyCall: startLockedApp("dialer-app")
377 onShownChanged: if (shown) greeter.lockedApp = ""
379 function maybeShow() {
380 if (!shell.forcedUnlock) {
381 show()
382 }
383 }
385 Timer {
386 id: forcedDelayTimer
387 interval: 1000 * 60
388 onTriggered: {
389 if (lockscreen.delayMinutes > 0) {
390 lockscreen.delayMinutes -= 1
391 if (lockscreen.delayMinutes > 0) {
392 start() // go again
393 }
394 }
395 }
396 }
398 Component.onCompleted: {
399 if (greeter.narrowMode) {
400 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
401 }
402 }
403 }
405 Connections {
406 target: LightDM.Greeter
408 onShowGreeter: greeter.show()
409 onHideGreeter: greeter.login()
411 onShowPrompt: {
412 shell.enabled = true;
413 if (!LightDM.Greeter.active) {
414 return; // could happen if hideGreeter() comes in before we prompt
415 }
416 if (greeter.narrowMode) {
417 lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase();
418 lockscreen.maybeShow();
419 }
420 }
422 onPromptlessChanged: {
423 if (!LightDM.Greeter.active) {
424 return; // could happen if hideGreeter() comes in before we prompt
425 }
426 if (greeter.narrowMode) {
427 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
428 lockscreen.hide()
429 } else {
430 lockscreen.reset();
431 lockscreen.maybeShow();
432 }
433 }
434 }
436 onAuthenticationComplete: {
437 shell.enabled = true;
438 if (LightDM.Greeter.authenticated) {
439 AccountsService.failedLogins = 0
440 }
441 // Else only penalize user for a failed login if they actually were
442 // prompted for a password. We do this below after the promptless
443 // early exit.
445 if (LightDM.Greeter.promptless) {
446 return;
447 }
449 if (LightDM.Greeter.authenticated) {
450 greeter.login();
451 } else {
452 AccountsService.failedLogins++
453 if (maxFailedLogins >= 2) { // require at least a warning
454 if (AccountsService.failedLogins === maxFailedLogins - 1) {
455 var title = lockscreen.alphaNumeric ?
456 i18n.tr("Sorry, incorrect passphrase.") :
457 i18n.tr("Sorry, incorrect passcode.")
458 var text = i18n.tr("This will be your last attempt.") + " " +
459 (lockscreen.alphaNumeric ?
460 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
461 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
462 lockscreen.showInfoPopup(title, text)
463 } else if (AccountsService.failedLogins >= maxFailedLogins) {
464 SystemImage.factoryReset() // Ouch!
465 }
466 }
467 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
468 lockscreen.delayMinutes = failedLoginsDelayMinutes
469 forcedDelayTimer.start()
470 }
472 lockscreen.clear(true);
473 if (greeter.narrowMode) {
474 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
475 }
476 }
477 }
478 }
480 Binding {
481 target: LightDM.Greeter
482 property: "active"
483 value: greeter.shown || lockscreen.shown || greeter.hasLockedApp
484 }
486 Rectangle {
487 anchors.fill: parent
488 color: "black"
489 opacity: greeterWrapper.showProgress * 0.8
490 }
492 Item {
493 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
494 id: greeterWrapper
495 objectName: "greeterWrapper"
496 x: (greeter.narrowMode && greeter.showProgress > 0) ? launcher.progress : 0
497 y: panel.panelHeight
498 width: parent.width
499 height: parent.height - panel.panelHeight
501 Behavior on x {
502 enabled: !launcher.dashSwipe
503 StandardAnimation {}
504 }
506 property bool fullyShown: showProgress === 1.0
507 onFullyShownChanged: {
508 // Wait until the greeter is completely covering lockscreen before resetting it.
509 if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
510 lockscreen.reset();
511 lockscreen.maybeShow();
512 }
513 }
515 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
516 onShowProgressChanged: {
517 if (showProgress === 0) {
518 if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || shell.forcedUnlock) {
519 greeter.login()
520 } else if (greeter.narrowMode) {
521 lockscreen.clear(false) // to reset focus if necessary
522 }
523 }
524 }
526 Greeter {
527 id: greeter
528 objectName: "greeter"
530 signal sessionStarted() // helpful for tests
532 property string lockedApp: ""
533 property bool hasLockedApp: lockedApp !== ""
535 available: true
536 hides: [launcher, panel.indicators]
537 shown: true
538 loadContent: required || lockscreen.required // keeps content in memory for quick show()
540 locked: shell.locked
542 background: shell.background
544 width: parent.width
545 height: parent.height
547 dragHandleWidth: shell.edgeSize
549 function startUnlock() {
550 if (narrowMode) {
551 if (!LightDM.Greeter.authenticated) {
552 lockscreen.maybeShow()
553 }
554 hide()
555 } else {
556 show()
557 tryToUnlock()
558 }
559 }
561 function login() {
562 enabled = false;
563 if (LightDM.Greeter.startSessionSync()) {
564 sessionStarted();
565 greeter.hide();
566 lockscreen.hide();
567 launcher.hide();
568 }
569 enabled = true;
570 }
572 onShownChanged: {
573 if (shown) {
574 // Disable everything so that user can't swipe greeter or
575 // launcher until we get the next prompt/authenticate, which
576 // will re-enable the shell.
577 shell.enabled = false;
579 if (greeter.narrowMode) {
580 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
581 } else {
582 reset()
583 }
584 greeter.lockedApp = "";
585 greeter.forceActiveFocus();
586 }
587 }
589 Component.onCompleted: {
590 Connectivity.unlockAllModems()
591 }
593 onUnlocked: greeter.hide()
594 onSelected: {
595 // Update launcher items for new user
596 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
597 AccountsService.user = user;
598 LauncherModel.setUser(user);
599 }
601 onTease: launcher.tease()
603 Binding {
604 target: ApplicationManager
605 property: "suspended"
606 value: (greeter.shown && greeterWrapper.showProgress == 1) || lockscreen.shown
607 }
608 }
609 }
611 Connections {
612 id: callConnection
613 target: callManager
615 onHasCallsChanged: {
616 if (shell.locked && callManager.hasCalls) {
617 // We just received an incoming call while locked. The
618 // indicator will have already launched dialer-app for us, but
619 // there is a race between "hasCalls" changing and the dialer
620 // starting up. So in case we lose that race, we'll start/
621 // focus the dialer ourselves here too. Even if the indicator
622 // didn't launch the dialer for some reason (or maybe a call
623 // started via some other means), if an active call is
624 // happening, we want to be in the dialer.
625 startLockedApp("dialer-app")
626 }
627 }
628 }
630 Connections {
631 id: powerConnection
632 target: Powerd
634 onStatusChanged: {
635 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
636 !callManager.hasCalls && !edgeDemo.running) {
637 greeter.showNow()
638 }
639 }
640 }
642 function showHome() {
643 if (edgeDemo.running) {
644 return
645 }
647 if (LightDM.Greeter.active) {
648 greeter.startUnlock()
649 }
651 var animate = !LightDM.Greeter.active && !stages.shown
652 dash.setCurrentScope(0, animate, false)
653 ApplicationManager.requestFocusApplication("unity8-dash")
654 }
656 function showDash() {
657 if (greeter.hasLockedApp || // just in case user gets here
658 (!greeter.narrowMode && shell.locked)) {
659 return
660 }
662 if (greeter.shown) {
663 greeter.hideRight();
664 launcher.fadeOut();
665 }
667 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
668 ApplicationManager.requestFocusApplication("unity8-dash")
669 launcher.fadeOut();
670 }
671 }
673 Item {
674 id: overlay
675 z: 10
677 anchors.fill: parent
679 Panel {
680 id: panel
681 objectName: "panel"
682 anchors.fill: parent //because this draws indicator menus
683 indicators {
684 hides: [launcher]
685 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
686 contentEnabled: edgeDemo.panelContentEnabled
687 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
689 minimizedPanelHeight: units.gu(3)
690 expandedPanelHeight: units.gu(7)
692 indicatorsModel: visibleIndicators.model
693 }
695 VisibleIndicators {
696 id: visibleIndicators
697 // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
698 Component.onCompleted: initialise(indicatorProfile)
699 }
700 callHint {
701 greeterShown: greeter.shown || lockscreen.shown
702 }
704 property bool topmostApplicationIsFullscreen:
705 ApplicationManager.focusedApplicationId &&
706 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
708 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
709 || greeter.hasLockedApp
710 }
712 Launcher {
713 id: launcher
714 objectName: "launcher"
716 readonly property bool dashSwipe: progress > 0
718 anchors.top: parent.top
719 anchors.bottom: parent.bottom
720 width: parent.width
721 dragAreaWidth: shell.edgeSize
722 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
724 onShowDashHome: showHome()
725 onDash: showDash()
726 onDashSwipeChanged: {
727 if (dashSwipe) {
728 dash.setCurrentScope(0, false, true)
729 }
730 }
731 onLauncherApplicationSelected: {
732 if (greeter.hasLockedApp) {
733 greeter.startUnlock()
734 }
735 if (!edgeDemo.running)
736 shell.activateApplication(appId)
737 }
738 onShownChanged: {
739 if (shown) {
740 panel.indicators.hide()
741 }
742 }
743 }
745 Wizard {
746 id: wizard
747 anchors.fill: parent
748 background: shell.background
749 }
751 Rectangle {
752 id: modalNotificationBackground
754 visible: notifications.useModal && (notifications.state == "narrow")
755 color: "#000000"
756 anchors.fill: parent
757 opacity: 0.9
759 MouseArea {
760 anchors.fill: parent
761 }
762 }
764 Notifications {
765 id: notifications
767 model: NotificationBackend.Model
768 margin: units.gu(1)
770 y: topmostIsFullscreen ? 0 : panel.panelHeight
771 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
773 states: [
774 State {
775 name: "narrow"
776 when: overlay.width <= units.gu(60)
777 AnchorChanges {
778 target: notifications
779 anchors.left: parent.left
780 anchors.right: parent.right
781 }
782 },
783 State {
784 name: "wide"
785 when: overlay.width > units.gu(60)
786 AnchorChanges {
787 target: notifications
788 anchors.left: undefined
789 anchors.right: parent.right
790 }
791 PropertyChanges { target: notifications; width: units.gu(38) }
792 }
793 ]
794 }
795 }
797 Dialogs {
798 id: dialogs
799 anchors.fill: parent
800 z: overlay.z + 10
801 onPowerOffClicked: {
802 shutdownFadeOutRectangle.enabled = true;
803 shutdownFadeOutRectangle.visible = true;
804 shutdownFadeOut.start();
805 }
807 Connections {
808 target: physicalKeysFilter
809 onPowerKeyLongPress: dialogs.showPowerDialog();
810 }
811 }
813 EdgeDemo {
814 id: edgeDemo
815 objectName: "edgeDemo"
816 z: dialogs.z + 10
817 paused: Powerd.status === Powerd.Off || wizard.active // Saves power
818 greeter: greeter
819 launcher: launcher
820 panel: panel
821 stages: stages
822 }
824 Connections {
825 target: SessionBroadcast
826 onShowHome: showHome()
827 }
829 Rectangle {
830 id: shutdownFadeOutRectangle
831 z: edgeDemo.z + 10
832 enabled: false
833 visible: false
834 color: "black"
835 anchors.fill: parent
836 opacity: 0.0
837 NumberAnimation on opacity {
838 id: shutdownFadeOut
839 from: 0.0
840 to: 1.0
841 onStopped: {
842 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
843 DBusUnitySessionService.Shutdown();
844 }
845 }
846 }
847 }