on 16fa310 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 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 property bool openDrawerAfterUnlock: false
342 Connections {
343 target: greeter
344 onActiveChanged: {
345 print("active changed", greeter.active, greeterLoader.openDrawerAfterUnlock)
346 if (!greeter.active && greeterLoader.openDrawerAfterUnlock) {
347 print("should open drawer")
348 launcher.openDrawer(false);
349 greeterLoader.openDrawerAfterUnlock = false;
350 }
351 }
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]
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 Timer {
390 // See powerConnection for why this is useful
391 id: showGreeterDelayed
392 interval: 1
393 onTriggered: {
394 greeter.forceShow();
395 }
396 }
398 Connections {
399 id: callConnection
400 target: callManager
402 onHasCallsChanged: {
403 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
404 // We just received an incoming call while locked. The
405 // indicator will have already launched dialer-app for us, but
406 // there is a race between "hasCalls" changing and the dialer
407 // starting up. So in case we lose that race, we'll start/
408 // focus the dialer ourselves here too. Even if the indicator
409 // didn't launch the dialer for some reason (or maybe a call
410 // started via some other means), if an active call is
411 // happening, we want to be in the dialer.
412 startLockedApp("dialer-app")
413 }
414 }
415 }
417 Connections {
418 id: powerConnection
419 target: Powerd
421 onStatusChanged: {
422 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
423 !callManager.hasCalls && !wizard.active) {
424 // We don't want to simply call greeter.showNow() here, because
425 // that will take too long. Qt will delay button event
426 // handling until the greeter is done loading and may think the
427 // user held down the power button the whole time, leading to a
428 // power dialog being shown. Instead, delay showing the
429 // greeter until we've finished handling the event. We could
430 // make the greeter load asynchronously instead, but that
431 // introduces a whole host of timing issues, especially with
432 // its animations. So this is simpler.
433 showGreeterDelayed.start();
434 }
435 }
436 }
438 function showHome() {
439 greeter.notifyUserRequestedApp();
441 if (shell.mode === "greeter") {
442 SessionBroadcast.requestHomeShown(AccountsService.user);
443 } else {
444 if (!greeter.active) {
445 launcher.openDrawer(false);
446 } else {
447 greeterLoader.openDrawerAfterUnlock = true;
448 }
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 }
498 Launcher {
499 id: launcher
500 objectName: "launcher"
502 anchors.top: parent.top
503 anchors.topMargin: inverted ? 0 : panel.panelHeight
504 anchors.bottom: parent.bottom
505 width: parent.width
506 dragAreaWidth: shell.edgeSize
507 available: tutorial.launcherEnabled
508 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
509 && !greeter.hasLockedApp
510 && !shell.waitingOnGreeter
511 && settings.enableLauncher
512 inverted: shell.usageScenario !== "desktop"
513 superPressed: physicalKeysMapper.superPressed
514 superTabPressed: physicalKeysMapper.superTabPressed
515 panelWidth: units.gu(settings.launcherWidth)
516 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
517 blurSource: greeter.shown ? greeter : stages
518 topPanelHeight: panel.panelHeight
519 drawerEnabled: !greeter.active
521 onShowDashHome: showHome()
522 onLauncherApplicationSelected: {
523 greeter.notifyUserRequestedApp();
524 shell.activateApplication(appId);
525 }
526 onShownChanged: {
527 if (shown) {
528 panel.indicators.hide()
529 }
530 }
531 onFocusChanged: {
532 if (!focus) {
533 stage.focus = true;
534 }
535 }
537 GlobalShortcut {
538 shortcut: Qt.MetaModifier | Qt.Key_A
539 onTriggered: {
540 launcher.openDrawer(true);
541 }
542 }
543 GlobalShortcut {
544 shortcut: Qt.AltModifier | Qt.Key_F1
545 onTriggered: {
546 launcher.openForKeyboardNavigation();
547 }
548 }
549 GlobalShortcut {
550 shortcut: Qt.MetaModifier | Qt.Key_0
551 onTriggered: {
552 if (LauncherModel.get(9)) {
553 activateApplication(LauncherModel.get(9).appId);
554 }
555 }
556 }
557 Repeater {
558 model: 9
559 GlobalShortcut {
560 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
561 onTriggered: {
562 if (LauncherModel.get(index)) {
563 activateApplication(LauncherModel.get(index).appId);
564 }
565 }
566 }
567 }
568 }
570 KeyboardShortcutsOverlay {
571 objectName: "shortcutsOverlay"
572 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
573 && height < parent.height - padding - panel.panelHeight
574 anchors.centerIn: parent
575 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
576 anchors.verticalCenterOffset: panel.panelHeight/2
577 visible: opacity > 0
578 opacity: enabled ? 0.95 : 0
580 Behavior on opacity {
581 UbuntuNumberAnimation {}
582 }
583 }
585 Tutorial {
586 id: tutorial
587 objectName: "tutorial"
588 anchors.fill: parent
590 paused: callManager.hasCalls || !greeter || greeter.active ||
591 wizard.active
592 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
593 inputMethod.visible ||
594 (launcher.shown && !launcher.lockedVisible) ||
595 panel.indicators.shown || stage.rightEdgeDragProgress > 0
596 usageScenario: shell.usageScenario
597 lastInputTimestamp: inputFilter.lastInputTimestamp
598 launcher: launcher
599 panel: panel
600 stage: stage
601 }
603 Wizard {
604 id: wizard
605 objectName: "wizard"
606 anchors.fill: parent
607 deferred: shell.mode === "greeter"
609 function unlockWhenDoneWithWizard() {
610 if (!active) {
611 Connectivity.unlockAllModems();
612 }
613 }
615 Component.onCompleted: unlockWhenDoneWithWizard()
616 onActiveChanged: unlockWhenDoneWithWizard()
617 }
619 MouseArea { // modal notifications prevent interacting with other contents
620 anchors.fill: parent
621 visible: notifications.useModal
622 enabled: visible
623 }
625 Notifications {
626 id: notifications
628 model: NotificationBackend.Model
629 margin: units.gu(1)
630 hasMouse: shell.hasMouse
631 background: wallpaperResolver.cachedBackground
633 y: topmostIsFullscreen ? 0 : panel.panelHeight
634 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
636 states: [
637 State {
638 name: "narrow"
639 when: overlay.width <= units.gu(60)
640 AnchorChanges {
641 target: notifications
642 anchors.left: parent.left
643 anchors.right: parent.right
644 }
645 },
646 State {
647 name: "wide"
648 when: overlay.width > units.gu(60)
649 AnchorChanges {
650 target: notifications
651 anchors.left: undefined
652 anchors.right: parent.right
653 }
654 PropertyChanges { target: notifications; width: units.gu(38) }
655 }
656 ]
657 }
658 }
660 Dialogs {
661 id: dialogs
662 objectName: "dialogs"
663 anchors.fill: parent
664 z: overlay.z + 10
665 usageScenario: shell.usageScenario
666 onPowerOffClicked: {
667 shutdownFadeOutRectangle.enabled = true;
668 shutdownFadeOutRectangle.visible = true;
669 shutdownFadeOut.start();
670 }
671 }
673 Connections {
674 target: SessionBroadcast
675 onShowHome: if (shell.mode !== "greeter") showHome()
676 }
678 URLDispatcher {
679 id: urlDispatcher
680 objectName: "urlDispatcher"
681 active: shell.mode === "greeter"
682 onUrlRequested: shell.activateURL(url)
683 }
685 ItemGrabber {
686 id: itemGrabber
687 anchors.fill: parent
688 z: dialogs.z + 10
689 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
690 Connections {
691 target: stage
692 ignoreUnknownSignals: true
693 onItemSnapshotRequested: itemGrabber.capture(item)
694 }
695 }
697 Timer {
698 id: cursorHidingTimer
699 interval: 3000
700 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
701 onTriggered: cursor.opacity = 0;
702 }
704 Cursor {
705 id: cursor
706 objectName: "cursor"
707 visible: shell.hasMouse
708 z: itemGrabber.z + 1
709 topBoundaryOffset: panel.panelHeight
711 confiningItem: stage.itemConfiningMouseCursor
713 property bool mouseNeverMoved: true
714 Binding {
715 target: cursor; property: "x"; value: shell.width / 2
716 when: cursor.mouseNeverMoved && cursor.visible
717 }
718 Binding {
719 target: cursor; property: "y"; value: shell.height / 2
720 when: cursor.mouseNeverMoved && cursor.visible
721 }
723 height: units.gu(3)
725 readonly property var previewRectangle: stage.previewRectangle.target &&
726 stage.previewRectangle.target.dragging ?
727 stage.previewRectangle : null
729 onPushedLeftBoundary: {
730 if (buttons === Qt.NoButton) {
731 launcher.pushEdge(amount);
732 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
733 previewRectangle.maximizeLeft(amount);
734 }
735 }
737 onPushedRightBoundary: {
738 if (buttons === Qt.NoButton) {
739 stage.pushRightEdge(amount);
740 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
741 previewRectangle.maximizeRight(amount);
742 }
743 }
745 onPushedTopBoundary: {
746 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
747 previewRectangle.maximize(amount);
748 }
749 }
750 onPushedTopLeftCorner: {
751 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
752 previewRectangle.maximizeTopLeft(amount);
753 }
754 }
755 onPushedTopRightCorner: {
756 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
757 previewRectangle.maximizeTopRight(amount);
758 }
759 }
760 onPushedBottomLeftCorner: {
761 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
762 previewRectangle.maximizeBottomLeft(amount);
763 }
764 }
765 onPushedBottomRightCorner: {
766 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
767 previewRectangle.maximizeBottomRight(amount);
768 }
769 }
770 onPushStopped: {
771 if (previewRectangle) {
772 previewRectangle.stop();
773 }
774 }
776 onMouseMoved: {
777 mouseNeverMoved = false;
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 }