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