on 389a48b help sign in
unity8/qml/Shell.qml
795 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 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 1.0
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(launcher.ignoreHideIfMouseOverLauncher);
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 }
253 }
255 GSettings {
256 id: settings
257 schema.id: "com.canonical.Unity8"
258 }
260 Item {
261 id: stages
262 objectName: "stages"
263 width: parent.width
264 height: parent.height
266 SurfaceManager {
267 id: surfaceMan
268 objectName: "surfaceManager"
269 }
270 TopLevelWindowModel {
271 id: topLevelSurfaceList
272 objectName: "topLevelSurfaceList"
273 applicationManager: ApplicationManager // it's a singleton
274 surfaceManager: surfaceMan
275 }
277 Stage {
278 id: stage
279 objectName: "stage"
280 anchors.fill: parent
281 focus: true
283 dragAreaWidth: shell.edgeSize
284 background: wallpaperResolver.background
286 applicationManager: ApplicationManager
287 topLevelSurfaceList: topLevelSurfaceList
288 inputMethodRect: inputMethod.visibleRect
290 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
291 ? "phone"
292 : shell.usageScenario
294 mode: usageScenario == "phone" ? "staged"
295 : usageScenario == "tablet" ? "stagedWithSideStage"
296 : "windowed"
298 shellOrientation: shell.orientation
299 shellOrientationAngle: shell.orientationAngle
300 orientations: shell.orientations
301 nativeWidth: shell.nativeWidth
302 nativeHeight: shell.nativeHeight
304 interactive: (!greeter || !greeter.shown)
305 && panel.indicators.fullyClosed
306 && !notifications.useModal
308 onInteractiveChanged: { if (interactive) { focus = true; } }
310 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
311 suspended: greeter.shown
312 altTabPressed: physicalKeysMapper.altTabPressed
313 oskEnabled: shell.oskEnabled
314 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
315 }
316 }
318 InputMethod {
319 id: inputMethod
320 objectName: "inputMethod"
321 surface: topLevelSurfaceList.inputMethodSurface
322 anchors {
323 fill: parent
324 topMargin: panel.panelHeight
325 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
326 }
327 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
328 }
330 Loader {
331 id: greeterLoader
332 objectName: "greeterLoader"
333 anchors.fill: parent
334 anchors.topMargin: panel.panelHeight
335 sourceComponent: {
336 if (shell.mode != "shell") {
337 if (screenWindow.primary) return integratedGreeter;
338 return secondaryGreeter;
339 }
340 return Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
341 }
342 onLoaded: {
343 item.objectName = "greeter"
344 }
345 }
347 Component {
348 id: integratedGreeter
349 Greeter {
351 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
352 hides: [launcher, panel.indicators]
353 tabletMode: shell.usageScenario != "phone"
354 forcedUnlock: wizard.active || shell.mode === "full-shell"
355 background: wallpaperResolver.cachedBackground
356 hasCustomBackground: wallpaperResolver.hasCustomBackground
357 allowFingerprint: !dialogs.hasActiveDialog &&
358 !notifications.topmostIsFullscreen &&
359 !panel.indicators.shown
361 // avoid overlapping with Launcher's edge drag area
362 // FIXME: Fix TouchRegistry & friends and remove this workaround
363 // Issue involves launcher's DDA getting disabled on a long
364 // left-edge drag
365 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
367 onSessionStarted: {
368 launcher.hide();
369 }
371 onTease: {
372 if (!tutorial.running) {
373 launcher.tease();
374 }
375 }
377 onEmergencyCall: startLockedApp("dialer-app")
378 }
379 }
381 Component {
382 id: secondaryGreeter
383 SecondaryGreeter {
384 hides: [launcher, panel.indicators]
385 }
386 }
388 Timer {
389 // See powerConnection for why this is useful
390 id: showGreeterDelayed
391 interval: 1
392 onTriggered: {
393 greeter.forceShow();
394 }
395 }
397 Connections {
398 id: callConnection
399 target: callManager
401 onHasCallsChanged: {
402 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
403 // We just received an incoming call while locked. The
404 // indicator will have already launched dialer-app for us, but
405 // there is a race between "hasCalls" changing and the dialer
406 // starting up. So in case we lose that race, we'll start/
407 // focus the dialer ourselves here too. Even if the indicator
408 // didn't launch the dialer for some reason (or maybe a call
409 // started via some other means), if an active call is
410 // happening, we want to be in the dialer.
411 startLockedApp("dialer-app")
412 }
413 }
414 }
416 Connections {
417 id: powerConnection
418 target: Powerd
420 onStatusChanged: {
421 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
422 !callManager.hasCalls && !wizard.active) {
423 // We don't want to simply call greeter.showNow() here, because
424 // that will take too long. Qt will delay button event
425 // handling until the greeter is done loading and may think the
426 // user held down the power button the whole time, leading to a
427 // power dialog being shown. Instead, delay showing the
428 // greeter until we've finished handling the event. We could
429 // make the greeter load asynchronously instead, but that
430 // introduces a whole host of timing issues, especially with
431 // its animations. So this is simpler.
432 showGreeterDelayed.start();
433 }
434 }
435 }
437 function showHome() {
438 greeter.notifyUserRequestedApp();
440 if (shell.mode === "greeter") {
441 SessionBroadcast.requestHomeShown(AccountsService.user);
442 } else {
443 var animate = !LightDMService.greeter.active && !stages.shown;
444 dash.setCurrentScope(0, animate, false);
445 ApplicationManager.requestFocusApplication("unity8-dash");
446 }
447 }
449 Item {
450 id: overlay
451 z: 10
453 anchors.fill: parent
455 Panel {
456 id: panel
457 objectName: "panel"
458 anchors.fill: parent //because this draws indicator menus
459 indicators {
460 hides: [launcher]
461 available: tutorial.panelEnabled
462 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
463 && (!greeter || !greeter.hasLockedApp)
464 && !shell.waitingOnGreeter
465 && settings.enableIndicatorMenu
466 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
468 minimizedPanelHeight: units.gu(3)
469 expandedPanelHeight: units.gu(7)
471 indicatorsModel: Indicators.IndicatorsModel {
472 // tablet and phone both use the same profile
473 // FIXME: use just "phone" for greeter too, but first fix
474 // greeter app launching to either load the app inside the
475 // greeter or tell the session to load the app. This will
476 // involve taking the url-dispatcher dbus name and using
477 // SessionBroadcast to tell the session.
478 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
479 Component.onCompleted: load();
480 }
481 }
483 callHint {
484 greeterShown: greeter.shown
485 }
487 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
488 ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
489 : false
490 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
491 || greeter.hasLockedApp
492 locked: greeter && greeter.active
493 }
495 Launcher {
496 id: launcher
497 objectName: "launcher"
499 anchors.top: parent.top
500 anchors.topMargin: inverted ? 0 : panel.panelHeight
501 anchors.bottom: parent.bottom
502 width: parent.width
503 dragAreaWidth: shell.edgeSize
504 available: tutorial.launcherEnabled
505 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
506 && !greeter.hasLockedApp
507 && !shell.waitingOnGreeter
508 && settings.enableLauncher
509 inverted: shell.usageScenario !== "desktop"
510 superPressed: physicalKeysMapper.superPressed
511 superTabPressed: physicalKeysMapper.superTabPressed
512 panelWidth: units.gu(settings.launcherWidth)
513 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
514 blurSource: greeter.shown ? greeter : stages
515 topPanelHeight: panel.panelHeight
516 drawerEnabled: !greeter.shown
518 onShowDashHome: showHome()
519 onLauncherApplicationSelected: {
520 greeter.notifyUserRequestedApp();
521 shell.activateApplication(appId);
522 }
523 onShownChanged: {
524 if (shown) {
525 panel.indicators.hide()
526 }
527 }
528 onFocusChanged: {
529 if (!focus) {
530 stage.focus = true;
531 }
532 }
534 GlobalShortcut {
535 shortcut: Qt.MetaModifier | Qt.Key_A
536 onTriggered: {
537 launcher.openDrawer(true);
538 }
539 }
540 GlobalShortcut {
541 shortcut: Qt.AltModifier | Qt.Key_F1
542 onTriggered: {
543 launcher.openForKeyboardNavigation();
544 }
545 }
546 GlobalShortcut {
547 shortcut: Qt.MetaModifier | Qt.Key_0
548 onTriggered: {
549 if (LauncherModel.get(9)) {
550 activateApplication(LauncherModel.get(9).appId);
551 }
552 }
553 }
554 Repeater {
555 model: 9
556 GlobalShortcut {
557 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
558 onTriggered: {
559 if (LauncherModel.get(index)) {
560 activateApplication(LauncherModel.get(index).appId);
561 }
562 }
563 }
564 }
565 }
567 KeyboardShortcutsOverlay {
568 objectName: "shortcutsOverlay"
569 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
570 && height < parent.height - padding - panel.panelHeight
571 anchors.centerIn: parent
572 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
573 anchors.verticalCenterOffset: panel.panelHeight/2
574 visible: opacity > 0
575 opacity: enabled ? 0.95 : 0
577 Behavior on opacity {
578 UbuntuNumberAnimation {}
579 }
580 }
582 Tutorial {
583 id: tutorial
584 objectName: "tutorial"
585 anchors.fill: parent
587 paused: callManager.hasCalls || !greeter || greeter.active ||
588 wizard.active
589 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
590 inputMethod.visible ||
591 (launcher.shown && !launcher.lockedVisible) ||
592 panel.indicators.shown || stage.rightEdgeDragProgress > 0
593 usageScenario: shell.usageScenario
594 lastInputTimestamp: inputFilter.lastInputTimestamp
595 launcher: launcher
596 panel: panel
597 stage: stage
598 }
600 Wizard {
601 id: wizard
602 objectName: "wizard"
603 anchors.fill: parent
604 deferred: shell.mode === "greeter"
606 function unlockWhenDoneWithWizard() {
607 if (!active) {
608 Connectivity.unlockAllModems();
609 }
610 }
612 Component.onCompleted: unlockWhenDoneWithWizard()
613 onActiveChanged: unlockWhenDoneWithWizard()
614 }
616 MouseArea { // modal notifications prevent interacting with other contents
617 anchors.fill: parent
618 visible: notifications.useModal
619 enabled: visible
620 }
622 Notifications {
623 id: notifications
625 model: NotificationBackend.Model
626 margin: units.gu(1)
627 hasMouse: shell.hasMouse
628 background: wallpaperResolver.cachedBackground
630 y: topmostIsFullscreen ? 0 : panel.panelHeight
631 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
633 states: [
634 State {
635 name: "narrow"
636 when: overlay.width <= units.gu(60)
637 AnchorChanges {
638 target: notifications
639 anchors.left: parent.left
640 anchors.right: parent.right
641 }
642 },
643 State {
644 name: "wide"
645 when: overlay.width > units.gu(60)
646 AnchorChanges {
647 target: notifications
648 anchors.left: undefined
649 anchors.right: parent.right
650 }
651 PropertyChanges { target: notifications; width: units.gu(38) }
652 }
653 ]
654 }
655 }
657 Dialogs {
658 id: dialogs
659 objectName: "dialogs"
660 anchors.fill: parent
661 z: overlay.z + 10
662 usageScenario: shell.usageScenario
663 onPowerOffClicked: {
664 shutdownFadeOutRectangle.enabled = true;
665 shutdownFadeOutRectangle.visible = true;
666 shutdownFadeOut.start();
667 }
668 }
670 Connections {
671 target: SessionBroadcast
672 onShowHome: if (shell.mode !== "greeter") showHome()
673 }
675 URLDispatcher {
676 id: urlDispatcher
677 objectName: "urlDispatcher"
678 active: shell.mode === "greeter"
679 onUrlRequested: shell.activateURL(url)
680 }
682 ItemGrabber {
683 id: itemGrabber
684 anchors.fill: parent
685 z: dialogs.z + 10
686 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
687 Connections {
688 target: stage
689 ignoreUnknownSignals: true
690 onItemSnapshotRequested: itemGrabber.capture(item)
691 }
692 }
694 Timer {
695 id: cursorHidingTimer
696 interval: 3000
697 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
698 onTriggered: cursor.opacity = 0;
699 }
701 Cursor {
702 id: cursor
703 visible: shell.hasMouse
704 z: itemGrabber.z + 1
705 opacity: 0
706 topBoundaryOffset: panel.panelHeight
708 confiningItem: stage.itemConfiningMouseCursor
710 height: units.gu(3)
712 readonly property var previewRectangle: stage.previewRectangle.target &&
713 stage.previewRectangle.target.dragging ?
714 stage.previewRectangle : null
716 onPushedLeftBoundary: {
717 if (buttons === Qt.NoButton) {
718 launcher.pushEdge(amount);
719 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
720 previewRectangle.maximizeLeft(amount);
721 }
722 }
724 onPushedRightBoundary: {
725 if (buttons === Qt.NoButton) {
726 stage.pushRightEdge(amount);
727 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
728 previewRectangle.maximizeRight(amount);
729 }
730 }
732 onPushedTopBoundary: {
733 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
734 previewRectangle.maximize(amount);
735 }
736 }
737 onPushedTopLeftCorner: {
738 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
739 previewRectangle.maximizeTopLeft(amount);
740 }
741 }
742 onPushedTopRightCorner: {
743 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
744 previewRectangle.maximizeTopRight(amount);
745 }
746 }
747 onPushedBottomLeftCorner: {
748 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
749 previewRectangle.maximizeBottomLeft(amount);
750 }
751 }
752 onPushedBottomRightCorner: {
753 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
754 previewRectangle.maximizeBottomRight(amount);
755 }
756 }
757 onPushStopped: {
758 if (previewRectangle) {
759 previewRectangle.stop();
760 }
761 }
763 onMouseMoved: {
764 cursor.opacity = 1;
765 }
767 Behavior on opacity { UbuntuNumberAnimation {} }
768 }
770 // non-visual objects
771 KeymapSwitcher {
772 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
773 }
774 BrightnessControl {}
776 Rectangle {
777 id: shutdownFadeOutRectangle
778 z: cursor.z + 1
779 enabled: false
780 visible: false
781 color: "black"
782 anchors.fill: parent
783 opacity: 0.0
784 NumberAnimation on opacity {
785 id: shutdownFadeOut
786 from: 0.0
787 to: 1.0
788 onStopped: {
789 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
790 DBusUnitySessionService.shutdown();
791 }
792 }
793 }
794 }