on cfcde21 help sign in
unity8/qml/Shell.qml
787 lines 27 kB history raw
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 */
17import QtQuick 2.4
18import QtQuick.Window 2.2
19import AccountsService 0.1
20import Unity.Application 0.1
21import Ubuntu.Components 1.3
22import Ubuntu.Components.Popups 1.3
23import Ubuntu.Gestures 0.1
24import Ubuntu.Telephony 0.1 as Telephony
25import Unity.Connectivity 0.1
26import Unity.Launcher 0.1
27import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
28import GSettings 1.0
29import ImageCache 0.1
30import Utils 0.1
31import Powerd 0.1
32import SessionBroadcast 0.1
33import "Greeter"
34import "Launcher"
35import "Panel"
36import "Components"
37import "Notifications"
38import "Stage"
39import "Tutorial"
40import "Wizard"
41import Unity.Notifications 1.0 as NotificationBackend
42import Unity.Session 0.1
43import Unity.DashCommunicator 0.1
44import Unity.Indicators 0.1 as Indicators
45import Cursor 1.1
46import WindowManager 0.1
49StyledItem {
50 id: shell
52 theme.name: "Ubuntu.Components.Themes.SuruDark"
54 // to be set from outside
55 property int orientationAngle: 0
56 property int orientation
57 property Orientations orientations
58 property real nativeWidth
59 property real nativeHeight
60 property alias indicatorAreaShowProgress: panel.indicatorAreaShowProgress
61 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
62 property string mode: "full-greeter"
63 property alias oskEnabled: inputMethod.enabled
64 function updateFocusedAppOrientation() {
65 stage.updateFocusedAppOrientation();
66 }
67 function updateFocusedAppOrientationAnimated() {
68 stage.updateFocusedAppOrientationAnimated();
69 }
70 property bool hasMouse: false
72 // to be read from outside
73 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
75 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
76 && stage.orientationChangesEnabled
77 && (!greeter || !greeter.animating)
79 readonly property bool showingGreeter: greeter && greeter.shown
81 property bool startingUp: true
82 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
84 property int supportedOrientations: {
85 if (startingUp) {
86 // Ensure we don't rotate during start up
87 return Qt.PrimaryOrientation;
88 } else if (showingGreeter || notifications.topmostIsFullscreen) {
89 return Qt.PrimaryOrientation;
90 } else {
91 return shell.orientations.map(stage.supportedOrientations);
92 }
93 }
95 readonly property var mainApp: stage.mainApp
97 onMainAppChanged: {
98 if (mainApp) {
99 _onMainAppChanged(mainApp.appId);
100 }
101 }
102 Connections {
103 target: ApplicationManager
104 onFocusRequested: {
105 if (shell.mainApp && shell.mainApp.appId === appId) {
106 _onMainAppChanged(appId);
107 }
108 }
109 }
110 function _onMainAppChanged(appId) {
111 if (wizard.active && appId != "" && appId != "unity8-dash") {
112 // If this happens on first boot, we may be in the
113 // wizard while receiving a call. But a call is more
114 // important than the wizard so just bail out of it.
115 wizard.hide();
116 }
118 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
119 // If we are in the middle of a call, make dialer lockedApp and show it.
120 // This can happen if user backs out of dialer back to greeter, then
121 // launches dialer again.
122 greeter.lockedApp = appId;
123 }
124 greeter.notifyAppFocusRequested(appId);
126 panel.indicators.hide();
127 launcher.hide();
128 }
130 // For autopilot consumption
131 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
133 // Note when greeter is waiting on PAM, so that we can disable edges until
134 // we know which user data to show and whether the session is locked.
135 readonly property bool waitingOnGreeter: greeter && greeter.waiting
137 property real edgeSize: units.gu(settings.edgeDragWidth)
139 WallpaperResolver {
140 id: wallpaperResolver
141 objectName: "wallpaperResolver"
143 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
144 readonly property bool hasCustomBackground: background != defaultBackground
146 // Use a cached version of the scaled-down wallpaper (as sometimes the
147 // image can be quite big compared to the device size, including for
148 // our default wallpaper). We use a name=wallpaper argument here to
149 // make sure we don't litter our cache with lots of scaled images. We
150 // only need to bother caching one at a time.
151 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
153 GSettings {
154 id: backgroundSettings
155 schema.id: "org.gnome.desktop.background"
156 }
158 candidates: [
159 AccountsService.backgroundFile,
160 backgroundSettings.pictureUri,
161 defaultBackground
162 ]
163 }
165 readonly property alias greeter: greeterLoader.item
167 function activateApplication(appId) {
168 // Either open the app in our own session, or -- if we're acting as a
169 // greeter -- ask the user's session to open it for us.
170 if (shell.mode === "greeter") {
171 activateURL("application:///" + appId + ".desktop");
172 } else {
173 startApp(appId);
174 }
175 }
177 function activateURL(url) {
178 SessionBroadcast.requestUrlStart(AccountsService.user, url);
179 greeter.notifyUserRequestedApp();
180 panel.indicators.hide();
181 }
183 function startApp(appId) {
184 if (ApplicationManager.findApplication(appId)) {
185 ApplicationManager.requestFocusApplication(appId);
186 } else {
187 ApplicationManager.startApplication(appId);
188 }
189 }
191 function startLockedApp(app) {
192 if (greeter.locked) {
193 greeter.lockedApp = app;
194 }
195 startApp(app); // locked apps are always in our same session
196 }
198 Binding {
199 target: LauncherModel
200 property: "applicationManager"
201 value: ApplicationManager
202 }
204 Component.onCompleted: {
205 finishStartUpTimer.start();
206 }
208 VolumeControl {
209 id: volumeControl
210 }
212 DashCommunicator {
213 id: dash
214 objectName: "dashCommunicator"
215 }
217 PhysicalKeysMapper {
218 id: physicalKeysMapper
219 objectName: "physicalKeysMapper"
221 onPowerKeyLongPressed: dialogs.showPowerDialog();
222 onVolumeDownTriggered: volumeControl.volumeDown();
223 onVolumeUpTriggered: volumeControl.volumeUp();
224 onScreenshotTriggered: itemGrabber.capture(shell);
225 }
227 GlobalShortcut {
228 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
229 }
231 WindowInputFilter {
232 id: inputFilter
233 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
234 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
235 }
237 WindowInputMonitor {
238 objectName: "windowInputMonitor"
239 onHomeKeyActivated: {
240 // Ignore when greeter is active, to avoid pocket presses
241 if (!greeter.active) {
242 launcher.fadeOut();
243 shell.showHome();
244 }
245 }
246 onTouchBegun: { cursor.opacity = 0; }
247 onTouchEnded: {
248 // move the (hidden) cursor to the last known touch position
249 var mappedCoords = mapFromItem(null, pos.x, pos.y);
250 cursor.x = mappedCoords.x;
251 cursor.y = mappedCoords.y;
252 cursor.mouseNeverMoved = false;
253 }
254 }
256 GSettings {
257 id: settings
258 schema.id: "com.canonical.Unity8"
259 }
261 Item {
262 id: stages
263 objectName: "stages"
264 width: parent.width
265 height: parent.height
267 TopLevelSurfaceList {
268 id: topLevelSurfaceList
269 objectName: "topLevelSurfaceList"
270 applicationsModel: ApplicationManager
271 }
273 Stage {
274 id: stage
275 objectName: "stage"
276 anchors.fill: parent
277 focus: true
279 dragAreaWidth: shell.edgeSize
280 background: wallpaperResolver.background
282 applicationManager: ApplicationManager
283 topLevelSurfaceList: topLevelSurfaceList
284 inputMethodRect: inputMethod.visibleRect
286 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
287 ? "phone"
288 : shell.usageScenario
290 mode: usageScenario == "phone" ? "staged"
291 : usageScenario == "tablet" ? "stagedWithSideStage"
292 : "windowed"
294 shellOrientation: shell.orientation
295 shellOrientationAngle: shell.orientationAngle
296 orientations: shell.orientations
297 nativeWidth: shell.nativeWidth
298 nativeHeight: shell.nativeHeight
300 interactive: (!greeter || !greeter.shown)
301 && panel.indicators.fullyClosed
302 && !notifications.useModal
304 onInteractiveChanged: { if (interactive) { focus = true; } }
306 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
307 suspended: greeter.shown
308 altTabPressed: physicalKeysMapper.altTabPressed
309 oskEnabled: shell.oskEnabled
310 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
311 }
312 }
314 InputMethod {
315 id: inputMethod
316 objectName: "inputMethod"
317 anchors {
318 fill: parent
319 topMargin: panel.panelHeight
320 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
321 }
322 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
323 }
325 Loader {
326 id: greeterLoader
327 objectName: "greeterLoader"
328 anchors.fill: parent
329 anchors.topMargin: panel.panelHeight
330 sourceComponent: shell.mode != "shell" ? integratedGreeter :
331 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
332 onLoaded: {
333 item.objectName = "greeter"
334 }
335 }
337 Component {
338 id: integratedGreeter
339 Greeter {
341 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
342 hides: [launcher, panel.indicators]
343 tabletMode: shell.usageScenario != "phone"
344 forcedUnlock: wizard.active || shell.mode === "full-shell"
345 background: wallpaperResolver.cachedBackground
346 hasCustomBackground: wallpaperResolver.hasCustomBackground
347 allowFingerprint: !dialogs.hasActiveDialog &&
348 !notifications.topmostIsFullscreen &&
349 !panel.indicators.shown
351 // avoid overlapping with Launcher's edge drag area
352 // FIXME: Fix TouchRegistry & friends and remove this workaround
353 // Issue involves launcher's DDA getting disabled on a long
354 // left-edge drag
355 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
357 onSessionStarted: {
358 launcher.hide();
359 }
361 onTease: {
362 if (!tutorial.running) {
363 launcher.tease();
364 }
365 }
367 onEmergencyCall: startLockedApp("dialer-app")
368 }
369 }
371 Timer {
372 // See powerConnection for why this is useful
373 id: showGreeterDelayed
374 interval: 1
375 onTriggered: {
376 greeter.forceShow();
377 }
378 }
380 Connections {
381 id: callConnection
382 target: callManager
384 onHasCallsChanged: {
385 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
386 // We just received an incoming call while locked. The
387 // indicator will have already launched dialer-app for us, but
388 // there is a race between "hasCalls" changing and the dialer
389 // starting up. So in case we lose that race, we'll start/
390 // focus the dialer ourselves here too. Even if the indicator
391 // didn't launch the dialer for some reason (or maybe a call
392 // started via some other means), if an active call is
393 // happening, we want to be in the dialer.
394 startLockedApp("dialer-app")
395 }
396 }
397 }
399 Connections {
400 id: powerConnection
401 target: Powerd
403 onStatusChanged: {
404 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
405 !callManager.hasCalls && !wizard.active) {
406 // We don't want to simply call greeter.showNow() here, because
407 // that will take too long. Qt will delay button event
408 // handling until the greeter is done loading and may think the
409 // user held down the power button the whole time, leading to a
410 // power dialog being shown. Instead, delay showing the
411 // greeter until we've finished handling the event. We could
412 // make the greeter load asynchronously instead, but that
413 // introduces a whole host of timing issues, especially with
414 // its animations. So this is simpler.
415 showGreeterDelayed.start();
416 }
417 }
418 }
420 function showHome() {
421 greeter.notifyUserRequestedApp();
423 if (shell.mode === "greeter") {
424 SessionBroadcast.requestHomeShown(AccountsService.user);
425 } else {
426 var animate = !LightDMService.greeter.active && !stages.shown;
427 dash.setCurrentScope(0, animate, false);
428 ApplicationManager.requestFocusApplication("unity8-dash");
429 }
430 }
432 Item {
433 id: overlay
434 z: 10
436 anchors.fill: parent
438 Panel {
439 id: panel
440 objectName: "panel"
441 anchors.fill: parent //because this draws indicator menus
442 indicators {
443 hides: [launcher]
444 available: tutorial.panelEnabled
445 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
446 && (!greeter || !greeter.hasLockedApp)
447 && !shell.waitingOnGreeter
448 && settings.enableIndicatorMenu
449 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
451 minimizedPanelHeight: units.gu(3)
452 expandedPanelHeight: units.gu(7)
454 indicatorsModel: Indicators.IndicatorsModel {
455 // tablet and phone both use the same profile
456 // FIXME: use just "phone" for greeter too, but first fix
457 // greeter app launching to either load the app inside the
458 // greeter or tell the session to load the app. This will
459 // involve taking the url-dispatcher dbus name and using
460 // SessionBroadcast to tell the session.
461 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
462 Component.onCompleted: load();
463 }
464 }
466 callHint {
467 greeterShown: greeter.shown
468 }
470 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
471 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
472 : false
473 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
474 || greeter.hasLockedApp
475 locked: greeter && greeter.active
476 }
478 Launcher {
479 id: launcher
480 objectName: "launcher"
482 anchors.top: parent.top
483 anchors.topMargin: inverted ? 0 : panel.panelHeight
484 anchors.bottom: parent.bottom
485 width: parent.width
486 dragAreaWidth: shell.edgeSize
487 available: tutorial.launcherEnabled
488 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
489 && !greeter.hasLockedApp
490 && !shell.waitingOnGreeter
491 && settings.enableLauncher
492 inverted: shell.usageScenario !== "desktop"
493 superPressed: physicalKeysMapper.superPressed
494 superTabPressed: physicalKeysMapper.superTabPressed
495 panelWidth: units.gu(settings.launcherWidth)
496 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
497 blurSource: greeter.shown ? greeter : stages
498 topPanelHeight: panel.panelHeight
499 drawerEnabled: !greeter.shown
501 onShowDashHome: showHome()
502 onLauncherApplicationSelected: {
503 greeter.notifyUserRequestedApp();
504 shell.activateApplication(appId);
505 }
506 onShownChanged: {
507 if (shown) {
508 panel.indicators.hide()
509 }
510 }
511 onFocusChanged: {
512 if (!focus) {
513 stage.focus = true;
514 }
515 }
517 GlobalShortcut {
518 shortcut: Qt.MetaModifier | Qt.Key_A
519 onTriggered: {
520 launcher.openDrawer(true);
521 }
522 }
523 GlobalShortcut {
524 shortcut: Qt.AltModifier | Qt.Key_F1
525 onTriggered: {
526 launcher.openForKeyboardNavigation();
527 }
528 }
529 GlobalShortcut {
530 shortcut: Qt.MetaModifier | Qt.Key_0
531 onTriggered: {
532 if (LauncherModel.get(9)) {
533 activateApplication(LauncherModel.get(9).appId);
534 }
535 }
536 }
537 Repeater {
538 model: 9
539 GlobalShortcut {
540 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
541 onTriggered: {
542 if (LauncherModel.get(index)) {
543 activateApplication(LauncherModel.get(index).appId);
544 }
545 }
546 }
547 }
548 }
550 KeyboardShortcutsOverlay {
551 objectName: "shortcutsOverlay"
552 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
553 && height < parent.height - padding - panel.panelHeight
554 anchors.centerIn: parent
555 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
556 anchors.verticalCenterOffset: panel.panelHeight/2
557 visible: opacity > 0
558 opacity: enabled ? 0.95 : 0
560 Behavior on opacity {
561 UbuntuNumberAnimation {}
562 }
563 }
565 Tutorial {
566 id: tutorial
567 objectName: "tutorial"
568 anchors.fill: parent
570 paused: callManager.hasCalls || !greeter || greeter.active ||
571 wizard.active
572 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
573 inputMethod.visible ||
574 (launcher.shown && !launcher.lockedVisible) ||
575 panel.indicators.shown || stage.rightEdgeDragProgress > 0
576 usageScenario: shell.usageScenario
577 lastInputTimestamp: inputFilter.lastInputTimestamp
578 launcher: launcher
579 panel: panel
580 stage: stage
581 }
583 Wizard {
584 id: wizard
585 objectName: "wizard"
586 anchors.fill: parent
587 deferred: shell.mode === "greeter"
589 function unlockWhenDoneWithWizard() {
590 if (!active) {
591 Connectivity.unlockAllModems();
592 }
593 }
595 Component.onCompleted: unlockWhenDoneWithWizard()
596 onActiveChanged: unlockWhenDoneWithWizard()
597 }
599 MouseArea { // modal notifications prevent interacting with other contents
600 anchors.fill: parent
601 visible: notifications.useModal
602 enabled: visible
603 }
605 Notifications {
606 id: notifications
608 model: NotificationBackend.Model
609 margin: units.gu(1)
610 hasMouse: shell.hasMouse
611 background: wallpaperResolver.cachedBackground
613 y: topmostIsFullscreen ? 0 : panel.panelHeight
614 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
616 states: [
617 State {
618 name: "narrow"
619 when: overlay.width <= units.gu(60)
620 AnchorChanges {
621 target: notifications
622 anchors.left: parent.left
623 anchors.right: parent.right
624 }
625 },
626 State {
627 name: "wide"
628 when: overlay.width > units.gu(60)
629 AnchorChanges {
630 target: notifications
631 anchors.left: undefined
632 anchors.right: parent.right
633 }
634 PropertyChanges { target: notifications; width: units.gu(38) }
635 }
636 ]
637 }
638 }
640 Dialogs {
641 id: dialogs
642 objectName: "dialogs"
643 anchors.fill: parent
644 z: overlay.z + 10
645 usageScenario: shell.usageScenario
646 onPowerOffClicked: {
647 shutdownFadeOutRectangle.enabled = true;
648 shutdownFadeOutRectangle.visible = true;
649 shutdownFadeOut.start();
650 }
651 }
653 Connections {
654 target: SessionBroadcast
655 onShowHome: if (shell.mode !== "greeter") showHome()
656 }
658 URLDispatcher {
659 id: urlDispatcher
660 objectName: "urlDispatcher"
661 active: shell.mode === "greeter"
662 onUrlRequested: shell.activateURL(url)
663 }
665 ItemGrabber {
666 id: itemGrabber
667 anchors.fill: parent
668 z: dialogs.z + 10
669 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
670 Connections {
671 target: stage
672 ignoreUnknownSignals: true
673 onItemSnapshotRequested: itemGrabber.capture(item)
674 }
675 }
677 Timer {
678 id: cursorHidingTimer
679 interval: 3000
680 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
681 onTriggered: cursor.opacity = 0;
682 }
684 Cursor {
685 id: cursor
686 objectName: "cursor"
687 visible: shell.hasMouse
688 z: itemGrabber.z + 1
689 topBoundaryOffset: panel.panelHeight
691 confiningItem: stage.itemConfiningMouseCursor
693 property bool mouseNeverMoved: true
694 Binding {
695 target: cursor; property: "x"; value: shell.width / 2
696 when: cursor.mouseNeverMoved && cursor.visible
697 }
698 Binding {
699 target: cursor; property: "y"; value: shell.height / 2
700 when: cursor.mouseNeverMoved && cursor.visible
701 }
703 height: units.gu(3)
705 readonly property var previewRectangle: stage.previewRectangle.target &&
706 stage.previewRectangle.target.dragging ?
707 stage.previewRectangle : null
709 onPushedLeftBoundary: {
710 if (buttons === Qt.NoButton) {
711 launcher.pushEdge(amount);
712 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
713 previewRectangle.maximizeLeft(amount);
714 }
715 }
717 onPushedRightBoundary: {
718 if (buttons === Qt.NoButton) {
719 stage.pushRightEdge(amount);
720 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
721 previewRectangle.maximizeRight(amount);
722 }
723 }
725 onPushedTopBoundary: {
726 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
727 previewRectangle.maximize(amount);
728 }
729 }
730 onPushedTopLeftCorner: {
731 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
732 previewRectangle.maximizeTopLeft(amount);
733 }
734 }
735 onPushedTopRightCorner: {
736 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
737 previewRectangle.maximizeTopRight(amount);
738 }
739 }
740 onPushedBottomLeftCorner: {
741 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
742 previewRectangle.maximizeBottomLeft(amount);
743 }
744 }
745 onPushedBottomRightCorner: {
746 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
747 previewRectangle.maximizeBottomRight(amount);
748 }
749 }
750 onPushStopped: {
751 if (previewRectangle) {
752 previewRectangle.stop();
753 }
754 }
756 onMouseMoved: {
757 mouseNeverMoved = false;
758 cursor.opacity = 1;
759 }
761 Behavior on opacity { UbuntuNumberAnimation {} }
762 }
764 // non-visual objects
765 KeymapSwitcher {}
766 BrightnessControl {}
768 Rectangle {
769 id: shutdownFadeOutRectangle
770 z: cursor.z + 1
771 enabled: false
772 visible: false
773 color: "black"
774 anchors.fill: parent
775 opacity: 0.0
776 NumberAnimation on opacity {
777 id: shutdownFadeOut
778 from: 0.0
779 to: 1.0
780 onStopped: {
781 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
782 DBusUnitySessionService.shutdown();
783 }
784 }
785 }
786 }