on ffe76cc help sign in
unity8/qml/Shell.qml
859 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 PhysicalKeysMapper {
158 id: physicalKeysMapper
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 physicalKeysMapper.onKeyPressed(event.key);
180 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
181 event.accepted = true;
182 } else {
183 event.accepted = false;
184 }
185 }
187 Keys.onReleased: {
188 physicalKeysMapper.onKeyReleased(event.key);
189 if (event.key = Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
190 event.accepted = true;
191 } else {
192 event.accepted = false;
193 }
194 }
195 }
197 Item {
198 id: stages
199 objectName: "stages"
200 width: parent.width
201 height: parent.height
202 visible: !ApplicationManager.empty
204 Connections {
205 target: ApplicationManager
206 onFocusRequested: {
207 if (greeter.narrowMode) {
208 if (appId === "dialer-app" && callManager.hasCalls && shell.locked) {
209 // If we are in the middle of a call, make dialer lockedApp and show it.
210 // This can happen if user backs out of dialer back to greeter, then
211 // launches dialer again.
212 greeter.lockedApp = appId;
213 }
214 if (greeter.hasLockedApp) {
215 if (appId === greeter.lockedApp) {
216 lockscreen.hide() // show locked app
217 } else {
218 greeter.startUnlock() // show lockscreen if necessary
219 }
220 }
221 greeter.hide();
222 } else {
223 if (LightDM.Greeter.active) {
224 greeter.startUnlock()
225 }
226 }
227 }
229 onFocusedApplicationIdChanged: {
230 if (greeter.hasLockedApp && greeter.lockedApp !== ApplicationManager.focusedApplicationId) {
231 greeter.startUnlock()
232 }
233 panel.indicators.hide();
234 }
236 onApplicationAdded: {
237 if (greeter.shown && appId != "unity8-dash") {
238 greeter.startUnlock()
240 // If this happens on first boot, we may be in edge
241 // tutorial or wizard while receiving a call. But a call
242 // is more important than wizard so just bail out of those.
243 if (edgeDemo.running) {
244 edgeDemo.hideEdgeDemos();
245 wizard.hide();
246 }
247 }
248 if (greeter.narrowMode && greeter.hasLockedApp && appId === greeter.lockedApp) {
249 lockscreen.hide() // show locked app
250 }
251 launcher.hide();
252 }
253 }
255 Loader {
256 id: applicationsDisplayLoader
257 objectName: "applicationsDisplayLoader"
258 anchors.fill: parent
260 // When we have a locked app, we only want to show that one app.
261 // FIXME: do this in a less traumatic way. We currently only allow
262 // locked apps in phone mode (see FIXME in Lockscreen component in
263 // this same file). When that changes, we need to do something
264 // nicer here. But this code is currently just to prevent a
265 // theoretical attack where user enters lockedApp mode, then makes
266 // the screen larger (maybe connects to monitor) and tries to enter
267 // tablet mode.
268 property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
269 source: tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
271 Binding {
272 target: applicationsDisplayLoader.item
273 property: "objectName"
274 value: "stage"
275 }
276 Binding {
277 target: applicationsDisplayLoader.item
278 property: "dragAreaWidth"
279 value: shell.edgeSize
280 }
281 Binding {
282 target: applicationsDisplayLoader.item
283 property: "maximizedAppTopMargin"
284 // Not just using panel.panelHeight as that changes depending on the focused app.
285 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
286 }
287 Binding {
288 target: applicationsDisplayLoader.item
289 property: "interactive"
290 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
291 }
292 Binding {
293 target: applicationsDisplayLoader.item
294 property: "spreadEnabled"
295 value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
296 }
297 Binding {
298 target: applicationsDisplayLoader.item
299 property: "inverseProgress"
300 value: launcher.progress
301 }
302 Binding {
303 target: applicationsDisplayLoader.item
304 property: "orientation"
305 value: shell.orientation
306 }
307 }
308 }
310 InputMethod {
311 id: inputMethod
312 objectName: "inputMethod"
313 anchors { fill: parent; topMargin: panel.panelHeight }
314 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
315 }
317 Connections {
318 target: SurfaceManager
319 onSurfaceCreated: {
320 if (surface.type == MirSurfaceItem.InputMethod) {
321 inputMethod.surface = surface;
322 }
323 }
325 onSurfaceDestroyed: {
326 if (inputMethod.surface == surface) {
327 inputMethod.surface = null;
328 surface.parent = null;
329 }
330 if (!surface.parent) {
331 // there's no one displaying it. delete it right away
332 surface.release();
333 }
334 }
335 }
336 Connections {
337 target: SessionManager
338 onSessionStopping: {
339 if (!session.parentSession && !session.application) {
340 // nothing is using it. delete it right away
341 session.release();
342 }
343 }
344 }
346 Lockscreen {
347 id: lockscreen
348 objectName: "lockscreen"
350 hides: [launcher, panel.indicators]
351 shown: false
352 enabled: true
353 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
354 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
355 y: panel.panelHeight
356 visible: required
357 width: parent.width
358 height: parent.height - panel.panelHeight
359 background: shell.background
360 darkenBackground: 0.4
361 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
362 minPinLength: 4
363 maxPinLength: 4
365 property string promptText
366 infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) :
367 alphaNumeric ? i18n.tr("Enter passphrase") :
368 i18n.tr("Enter passcode")
369 errorText: promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) :
370 alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" +
371 i18n.tr("Please re-enter") :
372 i18n.tr("Sorry, incorrect passcode")
374 // FIXME: We *should* show emergency dialer if there is a SIM present,
375 // regardless of whether the side stage is enabled. But right now,
376 // the assumption is that narrow screens are phones which have SIMs
377 // and wider screens are tablets which don't. When we do allow this
378 // on devices with a side stage and a SIM, work should be done to
379 // ensure that the main stage is disabled while the dialer is present
380 // in the side stage. See the FIXME in the stage loader in this file.
381 showEmergencyCallButton: !shell.sideStageEnabled
383 onEntered: LightDM.Greeter.respond(passphrase);
384 onCancel: greeter.show()
385 onEmergencyCall: startLockedApp("dialer-app")
387 onShownChanged: if (shown) greeter.lockedApp = ""
389 function maybeShow() {
390 if (!shell.forcedUnlock) {
391 show()
392 }
393 }
395 Timer {
396 id: forcedDelayTimer
397 interval: 1000 * 60
398 onTriggered: {
399 if (lockscreen.delayMinutes > 0) {
400 lockscreen.delayMinutes -= 1
401 if (lockscreen.delayMinutes > 0) {
402 start() // go again
403 }
404 }
405 }
406 }
408 Component.onCompleted: {
409 if (greeter.narrowMode) {
410 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
411 }
412 }
413 }
415 Connections {
416 target: LightDM.Greeter
418 onShowGreeter: greeter.show()
419 onHideGreeter: greeter.login()
421 onShowPrompt: {
422 shell.enabled = true;
423 if (!LightDM.Greeter.active) {
424 return; // could happen if hideGreeter() comes in before we prompt
425 }
426 if (greeter.narrowMode) {
427 lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase();
428 lockscreen.maybeShow();
429 }
430 }
432 onPromptlessChanged: {
433 if (!LightDM.Greeter.active) {
434 return; // could happen if hideGreeter() comes in before we prompt
435 }
436 if (greeter.narrowMode) {
437 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
438 lockscreen.hide()
439 } else {
440 lockscreen.reset();
441 lockscreen.maybeShow();
442 }
443 }
444 }
446 onAuthenticationComplete: {
447 shell.enabled = true;
448 if (LightDM.Greeter.authenticated) {
449 AccountsService.failedLogins = 0
450 }
451 // Else only penalize user for a failed login if they actually were
452 // prompted for a password. We do this below after the promptless
453 // early exit.
455 if (LightDM.Greeter.promptless) {
456 return;
457 }
459 if (LightDM.Greeter.authenticated) {
460 greeter.login();
461 } else {
462 AccountsService.failedLogins++
463 if (maxFailedLogins >= 2) { // require at least a warning
464 if (AccountsService.failedLogins === maxFailedLogins - 1) {
465 var title = lockscreen.alphaNumeric ?
466 i18n.tr("Sorry, incorrect passphrase.") :
467 i18n.tr("Sorry, incorrect passcode.")
468 var text = i18n.tr("This will be your last attempt.") + " " +
469 (lockscreen.alphaNumeric ?
470 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
471 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
472 lockscreen.showInfoPopup(title, text)
473 } else if (AccountsService.failedLogins >= maxFailedLogins) {
474 SystemImage.factoryReset() // Ouch!
475 }
476 }
477 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
478 lockscreen.delayMinutes = failedLoginsDelayMinutes
479 forcedDelayTimer.start()
480 }
482 lockscreen.clear(true);
483 if (greeter.narrowMode) {
484 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
485 }
486 }
487 }
488 }
490 Binding {
491 target: LightDM.Greeter
492 property: "active"
493 value: greeter.shown || lockscreen.shown || greeter.hasLockedApp
494 }
496 Rectangle {
497 anchors.fill: parent
498 color: "black"
499 opacity: greeterWrapper.showProgress * 0.8
500 }
502 Item {
503 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
504 id: greeterWrapper
505 objectName: "greeterWrapper"
506 x: (greeter.narrowMode && greeter.showProgress > 0) ? launcher.progress : 0
507 y: panel.panelHeight
508 width: parent.width
509 height: parent.height - panel.panelHeight
511 Behavior on x {
512 enabled: !launcher.dashSwipe
513 StandardAnimation {}
514 }
516 property bool fullyShown: showProgress === 1.0
517 onFullyShownChanged: {
518 // Wait until the greeter is completely covering lockscreen before resetting it.
519 if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
520 lockscreen.reset();
521 lockscreen.maybeShow();
522 }
523 }
525 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
526 onShowProgressChanged: {
527 if (showProgress === 0) {
528 if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || shell.forcedUnlock) {
529 greeter.login()
530 } else if (greeter.narrowMode) {
531 lockscreen.clear(false) // to reset focus if necessary
532 }
533 }
534 }
536 Greeter {
537 id: greeter
538 objectName: "greeter"
540 signal sessionStarted() // helpful for tests
542 property string lockedApp: ""
543 property bool hasLockedApp: lockedApp !== ""
545 available: true
546 hides: [launcher, panel.indicators]
547 shown: true
548 loadContent: required || lockscreen.required // keeps content in memory for quick show()
550 locked: shell.locked
552 background: shell.background
554 width: parent.width
555 height: parent.height
557 dragHandleWidth: shell.edgeSize
559 function startUnlock() {
560 if (narrowMode) {
561 if (!LightDM.Greeter.authenticated) {
562 lockscreen.maybeShow()
563 }
564 hide()
565 } else {
566 show()
567 tryToUnlock()
568 }
569 }
571 function login() {
572 enabled = false;
573 if (LightDM.Greeter.startSessionSync()) {
574 sessionStarted();
575 greeter.hide();
576 lockscreen.hide();
577 launcher.hide();
578 }
579 enabled = true;
580 }
582 onShownChanged: {
583 if (shown) {
584 // Disable everything so that user can't swipe greeter or
585 // launcher until we get the next prompt/authenticate, which
586 // will re-enable the shell.
587 shell.enabled = false;
589 if (greeter.narrowMode) {
590 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
591 } else {
592 reset()
593 }
594 greeter.lockedApp = "";
595 greeter.forceActiveFocus();
596 }
597 }
599 Component.onCompleted: {
600 Connectivity.unlockAllModems()
601 }
603 onUnlocked: greeter.hide()
604 onSelected: {
605 // Update launcher items for new user
606 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
607 AccountsService.user = user;
608 LauncherModel.setUser(user);
609 }
611 onTease: launcher.tease()
613 Binding {
614 target: ApplicationManager
615 property: "suspended"
616 value: (greeter.shown && greeterWrapper.showProgress == 1) || lockscreen.shown
617 }
618 }
619 }
621 Connections {
622 id: callConnection
623 target: callManager
625 onHasCallsChanged: {
626 if (shell.locked && callManager.hasCalls) {
627 // We just received an incoming call while locked. The
628 // indicator will have already launched dialer-app for us, but
629 // there is a race between "hasCalls" changing and the dialer
630 // starting up. So in case we lose that race, we'll start/
631 // focus the dialer ourselves here too. Even if the indicator
632 // didn't launch the dialer for some reason (or maybe a call
633 // started via some other means), if an active call is
634 // happening, we want to be in the dialer.
635 startLockedApp("dialer-app")
636 }
637 }
638 }
640 Connections {
641 id: powerConnection
642 target: Powerd
644 onStatusChanged: {
645 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
646 !callManager.hasCalls && !edgeDemo.running) {
647 greeter.showNow()
648 }
649 }
650 }
652 function showHome() {
653 if (edgeDemo.running) {
654 return
655 }
657 if (LightDM.Greeter.active) {
658 greeter.startUnlock()
659 }
661 var animate = !LightDM.Greeter.active && !stages.shown
662 dash.setCurrentScope(0, animate, false)
663 ApplicationManager.requestFocusApplication("unity8-dash")
664 }
666 function showDash() {
667 if (greeter.hasLockedApp || // just in case user gets here
668 (!greeter.narrowMode && shell.locked)) {
669 return
670 }
672 if (greeter.shown) {
673 greeter.hideRight();
674 launcher.fadeOut();
675 }
677 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
678 ApplicationManager.requestFocusApplication("unity8-dash")
679 launcher.fadeOut();
680 }
681 }
683 Item {
684 id: overlay
685 z: 10
687 anchors.fill: parent
689 Panel {
690 id: panel
691 objectName: "panel"
692 anchors.fill: parent //because this draws indicator menus
693 indicators {
694 hides: [launcher]
695 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
696 contentEnabled: edgeDemo.panelContentEnabled
697 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
699 minimizedPanelHeight: units.gu(3)
700 expandedPanelHeight: units.gu(7)
702 indicatorsModel: visibleIndicators.model
703 }
705 VisibleIndicators {
706 id: visibleIndicators
707 // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
708 Component.onCompleted: initialise(indicatorProfile)
709 }
710 callHint {
711 greeterShown: greeter.shown || lockscreen.shown
712 }
714 property bool topmostApplicationIsFullscreen:
715 ApplicationManager.focusedApplicationId &&
716 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
718 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
719 || greeter.hasLockedApp
720 }
722 Launcher {
723 id: launcher
724 objectName: "launcher"
726 readonly property bool dashSwipe: progress > 0
728 anchors.top: parent.top
729 anchors.bottom: parent.bottom
730 width: parent.width
731 dragAreaWidth: shell.edgeSize
732 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
734 onShowDashHome: showHome()
735 onDash: showDash()
736 onDashSwipeChanged: {
737 if (dashSwipe) {
738 dash.setCurrentScope(0, false, true)
739 }
740 }
741 onLauncherApplicationSelected: {
742 if (greeter.hasLockedApp) {
743 greeter.startUnlock()
744 }
745 if (!edgeDemo.running)
746 shell.activateApplication(appId)
747 }
748 onShownChanged: {
749 if (shown) {
750 panel.indicators.hide()
751 }
752 }
753 }
755 Wizard {
756 id: wizard
757 anchors.fill: parent
758 background: shell.background
759 }
761 Rectangle {
762 id: modalNotificationBackground
764 visible: notifications.useModal && (notifications.state == "narrow")
765 color: "#000000"
766 anchors.fill: parent
767 opacity: 0.9
769 MouseArea {
770 anchors.fill: parent
771 }
772 }
774 Notifications {
775 id: notifications
777 model: NotificationBackend.Model
778 margin: units.gu(1)
780 y: topmostIsFullscreen ? 0 : panel.panelHeight
781 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
783 states: [
784 State {
785 name: "narrow"
786 when: overlay.width <= units.gu(60)
787 AnchorChanges {
788 target: notifications
789 anchors.left: parent.left
790 anchors.right: parent.right
791 }
792 },
793 State {
794 name: "wide"
795 when: overlay.width > units.gu(60)
796 AnchorChanges {
797 target: notifications
798 anchors.left: undefined
799 anchors.right: parent.right
800 }
801 PropertyChanges { target: notifications; width: units.gu(38) }
802 }
803 ]
804 }
805 }
807 Dialogs {
808 id: dialogs
809 anchors.fill: parent
810 z: overlay.z + 10
811 onPowerOffClicked: {
812 shutdownFadeOutRectangle.enabled = true;
813 shutdownFadeOutRectangle.visible = true;
814 shutdownFadeOut.start();
815 }
817 Connections {
818 target: physicalKeysMapper
819 onPowerKeyLongPress: dialogs.showPowerDialog();
820 }
821 }
823 EdgeDemo {
824 id: edgeDemo
825 objectName: "edgeDemo"
826 z: dialogs.z + 10
827 paused: Powerd.status === Powerd.Off || wizard.active // Saves power
828 greeter: greeter
829 launcher: launcher
830 panel: panel
831 stages: stages
832 }
834 Connections {
835 target: SessionBroadcast
836 onShowHome: showHome()
837 }
839 Rectangle {
840 id: shutdownFadeOutRectangle
841 z: edgeDemo.z + 10
842 enabled: false
843 visible: false
844 color: "black"
845 anchors.fill: parent
846 opacity: 0.0
847 NumberAnimation on opacity {
848 id: shutdownFadeOut
849 from: 0.0
850 to: 1.0
851 onStopped: {
852 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
853 DBusUnitySessionService.Shutdown();
854 }
855 }
856 }
857 }