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