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