on 3d80664 help sign in
unity8/qml/Shell.qml
846 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 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: true
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 Binding {
302 target: applicationsDisplayLoader.item
303 property: "background"
304 value: shell.background
305 }
306 }
307 }
309 InputMethod {
310 id: inputMethod
311 objectName: "inputMethod"
312 anchors { fill: parent; topMargin: panel.panelHeight }
313 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
314 }
316 Connections {
317 target: SurfaceManager
318 onSurfaceCreated: {
319 if (surface.type == MirSurfaceItem.InputMethod) {
320 inputMethod.surface = surface;
321 }
322 }
324 onSurfaceDestroyed: {
325 if (inputMethod.surface == surface) {
326 inputMethod.surface = null;
327 surface.parent = null;
328 }
329 if (!surface.parent) {
330 // there's no one displaying it. delete it right away
331 surface.release();
332 }
333 }
334 }
335 Connections {
336 target: SessionManager
337 onSessionStopping: {
338 if (!session.parentSession && !session.application) {
339 // nothing is using it. delete it right away
340 session.release();
341 }
342 }
343 }
345 Lockscreen {
346 id: lockscreen
347 objectName: "lockscreen"
349 hides: [launcher, panel.indicators]
350 shown: false
351 enabled: true
352 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
353 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
354 y: panel.panelHeight
355 visible: required
356 width: parent.width
357 height: parent.height - panel.panelHeight
358 background: shell.background
359 darkenBackground: 0.4
360 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
361 minPinLength: 4
362 maxPinLength: 4
364 // FIXME: We *should* show emergency dialer if there is a SIM present,
365 // regardless of whether the side stage is enabled. But right now,
366 // the assumption is that narrow screens are phones which have SIMs
367 // and wider screens are tablets which don't. When we do allow this
368 // on devices with a side stage and a SIM, work should be done to
369 // ensure that the main stage is disabled while the dialer is present
370 // in the side stage. See the FIXME in the stage loader in this file.
371 showEmergencyCallButton: !shell.sideStageEnabled
373 onEntered: LightDM.Greeter.respond(passphrase);
374 onCancel: greeter.show()
375 onEmergencyCall: startLockedApp("dialer-app")
377 onShownChanged: if (shown) greeter.lockedApp = ""
379 function maybeShow() {
380 if (!shell.forcedUnlock) {
381 show()
382 }
383 }
385 Timer {
386 id: forcedDelayTimer
387 interval: 1000 * 60
388 onTriggered: {
389 if (lockscreen.delayMinutes > 0) {
390 lockscreen.delayMinutes -= 1
391 if (lockscreen.delayMinutes > 0) {
392 start() // go again
393 }
394 }
395 }
396 }
398 Component.onCompleted: {
399 if (greeter.narrowMode) {
400 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
401 }
402 }
403 }
405 Connections {
406 target: LightDM.Greeter
408 onShowGreeter: greeter.show()
409 onHideGreeter: greeter.login()
411 onShowPrompt: {
412 shell.enabled = true;
413 if (!LightDM.Greeter.active) {
414 return; // could happen if hideGreeter() comes in before we prompt
415 }
416 if (greeter.narrowMode) {
417 if (isDefaultPrompt) {
418 if (lockscreen.alphaNumeric) {
419 lockscreen.infoText = i18n.tr("Enter passphrase")
420 lockscreen.errorText = i18n.tr("Sorry, incorrect passphrase") + "\n" +
421 i18n.tr("Please re-enter")
422 } else {
423 lockscreen.infoText = i18n.tr("Enter passcode")
424 lockscreen.errorText = i18n.tr("Sorry, incorrect passcode")
425 }
426 } else {
427 lockscreen.infoText = i18n.tr("Enter %1").arg(text.toLowerCase())
428 lockscreen.errorText = i18n.tr("Sorry, incorrect %1").arg(text.toLowerCase())
429 }
431 lockscreen.maybeShow();
432 }
433 }
435 onPromptlessChanged: {
436 if (!LightDM.Greeter.active) {
437 return; // could happen if hideGreeter() comes in before we prompt
438 }
439 if (greeter.narrowMode) {
440 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
441 lockscreen.hide()
442 } else {
443 lockscreen.reset();
444 lockscreen.maybeShow();
445 }
446 }
447 }
449 onAuthenticationComplete: {
450 shell.enabled = true;
451 if (LightDM.Greeter.authenticated) {
452 AccountsService.failedLogins = 0
453 }
454 // Else only penalize user for a failed login if they actually were
455 // prompted for a password. We do this below after the promptless
456 // early exit.
458 if (LightDM.Greeter.promptless) {
459 return;
460 }
462 if (LightDM.Greeter.authenticated) {
463 greeter.login();
464 } else {
465 AccountsService.failedLogins++
466 if (maxFailedLogins >= 2) { // require at least a warning
467 if (AccountsService.failedLogins === maxFailedLogins - 1) {
468 var title = lockscreen.alphaNumeric ?
469 i18n.tr("Sorry, incorrect passphrase.") :
470 i18n.tr("Sorry, incorrect passcode.")
471 var text = i18n.tr("This will be your last attempt.") + " " +
472 (lockscreen.alphaNumeric ?
473 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
474 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
475 lockscreen.showInfoPopup(title, text)
476 } else if (AccountsService.failedLogins >= maxFailedLogins) {
477 SystemImage.factoryReset() // Ouch!
478 }
479 }
480 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
481 lockscreen.delayMinutes = failedLoginsDelayMinutes
482 forcedDelayTimer.start()
483 }
485 lockscreen.clear(true);
486 if (greeter.narrowMode) {
487 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
488 }
489 }
490 }
491 }
493 Binding {
494 target: LightDM.Greeter
495 property: "active"
496 value: greeter.shown || lockscreen.shown || greeter.hasLockedApp
497 }
499 Rectangle {
500 anchors.fill: parent
501 color: "black"
502 opacity: greeterWrapper.showProgress * 0.8
503 }
505 Item {
506 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
507 id: greeterWrapper
508 objectName: "greeterWrapper"
509 x: greeter.narrowMode ? launcher.progress : 0
510 y: panel.panelHeight
511 width: parent.width
512 height: parent.height - panel.panelHeight
514 Behavior on x {
515 enabled: !launcher.dashSwipe
516 StandardAnimation {}
517 }
519 property bool fullyShown: showProgress === 1.0
520 onFullyShownChanged: {
521 // Wait until the greeter is completely covering lockscreen before resetting it.
522 if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
523 lockscreen.reset();
524 lockscreen.maybeShow();
525 }
526 }
528 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
529 onShowProgressChanged: {
530 if (showProgress === 0) {
531 if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || shell.forcedUnlock) {
532 greeter.login()
533 } else if (greeter.narrowMode) {
534 lockscreen.clear(false) // to reset focus if necessary
535 }
536 }
537 }
539 Greeter {
540 id: greeter
541 objectName: "greeter"
543 signal sessionStarted() // helpful for tests
545 property string lockedApp: ""
546 property bool hasLockedApp: lockedApp !== ""
548 available: true
549 hides: [launcher, panel.indicators]
550 shown: true
551 loadContent: required || lockscreen.required // keeps content in memory for quick show()
553 locked: shell.locked
555 background: shell.background
557 width: parent.width
558 height: parent.height
560 dragHandleWidth: shell.edgeSize
562 function startUnlock() {
563 if (narrowMode) {
564 if (!LightDM.Greeter.authenticated) {
565 lockscreen.maybeShow()
566 }
567 hide()
568 } else {
569 show()
570 tryToUnlock()
571 }
572 }
574 function login() {
575 enabled = false;
576 if (LightDM.Greeter.startSessionSync()) {
577 sessionStarted();
578 greeter.hide();
579 lockscreen.hide();
580 launcher.hide();
581 }
582 enabled = true;
583 }
585 onShownChanged: {
586 if (shown) {
587 // Disable everything so that user can't swipe greeter or
588 // launcher until we get first prompt/authenticate, which
589 // will re-enable the shell.
590 shell.enabled = false;
592 if (greeter.narrowMode) {
593 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
594 } else {
595 reset()
596 }
597 greeter.lockedApp = "";
598 greeter.forceActiveFocus();
599 }
600 }
602 Component.onCompleted: {
603 Connectivity.unlockAllModems()
604 }
606 onUnlocked: greeter.hide()
607 onSelected: {
608 // Update launcher items for new user
609 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
610 AccountsService.user = user;
611 LauncherModel.setUser(user);
612 }
614 onTease: launcher.tease()
616 Binding {
617 target: ApplicationManager
618 property: "suspended"
619 value: (greeter.shown && greeterWrapper.showProgress == 1) || lockscreen.shown
620 }
621 }
622 }
624 Connections {
625 id: callConnection
626 target: callManager
628 onHasCallsChanged: {
629 if (shell.locked && callManager.hasCalls) {
630 // We just received an incoming call while locked. The
631 // indicator will have already launched dialer-app for us, but
632 // there is a race between "hasCalls" changing and the dialer
633 // starting up. So in case we lose that race, we'll start/
634 // focus the dialer ourselves here too. Even if the indicator
635 // didn't launch the dialer for some reason (or maybe a call
636 // started via some other means), if an active call is
637 // happening, we want to be in the dialer.
638 startLockedApp("dialer-app")
639 }
640 }
641 }
643 Connections {
644 id: powerConnection
645 target: Powerd
647 onStatusChanged: {
648 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
649 !callManager.hasCalls && !edgeDemo.running) {
650 greeter.showNow()
651 }
652 }
653 }
655 function showHome() {
656 if (edgeDemo.running) {
657 return
658 }
660 if (LightDM.Greeter.active) {
661 greeter.startUnlock()
662 }
664 var animate = !LightDM.Greeter.active && !stages.shown
665 dash.setCurrentScope(0, animate, false)
666 ApplicationManager.requestFocusApplication("unity8-dash")
667 }
669 function showDash() {
670 if (greeter.hasLockedApp || // just in case user gets here
671 (!greeter.narrowMode && shell.locked)) {
672 return
673 }
675 if (greeter.shown) {
676 greeter.hideRight();
677 launcher.fadeOut();
678 }
680 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
681 ApplicationManager.requestFocusApplication("unity8-dash")
682 launcher.fadeOut();
683 }
684 }
686 Item {
687 id: overlay
688 z: 10
690 anchors.fill: parent
692 Panel {
693 id: panel
694 objectName: "panel"
695 anchors.fill: parent //because this draws indicator menus
696 indicators {
697 hides: [launcher]
698 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
699 contentEnabled: edgeDemo.panelContentEnabled
700 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
702 minimizedPanelHeight: units.gu(3)
703 expandedPanelHeight: units.gu(7)
705 indicatorsModel: visibleIndicators.model
706 }
708 VisibleIndicators {
709 id: visibleIndicators
710 // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
711 Component.onCompleted: initialise(indicatorProfile)
712 }
713 callHint {
714 greeterShown: greeter.shown || lockscreen.shown
715 }
717 property bool topmostApplicationIsFullscreen:
718 ApplicationManager.focusedApplicationId &&
719 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
721 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
722 || greeter.hasLockedApp
723 }
725 Launcher {
726 id: launcher
727 objectName: "launcher"
729 readonly property bool dashSwipe: progress > 0
731 anchors.top: parent.top
732 anchors.topMargin: inverted ? 0 : panel.panelHeight
733 anchors.bottom: parent.bottom
734 width: parent.width
735 dragAreaWidth: shell.edgeSize
736 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
737 inverted: usageModeSettings.usageMode === "Staged"
739 onShowDashHome: showHome()
740 onDash: showDash()
741 onDashSwipeChanged: {
742 if (dashSwipe) {
743 dash.setCurrentScope(0, false, true)
744 }
745 }
746 onLauncherApplicationSelected: {
747 if (greeter.hasLockedApp) {
748 greeter.startUnlock()
749 }
750 if (!edgeDemo.running)
751 shell.activateApplication(appId)
752 }
753 onShownChanged: {
754 if (shown) {
755 panel.indicators.hide()
756 }
757 }
758 }
760 Rectangle {
761 id: modalNotificationBackground
763 visible: notifications.useModal && (notifications.state == "narrow")
764 color: "#000000"
765 anchors.fill: parent
766 opacity: 0.9
768 MouseArea {
769 anchors.fill: parent
770 }
771 }
773 Notifications {
774 id: notifications
776 model: NotificationBackend.Model
777 margin: units.gu(1)
779 y: topmostIsFullscreen ? 0 : panel.panelHeight
780 width: parent.width
781 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
783 states: [
784 State {
785 name: "narrow"
786 when: overlay.width <= units.gu(60)
787 AnchorChanges { target: notifications; anchors.left: parent.left }
788 },
789 State {
790 name: "wide"
791 when: overlay.width > units.gu(60)
792 AnchorChanges { target: notifications; anchors.left: undefined }
793 PropertyChanges { target: notifications; width: units.gu(38) }
794 }
795 ]
796 }
797 }
799 Dialogs {
800 id: dialogs
801 anchors.fill: parent
802 z: overlay.z + 10
803 onPowerOffClicked: {
804 shutdownFadeOutRectangle.enabled = true;
805 shutdownFadeOutRectangle.visible = true;
806 shutdownFadeOut.start();
807 }
808 }
810 EdgeDemo {
811 id: edgeDemo
812 objectName: "edgeDemo"
813 z: dialogs.z + 10
814 paused: Powerd.status === Powerd.Off // Saves power
815 greeter: greeter
816 launcher: launcher
817 panel: panel
818 stages: stages
819 }
821 Connections {
822 target: SessionBroadcast
823 onShowHome: showHome()
824 }
826 Rectangle {
827 id: shutdownFadeOutRectangle
828 z: edgeDemo.z + 10
829 enabled: false
830 visible: false
831 color: "black"
832 anchors.fill: parent
833 opacity: 0.0
834 NumberAnimation on opacity {
835 id: shutdownFadeOut
836 from: 0.0
837 to: 1.0
838 onStopped: {
839 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
840 DBusUnitySessionService.Shutdown();
841 }
842 }
843 }
844 }