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