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