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