on 81c13fa help sign in
unity8/qml/Shell.qml
799 lines 28 kB history raw
1/*
2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
17import QtQuick 2.4
18import QtQuick.Window 2.2
19import AccountsService 0.1
20import Unity.Application 0.1
21import Ubuntu.Components 1.3
22import Ubuntu.Components.Popups 1.3
23import Ubuntu.Gestures 0.1
24import Ubuntu.Telephony 0.1 as Telephony
25import Unity.Connectivity 0.1
26import Unity.Launcher 0.1
27import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
28import GSettings 1.0
29import ImageCache 0.1
30import Utils 0.1
31import Powerd 0.1
32import SessionBroadcast 0.1
33import "Greeter"
34import "Launcher"
35import "Panel"
36import "Components"
37import "Notifications"
38import "Stage"
39import "Tutorial"
40import "Wizard"
41import 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 0.1
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();
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:///usr/share/backgrounds/warty-final-ubuntu.png"
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 TopLevelSurfaceList {
268 id: topLevelSurfaceList
269 objectName: "topLevelSurfaceList"
270 applicationsModel: ApplicationManager
271 }
273 Stage {
274 id: stage
275 objectName: "stage"
276 anchors.fill: parent
277 focus: true
279 dragAreaWidth: shell.edgeSize
280 background: wallpaperResolver.background
281 leftEdgeDragProgress: !greeter || greeter.locked || !tutorial.launcherLongSwipeEnabled ? 0 :
282 Math.max(0, (launcher.dragDistance * (stage.width - launcher.panelWidth) / stage.width) - launcher.panelWidth)
284 applicationManager: ApplicationManager
285 topLevelSurfaceList: topLevelSurfaceList
286 inputMethodRect: inputMethod.visibleRect
288 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
289 ? "phone"
290 : shell.usageScenario
292 mode: usageScenario == "phone" ? "staged"
293 : usageScenario == "tablet" ? "stagedWithSideStage"
294 : "windowed"
296 shellOrientation: shell.orientation
297 shellOrientationAngle: shell.orientationAngle
298 orientations: shell.orientations
299 nativeWidth: shell.nativeWidth
300 nativeHeight: shell.nativeHeight
302 interactive: (!greeter || !greeter.shown)
303 && panel.indicators.fullyClosed
304 && launcher.progress == 0
305 && !notifications.useModal
307 onInteractiveChanged: { if (interactive) { focus = true; } }
309 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
310 suspended: greeter.shown
311 keepDashRunning: launcher.shown || launcher.dashSwipe
312 altTabPressed: physicalKeysMapper.altTabPressed
313 oskEnabled: shell.oskEnabled
315 // TODO: This is not implemented yet in the new stage...
316 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
317 }
318 }
320 InputMethod {
321 id: inputMethod
322 objectName: "inputMethod"
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 ? 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 launcherOffset: launcher.progress
351 forcedUnlock: wizard.active || shell.mode === "full-shell"
352 background: wallpaperResolver.cachedBackground
353 hasCustomBackground: wallpaperResolver.hasCustomBackground
354 allowFingerprint: !dialogs.hasActiveDialog &&
355 !notifications.topmostIsFullscreen &&
356 !panel.indicators.shown
358 // avoid overlapping with Launcher's edge drag area
359 // FIXME: Fix TouchRegistry & friends and remove this workaround
360 // Issue involves launcher's DDA getting disabled on a long
361 // left-edge drag
362 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
364 onSessionStarted: {
365 launcher.hide();
366 }
368 onTease: {
369 if (!tutorial.running) {
370 launcher.tease();
371 }
372 }
374 onEmergencyCall: startLockedApp("dialer-app")
375 }
376 }
378 Timer {
379 // See powerConnection for why this is useful
380 id: showGreeterDelayed
381 interval: 1
382 onTriggered: {
383 greeter.forceShow();
384 }
385 }
387 Connections {
388 id: callConnection
389 target: callManager
391 onHasCallsChanged: {
392 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
393 // We just received an incoming call while locked. The
394 // indicator will have already launched dialer-app for us, but
395 // there is a race between "hasCalls" changing and the dialer
396 // starting up. So in case we lose that race, we'll start/
397 // focus the dialer ourselves here too. Even if the indicator
398 // didn't launch the dialer for some reason (or maybe a call
399 // started via some other means), if an active call is
400 // happening, we want to be in the dialer.
401 startLockedApp("dialer-app")
402 }
403 }
404 }
406 Connections {
407 id: powerConnection
408 target: Powerd
410 onStatusChanged: {
411 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
412 !callManager.hasCalls && !wizard.active) {
413 // We don't want to simply call greeter.showNow() here, because
414 // that will take too long. Qt will delay button event
415 // handling until the greeter is done loading and may think the
416 // user held down the power button the whole time, leading to a
417 // power dialog being shown. Instead, delay showing the
418 // greeter until we've finished handling the event. We could
419 // make the greeter load asynchronously instead, but that
420 // introduces a whole host of timing issues, especially with
421 // its animations. So this is simpler.
422 showGreeterDelayed.start();
423 }
424 }
425 }
427 function showHome() {
428 greeter.notifyUserRequestedApp();
430 if (shell.mode === "greeter") {
431 SessionBroadcast.requestHomeShown(AccountsService.user);
432 } else {
433 var animate = !LightDMService.greeter.active && !stages.shown;
434 dash.setCurrentScope(0, animate, false);
435 ApplicationManager.requestFocusApplication("unity8-dash");
436 }
437 }
439 function showDash() {
440 if (greeter.notifyShowingDashFromDrag()) {
441 launcher.fadeOut();
442 }
444 if (!greeter.locked && tutorial.launcherLongSwipeEnabled
445 && ApplicationManager.focusedApplicationId != "unity8-dash") {
446 ApplicationManager.requestFocusApplication("unity8-dash")
447 launcher.fadeOut();
448 }
449 }
451 Item {
452 id: overlay
453 z: 10
455 anchors.fill: parent
457 Panel {
458 id: panel
459 objectName: "panel"
460 anchors.fill: parent //because this draws indicator menus
461 indicators {
462 hides: [launcher]
463 available: tutorial.panelEnabled
464 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
465 && (!greeter || !greeter.hasLockedApp)
466 && !shell.waitingOnGreeter
467 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
469 minimizedPanelHeight: units.gu(3)
470 expandedPanelHeight: units.gu(7)
472 indicatorsModel: Indicators.IndicatorsModel {
473 // tablet and phone both use the same profile
474 // FIXME: use just "phone" for greeter too, but first fix
475 // greeter app launching to either load the app inside the
476 // greeter or tell the session to load the app. This will
477 // involve taking the url-dispatcher dbus name and using
478 // SessionBroadcast to tell the session.
479 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
480 Component.onCompleted: load();
481 }
482 }
484 callHint {
485 greeterShown: greeter.shown
486 }
488 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
489 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
490 : false
491 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
492 || greeter.hasLockedApp
493 locked: greeter && greeter.active
494 }
496 Launcher {
497 id: launcher
498 objectName: "launcher"
500 /*
501 * Since the Dash doesn't have the same controll over surfaces that the
502 * Shell does, it can't slowly move the scope out of the way, as the shell
503 * does with apps, and the dash is show instantly. This allows for some
504 * leeway and prevents accidental home swipes.
505 */
506 readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
507 readonly property bool dashSwipe: progress > offset
509 anchors.top: parent.top
510 anchors.topMargin: inverted ? 0 : panel.panelHeight
511 anchors.bottom: parent.bottom
512 width: parent.width
513 dragAreaWidth: shell.edgeSize
514 available: tutorial.launcherEnabled
515 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
516 && !greeter.hasLockedApp
517 && !shell.waitingOnGreeter
518 inverted: shell.usageScenario !== "desktop"
519 superPressed: physicalKeysMapper.superPressed
520 superTabPressed: physicalKeysMapper.superTabPressed
521 panelWidth: units.gu(settings.launcherWidth)
522 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
524 onShowDashHome: showHome()
525 onDash: showDash()
526 onDashSwipeChanged: {
527 if (dashSwipe) {
528 dash.setCurrentScope(0, false, true)
529 }
530 }
531 onLauncherApplicationSelected: {
532 greeter.notifyUserRequestedApp();
533 shell.activateApplication(appId);
534 }
535 onShownChanged: {
536 if (shown) {
537 panel.indicators.hide()
538 }
539 }
540 onFocusChanged: {
541 if (!focus) {
542 stage.focus = true;
543 }
544 }
546 GlobalShortcut {
547 shortcut: Qt.AltModifier | Qt.Key_F1
548 onTriggered: {
549 launcher.openForKeyboardNavigation();
550 }
551 }
552 GlobalShortcut {
553 shortcut: Qt.MetaModifier | Qt.Key_0
554 onTriggered: {
555 if (LauncherModel.get(9)) {
556 activateApplication(LauncherModel.get(9).appId);
557 }
558 }
559 }
560 Repeater {
561 model: 9
562 GlobalShortcut {
563 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
564 onTriggered: {
565 if (LauncherModel.get(index)) {
566 activateApplication(LauncherModel.get(index).appId);
567 }
568 }
569 }
570 }
571 }
573 KeyboardShortcutsOverlay {
574 objectName: "shortcutsOverlay"
575 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
576 && height < parent.height - padding - panel.panelHeight
577 anchors.centerIn: parent
578 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
579 anchors.verticalCenterOffset: panel.panelHeight/2
580 visible: opacity > 0
581 opacity: enabled ? 0.95 : 0
583 Behavior on opacity {
584 UbuntuNumberAnimation {}
585 }
586 }
588 Tutorial {
589 id: tutorial
590 objectName: "tutorial"
591 anchors.fill: parent
593 paused: callManager.hasCalls || !greeter || greeter.active ||
594 wizard.active
595 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
596 inputMethod.visible ||
597 (launcher.shown && !launcher.lockedVisible) ||
598 panel.indicators.shown || stage.rightEdgeDragProgress > 0
599 usageScenario: shell.usageScenario
600 lastInputTimestamp: inputFilter.lastInputTimestamp
601 launcher: launcher
602 panel: panel
603 stage: stage
604 }
606 Wizard {
607 id: wizard
608 objectName: "wizard"
609 anchors.fill: parent
610 deferred: shell.mode === "greeter"
612 function unlockWhenDoneWithWizard() {
613 if (!active) {
614 Connectivity.unlockAllModems();
615 }
616 }
618 Component.onCompleted: unlockWhenDoneWithWizard()
619 onActiveChanged: unlockWhenDoneWithWizard()
620 }
622 MouseArea { // modal notifications prevent interacting with other contents
623 anchors.fill: parent
624 visible: notifications.useModal
625 enabled: visible
626 }
628 Notifications {
629 id: notifications
631 model: NotificationBackend.Model
632 margin: units.gu(1)
633 hasMouse: shell.hasMouse
634 background: wallpaperResolver.cachedBackground
636 y: topmostIsFullscreen ? 0 : panel.panelHeight
637 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
639 states: [
640 State {
641 name: "narrow"
642 when: overlay.width <= units.gu(60)
643 AnchorChanges {
644 target: notifications
645 anchors.left: parent.left
646 anchors.right: parent.right
647 }
648 },
649 State {
650 name: "wide"
651 when: overlay.width > units.gu(60)
652 AnchorChanges {
653 target: notifications
654 anchors.left: undefined
655 anchors.right: parent.right
656 }
657 PropertyChanges { target: notifications; width: units.gu(38) }
658 }
659 ]
660 }
661 }
663 Dialogs {
664 id: dialogs
665 objectName: "dialogs"
666 anchors.fill: parent
667 z: overlay.z + 10
668 usageScenario: shell.usageScenario
669 onPowerOffClicked: {
670 shutdownFadeOutRectangle.enabled = true;
671 shutdownFadeOutRectangle.visible = true;
672 shutdownFadeOut.start();
673 }
674 }
676 Connections {
677 target: SessionBroadcast
678 onShowHome: if (shell.mode !== "greeter") showHome()
679 }
681 URLDispatcher {
682 id: urlDispatcher
683 objectName: "urlDispatcher"
684 active: shell.mode === "greeter"
685 onUrlRequested: shell.activateURL(url)
686 }
688 ItemGrabber {
689 id: itemGrabber
690 anchors.fill: parent
691 z: dialogs.z + 10
692 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
693 Connections {
694 target: stage
695 ignoreUnknownSignals: true
696 onItemSnapshotRequested: itemGrabber.capture(item)
697 }
698 }
700 Cursor {
701 id: cursor
702 visible: shell.hasMouse
703 z: itemGrabber.z + 1
704 topBoundaryOffset: panel.panelHeight
706 confiningItem: stage.itemConfiningMouseCursor
708 property bool mouseNeverMoved: true
709 Binding {
710 target: cursor; property: "x"; value: shell.width / 2
711 when: cursor.mouseNeverMoved && cursor.visible
712 }
713 Binding {
714 target: cursor; property: "y"; value: shell.height / 2
715 when: cursor.mouseNeverMoved && cursor.visible
716 }
718 height: units.gu(3)
720 readonly property var previewRectangle: stage.previewRectangle.target &&
721 stage.previewRectangle.target.dragging ?
722 stage.previewRectangle : null
724 onPushedLeftBoundary: {
725 if (buttons === Qt.NoButton) {
726 launcher.pushEdge(amount);
727 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
728 previewRectangle.maximizeLeft(amount);
729 }
730 }
732 onPushedRightBoundary: {
733 if (buttons === Qt.NoButton) {
734 stage.pushRightEdge(amount);
735 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
736 previewRectangle.maximizeRight(amount);
737 }
738 }
740 onPushedTopBoundary: {
741 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
742 previewRectangle.maximize(amount);
743 }
744 }
745 onPushedTopLeftCorner: {
746 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
747 previewRectangle.maximizeTopLeft(amount);
748 }
749 }
750 onPushedTopRightCorner: {
751 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
752 previewRectangle.maximizeTopRight(amount);
753 }
754 }
755 onPushedBottomLeftCorner: {
756 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
757 previewRectangle.maximizeBottomLeft(amount);
758 }
759 }
760 onPushedBottomRightCorner: {
761 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
762 previewRectangle.maximizeBottomRight(amount);
763 }
764 }
765 onPushStopped: {
766 if (previewRectangle) {
767 previewRectangle.stop();
768 }
769 }
771 onMouseMoved: {
772 mouseNeverMoved = false;
773 cursor.opacity = 1;
774 }
775 }
777 // non-visual object
778 KeymapSwitcher {}
780 Rectangle {
781 id: shutdownFadeOutRectangle
782 z: cursor.z + 1
783 enabled: false
784 visible: false
785 color: "black"
786 anchors.fill: parent
787 opacity: 0.0
788 NumberAnimation on opacity {
789 id: shutdownFadeOut
790 from: 0.0
791 to: 1.0
792 onStopped: {
793 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
794 DBusUnitySessionService.shutdown();
795 }
796 }
797 }
798 }