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