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