Unity 8
Shell.qml
1 /*
2  * Copyright (C) 2013-2016 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.4
18 import QtQuick.Window 2.2
19 import AccountsService 0.1
20 import Unity.Application 0.1
21 import Ubuntu.Components 1.3
22 import Ubuntu.Components.Popups 1.3
23 import Ubuntu.Gestures 0.1
24 import Ubuntu.Telephony 0.1 as Telephony
25 import Unity.Connectivity 0.1
26 import Unity.Launcher 0.1
27 import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
28 import GSettings 1.0
29 import Utils 0.1
30 import Powerd 0.1
31 import SessionBroadcast 0.1
32 import "Greeter"
33 import "Launcher"
34 import "Panel"
35 import "Components"
36 import "Notifications"
37 import "Stages"
38 import "Tutorial"
39 import "Wizard"
40 import Unity.Notifications 1.0 as NotificationBackend
41 import Unity.Session 0.1
42 import Unity.DashCommunicator 0.1
43 import Unity.Indicators 0.1 as Indicators
44 import Cursor 1.0
45 
46 
47 Item {
48  id: shell
49 
50  // to be set from outside
51  property int orientationAngle: 0
52  property int orientation
53  property Orientations orientations
54  property real nativeWidth
55  property real nativeHeight
56  property alias indicatorAreaShowProgress: panel.indicatorAreaShowProgress
57  property bool beingResized
58  property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
59  property string mode: "full-greeter"
60  property alias oskEnabled: inputMethod.enabled
61  function updateFocusedAppOrientation() {
62  applicationsDisplayLoader.item.updateFocusedAppOrientation();
63  }
64  function updateFocusedAppOrientationAnimated() {
65  applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();
66  }
67  property bool hasMouse: false
68 
69  // to be read from outside
70  readonly property int mainAppWindowOrientationAngle:
71  applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindowOrientationAngle : 0
72 
73  readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
74  && (applicationsDisplayLoader.item && applicationsDisplayLoader.item.orientationChangesEnabled)
75  && (!greeter || !greeter.animating)
76 
77  readonly property bool showingGreeter: greeter && greeter.shown
78 
79  property bool startingUp: true
80  Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
81 
82  property int supportedOrientations: {
83  if (startingUp) {
84  // Ensure we don't rotate during start up
85  return Qt.PrimaryOrientation;
86  } else if (greeter && greeter.shown) {
87  return Qt.PrimaryOrientation;
88  } else if (applicationsDisplayLoader.item) {
89  return shell.orientations.map(applicationsDisplayLoader.item.supportedOrientations);
90  } else {
91  // we just don't care
92  return Qt.PortraitOrientation
93  | Qt.LandscapeOrientation
94  | Qt.InvertedPortraitOrientation
95  | Qt.InvertedLandscapeOrientation;
96  }
97  }
98 
99  // For autopilot consumption
100  readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
101 
102  // internal props from here onwards
103  readonly property var mainApp:
104  applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null
105  readonly property var mainAppWindow:
106  applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindow : null
107 
108  // Disable everything while greeter is waiting, so that the user can't swipe
109  // the greeter or launcher until we know whether the session is locked.
110  enabled: greeter && !greeter.waiting
111 
112  property real edgeSize: units.gu(2)
113 
114  WallpaperResolver {
115  id: wallpaperResolver
116  width: shell.width
117  }
118 
119  readonly property alias greeter: greeterLoader.item
120 
121  function activateApplication(appId) {
122  if (ApplicationManager.findApplication(appId)) {
123  ApplicationManager.requestFocusApplication(appId);
124  } else {
125  ApplicationManager.startApplication(appId);
126  }
127  }
128 
129  function startLockedApp(app) {
130  if (greeter.locked) {
131  greeter.lockedApp = app;
132  }
133  shell.activateApplication(app);
134  }
135 
136  Binding {
137  target: LauncherModel
138  property: "applicationManager"
139  value: ApplicationManager
140  }
141 
142  Component.onCompleted: {
143  theme.name = "Ubuntu.Components.Themes.SuruDark"
144  if (ApplicationManager.count > 0) {
145  ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
146  }
147  finishStartUpTimer.start();
148  }
149 
150  LightDM{id: lightDM} // Provide backend access
151  VolumeControl {
152  id: volumeControl
153  indicators: panel.indicators
154  }
155 
156  DashCommunicator {
157  id: dash
158  objectName: "dashCommunicator"
159  }
160 
161  PhysicalKeysMapper {
162  id: physicalKeysMapper
163  objectName: "physicalKeysMapper"
164 
165  onPowerKeyLongPressed: dialogs.showPowerDialog();
166  onVolumeDownTriggered: volumeControl.volumeDown();
167  onVolumeUpTriggered: volumeControl.volumeUp();
168  onScreenshotTriggered: screenGrabber.capture();
169  }
170 
171  GlobalShortcut {
172  // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
173  }
174 
175  WindowInputFilter {
176  id: inputFilter
177  Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
178  Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
179  }
180 
181  WindowInputMonitor {
182  onHomeKeyActivated: { launcher.fadeOut(); shell.showHome(); }
183  onTouchBegun: { cursor.opacity = 0; }
184  onTouchEnded: {
185  // move the (hidden) cursor to the last known touch position
186  var mappedCoords = mapFromItem(null, pos.x, pos.y);
187  cursor.x = mappedCoords.x;
188  cursor.y = mappedCoords.y;
189  }
190  }
191 
192  GSettings {
193  id: settings
194  schema.id: "com.canonical.Unity8"
195  }
196 
197  Item {
198  id: stages
199  objectName: "stages"
200  width: parent.width
201  height: parent.height
202 
203  Connections {
204  target: ApplicationManager
205 
206  // This signal is also fired when we try to focus the current app
207  // again. We rely on this!
208  onFocusedApplicationIdChanged: {
209  var appId = ApplicationManager.focusedApplicationId;
210 
211  if (wizard.active && appId != "" && appId != "unity8-dash") {
212  // If this happens on first boot, we may be in edge
213  // tutorial or wizard while receiving a call. But a call
214  // is more important than wizard so just bail out of those.
215  tutorial.finish();
216  wizard.hide();
217  }
218 
219  if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
220  // If we are in the middle of a call, make dialer lockedApp and show it.
221  // This can happen if user backs out of dialer back to greeter, then
222  // launches dialer again.
223  greeter.lockedApp = appId;
224  }
225  greeter.notifyAppFocused(appId);
226 
227  panel.indicators.hide();
228  }
229 
230  onApplicationAdded: {
231  launcher.hide();
232  }
233  }
234 
235  Loader {
236  id: applicationsDisplayLoader
237  objectName: "applicationsDisplayLoader"
238  anchors.fill: parent
239 
240  // When we have a locked app, we only want to show that one app.
241  // FIXME: do this in a less traumatic way. We currently only allow
242  // locked apps in phone mode (see FIXME in Lockscreen component in
243  // this same file). When that changes, we need to do something
244  // nicer here. But this code is currently just to prevent a
245  // theoretical attack where user enters lockedApp mode, then makes
246  // the screen larger (maybe connects to monitor) and tries to enter
247  // tablet mode.
248 
249  property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
250  ? "phone"
251  : shell.usageScenario
252  readonly property string qmlComponent: {
253  if(shell.mode === "greeter") {
254  return "Stages/ShimStage.qml"
255  } else if (applicationsDisplayLoader.usageScenario === "phone") {
256  return "Stages/PhoneStage.qml";
257  } else if (applicationsDisplayLoader.usageScenario === "tablet") {
258  return "Stages/TabletStage.qml";
259  } else {
260  return "Stages/DesktopStage.qml";
261  }
262  }
263  onQmlComponentChanged: {
264  if (item) item.stageAboutToBeUnloaded();
265  source = qmlComponent;
266  }
267 
268  property bool interactive: (!greeter || !greeter.shown)
269  && panel.indicators.fullyClosed
270  && launcher.progress == 0
271  && !notifications.useModal
272 
273  onInteractiveChanged: { if (interactive) { focus = true; } }
274 
275  Binding {
276  target: applicationsDisplayLoader.item
277  property: "objectName"
278  value: "stage"
279  }
280  Binding {
281  target: applicationsDisplayLoader.item
282  property: "dragAreaWidth"
283  value: shell.edgeSize
284  }
285  Binding {
286  target: applicationsDisplayLoader.item
287  property: "maximizedAppTopMargin"
288  // Not just using panel.panelHeight as that changes depending on the focused app.
289  value: panel.indicators.minimizedPanelHeight
290  }
291  Binding {
292  target: applicationsDisplayLoader.item
293  property: "interactive"
294  value: applicationsDisplayLoader.interactive
295  }
296  Binding {
297  target: applicationsDisplayLoader.item
298  property: "spreadEnabled"
299  value: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
300  }
301  Binding {
302  target: applicationsDisplayLoader.item
303  property: "inverseProgress"
304  value: greeter && greeter.locked ? 0 : launcher.progress
305  }
306  Binding {
307  target: applicationsDisplayLoader.item
308  property: "shellOrientationAngle"
309  value: shell.orientationAngle
310  }
311  Binding {
312  target: applicationsDisplayLoader.item
313  property: "shellOrientation"
314  value: shell.orientation
315  }
316  Binding {
317  target: applicationsDisplayLoader.item
318  property: "orientations"
319  value: shell.orientations
320  }
321  Binding {
322  target: applicationsDisplayLoader.item
323  property: "background"
324  value: wallpaperResolver.background
325  }
326  Binding {
327  target: applicationsDisplayLoader.item
328  property: "nativeWidth"
329  value: shell.nativeWidth
330  }
331  Binding {
332  target: applicationsDisplayLoader.item
333  property: "nativeHeight"
334  value: shell.nativeHeight
335  }
336  Binding {
337  target: applicationsDisplayLoader.item
338  property: "beingResized"
339  value: shell.beingResized
340  }
341  Binding {
342  target: applicationsDisplayLoader.item
343  property: "keepDashRunning"
344  value: launcher.shown || launcher.dashSwipe
345  }
346  Binding {
347  target: applicationsDisplayLoader.item
348  property: "suspended"
349  value: greeter.shown
350  }
351  Binding {
352  target: applicationsDisplayLoader.item
353  property: "altTabPressed"
354  value: physicalKeysMapper.altTabPressed
355  }
356  Binding {
357  target: applicationsDisplayLoader.item
358  property: "leftMargin"
359  value: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
360  }
361  }
362  }
363 
364  InputMethod {
365  id: inputMethod
366  objectName: "inputMethod"
367  anchors {
368  fill: parent
369  topMargin: panel.panelHeight
370  leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
371  }
372  z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
373  }
374 
375  Connections {
376  target: SessionManager
377  onSessionStopping: {
378  if (!session.parentSession && !session.application) {
379  // nothing is using it. delete it right away
380  session.release();
381  }
382  }
383  }
384 
385  Loader {
386  id: greeterLoader
387  anchors.fill: parent
388  anchors.topMargin: panel.panelHeight
389  sourceComponent: shell.mode != "shell" ? integratedGreeter :
390  Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
391  onLoaded: {
392  item.objectName = "greeter"
393  }
394  }
395 
396  Component {
397  id: integratedGreeter
398  Greeter {
399 
400  hides: [launcher, panel.indicators]
401  tabletMode: shell.usageScenario != "phone"
402  launcherOffset: launcher.progress
403  forcedUnlock: wizard.active
404  background: wallpaperResolver.background
405 
406  // avoid overlapping with Launcher's edge drag area
407  // FIXME: Fix TouchRegistry & friends and remove this workaround
408  // Issue involves launcher's DDA getting disabled on a long
409  // left-edge drag
410  dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
411 
412  onSessionStarted: {
413  launcher.hide();
414  }
415 
416  onTease: {
417  if (!tutorial.running) {
418  launcher.tease();
419  }
420  }
421 
422  onEmergencyCall: startLockedApp("dialer-app")
423  }
424  }
425 
426  Timer {
427  // See powerConnection for why this is useful
428  id: showGreeterDelayed
429  interval: 1
430  onTriggered: {
431  greeter.forceShow();
432  }
433  }
434 
435  Connections {
436  id: callConnection
437  target: callManager
438 
439  onHasCallsChanged: {
440  if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
441  // We just received an incoming call while locked. The
442  // indicator will have already launched dialer-app for us, but
443  // there is a race between "hasCalls" changing and the dialer
444  // starting up. So in case we lose that race, we'll start/
445  // focus the dialer ourselves here too. Even if the indicator
446  // didn't launch the dialer for some reason (or maybe a call
447  // started via some other means), if an active call is
448  // happening, we want to be in the dialer.
449  startLockedApp("dialer-app")
450  }
451  }
452  }
453 
454  Connections {
455  id: powerConnection
456  target: Powerd
457 
458  onStatusChanged: {
459  if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
460  !callManager.hasCalls && !wizard.active) {
461  // We don't want to simply call greeter.showNow() here, because
462  // that will take too long. Qt will delay button event
463  // handling until the greeter is done loading and may think the
464  // user held down the power button the whole time, leading to a
465  // power dialog being shown. Instead, delay showing the
466  // greeter until we've finished handling the event. We could
467  // make the greeter load asynchronously instead, but that
468  // introduces a whole host of timing issues, especially with
469  // its animations. So this is simpler.
470  showGreeterDelayed.start();
471  }
472  }
473  }
474 
475  function showHome() {
476  greeter.notifyAboutToFocusApp("unity8-dash");
477 
478  var animate = !lightDM.greeter.active && !stages.shown
479  dash.setCurrentScope(0, animate, false)
480  ApplicationManager.requestFocusApplication("unity8-dash")
481  }
482 
483  function showDash() {
484  if (greeter.notifyShowingDashFromDrag()) {
485  launcher.fadeOut();
486  }
487 
488  if (!greeter.locked && ApplicationManager.focusedApplicationId != "unity8-dash") {
489  ApplicationManager.requestFocusApplication("unity8-dash")
490  launcher.fadeOut();
491  }
492  }
493 
494  Item {
495  id: overlay
496  z: 10
497 
498  anchors.fill: parent
499 
500  Panel {
501  id: panel
502  objectName: "panel"
503  anchors.fill: parent //because this draws indicator menus
504  indicators {
505  hides: [launcher]
506  available: tutorial.panelEnabled
507  && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
508  && (!greeter || !greeter.hasLockedApp)
509  width: parent.width > units.gu(60) ? units.gu(40) : parent.width
510 
511  minimizedPanelHeight: units.gu(3)
512  expandedPanelHeight: units.gu(7)
513 
514  indicatorsModel: Indicators.IndicatorsModel {
515  // tablet and phone both use the same profile
516  profile: "phone"
517  Component.onCompleted: load();
518  }
519  }
520 
521  callHint {
522  greeterShown: greeter.shown
523  }
524 
525  readonly property bool topmostApplicationIsFullscreen: mainApp && mainApp.fullscreen
526 
527  fullscreenMode: (topmostApplicationIsFullscreen && !lightDM.greeter.active && launcher.progress == 0)
528  || greeter.hasLockedApp
529  locked: greeter && greeter.active
530  }
531 
532  Launcher {
533  id: launcher
534  objectName: "launcher"
535 
536  readonly property bool dashSwipe: progress > 0
537 
538  anchors.top: parent.top
539  anchors.topMargin: inverted ? 0 : panel.panelHeight
540  anchors.bottom: parent.bottom
541  width: parent.width
542  dragAreaWidth: shell.edgeSize
543  available: tutorial.launcherEnabled
544  && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
545  && !greeter.hasLockedApp
546  inverted: shell.usageScenario !== "desktop"
547  superPressed: physicalKeysMapper.superPressed
548  superTabPressed: physicalKeysMapper.superTabPressed
549  panelWidth: units.gu(settings.launcherWidth)
550  lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
551 
552  onShowDashHome: showHome()
553  onDash: showDash()
554  onDashSwipeChanged: {
555  if (dashSwipe) {
556  dash.setCurrentScope(0, false, true)
557  }
558  }
559  onLauncherApplicationSelected: {
560  greeter.notifyAboutToFocusApp(appId);
561  shell.activateApplication(appId);
562  }
563  onShownChanged: {
564  if (shown) {
565  panel.indicators.hide()
566  }
567  }
568  onFocusChanged: {
569  if (!focus) {
570  applicationsDisplayLoader.focus = true;
571  }
572  }
573 
574  GlobalShortcut {
575  shortcut: Qt.AltModifier | Qt.Key_F1
576  onTriggered: {
577  launcher.openForKeyboardNavigation();
578  }
579  }
580  GlobalShortcut {
581  shortcut: Qt.MetaModifier | Qt.Key_0
582  onTriggered: {
583  if (LauncherModel.get(9)) {
584  activateApplication(LauncherModel.get(9).appId);
585  }
586  }
587  }
588  Repeater {
589  model: 9
590  GlobalShortcut {
591  shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
592  onTriggered: {
593  if (LauncherModel.get(index)) {
594  activateApplication(LauncherModel.get(index).appId);
595  }
596  }
597  }
598  }
599  }
600 
601  Tutorial {
602  id: tutorial
603  objectName: "tutorial"
604  anchors.fill: parent
605 
606  paused: callManager.hasCalls || greeter.shown
607  keyboardVisible: inputMethod.state === "shown"
608  usageScenario: shell.usageScenario
609  lastInputTimestamp: inputFilter.lastInputTimestamp
610  launcher: launcher
611  panel: panel
612  stage: applicationsDisplayLoader.item
613  }
614 
615  Wizard {
616  id: wizard
617  objectName: "wizard"
618  anchors.fill: parent
619 
620  function unlockWhenDoneWithWizard() {
621  if (!active) {
622  Connectivity.unlockAllModems();
623  }
624  }
625 
626  Component.onCompleted: unlockWhenDoneWithWizard()
627  onActiveChanged: unlockWhenDoneWithWizard()
628  }
629 
630  Rectangle {
631  id: modalNotificationBackground
632 
633  visible: notifications.useModal
634  color: "#000000"
635  anchors.fill: parent
636  opacity: 0.9
637 
638  MouseArea {
639  anchors.fill: parent
640  }
641  }
642 
643  Notifications {
644  id: notifications
645 
646  model: NotificationBackend.Model
647  margin: units.gu(1)
648  hasMouse: shell.hasMouse
649  inverseMode: panel.indicators.shown
650  background: wallpaperResolver.background
651 
652  y: topmostIsFullscreen ? 0 : panel.panelHeight
653  height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
654 
655  states: [
656  State {
657  name: "narrow"
658  when: overlay.width <= units.gu(60)
659  AnchorChanges {
660  target: notifications
661  anchors.left: parent.left
662  anchors.right: parent.right
663  }
664  },
665  State {
666  name: "wide"
667  when: overlay.width > units.gu(60)
668  AnchorChanges {
669  target: notifications
670  anchors.left: undefined
671  anchors.right: parent.right
672  }
673  PropertyChanges { target: notifications; width: units.gu(38) }
674  }
675  ]
676  }
677  }
678 
679  Dialogs {
680  id: dialogs
681  objectName: "dialogs"
682  anchors.fill: parent
683  z: overlay.z + 10
684  usageScenario: shell.usageScenario
685  onPowerOffClicked: {
686  shutdownFadeOutRectangle.enabled = true;
687  shutdownFadeOutRectangle.visible = true;
688  shutdownFadeOut.start();
689  }
690  }
691 
692  Connections {
693  target: SessionBroadcast
694  onShowHome: showHome()
695  }
696 
697  ScreenGrabber {
698  id: screenGrabber
699  rotationAngle: -shell.orientationAngle
700  z: dialogs.z + 10
701  }
702 
703  Cursor {
704  id: cursor
705  visible: shell.hasMouse
706  z: screenGrabber.z + 1
707 
708  onPushedLeftBoundary: {
709  if (buttons === Qt.NoButton) {
710  launcher.pushEdge(amount);
711  }
712  }
713 
714  onPushedRightBoundary: {
715  if (buttons === Qt.NoButton && applicationsDisplayLoader.item
716  && applicationsDisplayLoader.item.pushRightEdge) {
717  applicationsDisplayLoader.item.pushRightEdge(amount);
718  }
719  }
720 
721  onMouseMoved: { cursor.opacity = 1; }
722  }
723 
724  // keymap switching
725  GlobalShortcut {
726  shortcut: Qt.MetaModifier|Qt.Key_Space
727  onTriggered: keymapPriv.nextKeymap()
728  active: keymapPriv.keymapCount > 1
729  }
730 
731  GlobalShortcut {
732  shortcut: Qt.MetaModifier|Qt.ShiftModifier|Qt.Key_Space
733  onTriggered: keymapPriv.previousKeymap()
734  active: keymapPriv.keymapCount > 1
735  }
736 
737  QtObject {
738  id: keymapPriv
739 
740  readonly property var keymaps: AccountsService.keymaps
741  readonly property int keymapCount: keymaps.length
742  property int currentKeymapIndex: 0 // the new one that we're setting
743  onCurrentKeymapIndexChanged: switchToKeymap();
744 
745  function nextKeymap() {
746  var nextIndex = 0;
747 
748  if (currentKeymapIndex !== -1 && currentKeymapIndex < keymapCount - 1) {
749  nextIndex = currentKeymapIndex + 1;
750  }
751  currentKeymapIndex = nextIndex;
752  }
753 
754  function previousKeymap() {
755  var prevIndex = keymapCount - 1;
756 
757  if (currentKeymapIndex > 0) {
758  prevIndex = currentKeymapIndex - 1;
759  }
760  currentKeymapIndex = prevIndex;
761  }
762 
763  function switchToKeymap() {
764  if (mainAppWindow) {
765  mainAppWindow.switchToKeymap(keymaps[currentKeymapIndex]);
766  }
767  }
768  }
769 
770  onMainAppWindowChanged: keymapPriv.switchToKeymap()
771 
772  Rectangle {
773  id: shutdownFadeOutRectangle
774  z: cursor.z + 1
775  enabled: false
776  visible: false
777  color: "black"
778  anchors.fill: parent
779  opacity: 0.0
780  NumberAnimation on opacity {
781  id: shutdownFadeOut
782  from: 0.0
783  to: 1.0
784  onStopped: {
785  if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
786  DBusUnitySessionService.shutdown();
787  }
788  }
789  }
790  }
791 }